Skip to main content

oak-domain-investments

The investments domain for the OakQuant platform — a complete, goal-linked investing product delivered as a single discoverable plugin, spanning all four OakQuant layers with zero edits to any core library.

License: Apache-2.0 Python Status: Live in production PyPI

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 FastAPI app, grove's Celery worker + beat, grove's config store, and acorn's Oracle tool registry — never by editing the core libraries. Install it and a full investing capability appears across the platform; leave it out and nothing breaks. See the oak-domain-plugins skill for the wider plugin shape.

This is OakQuant's largest and most mature domain. It began as a single watchlist migration and is now the whole B2C investing product: financial-goal creation with AI vision and personalized inspiration imagery, brokerage linkage via Plaid, portfolio and goal dashboards, a self-learning single-stock research flow, an index×sector sentiment heatmap, a behavioral Financial DNA assessment, an advisor review queue with SLAs, and a continually-learning prediction engine. Grove core owns zero investing code or config — everything investing lives here. The bulk of this program 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 entry-point group and imports only that entry point's module, so the timber layer never pulls in grove/FastAPI or acorn, and the acorn layer never pulls in the SQLAlchemy model stack.

Layer Entry-point group Object Contributes
timber timber.domains oak_domain_investments:InvestmentsDomain ORM models (Python + bundled YAML built by the ModelFactory at Step 8.5) and ~40 registered services (watchlist, goals, portfolio, Plaid, stock/market data, forecasting, prediction brain, research, Financial DNA, advisor review, …)
grove (web) grove.domains oak_domain_investments.grove_plugin:InvestmentsGroveDomain Ten FastAPI routers (watchlist, goals, portfolio, Plaid, advisor, prediction track-record, semantic search, stock lookup, calendar-generate) + UI options-helpers
grove (worker) grove.workers oak_domain_investments.worker_plugin:InvestmentsWorkerDomain The adaptive-prediction Celery tasks + their beat schedule (predict / predict-universe / observe / mature)
grove (config) grove.feature_packages oak_domain_investments:feature_packages_dir Nine bundled Tier-1 config packages (workflows, tasks, HITL screens, pages, nav, home widgets, schedulers) seeded into the grove DB
grove (secrets) grove.secret_specs oak_domain_investments:secret_specs The market-data provider + Plaid secret specs grove fetches from ranger at boot (all optional; a missing key degrades a feature, never blocks startup)
acorn acorn.domains oak_domain_investments.acorn_plugin:InvestmentsAcornDomain The Oracle "investments" assistant profile (financial persona) + ~20 gated agent tools
sky (host widget registry) sky-investments The goal/portfolio/watchlist/heatmap/oak-tree 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 service_registry.domain("investments")), so there is one implementation behind every surface. The grove routers mount outside grove's auth middleware and so re-assert the X-Grove-API-Key service boundary; caller identity and enterprise scope arrive as canopy-forwarded X-User-* headers. Every investing surface is capability-gated: only an investments-provisioned tenant can reach it.

Capabilities

Goals & vision (in production)

Config-driven financial-goal creation on the shared HITL engine: goal + timeline intake → deterministic budget/feasibility → AI-written vision → a generated inspiration-image gallery → a persisted InvestmentGoal with calendar milestones. GoalService owns the lifecycle (create/update/progress/soft-delete, assisted rewrite, in-screen budget/vision/image recalculation). The premium goal dashboard adds time-adjusted progress and a summary roll-up.

Personalized goal imagery (in production)

GoalImageService composes grove's generic image service with cambium to generate inspiration imagery for a goal. When a user has uploaded a self photo (a consent-gated PersonReference), goal images preserve their likeness via gemini-2.5-flash-image. Image regeneration is generative (token cost), so it runs only on explicit user confirmation.

Portfolio & holdings (in production)

Manual holdings CRUD, buy/sell transactions, and brokerage→goal allocation (PortfolioService + InvestmentService). The portfolio dashboard (premium) returns holdings, per-lot positions, watchlist, and a value timeline backed by a daily PortfolioValueSnapshot scheduler. Brokerage truth (a PortfolioHolding) and goal allocation (a goal-linked Position carrying portfolio_holding_id) are kept strictly distinct — allocating never mutates the holding.

Brokerage linkage via Plaid (in production, Plaid PRODUCTION)

PlaidBridge owns link-token creation, public-token exchange, linked-account listing, idempotent holdings sync, and unlink — composing the configured Plaid singleton with timber's DB and grove's generic tier guard. The onboard_investments HITL flow lets a user connect a brokerage (auto-syncing real holdings) or add a position manually, launchable from home or from chat.

Watchlist & accumulation (in production)

The rich, goal-linked WatchlistItem (AI + advisor recommendations, price snapshots, buy/sell/stop targets, and an accumulation plan). Items may be goal-linked or unlinked (a personal watchlist, goal_id nullable). The home watchlist gadget aggregates every tracked symbol across all goals plus the unlinked list. GrowthService runs a forward paper-accrual of accumulation plans and rolls the latest per-item value into goal attainment; projection.py builds the value-vs-contributed projection (profit = the gap) and can re-fit from actual snapshots.

Single-stock research (the oak tree) (in production)

stock_research_service runs a config-driven, cost-disciplined flow — explore → fundamentals → technicals → growth → news sentiment → forecast → summarize — rendered as an interactive oak tree with category drill-down. Five of six steps are deterministic/statistical; only the summary may touch an LLM, on a cambium-distilled payload. The price forecast uses an ensemble whose signal weights are learned from realized outcomes and records each forecast into a feedback ledger so accuracy improves over time.

Index & sector research (the heatmap) (in production)

SectorResearchService explores indices across geographies/types, scores sentiment + volatility across the ~11 GICS sectors, surfaces top companies and movers/shakers, and renders an index×sector sentiment heatmap with per-cell drill-down into single-stock research. Sentiment is batched-LLM per index with a lexical fallback.

Macro — current-events deep dive (built)

macro/ answers the question single-stock research cannot: not "how is this company doing" but "what is happening to it". It reads world-scoped news (GDELT — keyless, so it works with no vendor keys at all — plus AlphaVantage topic news and Finnhub general news), collapses syndicated copies of one story into one event, and buckets articles into a curated 18-theme macro taxonomy (data/macro/themes.yaml): tariffs, sanctions, armed conflict, drought and crop failure, critical-minerals shortages, political uncertainty, central-bank policy, supply-chain disruption, the AI capital cycle, and more.

Each theme is then scored on four named terms rather than one opaque number — attention (how loud, relative to the rest of the board), conviction (how one-directional the coverage is), market confirmation (do the instruments the theme says should move actually move that way, scaled by each instrument's own volatility), and breadth (how much of the sector map it touches). That third term is what separates this from a news summary: a theme with loud coverage and flat proxies is reported as loud but unpriced, which is a different and far more useful object than "important".

The deep dive then crosses the affected sectors against the user's own holdings and watchlist, writes the desk note (one supervised model call per theme, over facts it did not choose), and produces dated, falsifiable things to watch — the taxonomy's own release/meeting calendar plus one-sigma trigger levels computed from each proxy's current price.

Three refusals are built in, each of them a bug this codebase has already paid for once. A missing market term is redistributed, never imputed as zero (zero says "the market disagrees"; missing says "we could not look"). An empty board renders its cause, never a blank panel that reads as a quiet week. And no watch item ever claims an official release date — the taxonomy knows CPI is monthly, it does not know the next print is the 11th, so every cadence item carries a review date, labelled as one.

Degrades rather than blanks: with no vendor keys the board is thinner but real, and with no model key the briefing is still written from the curated taxonomy.

Adaptive prediction engine (in production)

A shared meta-learning "brain" (adapt/: estimate, cascade, cluster, ledger, backtest, track-record) that predicts short-horizon moves for a resolved universe (watchlist ∪ default), observes realized outcomes, and matures/scores them — closing an autonomous continual-learning loop on grove's beat. The cascade blends multiple strategies (reversal → news-cluster → deep) over one shared model. A premium+ track-record endpoint exposes the honest, outcome-scored accuracy.

Evaluation harness — adapt evaluate (in production, CI-gated)

adapt/evaluate/: the instrument the prediction engine is judged on. Overlap-aware effective sample size, a circular block bootstrap over dates, purged and embargoed walk-forward, a five-entry baseline board (random sign, always-up, buy-and-hold, persistence, 12-1 momentum), a pre-registered trials ledger and deflated-Sharpe correction — behind one command, wired into CI as GATE 0.

⭐ It exists because the engine was unmeasured, not bad: 5-day bets placed daily overlap by four days, so 47.84% over 1,392 bets is 0.83σ from chance, not 1.61σ. Every rate on this package's surfaces now carries its denominator, its effective sample size and an interval. See docs/evaluation-harness.md.

Financial DNA (in production)

FdnaService computes a behavioral investor profile — persona (e.g. architect / pragmatist / guardian / explorer / delegator), risk score, key dimensions, and deterministic allocation / strategy / guardrail recommendations — from a 16-screen / 6-phase HITL assessment scored on both answers and answer timing. Reachable as a launchable flow and via the acorn assistant.

Advisor portfolio review (built, enterprise + admin)

AdvisorReviewService detects holdings drift from the Financial DNA target allocation, drafts an AI rebalancing recommendation, and routes it to the advisor_review work queue with a real SLA; an advisor claims / reviews / approves or rejects it. Includes SLA-monitor and drift-scan schedulers and an admin SLA override.

Market data & analytics (in production)

A self-contained market-data stack migrated out of timber core: multi-provider fetchers (yfinance keyless default, with Alpha Vantage / Finnhub / Polygon when keyed); data_processor (returns, risk, technical indicators, portfolio metrics); analytics (fundamental ratios, growth, valuation); sentiment (lexical + optional LLM); forecasting (signal→model→ensemble); and stock_search over a bundled symbol catalog that powers the type-ahead and HITL stock pickers.

Two fetch services are registered and they are not interchangeable by accident:

registry name service reads through the cache?
data_fetcher stock_data_service no — every call hits the vendor
stock_cache stock_cache_service yesStockDataSnapshot, per-metric TTL (info 24h · news 1h · price 1h · financials 168h), past price ranges immutable

stock_cache exposes the same four fetch_* methods as data_fetcher (provider_order included), so it substitutes for it with no call-site change; the get_* names remain as aliases. The research flow (stock_research_service) uses stock_cache. Other callers — watchlist, alerts, growth, portfolio, sector research, the worker's prediction loop — still hold data_fetcher and refetch on every call.

Cross-cutting integrations

The domain also contributes, via the registry, its own calendar event generators (earnings / ex-dividend from holdings, goal target-dates + milestones) and calendar taxonomygenerate_goal_events takes an optional goal_id= so the goal write path can run the SAME generator for one goal and put a new (or restored, or re-dated) goal on the calendar inside the request, rather than waiting up to 24h for daily_calendar_refresh; hydration handlers for the home goals/portfolio widgets; Rustle context-access rules for its context-conversations; monitor session specs so grove's stuck-workflow monitor can name investing session tables generically; usage queries so the billing guard can count investing tier limits (max_goals, max_holdings, max_watchlist, max_goal_workflow_runs, max_index_research_runs) without grove naming any investing model; and the nav items + home widgets grove composes into its generic shell.

Grove HTTP surface

InvestmentsGroveDomain mounts ten routers on grove's app (each behind the X-Grove-API-Key boundary and the investments capability gate):

Prefix Purpose
/api/v3/watchlist goal + unlinked watchlist, add/update, projection, advisor review
/api/v3/goals goal list/detail, dashboard, assisted rewrite, in-screen recalc (budget/vision/images), reopen-save, soft-delete
/api/v3/portfolio home snapshot, premium dashboard, manual holdings CRUD, transactions, brokerage→goal allocation, market data
/api/v3/external/plaid link-token, token-exchange, linked accounts, holdings sync, unlink, health
/api/v3/advisor advisor inbox, review detail, claim/approve/reject, admin SLA override
/api/v3/adapt premium+ prediction track-record
/api/v3/investments semantic + hybrid vector search over cached financial data
/api/v3/ui · /api/human-tasks stock symbol search / by-symbol lookup (type-ahead + HITL pickers)
/api/v3/calendar on-demand single-user calendar-event generation

Acorn agent surface

InvestmentsAcornDomain registers the "investments" assistant profile (the Oracle financial persona, greeting, suggestions, and investments-tagged tool resolution) plus ~20 gated tools, each user-scoped through the forwarded principal and returning markdown. They cover the user's own data (get_user_goals, get_portfolio_holdings, get_goal_investments, get_watchlist, get_financial_dna_profile), watchlist writes (add_to_watchlist), analysis (analyze_stock_info, analyze_stock_news, analyze_stock_price, analyze_portfolio, market_outlook, advice_on_topic, explain_concept, investment_philosophy, quote_of_the_day), semantic search (vector_search, search_stocks, search_indices, hybrid_search), and research_stock, which drives the grove oak-tree flow and hands back the interactive A2UI screen. All financial knowledge lives in this package — acorn core keeps only the generic @tool / grove-passthrough / profile mechanisms.

Feature packages (bundled grove config)

The grove.feature_packages entry point points grove at feature_packages/, ten portable, self-describing config bundles that grove's sync_configs discovers and seeds into the grove DB — so the domain's workflows/tasks/HITL screens/pages/nav travel with the package:

goal_workflow · onboard_investments · financial_dna · stock_research · index_research · portfolio · advisor_portfolio_review · investment_growth · macro · investments (base decisioning tables).

Models

Two Python models (WatchlistItem, WatchlistValueSnapshot) plus ~30 tables built from bundled YAML by the timber ModelFactory at Step 8.5 — byte-identical to grove's prior definitions, so the live tables map field-for-field and create_all is a no-op for them. Notable tables: investment_goals, goal_images, goal_milestones, positions, portfolio_holdings, portfolio_transactions, portfolio_value_snapshots, plaid_item, financial_account, the read-through cache tables (cached_stock_data, cached_market_indices, stock_data_snapshots, …), the workflow-session tables (stock_research_sessions, index_research_sessions, financial_dna_sessions, create_financial_goal_sessions, onboard_investments_sessions, advisor_review_sessions, macro_sessions), and the prediction feedback tables (predictions, prediction_outcomes, prediction_scores, news_clusters).

How it plugs in

Each host loads only its own entry-point group; discovery is automatic once the package is installed:

[project.entry-points."timber.domains"]
investments = "oak_domain_investments:InvestmentsDomain"

[project.entry-points."grove.domains"]
investments = "oak_domain_investments.grove_plugin:InvestmentsGroveDomain"

[project.entry-points."grove.workers"]
investments = "oak_domain_investments.worker_plugin:InvestmentsWorkerDomain"

[project.entry-points."grove.feature_packages"]
investments = "oak_domain_investments:feature_packages_dir"

[project.entry-points."grove.secret_specs"]
investments = "oak_domain_investments:secret_specs"

[project.entry-points."acorn.domains"]
investments = "oak_domain_investments.acorn_plugin:InvestmentsAcornDomain"

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.

Install

pip install oak-domain-investments

Requires Python 3.13+ (both first-party dependencies declare ^3.13; the package advertised 3.11 until 0.53.0 and could never install there). Depends on timber-common>=0.6.16, plaid-python, and cambium-ai. 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

Secrets & configuration

All provider secrets are optional and fetched from ranger by grove's bootstrap via the grove.secret_specs entry point — a missing key degrades a feature rather than blocking startup:

  • Market dataALPHA_VANTAGE_API_KEY, FINNHUB_API_KEY, POLYGON_API_KEY (absent → the fetchers fall back to the keyless yfinance provider).
  • PlaidPLAID_CLIENT_ID, PLAID_SECRET, PLAID_SECRET_PRODUCTION, PLAID_ENVIRONMENT, PLAID_RECOVERY (absent → brokerage linking is simply unavailable).

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.55.0.tar.gz (906.5 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.55.0-py3-none-any.whl (810.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: oak_domain_investments-0.55.0.tar.gz
  • Upload date:
  • Size: 906.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for oak_domain_investments-0.55.0.tar.gz
Algorithm Hash digest
SHA256 70f465da1dd6f7caa83eab92ac8c14d1ad22fda949d54b655c693b3b9e746060
MD5 94ebd8cb3c04cc2063d31507e2e82134
BLAKE2b-256 2c3baab5261eb0e2f1410c0e4b0928555ce19578f76c12364b38156f2cac8de3

See more details on using hashes here.

Provenance

The following attestation bundles were made for oak_domain_investments-0.55.0.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.55.0-py3-none-any.whl.

File metadata

File hashes

Hashes for oak_domain_investments-0.55.0-py3-none-any.whl
Algorithm Hash digest
SHA256 88406acaeed451e829f66b05d471bad12499a5379f6970daf05728aa4ed2c78e
MD5 53c34e397d7a91060fdaabf021d808f1
BLAKE2b-256 44d90f16fa97a04dc4460693c7fead626858db6f6bc4abd0e4a7207e74332dc0

See more details on using hashes here.

Provenance

The following attestation bundles were made for oak_domain_investments-0.55.0-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

This release

0.55.0 This release

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

0.41.2

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