Skip to main content

oak-domain-investments

The investments domain plugin for OakQuant's Timber substrate — discoverable, four-layer, and now the platform's first complete business domain.

License: Apache-2.0 Python

What it is

Investing is a discoverable plugin. The package advertises itself through entry points and registers into the host's shared registries (Timber's model/service registries, grove's app, acorn's tool registry) — never by editing the core libraries. Install it and investing appears across the platform; leave it out and nothing breaks. See the oak-domain-plugins skill for the wider plugin shape.

What started as the watchlist migration has grown into a complete, goal-linked investing capability: a rich watchlist, accumulation plans, a value projection, a growth scheduler that rolls into goal attainment, holdings linkage to brokerage truth, and an unlinked personal watchlist. The whole program (Phases 1 / 1.5 / 2 / 2.5 / 3) is in production.

The four-layer shape

A single installed package plugs into four OakQuant layers, one entry point per group. Each host discovers only its own group and imports only that entry point's module, so the timber layer never pulls in grove/FastAPI or acorn.

Layer Entry-point group Class Contributes
timber timber.domains InvestmentsDomain WatchlistItem + WatchlistValueSnapshot models and the watchlist / projection / growth / holdings-link services, registered at init (Step 8.5) into Timber's model/service registries
grove grove.domains InvestmentsGroveDomain The watchlist HTTP router, mounted at /api/v3/watchlist on grove's app at startup
acorn acorn.domains InvestmentsAcornDomain The get_watchlist, get_goal_investments, and add_to_watchlist agent tools, registered into acorn's Oracle at startup
sky (host registry) sky-investments The goal watchlist, projection modal, and home watchlist gadget widgets (registered into sky's widget registry; lives in the sky repo)

Grove and acorn delegate to the same services the timber layer registers (reached through Timber's service_registry.domain("investments")), so there is one implementation behind every surface. Grove core additionally wires the daily growth scheduler and the allocate-to-goal endpoint, both of which consume these domain services.

Models (oak_domain_investments/models/)

File-per-table, each a common.models.base.Base model with TimestampMixin, so Step 9 create_all creates the table.

  • WatchlistItem (watchlist_items) — the rich, goal-linked watchlist row (migrated field-for-field from grove's live table so existing rows keep working):
    • id (UUID str PK), user_id, symbol, company_name, sector, industry, notes
    • goal_id (FK → investment_goals.id, nullable since Phase 1.5 — a NULL goal_id is an unlinked personal item)
    • price snapshot (added_price, current_price, price_change_pct, price_updated_at)
    • AI fields (ai_recommendation, ai_confidence, ai_analysis, ai_analyzed_at)
    • advisor fields (advisor_recommendation, advisor_notes, advisor_reviewed_at, advisor_id)
    • targets (target_buy_price, target_sell_price, stop_loss_price)
    • accumulation plan (planned_amount, planned_quantity, accumulation_frequency ∈ one_time/weekly/biweekly/monthly/quarterly, accumulation_amount)
    • status (default watching), source, removed_reason
  • WatchlistValueSnapshot (watchlist_value_snapshots) — a per-item, per-day value point written by the growth scheduler: watchlist_item_id, goal_id, user_id, snapshot_date, accrued_periods, contributed, shares, value. Feeds the projection's "actual" series.

Exported as DOMAIN_MODELS and registered before create_all.

Services (oak_domain_investments/services/)

Each takes its DB from the injected ctx.db; market enrichment uses Timber's stock_data_service on a best-effort basis. Per-tier limits (e.g. max_watchlist) are a grove-billing concern enforced by the grove router, not here.

  • watchlist_service.py (WatchlistService) — add_to_watchlist (goal-linked, idempotent), add_unlinked (user-scoped, NULL goal_id), get_watchlist(goal_id), get_user_watchlist(user_id) (across all goals plus unlinked), update_watchlist_item, add_advisor_review.
  • projection.py — pure functions: build_projection (value vs contributed over a horizon, profit = the gap) and reproject (re-fit now from actual snapshots plus the latest model).
  • growth.py (GrowthService) — run_growth: forward paper-accrual of accumulation plans, real valuation of linked positions, idempotent per item+date, and it rolls the latest per-item value into InvestmentGoal.current_value and goal_progress_pct.
  • holdings_link.py (HoldingsLinkService) — keeps brokerage truth and goal allocation distinct: resolve_holding_id, link_position (idempotent, never re-points), reconcile_user (backfill), goal_investments(goal_id), allocations_for_holding(holding_id).
  • info_service.py (InvestmentsInfoService) — reports migration status.

Grove HTTP surface (grove_plugin.py)

InvestmentsGroveDomain mounts a router (re-asserting grove's X-Grove-API-Key boundary and goal-ownership, IDOR → 404) at /api/v3/watchlist:

  • GET {prefix} — the goal's watchlist
  • POST {prefix} — add a symbol (applies the max_watchlist tier gate)
  • GET {prefix}/all — every item the user tracks, across all goals plus unlinked
  • POST {prefix}/unlinked — add an unlinked personal item
  • GET {prefix}/{item_id}/projection — the value projection (tolerates a null goal_id)
  • PUT {prefix}/{item_id} — update an item (including the accumulation plan)
  • POST {prefix}/{item_id}/advisor-review — record an advisor review

Acorn agent tools (acorn_plugin.py)

InvestmentsAcornDomain contributes three Oracle tools (read tools at RiskTier.READ, the add tool at RiskTier.WRITE), each a synchronous, user-scoped read/write through Timber's db_service (no HTTP round-trip), returning markdown:

  • get_watchlist(user_id) — the user's watchlist
  • get_goal_investments(user_id, goal_id) — the positions and watchlist behind a goal
  • add_to_watchlist(user_id, symbol) — add an unlinked symbol

How discovery works

Each host loads only its own entry-point group:

  • timber.domainsoak_domain_investments:InvestmentsDomain
  • grove.domainsoak_domain_investments.grove_plugin:InvestmentsGroveDomain
  • acorn.domainsoak_domain_investments.acorn_plugin:InvestmentsAcornDomain

Once the package is installed, discovery is automatic via these entry points. For local development against checkouts, each host also honors an env-var fallback (e.g. TIMBER_DOMAIN_PLUGINS=oak_domain_investments:InvestmentsDomain) so the plugin loads without a reinstall.

Migration status

InvestmentsInfoService reports what has migrated into this package. Capabilities built on the migrated watchlist (plans, projection, growth, holdings linkage) live here too. Investment logic that has not migrated yet still lives in timber core (common/services/data_fetcher/, data_processor/portfolio_metrics.py, vendors/plaid_service.py) and moves over one capability at a time.

Install

pip install oak-domain-investments

Requires Python 3.11+ and timber-common>=0.6.16. The import package is oak_domain_investments.

Develop / test against a sibling timber checkout:

# from the repo root, with ../timber on the path
PYTHONPATH=../timber:. TIMBER_DOMAIN_PLUGINS=oak_domain_investments:InvestmentsDomain \
  python3 -m pytest tests/ -q

License

Apache-2.0. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oak_domain_investments-0.41.2.tar.gz (437.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oak_domain_investments-0.41.2-py3-none-any.whl (529.4 kB view details)

Uploaded Python 3

File details

Details for the file oak_domain_investments-0.41.2.tar.gz.

File metadata

  • Download URL: oak_domain_investments-0.41.2.tar.gz
  • Upload date:
  • Size: 437.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for oak_domain_investments-0.41.2.tar.gz
Algorithm Hash digest
SHA256 cd4dd4f3caf7ab32cc012a95d40f3cb9200359971a7462cca2505e8f3eabdc97
MD5 9c21c75229f07a8a1bcd08cf66fefe36
BLAKE2b-256 d75444638f3afe6002bb423046cab10ea86d2d11ebed373510655992f30f4863

See more details on using hashes here.

Provenance

The following attestation bundles were made for oak_domain_investments-0.41.2.tar.gz:

Publisher: release.yml on oakquant-ai/oak-domain-investments

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oak_domain_investments-0.41.2-py3-none-any.whl.

File metadata

File hashes

Hashes for oak_domain_investments-0.41.2-py3-none-any.whl
Algorithm Hash digest
SHA256 3db26f8c3e551fb174f205b78549f75146a2eb717f88c3c10b2fb9de2df04bd8
MD5 bfb17b3b8bcf71956b4faf1ca57942b4
BLAKE2b-256 7f954bd61b0a1f58c199cd7f2538c4290526a0e32e79795ab0c5695a33c6c58d

See more details on using hashes here.

Provenance

The following attestation bundles were made for oak_domain_investments-0.41.2-py3-none-any.whl:

Publisher: release.yml on oakquant-ai/oak-domain-investments

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.56.0

2 files

0.55.0

2 files

0.54.0

2 files

0.53.0

2 files

0.52.1

2 files

0.52.0

2 files

0.51.1

2 files

0.51.0

2 files

0.50.0

2 files

0.49.0

2 files

0.48.0

2 files

0.47.0

2 files

0.46.0

2 files

0.45.0

2 files

0.44.0

2 files

0.43.0

2 files

0.42.3

2 files

0.42.2

2 files

0.42.1

2 files

0.42.0

2 files

0.41.4

2 files

0.41.3

2 files

This release

0.41.2 This release

2 files

0.41.1

2 files

0.41.0

2 files

0.40.0

2 files

0.39.0

2 files

0.38.0

2 files

0.37.0

2 files

0.36.0

2 files

0.35.0

2 files

0.34.0

2 files

0.33.0

2 files

0.32.0

2 files

0.31.0

2 files

0.30.0

2 files

0.29.0

2 files

0.28.0

2 files

0.27.0

2 files

0.26.0

2 files

0.25.0

2 files

0.24.0

2 files

0.23.0

2 files

0.22.0

2 files

0.21.0

2 files

0.20.0

2 files

0.19.0

2 files

0.18.0

2 files

0.17.0

2 files

0.16.0

2 files

0.15.0

2 files

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page