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:
@@ -55,14 +55,43 @@ stop levels, implied multiples at each level, and valuation reasoning are comput
|
||||
here. Rationale: the spec requires the structured object to be consumable without
|
||||
the agent, and pure functions make the many numeric requirements testable.
|
||||
|
||||
### D5 — `AnalysisAgent` interface; Claude default via `@anthropic-ai/sdk`
|
||||
The agent takes the `Evaluation` object and returns narrative sections. Default
|
||||
impl calls a current Claude model, instructed to use ONLY the supplied figures,
|
||||
preserve any flagged caveats/discrepancies, and include the disclaimer. Structured
|
||||
output (tool/JSON schema) is preferred so the UI can render narrative per section.
|
||||
Alternative: free-text prompt with the raw data — rejected because grounding and
|
||||
per-section rendering are weaker. When no key is set, the pipeline returns the
|
||||
`Evaluation` with `narrative: unavailable`.
|
||||
### D5 — `AnalysisAgent` interface; Claude Code CLI default, API alternate
|
||||
The agent takes the `Evaluation` object and returns narrative sections. Consumers
|
||||
depend only on the interface, so the transport is swappable. **Default transport:
|
||||
the local Claude Code CLI in headless mode** — `claude -p --output-format json`,
|
||||
running on the operator's existing Claude auth (subscription), so an internal
|
||||
two-person tool incurs ~no metered cost. **Alternate transport:** the Anthropic API
|
||||
via `@anthropic-ai/sdk`, selected by config, for when the app is served to external
|
||||
users (subscription-backed serving is not appropriate then). Default model is
|
||||
**Opus 4.8** (CLI alias `opus`); a per-request deep-dive flag escalates to
|
||||
**Fable 5** (CLI alias `fable`) — model is a per-request parameter, not a build-time
|
||||
constant. Structured narrative is requested via the CLI `--json-schema` option (or
|
||||
the API alternate's structured-output). The agent is instructed to use the supplied
|
||||
figures plus attributed web-sourced facts, preserve flagged caveats, and include
|
||||
the disclaimer. When no agent is available the pipeline returns the `Evaluation`
|
||||
with `narrative: unavailable`.
|
||||
|
||||
CLI invocation shape (verified against `claude --help`): `claude -p "<prompt>"
|
||||
--model <opus|fable> --output-format json --json-schema '<narrative schema>'
|
||||
--append-system-prompt "<instructions>" --allowedTools "WebSearch WebFetch"
|
||||
--permission-mode dontAsk`. The JSON result carries the structured output plus
|
||||
`total_cost_usd` and token usage (consumed by cost-controls). API-alternate notes
|
||||
(from the SDK reference): Opus 4.8 and Fable 5 take `thinking: {type: "adaptive"}`,
|
||||
reject `budget_tokens`/sampling params; Fable 5 has thinking always-on and can
|
||||
return `stop_reason: "refusal"` (enable a server-side fallback to Opus 4.8); stream
|
||||
long outputs.
|
||||
|
||||
### D8 — Live web grounding via the agent's web tools
|
||||
The agent grounds macro/news facts through its web search + fetch capability —
|
||||
Claude Code's `WebSearch`/`WebFetch` tools on the default transport, or Anthropic's
|
||||
server-side web tools on the API alternate — scoped to the ticker, with sources
|
||||
attributed in the output. Rationale: the reference DE evaluation's credibility came
|
||||
from live, cited facts (analyst target changes, tariff figures, peer read-throughs,
|
||||
catalyst dates) that don't exist in model weights or a fundamentals feed — this is
|
||||
the layer that closes the gap to a search-augmented tool like Perplexity. A bonus of
|
||||
the CLI default: this grounding harness is built in, so there is nothing to wire up.
|
||||
Grounding degrades gracefully: on tool error the agent still writes from the
|
||||
structured evaluation and notes that live grounding was unavailable.
|
||||
|
||||
### D6 — Typed results end-to-end; errors are values
|
||||
Data, evaluation, and agent layers return typed results/errors (not thrown
|
||||
@@ -76,6 +105,44 @@ provider layer, with retry/backoff for transient/rate-limit errors. Rationale:
|
||||
free tiers are tightly limited; caching one evaluation's repeated reads avoids
|
||||
burning quota. Persistence-backed cache is a later change.
|
||||
|
||||
### D9 — Cost controls: capture real usage, price from a table, guard the budget
|
||||
Every agent call's `usage` (input/output/cache tokens) and web-search count are
|
||||
captured from the API response — never estimated — and priced via a configurable
|
||||
table (per-model token prices + per-search price) so provider price changes are
|
||||
config, not code. Month-to-date spend is kept in lightweight local storage (a JSON
|
||||
file/`localStorage`-class store, not a database — consistent with the no-persistence
|
||||
scope) and checked before each evaluation against a configurable monthly budget:
|
||||
soft threshold warns, cap blocks. The pipeline evaluates the guard *before*
|
||||
dispatching the agent (and pre-empts a deep-dive whose projected cost exceeds the
|
||||
remaining budget), so a runaway loop can't blow the cap. Rationale: cost is the one
|
||||
resource a user can't see mid-run; real per-report numbers replace the design-time
|
||||
estimate and let the user tune the Opus/Fable mix. Alternative: rely on
|
||||
Anthropic-console billing after the fact — rejected because it's not in-app, not
|
||||
per-ticker, and offers no pre-emptive block.
|
||||
|
||||
### D10 — Instrument-type-aware from the start; equity branch only in v1
|
||||
The resolved profile and the `Evaluation` object carry an `instrumentType`
|
||||
discriminant (`equity` | `etf`). Instrument-agnostic sections (current standing,
|
||||
technicals, timing, entry/exit, stop-loss, macro) are shared; the
|
||||
fundamentals/valuation sections live under a per-type branch. v1 implements only
|
||||
the equity branch and returns an unsupported-type result for anything else.
|
||||
Rationale: an ETF is ~half the same (all technicals/timing) and ~half different
|
||||
(no EPS/earnings/segments; instead holdings, expense ratio, NAV premium/discount,
|
||||
weighted fundamentals). Baking the discriminant and branch seam in now is nearly
|
||||
free and avoids a refactor when the ETF branch is added as a fast-follow change.
|
||||
Alternative: equities-only with no seam — rejected as false economy given ETFs are
|
||||
an explicit near-term goal.
|
||||
|
||||
### Roadmap (deferred, compose on this change)
|
||||
- **ETF evaluation branch** — fills the `etf` branch (holdings, expense ratio, NAV
|
||||
premium/discount, sector/geo weights, weighted fundamentals) and an ETF agent
|
||||
prompt variant; reuses everything instrument-agnostic here.
|
||||
- **Screening / candidate-finder** — a `screening` capability: criteria/thesis →
|
||||
grounded, ranked shortlist (fundamental screener filter + web verification) where
|
||||
each candidate links into this evaluator. Deliberately built *after* the
|
||||
evaluator exists, so every surfaced idea has a rigorous place to be checked
|
||||
rather than being trusted as an oracle.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Free-tier data is delayed/incomplete — segments, estimates, or history may be
|
||||
|
||||
Reference in New Issue
Block a user