Investments domain plugin for the OakQuant timber substrate (watchlist capability migrated from grove core; more to follow).
Project description
oak-domain-investments
The investments domain plugin for OakQuant's Timber substrate — discoverable, 3-layer, migrating in one capability at a time.
What it is
Investment logic is moving out of OakQuant's grove/timber core and into this package, deliberately one capability at a time so each move is small, isolated, and low-risk. This README documents the current state and is expected to grow as more capabilities land.
The package is a discoverable plugin: it 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. See the oak-domain-plugins skill for the wider plugin shape.
First migrated capability: the watchlist (capability #1). It exercises all three layers of the plugin system end to end.
The 3-layer shape
A single installed package plugs into three 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 ORM model + WatchlistService, registered at init (Step 8.5) into Timber's model/service registries |
| grove | grove.domains |
InvestmentsGroveDomain |
The watchlist HTTP router, mounted on grove's app at startup |
| acorn | acorn.domains |
InvestmentsAcornDomain |
A get_watchlist agent tool, registered into acorn's Oracle at startup |
The grove and acorn plugins delegate to the same WatchlistService that the timber layer registers (reached through Timber's service_registry.domain("investments")), so there is one implementation behind all three surfaces.
Capability #1: the watchlist
Symbols a user tracks without necessarily owning them.
Model — WatchlistItem (oak_domain_investments/models.py)
A common.models.base.Base model (TimestampMixin) for table watchlist_items, migrated field-for-field from grove's config/models/watchlist_models.yaml so existing rows keep working:
id(UUID str PK),user_id(FK →users.id),symbol(uppercase ticker),name,notesalert_above/alert_below(price alert thresholds)status—active|removed(the service soft-deletes via status)- Unique index on
(user_id, symbol); index on(user_id, status)
Exported as DOMAIN_MODELS and registered before Step 9 create_all, so the table exists.
Service — WatchlistService (oak_domain_investments/services/watchlist_service.py)
Takes its DB from the injected ctx.db; enriches with Timber's stock_data_service on a best-effort basis. The per-tier max_watchlist limit is not here — it's a grove-billing concern enforced by the grove router.
get_watchlist(user_id, include_prices=True)— active items, with optional current prices +alert_triggeredstateadd_to_watchlist(user_id, symbol, name=None, notes=None)— idempotent on active(user, symbol); resolves company name best-effortremove_from_watchlist(item_id)— soft-delete (status="removed")update_alerts(item_id, alert_above=None, alert_below=None)— set/clear alerts (a non-positive value clears)
Grove HTTP surface (oak_domain_investments/grove_plugin.py)
InvestmentsGroveDomain mounts a router that delegates to WatchlistService and applies the best-effort max_watchlist tier gate on add:
GET {prefix}— list the user's watchlistPOST {prefix}— add a symbolDELETE {prefix}/{item_id}— remove (soft-delete)PUT {prefix}/{item_id}/alerts— set/clear price alerts
Phase 1 mounts at /investments/watchlist (a non-conflicting path while grove core's /api/v3/watchlist still exists). Phase 2 — after the core route is removed — repoints this to the canonical /api/v3/watchlist.
Acorn agent tool (oak_domain_investments/acorn_plugin.py)
InvestmentsAcornDomain contributes a get_watchlist(user_id) tool (RiskTier.READ) to acorn's Oracle. It reads WatchlistItem directly through Timber's db_service (a synchronous, user-scoped read, no HTTP round-trip) and returns the active watchlist as markdown.
Migration status
InvestmentsInfoService (services/info_service.py) reports what has migrated: MIGRATED_CAPABILITIES = ["watchlist"]. This list grows as portfolio, thresholds, and other capabilities move in. Investment logic that hasn't migrated yet still lives in timber core (common/services/data_fetcher/, data_processor/portfolio_metrics.py, vendors/plaid_service.py).
How discovery works
Each host loads only its own entry-point group:
timber.domains→oak_domain_investments:InvestmentsDomaingrove.domains→oak_domain_investments.grove_plugin:InvestmentsGroveDomainacorn.domains→oak_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.
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.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oak_domain_investments-0.2.0.tar.gz.
File metadata
- Download URL: oak_domain_investments-0.2.0.tar.gz
- Upload date:
- Size: 17.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3b4fe679c30712d46bb6c492dddf8117a26f6e272e4f132e893adada2944d2e
|
|
| MD5 |
4ea97b334b5fddd59021cf77b7c2208d
|
|
| BLAKE2b-256 |
4091b9422b1ff8728257faf318d16cdf3f5dce36a95832de30012aba923367a7
|
Provenance
The following attestation bundles were made for oak_domain_investments-0.2.0.tar.gz:
Publisher:
release.yml on oakquant-ai/oak-domain-investments
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oak_domain_investments-0.2.0.tar.gz -
Subject digest:
a3b4fe679c30712d46bb6c492dddf8117a26f6e272e4f132e893adada2944d2e - Sigstore transparency entry: 1986209562
- Sigstore integration time:
-
Permalink:
oakquant-ai/oak-domain-investments@a3df3a1984b816e3161d5836a9cfd00dbfe301a6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/oakquant-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a3df3a1984b816e3161d5836a9cfd00dbfe301a6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file oak_domain_investments-0.2.0-py3-none-any.whl.
File metadata
- Download URL: oak_domain_investments-0.2.0-py3-none-any.whl
- Upload date:
- Size: 18.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c8e77232482583b3c7a15c9113478a4f8b5abf90c474ba1f6256ce5b68fbf47c
|
|
| MD5 |
e92b13480b66c26fff56df75d1ed5a8c
|
|
| BLAKE2b-256 |
8247a177a654602508949b2940232d6184f98b5144cf4f1a7b5faacf51181c32
|
Provenance
The following attestation bundles were made for oak_domain_investments-0.2.0-py3-none-any.whl:
Publisher:
release.yml on oakquant-ai/oak-domain-investments
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oak_domain_investments-0.2.0-py3-none-any.whl -
Subject digest:
c8e77232482583b3c7a15c9113478a4f8b5abf90c474ba1f6256ce5b68fbf47c - Sigstore transparency entry: 1986209698
- Sigstore integration time:
-
Permalink:
oakquant-ai/oak-domain-investments@a3df3a1984b816e3161d5836a9cfd00dbfe301a6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/oakquant-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a3df3a1984b816e3161d5836a9cfd00dbfe301a6 -
Trigger Event:
push
-
Statement type: