# Feature Specification: Pricing Awareness and Market Data Streaming

> For the complete documentation index, see [llms.txt](https://deploy-preview-472--lucky-concha-f3599f.netlify.app/llms.txt).

**Feature Branch**: `008-pricing-awareness-market-data`  
**Created**: 2026-03-31  
**Status**: Implemented  
**Input**: Transition delta from `007-observability-lgtm-compose`

## User Stories

*   As a trader, I want each executed trade to include its execution price so blotters are economically meaningful.
*   As a portfolio user, I want positions to expose average cost basis so unrealized P&L can be computed in real time.
*   As a frontend user, I want live market price updates to refresh position value and totals without page refresh.
*   As a frontend user, I want an initial snapshot price rendered immediately while stream subscription is still connecting.
*   As a maintainer, I want market data bootstrap to support deterministic snapshots and optional startup sync from yfinance.

## Functional Requirements

*   FR-1001: Trade execution payloads SHALL include `price` (maximum 3 decimals).
*   FR-1002: `trade-service` SHALL fetch current market price and stamp it on the trade order before publishing to NATS.
*   FR-1003: `trade-processor` SHALL persist `trade.price` for settled trades.
*   FR-1004: Position schema SHALL include pre-aggregated volume-weighted `averageCostBasis` and update it for each processed trade.
*   FR-1005: State SHALL add pricing subject strategy using `pricing.<TICKER>` topics.
*   FR-1006: State SHALL add a `price-publisher` that emits bounded-random-walk ticks on `pricing.<TICKER>` anchored to each ticker's startup open/close range.
*   FR-1006a: At publisher startup, each ticker SHALL be assigned one volatility band profile for the full process lifetime:
    *   20% of tickers: bounds may exceed open/close by up to 4%,
    *   60% of tickers: bounds may exceed open/close by up to 2%,
    *   20% of tickers: bounds remain strictly within open/close.
*   FR-1007: `price-publisher` SHALL support startup bootstrap mode:
    *   `snapshot` using local seed data,
    *   `yfinance` to fetch latest open/close once at startup, then switch to synthetic tick generation.
*   FR-1008: Trade blotter SHALL display execution price and relative execution time (`x min ago` for same-day trades).
*   FR-1009: Position blotter SHALL display streaming market price, position value, and net P&L based on `averageCostBasis`.
*   FR-1010: Main trade screen SHALL display total portfolio value and total portfolio cost basis.
*   FR-1011: Position blotter SHALL include `OPEN` price and render market price with directional marker (`▲` above open, `▼` below open, `■` at open).
*   FR-1012: Position blotter SHALL apply conditional highlight styling:
    *   market price below open: pink background with dark red text,
    *   market price above open: green background with dark green text,
    *   position value and P&L follow equivalent negative/positive semantics.
*   FR-1013: Trade ticket SHALL stream selected security market price in real time using `pricing.<TICKER>`.
*   FR-1014: Supported tradable ticker universe SHALL be aligned between `reference-data` and `price-publisher` via shared runtime configuration.
*   FR-1015: Legacy symbol `FB` SHALL be normalized to `META` in reference data responses for this state.
*   FR-1016: Price publisher SHALL emit pricing updates on randomized cadence (`750-1500ms` default) and publish only a randomized subset per cycle (`25%` default, configurable).
*   FR-1017: Default sample universe SHALL include core financial-services institutions used in demonstrations: `MS`, `UBS`, `C`, `GS`, `DB`, `JPM`, `COF`, `DFS`, `FNMA`, and at least one Fidelity-related listed symbol (`FIS` and/or `FNF`).
*   FR-1018: State SHALL expose price snapshot REST retrieval for both single ticker and multi-ticker retrieval in one request (for example ticket bootstrap vs. grid bootstrap).
*   FR-1019: Ticket and blotter screens that display streaming market price SHALL issue snapshot bootstrap retrieval in parallel with stream subscription and render the freshest price by server timestamp.
*   FR-1020: Price payloads delivered via both REST snapshot and `pricing.<TICKER>` stream SHALL include server-assigned event time (`asOf`) so clients can deterministically resolve snapshot-vs-stream race conditions.
*   FR-1021: API explorer runtime (`/api/docs`) SHALL expose a companion pub/sub inspector page at canonical route `pubsub-inspector.html` (with no-extension compatibility path retained).
*   FR-1022: Pub/sub inspector SHALL auto-connect to the state message bus websocket route using environment-derived address resolution (no manual endpoint input).
*   FR-1023: Pub/sub inspector SHALL start unsubscribed with an empty feed; all subscriptions are user-initiated.
*   FR-1024: Pub/sub inspector SHALL render one-click topic buttons generated from cumulative `system/messaging-subject-map.md` for the state.
*   FR-1025: Pub/sub inspector SHALL visually distinguish wildcard subjects and parameterized subjects; parameterized buttons SHALL prefill topic input with a placeholder pattern for user substitution.
*   FR-1026: Pub/sub inspector SHALL allow arbitrary topic/pattern subscription input and maintain an active-subscription list with per-subscription message counters and unsubscribe controls.
*   FR-1027: Pub/sub inspector feed rows SHALL show delivery topic, matched subscription pattern (when different), receipt timestamp, payload preview, and expandable pretty JSON payload.
*   FR-1028: Pub/sub inspector in-memory feed buffer SHALL cap at 2000 messages with oldest-first eviction while retaining an uncapped session-total counter.
*   FR-1029: Pub/sub inspector SHALL support feed filter, pause/resume display updates, and clear (buffer reset + per-subscription counter reset).
*   FR-1030: API explorer index SHALL link to the inspector using a runtime-computed relative URL that resolves correctly under ingress sub-path mounting.
*   FR-1031: For states where pub/sub inspector is enabled, main-app `System` menu and About-page `Tools` section SHALL include a Pub/Sub Inspector link bound to generated state metadata (`StateUiMetadata.pubSubInspectorUrl`).
*   FR-1032: API explorer and pub/sub inspector pages SHALL include a top-right navigation link back to the TraderX main app web root (`/`).

## Non-Functional Requirements

*   NFR-1001: Market price stamping SHALL preserve existing request/response latency characteristics for baseline local runtime.
*   NFR-1002: Pricing publisher component SHALL remain lightweight and local-dev friendly.
*   NFR-1003: Pricing subjects SHALL remain wildcard-friendly for browser subscribers using NATS WebSocket.
*   NFR-1004: Numeric precision for persisted execution price and cost basis SHALL be capped at 3 decimals.
*   NFR-1005: Existing `006` functional flows SHALL remain backward-compatible (trade submit, account validation, realtime updates).
*   NFR-1006: Pricing stream defaults SHALL reduce local runtime churn versus per-symbol 1-second fanout while preserving timely UI valuation refresh.
*   NFR-1007: Pricing visualization semantics (marker + color) SHALL remain deterministic and derived solely from open/market and cost/value comparisons.
*   NFR-1008: For all services in this state that expose Prometheus-compatible metrics, Prometheus scraping and provisioned Grafana visualization SHALL be maintained.
*   NFR-1009: `asOf` timestamps for snapshots and stream ticks SHALL be server-assigned UTC instants and consistently comparable across payload sources for the same ticker.
*   NFR-1010: Pub/sub inspector SHALL be generated as a self-contained static HTML asset in `api-explorer/` with no runtime build step.
*   NFR-1011: Pub/sub inspector message bus client dependency SHALL be vendored as a local asset in generated runtime output (no CDN dependency).
*   NFR-1012: Generated topic-button data for the inspector SHALL be pipeline-derived from the state's cumulative messaging subject map, not manually curated.
*   NFR-1013: Pub/sub inspector UI responsiveness SHALL remain acceptable at sustained 2000-message buffer occupancy.
*   NFR-1014: Browser-offered documentation/tool URLs (`/api/docs/`, `pubSubInspectorUrl`, and API explorer nav links) SHALL return non-404 responses with browser-renderable content types (HTML for pages, JSON for catalog payloads).
*   NFR-1015: Inherited observability ingress routes SHALL preserve Grafana anonymous Viewer access, non-default state-scoped admin credentials, and deterministic Promtail Docker API version selection from state `007`.

## Success Criteria

*   SC-1001: Runtime includes healthy `price-publisher` and visible `pricing.<TICKER>` stream activity.
*   SC-1002: New trades persist with non-null `price`.
*   SC-1003: Positions persist with non-null `averageCostBasis`.
*   SC-1004: UI reflects streaming valuation updates without requiring manual page refresh.
*   SC-1005: Unknown ticker validation still returns `404`.
*   SC-1006: Supported ticker list and pricing stream universe stay aligned (no supported symbol missing price stream/quote).
*   SC-1007: `FB` is not exposed by reference-data in this state; `META` is exposed and priced.
*   SC-1008: Default sample startup includes the defined financial-services symbols and all return quotes from `price-publisher`.
*   SC-1009: For each ticker, UI bootstrap + stream handoff keeps the latest (`max(asOf)`) price when snapshot and stream updates arrive near-simultaneously.
*   SC-1010: `/api/docs/pubsub-inspector.html` loads successfully from generated runtime and auto-connects to websocket bus route (and `/api/docs/pubsub-inspector` remains compatible).
*   SC-1011: Inspector-generated topic buttons match the state's cumulative `system/messaging-subject-map.md`.
*   SC-1012: Inspector feed displays delivery topic plus wildcard subscription context (`pricing.*`\-style) where applicable.
*   SC-1013: Inspector enforces 2000-message buffer cap with FIFO eviction while session-total counter continues increasing.
*   SC-1014: API explorer and pub/sub inspector each expose a working top-right link to TraderX main app web root (`/`), and main-app metadata-driven menus/pages expose inspector link when enabled.
*   SC-1015: `http://localhost:8080/grafana/` remains anonymously readable after state `008` generation and startup.

### Durable priced bookings (#461)

The pricing overlay MUST retain the baseline after-commit publication contract, including position quantity and cost-basis snapshots. Both market submissions and filled limit orders that enter the booking service MUST obey it. A force-fill regression must compare the pre-fill position with a fresh REST position read after trade/position events, applying the signed fill quantity. Reload must retain that quantity. `scripts/test-trade-booking-commit.sh` tests both service implementations; `scripts/test-realtime-order-stream-overlay.sh` checks fill events and durable REST state in a running matcher environment.
