phase-8g: add sqllite.

This commit is contained in:
Sai Kiran Vella
2026-06-05 23:34:25 -04:00
parent 447a86b46e
commit 83116baa3c
20 changed files with 2514 additions and 239 deletions
+18 -18
View File
@@ -70,10 +70,15 @@ server/
PortfolioAdvisor.ts ← cross-references holdings with screener signals → hold/sell/add advice
index.ts ← barrel re-export (import services from here, not individual files)
repositories/ ← data persistence only (JSON file read/write)
MarketCallRepository.ts ← persists market thesis entries to market-calls.json.
CRUD: list/get/create/delete.
PortfolioRepository.ts ← read/write portfolio.json. Methods: read, upsert, remove.
repositories/ ← data persistence (SQLite via better-sqlite3)
MarketCallRepository.ts ← market_calls table. CRUD: list/get/create/delete.
Accepts injected Db instance.
PortfolioRepository.ts ← holdings table. Methods: exists, read, upsert, remove.
Accepts injected Db instance.
db/
index.ts ← createDb(path?) → opens/creates market-screener.db, runs DDL,
migrates legacy portfolio.json + market-calls.json on first boot.
clients/ ← external API connectors, one class per third-party system
YahooFinanceClient.ts ← wraps yahoo-finance2 v3, retry + backoff. Methods: fetchSummary,
@@ -161,8 +166,9 @@ ui/ ← SvelteKit dashboard (lives inside this repo, not a
portfolio/ ← portfolio advice view
safe-buys/ ← filtered strong-buy view
market-calls.json ← persisted market thesis calls (written by MarketCallRepository)
portfolio.json ← user's holdings: ticker, shares, costBasis, source, type
market-screener.db ← SQLite database (created on first boot). Contains holdings + market_calls tables.
Legacy portfolio.json / market-calls.json are auto-migrated on first boot
and renamed to *.json.migrated.
.env ← SIMPLEFIN_ACCESS_URL or SIMPLEFIN_SETUP_TOKEN, ANTHROPIC_API_KEY, API_KEY (optional — enables Bearer auth on all routes)
```
@@ -434,19 +440,13 @@ new ScreenerEngine({ logger: noopLogger })
---
## portfolio.json Format
## Holdings Format
```json
{
"holdings": [
{ "ticker": "AAPL", "shares": 10, "costBasis": 150.00, "source": "Robinhood", "type": "stock" },
{ "ticker": "VOO", "shares": 8, "costBasis": 380.00, "source": "Vanguard", "type": "etf" },
{ "ticker": "BTC-USD", "shares": 0.25, "costBasis": 45000, "source": "Coinbase", "type": "crypto" }
]
}
```
Holdings are stored in the `holdings` table in `market-screener.db`. To seed initial data, add holdings via the Portfolio UI or by inserting into the database directly.
`type` values: `stock`, `etf`, `crypto`. Crypto is priced via Yahoo (BTC-USD style) but not fundamentally scored.
`type` values: `stock`, `etf`, `bond`, `crypto`. Crypto is priced via Yahoo (BTC-USD style) but not fundamentally scored.
If you have an existing `portfolio.json`, it will be auto-migrated to SQLite on first boot and renamed to `portfolio.json.migrated`.
---
@@ -474,7 +474,7 @@ Test output uses the built-in `spec` reporter.
**Key unit:** `ytm` in `Bond.metrics` is stored as a percentage (e.g. `6.5` = 6.5%). `BondScorer._sanitize` divides by 100 before spread calculation.
**Coverage gaps (known):**
- `MarketCallRepository.ts`no tests; CRUD against `market-calls.json` is untested
- `MarketCallRepository.ts`covered by `tests/MarketCallRepository.test.ts` using in-memory SQLite
- `LLMAnalyst.test.js` — tests a local copy of the fence-stripping regex rather than importing from source; will silently drift if the regex changes
- API controllers (`server/controllers/`) — no integration tests; covered implicitly by manual testing only
- Expert scoring features (analyst, DCF, 52W) — not yet covered in `StockScorer.test.js`