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
@@ -21,15 +21,25 @@ consumers.
### Requirement: Ticker resolution for NYSE and Nasdaq
The system SHALL accept a ticker symbol, validate that it resolves to an
NYSE- or Nasdaq-listed equity, and return a normalized company profile
(name, exchange, sector/industry, currency, shares outstanding).
NYSE- or Nasdaq-listed security, and return a normalized profile that includes an
**instrument type** discriminant (`equity` or `etf`) plus name, exchange, currency,
and — for equities — sector/industry and shares outstanding. v1 fully supports the
`equity` type; ETF-specific data is a later change, but resolution SHALL still
classify ETFs so an unsupported-type path is explicit rather than silently wrong.
#### Scenario: Valid listed ticker resolves
- **WHEN** a user submits a ticker listed on NYSE or Nasdaq (e.g. `DE`)
- **THEN** the system returns a company profile with name, exchange, and sector
#### Scenario: Valid listed equity resolves
- **WHEN** a user submits an equity ticker listed on NYSE or Nasdaq (e.g. `DE`)
- **THEN** the system returns a profile with instrument type `equity`, name,
exchange, and sector
#### Scenario: ETF is classified, not misread as an equity
- **WHEN** a user submits an ETF ticker (e.g. `SPY`)
- **THEN** the system returns a profile with instrument type `etf`
- **AND** the app reports ETF evaluation as not yet supported rather than running
the equity branch against it
#### Scenario: Unknown or unlisted ticker is rejected
- **WHEN** a user submits a symbol that does not resolve to an NYSE/Nasdaq equity
- **WHEN** a user submits a symbol that does not resolve to an NYSE/Nasdaq security
- **THEN** the system returns a not-found result with a clear message
- **AND** no evaluation is attempted