Files
equitysearch/openspec/changes/stock-deep-evaluation/specs/market-data/spec.md
T
paulandClaude Opus 4.8 902758ce67 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>
2026-08-21 17:03:04 -04:00

5.3 KiB

ADDED Requirements

Requirement: Provider-agnostic data interface

The system SHALL define a DataProvider interface that all market-data access goes through. Evaluation and UI code MUST depend only on this interface and its normalized data models, never on a concrete provider SDK or response shape. The active provider SHALL be selectable via configuration without code changes in consumers.

Scenario: Default free provider is used when none configured

  • WHEN the app starts with no market-data provider explicitly configured
  • THEN a default free-tier provider implementation is selected
  • AND all normalized data models are populated from that provider

Scenario: Provider can be swapped via configuration

  • WHEN an operator sets the provider configuration to a different implementation
  • THEN the system routes all data requests through the new provider
  • AND no changes to evaluation or UI code are required

Requirement: Ticker resolution for NYSE and Nasdaq

The system SHALL accept a ticker symbol, validate that it resolves to an 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 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 security
  • THEN the system returns a not-found result with a clear message
  • AND no evaluation is attempted

Requirement: Company fundamentals and financial statements

The system SHALL provide normalized fundamentals sufficient for valuation and earnings analysis: TTM and per-fiscal-year revenue, operating profit, margins, net income, diluted EPS, operating cash flow, free cash flow, dividend per share, and total/net debt. Where the provider exposes it, per-segment revenue and operating profit SHALL be included.

Scenario: Fundamentals returned for a resolved ticker

  • WHEN fundamentals are requested for a resolved ticker
  • THEN the system returns TTM and at least the last three fiscal years of the listed metrics
  • AND each metric carries the fiscal period it belongs to

Scenario: Missing metric is represented explicitly

  • WHEN the provider does not supply a given metric
  • THEN the normalized model marks that metric as unavailable rather than zero

Requirement: Price history and computed technical indicators

The system SHALL provide daily OHLCV price history and compute technical context used for timing: 20/50/200-day moving averages, recent swing highs and lows, distance from 52-week high and low, average daily volume and today's volume as a multiple of it, and realized volatility (e.g. 60-day annualized).

Scenario: Technical context computed from price history

  • WHEN price history is requested for a resolved ticker
  • THEN the system returns the moving averages, 52-week high/low, volume ratio, and realized volatility computed from that history

Scenario: Insufficient history degrades gracefully

  • WHEN fewer than 200 trading days of history are available
  • THEN longer-window indicators (e.g. 200-day average) are marked unavailable
  • AND shorter-window indicators are still returned

Requirement: Analyst coverage and consensus estimates

The system SHALL provide, where available, analyst rating counts (bullish / neutral / bearish), average and median price targets, target range, and forward consensus estimates (next fiscal-year EPS and revenue).

Scenario: Analyst data returned when available

  • WHEN analyst coverage exists for a ticker
  • THEN the system returns rating counts, average/median/target range, and forward EPS/revenue estimates

Scenario: No coverage is handled

  • WHEN no analyst coverage exists for a ticker
  • THEN the system returns an empty coverage result the evaluation can note

Requirement: Rate limiting, caching, and data-freshness disclosure

The system SHALL respect provider rate limits, cache responses to reduce calls, and expose each dataset's freshness (as-of timestamp and whether it is delayed or real-time) so downstream output can disclose it.

Scenario: Rate-limit and transient errors are handled

  • WHEN the provider returns a rate-limit or transient error
  • THEN the system retries within limits and, if still failing, returns a typed error rather than throwing an unhandled exception

Scenario: Data freshness is surfaced

  • WHEN any dataset is returned
  • THEN it includes an as-of timestamp and a delayed/real-time flag