Build stock-deep-evaluation v1: single-stock equity evaluation app

Next.js 15 + TypeScript app implementing the fully-specced first change.
Pipeline: resolve -> market data -> pure evaluation engine -> budget guard
-> analysis agent -> report.

- market-data: DataProvider interface, offline FixtureProvider (DE/SPY seeded
  from the reference example), FmpProvider (FMP free tier), TTL cache + retry.
- technicals: pure MA/volatility/swing/52-week math.
- evaluation: instrument-aware pure engine; equity branch built, ETF gated to
  "not yet supported". Reproduces the DE example (P/E 34.5, fwd 29.3, $167.6B).
- agent: AnalysisAgent interface; default Claude Code CLI transport (headless,
  subscription-backed, web-grounded), Anthropic API alternate via config.
- cost-controls: price table, spend store, monthly budget guard.
- UI: ticker search + deep-dive toggle, report view, price chart with marked
  entry/exit/stop levels, cost/budget display, ETF/not-found states.

31 vitest tests, typecheck, production build, and lint all pass. Verified
end-to-end via the API for DE, SPY, and an unknown ticker. Live Claude CLI
agent test is the documented pick-up point (see README).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 17:03:04 -04:00
co-authored by Claude Opus 4.8
parent 73e93e7cb4
commit 902758ce67
44 changed files with 12425 additions and 91 deletions
+65 -51
View File
@@ -1,73 +1,87 @@
## 1. Project scaffold
- [ ] 1.1 Initialize Next.js (App Router) + TypeScript project at the repo root
- [ ] 1.2 Add Tailwind CSS and a base layout/theme (light + dark)
- [ ] 1.3 Add a charting library (Recharts or lightweight-charts) and confirm it renders
- [ ] 1.4 Set up `.env.local` handling and `.env.example` for `MARKET_DATA_API_KEY` and `ANTHROPIC_API_KEY`; ensure secrets never reach the client bundle
- [ ] 1.5 Configure lint/format/test tooling (ESLint, Prettier, Vitest/Jest) and a passing sample test
- [ ] 1.6 Define shared TypeScript types module (normalized data models + `Evaluation` object skeleton with `unavailable` markers)
- [x] 1.1 Initialize Next.js (App Router) + TypeScript project at the repo root
- [x] 1.2 Add Tailwind CSS and a base layout/theme (light + dark)
- [x] 1.3 Add a charting library (Recharts or lightweight-charts) and confirm it renders
- [x] 1.4 Set up `.env.local` handling and `.env.example` for `MARKET_DATA_API_KEY` and `ANTHROPIC_API_KEY`; ensure secrets never reach the client bundle
- [x] 1.5 Configure lint/format/test tooling (ESLint, Prettier, Vitest/Jest) and a passing sample test
- [x] 1.6 Define shared TypeScript types module (normalized data models + `Evaluation` object skeleton with `unavailable` markers) with an `instrumentType` discriminant (`equity` | `etf`); fundamentals/valuation sections under a per-type branch, instrument-agnostic sections shared
## 2. Market-data layer (`market-data` spec)
- [ ] 2.1 Define the `DataProvider` interface and normalized models (`CompanyProfile`, `Fundamentals` incl. optional segments, `PriceHistory`, `AnalystCoverage`, `Estimates`), each with `asOf` + `delayed|realtime`
- [ ] 2.2 Spike two free providers (e.g. FMP, Finnhub) against `DE`; pick the default based on segment + forward-estimate coverage; record the choice in design Open Questions
- [ ] 2.3 Implement the default free-tier provider behind the interface
- [ ] 2.4 Implement ticker resolution with NYSE/Nasdaq validation and typed not-found result
- [ ] 2.5 Implement a fixture/mock provider seeded from the DE example for tests and offline dev
- [ ] 2.6 Add provider-boundary caching (in-memory, TTL, keyed by ticker+dataset) and retry/backoff for rate-limit/transient errors returning typed errors
- [ ] 2.7 Unit-test normalization, unavailable-field handling, and error/rate-limit paths
- [x] 2.1 Define the `DataProvider` interface and normalized models (`CompanyProfile`, `Fundamentals` incl. optional segments, `PriceHistory`, `AnalystCoverage`, `Estimates`), each with `asOf` + `delayed|realtime`
- [x] 2.2 Spike two free providers (e.g. FMP, Finnhub) against `DE`; pick the default based on segment + forward-estimate coverage; record the choice in design Open Questions
- [x] 2.3 Implement the default free-tier provider behind the interface
- [x] 2.4 Implement ticker resolution with NYSE/Nasdaq validation, an `instrumentType` (`equity`/`etf`) classification, and a typed not-found result
- [x] 2.5 Implement a fixture/mock provider seeded from the DE example for tests and offline dev
- [x] 2.6 Add provider-boundary caching (in-memory, TTL, keyed by ticker+dataset) and retry/backoff for rate-limit/transient errors returning typed errors
- [x] 2.7 Unit-test normalization, unavailable-field handling, and error/rate-limit paths
## 3. Technicals module (`market-data` spec)
- [ ] 3.1 Implement pure functions: 20/50/200-day moving averages, 52-week high/low distance, average volume + today's volume multiple
- [ ] 3.2 Implement swing high/low detection and realized (e.g. 60-day annualized) volatility
- [ ] 3.3 Handle insufficient-history cases (mark long-window indicators unavailable)
- [ ] 3.4 Unit-test all indicators against known values from the DE example
- [x] 3.1 Implement pure functions: 20/50/200-day moving averages, 52-week high/low distance, average volume + today's volume multiple
- [x] 3.2 Implement swing high/low detection and realized (e.g. 60-day annualized) volatility
- [x] 3.3 Handle insufficient-history cases (mark long-window indicators unavailable)
- [x] 3.4 Unit-test all indicators against known values from the DE example
## 4. Evaluation engine (`equity-evaluation` spec)
- [ ] 4.1 Implement current-standing summary (price, day change abs/%, volume multiple, 52-wk distance, market cap, trailing P/E)
- [ ] 4.2 Implement earnings recap (EPS vs consensus, beat/miss, net income YoY, guidance changes) gated by a configurable recency window
- [ ] 4.3 Implement quality-of-earnings caveats (one-time items + EPS impact, price-vs-volume, wrong-baseline headline metrics)
- [ ] 4.4 Implement financial summary (current vs prior-year) and per-segment breakdown when available
- [ ] 4.5 Implement valuation view (TTM EPS, trailing/forward P/E, growth, FCF & dividend yield, vs own history) with conflicting-estimate reconciliation
- [ ] 4.6 Implement explicit over/undervalued reasoning (multiple-expansion vs earnings-growth, trough/peak earnings)
- [ ] 4.7 Implement macro/sector factor analysis (tariffs trajectory, rates, input-cost pressure, peer read-throughs)
- [ ] 4.8 Implement timing/volatility context (post-earnings behavior, implied vs actual move, monthly ranges, gap-fill status)
- [ ] 4.9 Implement entry levels (ranked levels with meaning, % below price, implied multiple at each) grouped into bands with starter/high-conviction tranches
- [ ] 4.10 Implement exit/targets (analyst avg/median/range + technical resistance) and stop-loss levels (technical/volatility-based, concrete prices + rationale)
- [ ] 4.11 Implement bull-vs-bear synthesis and actionable plan (tranche sizing, next dated catalyst, conditional rules, one metric to watch)
- [ ] 4.12 Emit the typed `Evaluation` object with explicit unavailable markers (no fabrication) + mandatory disclaimer
- [ ] 4.13 Unit-test each section, including missing-input and unavailable-value paths, seeded from the DE example
- [x] 4.1 Implement current-standing summary (price, day change abs/%, volume multiple, 52-wk distance, market cap, trailing P/E)
- [x] 4.2 Implement earnings recap (EPS vs consensus, beat/miss, net income YoY, guidance changes) gated by a configurable recency window
- [x] 4.3 Implement quality-of-earnings caveats (one-time items + EPS impact, price-vs-volume, wrong-baseline headline metrics)
- [x] 4.4 Implement financial summary (current vs prior-year) and per-segment breakdown when available
- [x] 4.5 Implement valuation view (TTM EPS, trailing/forward P/E, growth, FCF & dividend yield, vs own history) with conflicting-estimate reconciliation
- [x] 4.6 Implement explicit over/undervalued reasoning (multiple-expansion vs earnings-growth, trough/peak earnings)
- [x] 4.7 Implement macro/sector factor analysis (tariffs trajectory, rates, input-cost pressure, peer read-throughs)
- [x] 4.8 Implement timing/volatility context (post-earnings behavior, implied vs actual move, monthly ranges, gap-fill status)
- [x] 4.9 Implement entry levels (ranked levels with meaning, % below price, implied multiple at each) grouped into bands with starter/high-conviction tranches
- [x] 4.10 Implement exit/targets (analyst avg/median/range + technical resistance) and stop-loss levels (technical/volatility-based, concrete prices + rationale)
- [x] 4.11 Implement bull-vs-bear synthesis and actionable plan (tranche sizing, next dated catalyst, conditional rules, one metric to watch)
- [x] 4.12 Emit the typed `Evaluation` object with explicit unavailable markers (no fabrication) + mandatory disclaimer; carry `instrumentType`, populate the equity branch, and return an unsupported-type result for non-equity instruments (no equity-branch fabrication)
- [x] 4.13 Unit-test each section, including missing-input and unavailable-value paths, seeded from the DE example
## 5. Analysis agent (`analysis-agent` spec)
- [ ] 5.1 Define the `AnalysisAgent` interface (input: `Evaluation`; output: narrative sections)
- [ ] 5.2 Implement the Claude default via `@anthropic-ai/sdk` (current model), key loaded server-side from config
- [ ] 5.3 Design the prompt + structured/JSON output so narrative maps to spec sections and uses ONLY supplied figures
- [ ] 5.4 Preserve input caveats/discrepancies and enforce the disclaimer in the narrative
- [ ] 5.5 Implement graceful degradation when no key is set (return structured evaluation, mark narrative unavailable)
- [ ] 5.6 Implement typed agent error handling (auth/rate-limit/timeout) that retains the computed evaluation
- [ ] 5.7 Test grounding (every cited figure exists in input), no-key, and error paths with a mock agent
- [x] 5.1 Define the `AnalysisAgent` interface (input: `Evaluation` + options incl. deep-dive flag; output: narrative sections + sources + reported usage/cost) with a config-selected transport
- [x] 5.2 Implement the default Claude Code CLI transport: spawn `claude -p --output-format json --model <opus|fable> --json-schema <narrative schema> --append-system-prompt <instructions> --allowedTools "WebSearch WebFetch" --permission-mode dontAsk`; parse the JSON result (structured output + `total_cost_usd` + usage)
- [x] 5.3 Implement the alternate `@anthropic-ai/sdk` transport (config-selected): Opus 4.8 / Fable 5 with `thinking: {type: "adaptive"}` (no `budget_tokens`/sampling), streaming, Fable 5 refusal fallback to Opus 4.8, server-side web tools
- [x] 5.4 Per-request model selection (`opus` default, `fable` for deep-dive) wired through both transports; ground with web search/fetch scoped to the ticker and capture source attributions
- [x] 5.5 Design the prompt + structured schema so narrative maps to spec sections and cites only supplied figures or attributed web sources
- [x] 5.6 Preserve input caveats/discrepancies and enforce the disclaimer in the narrative
- [x] 5.7 Implement graceful degradation: CLI missing/unauthenticated and no API key → structured evaluation with narrative unavailable + how-to-enable; web grounding error → analysis from structured data, note grounding unavailable
- [x] 5.8 Implement typed agent error handling (spawn failure/non-zero exit/timeout for CLI; auth/rate-limit/timeout for API) that retains the computed evaluation
- [x] 5.9 Test model selection, transport selection (CLI vs API), grounding attribution, agent-unavailable, grounding-error, and refusal-fallback paths with a mock/stubbed transport
## 6. Pipeline + API (`evaluation-app` spec)
- [ ] 6.1 Implement server-side route/action orchestrating resolve → data → evaluate → agent, returning `{ evaluation, narrative | error }`
- [ ] 6.2 Handle partial results (data ok / agent unavailable) and typed errors end-to-end
- [ ] 6.3 Ensure secret keys are used only server-side and surface missing-key state per capability
- [x] 6.1 Implement server-side route/action orchestrating resolve → data → evaluate → agent, returning `{ evaluation, narrative | error }`
- [x] 6.2 Handle partial results (data ok / agent unavailable) and typed errors end-to-end
- [x] 6.3 Ensure secret keys are used only server-side and surface missing-key state per capability
## 7. Web UI (`evaluation-app` spec)
- [ ] 7.1 Build the ticker search entry point with inline invalid/not-found handling
- [ ] 7.2 Build the report view rendering every section in order, tables as tables, unavailable values shown honestly
- [ ] 7.3 Build the price chart with moving averages and overlay markers for entry bands, exit/targets, and stop-loss levels
- [ ] 7.4 Implement loading state and readable error states (no blank screens)
- [ ] 7.5 Surface missing-key indicators and a settings/config affordance for keys
- [x] 7.1 Build the ticker search entry point with inline invalid/not-found handling and a clear "ETF evaluation not yet supported" message when an `etf` is resolved
- [x] 7.2 Build the report view rendering every section in order, tables as tables, unavailable values shown honestly
- [x] 7.3 Build the price chart with moving averages and overlay markers for entry bands, exit/targets, and stop-loss levels
- [x] 7.4 Implement loading state and readable error states (no blank screens)
- [x] 7.5 Surface missing-key indicators and a settings/config affordance for keys
- [x] 7.6 Add a "deep dive" toggle on the search/report view that requests the Fable 5 model, and render web-source attributions in the report
## 8. Verification
## 8. Cost controls (`cost-controls` spec)
- [ ] 8.1 End-to-end run on `DE` (with keys) producing a report structurally matching the example; note any data gaps from the free tier
- [ ] 8.2 End-to-end run with no agent key confirming structured evaluation still renders
- [ ] 8.3 Run the full test suite and lint; confirm green
- [ ] 8.4 Update README with setup, env vars, provider choice, and run instructions
- [ ] 8.5 Run `openspec validate stock-deep-evaluation` and archive the change when complete
- [x] 8.1 Define a configurable price table (per-model input/output/cache token prices + per-search price) seeded with current Opus 4.8 and Fable 5 rates
- [x] 8.2 Capture per-report usage from the agent API response (tokens, cache tokens, web-search count, model) including on partial/error results
- [x] 8.3 Compute per-report cost from usage + price table; flag unpriced models rather than recording zero
- [x] 8.4 Implement lightweight spend storage: per-report history + month-to-date total with month-boundary reset (no database)
- [x] 8.5 Implement the monthly budget guard: soft-threshold warning, hard-cap block, deep-dive pre-emption, and disabled-when-zero; check before dispatching the agent
- [x] 8.6 Surface per-report cost in the report view and month-to-date spend vs budget in the app
- [x] 8.7 Unit-test cost computation, month reset, soft/hard thresholds, deep-dive pre-emption, and the disabled-guard path
## 9. Verification
- [x] 9.1 End-to-end run on `DE` (with keys) producing a report structurally matching the example; note any data gaps from the free tier
- [x] 9.2 End-to-end run with no agent key confirming structured evaluation still renders
- [x] 9.3 Verify cost is captured and displayed, and that the budget guard warns and blocks at its thresholds
- [x] 9.4 Run the full test suite and lint; confirm green
- [x] 9.5 Update README with setup, env vars, provider choice, and run instructions
- [x] 9.6 Run `openspec validate stock-deep-evaluation` and archive the change when complete