Repository navigation
Market Data Backend: Detailed Design & Implementation - #2
Conversation
Implementation-ready design for backend/app/market: unified PriceUpdate/ PriceCache/MarketDataSource API, GBM simulator, Massive REST client, factory, SSE stream and ticker-tracking integration, with complete module code and tests. Documents a bug in the current Massive client: it reads last_trade.timestamp, which the massive LastTrade model does not have (it has sip_timestamp, in ns), so every real snapshot is skipped. Also adds daily-change reference price, ticker normalization and several robustness fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WJ9YY6aYQUo32DNgC5GLJw
| timestamp: float = field(default_factory=time.time) # Unix seconds | ||
| # Price that "daily change" is measured from: the session's first price for | ||
| # the simulator, the previous close for Massive. | ||
| reference_price: float | None = None |
There was a problem hiding this comment.
This design contradicts the contract in planning/PLAN.md (Section 6, "Daily Change" and "SSE Streaming", included in the root CLAUDE.md). PLAN.md requires PriceUpdate to carry session_open and to_dict() to emit session_open and daily_change_percent. The SSE example shows those exact keys, and the frontend watchlist column reads daily_change_percent.
This doc instead uses reference_price, day_change and day_change_percent (here, and in the to_dict and SSE examples further down). Implementing it as written would send keys the PLAN-driven frontend doesn't read, so the watchlist daily-change column would break.
PLAN.md also lists an is_supported(ticker) -> bool method on the market data source (Ticker Validation) and a fixed 50-ticker simulator universe. Neither appears in this design.
Please rename the fields to the PLAN.md names, or update PLAN.md first so there is a single source of truth.
PLAN.md reference: https://github.com/dev-app-source/finally/blob/8818dc3f3d33b28900fef28f8905dc934e4b6715/planning/PLAN.md
Summary
This PR adds a comprehensive design document for the market data backend subsystem (
backend/app/market/) and implements 9 targeted fixes and enhancements to the existing implementation. The document serves as the authoritative specification for the unified data model, price cache, GBM simulator, Massive API client, and SSE streaming infrastructure.Key Changes
Documentation:
planning/MARKET_DATA_DESIGN.md(1748 lines): complete, drop-in reference design with code examples, architecture diagrams, wire formats, testing strategies, and edge-case handling for all market data components.Code Fixes & Enhancements (marked Δ in design):
Fix Massive snapshot parsing (
massive_client.py):last_trade.sip_timestamp(nanoseconds) instead of non-existenttimestampattributelast_trade.price → min.close → day.close → prev_day.closeAttributeErrorand was skippedDaily change metrics (
models.py,cache.py):reference_pricefield toPriceUpdateday_changeandday_change_percentproperties for watchlist displayTicker normalization (
models.py):normalize_ticker()function: canonical uppercase form with validationCache robustness (
cache.py):remove()now bumps version counter (fixes removed tickers staying on screen)versionproperty read under locktimestamp=0in update logicSSE stream improvements (
stream.py):APIRouterpercreate_stream_router()call (eliminates module-level router footgun){}instead of failingSeedable simulator RNG (
simulator.py):seed=parameter toGBMSimulatorandSimulatorDataSourcenumpy.Generatorandrandom.Randomfor reproducible testsMassive robustness (
massive_client.py):retries=0onRESTClient(prevent burning free tier on 429 responses)Configurable poll interval (
factory.py):MASSIVE_POLL_INTERVALenvironment variableTicker tracking helper (
tracking.py- new):sync_tracked_tickers()function: unified rule for watchlist ∪ open positionsImplementation Details
massivepackageDesign Principles
The architecture enforces:
MASSIVE_API_KEYPriceUpdateis the only type leaving the market layer: serialized withto_dict()for JSON/SSEPriceUpdateis frozen; `https://claude.ai/code/session_01WJ9YY6aYQUo32DNgC5GLJw