SRS: Dashboard - Common — Overview (UC 4.2.1–4.2.5)
| Field | Value |
|---|---|
| BA in Charge | Trang Nguyen |
| Date Created | 2026-08-08 |
| Version | v9 |
| Document References | RFQ_ Stack Trading Prop Tech V7.pdf (§3.2 Calculations and Endpoints for our Dashboard, §Risk Metrics Logic Definitions, §Risk Metrics Endpoints) · RFQ_ Website and Dashboard Implementation V7.pdf (§Part C: Trader Dashboard Implementation, Key Features) |
UC Index
| UC_ID | Use Case Name | Business Description |
|---|---|---|
| UC_4.2.1 | Position Monitor Table | Live grid of the trader's open positions (Symbol, Side, Net Position, Unrealized/Realized/Total PnL, Forex-only Est. Swap) streamed via thePOSITION_METRICS WebSocket event. |
| UC_4.2.2 | The Pulse | A 0–100 discipline score that rewards risk-reducing behavior and penalizes averaging-down/erratic sizing, reset every trading session. |
| UC_4.2.3 | Equity Curve | A time-series chart of the trader's Net Equity (absolute value = Initial Starting Capital + Realized + Unrealized PnL), filterable by 1D/1W/1M timeframe.[CR] (Source: CR-20260831-001). |
| UC_4.2.4 | Profit factor | The ratio of gross profit to gross loss across the trader's closed trades at their current level. |
| UC_4.2.5 | Win Rate | Win-rate statistics for a selectable lookback window (10/30/50/100 trades), including a per-trade win/loss dot grid. |
Note on UC_4.3 relationship
UC_4.3 ("12 Performance Metrics", Ref: UC_4.3) documents 5 dedicated Performance-screen widgets (Sharpe Ratio, Sortino Ratio, Avg. Win vs. Loss, Holding Time Analysis, Trade Efficiency Matrix). The Pulse (BR_4.2.2.1), Equity Curve (BR_4.2.3.x), Profit factor (BR_4.2.4.1), and Win Rate (UC_4.2.5 §7 Screen Description row 3) each carry their own formula inline in this UC — there is no cross-UC canonical-formula dependency between this UC and UC_4.3.
Overview — Performance Widgets
UC_4.2.1 — Position Monitor Table
1. Overview
| Field | Content |
|---|---|
| ID | UC_4.2.1 |
| Use Case | Position Monitor Table |
| Description | Real-time grid of the trader's currently open positions on the Overview screen — Symbol, Side, Net Position, Unrealized/Realized/Total PnL, and a conditional Forex-only Est. Swap column — streamed via thePOSITION_METRICS WebSocket event. Same UC for both SIM and LIVE account types (Ref: UC_4.1.1 §2 Screen Description). |
| Zapier Flow | — |
| Zapier Table | — |
| 3rd Party | — |
Dashboard.png Overview screen — Position Monitor Table (top-right panel), populated state. Dashboard - Day 0.png Overview screen — Position Monitor Table, empty state (no open positions).
Motion reference: UC_4.2.1 motion clips
2. Trigger
The Overview screen (Ref: UC_4.1.1) has rendered.
3. Pre-conditions
- User is on the Overview screen (Ref: UC_4.1.1 §1 Overview).
4. Post-conditions
- The table displays the trader's current open positions and updates live as fills, partial fills, or closes occur.
5. Basic Flow
- On Overview screen load — including a page reload/F5 — frontend subscribes to the
POSITION_METRICSWebSocket event, and the table renders immediately from the Backend's latest held snapshot of the user's open positions rather than appearing empty while waiting for the next event (Ref: BR_4.2.1.3). - As fills, partial fills, or closes occur, Backend sends updated data for only the affected position(s), keyed by
position_id(the position identifier), throttled to 1 evaluation/second (Source: RFQ_ Stack Trading Prop Tech V7.pdf §3.2 for the payload fields; Ref: BR_4.2.1.3 for the snapshot/delta mechanism andposition_idkeying, which supersedes a full-array re-send on every event). - Frontend uses
position_idto identify the corresponding row and maps/updates only the changed data into that row; each row updates independently — the table does NOT wait for a synchronized batch update across all positions (Ref: BR_4.2.1.1). - If a position closes entirely, Backend flags the closure on that position's update and Frontend removes the row from the table (Ref: BR_4.2.1.3).
- If the user has no open positions, the table shows the empty state instead of rows (Ref: BR_4.2.1.2).
6. Exceptional Flow
- WebSocket connection drops, market is closed, or page reload (F5): the table continues displaying the latest successfully captured snapshot rather than becoming empty — it does not reset to empty or zero simply because no new real-time events are arriving. This snapshot/reconnection resiliency behavior is shared across all real-time Dashboard widgets fed by WebSocket events (Ref: CR-14).
7. Business Rules
BR_4.2.1.1: Est. Swap — Forex-Only Visibility Window, Asynchronous Streaming
The Est. Swap column is visible only for Forex positions — it is hidden entirely (not shown as $0, the column itself does not render) for Futures positions. For Forex positions, the column follows this visibility window:
- Animates into view exactly 60 minutes before the Forex daily rollover.
- Disappears 1 minute after the rollover completes.
- If the column is visible but the backend has not yet pushed a swap estimate for a given position, the cell shows a neutral placeholder (
$0) until the firstest_swapvalue arrives. - Whenever the backend receives an updated swap estimate from the execution gateway, it pushes the new
est_swapvalue via the standardPOSITION_METRICSWebSocket channel (subject to the same 1 evaluation/second throttle) — the frontend table updates each row independently as data arrives, it does NOT wait for a synchronized batch calculation across all active positions.
Rollover time — DST-aware (America/New_York timezone), not a fixed UTC offset: the Forex daily rollover is always 5:00 PM Eastern Time, which is a wall-clock anchor, not a fixed UTC time. Because the US observes Daylight Saving Time, the equivalent UTC time shifts twice a year:
- EDT (roughly mid-March → early November): 5:00 PM ET = 21:00 UTC.
- EST (roughly early November → mid-March): 5:00 PM ET = 22:00 UTC.
The timekeeper must resolve the rollover trigger (and therefore the column's 60-minutes-before / 1-minute-after visibility window) from the US Eastern Time DST calendar, not from a hardcoded UTC offset — otherwise the 60-minute pre-rollover animation and the column's disappearance would fire at the wrong wall-clock time on the two DST transition days each year, breaking the UI timer.
BR_4.2.1.2: Empty State
When the trader has no open positions, the table displays a centered empty-state indicator (icon + text) reading "No open positions" instead of any rows. (Source: BA confirmation, 2026-08-08, QnA_init_docs.md A-04; visual per wireframe Dashboard - Day 0.png.)
BR_4.2.1.3: Snapshot Persistence, Delta Update by Position Identifier, and Row Removal on Close
Backend snapshot persistence (reload/F5): Backend maintains and holds the latest POSITION_METRICS snapshot (the current set of open positions and their latest field values) for the user, independent of any single WebSocket connection. On Overview screen load — including a page reload/F5 — the table is restored from this held snapshot immediately, so it does not appear empty while waiting for the next WebSocket event. This is the Position-Monitor-specific application of the shared snapshot rule (Ref: CR-14).
Delta update by position identifier (position_id): after the initial snapshot render, each subsequent POSITION_METRICS WebSocket event carries only the updated data for the position(s) affected by that fill/partial-fill/close — not a full re-send of every open position. position_id is the position identifier: Frontend uses it to find the matching row already on screen and maps/updates only the changed fields (net_pos, unrealized_pnl, realized_pnl, total_pnl, est_swap) into that row, leaving every other row untouched. symbol remains a display-only field on the row (the traded instrument) and is not used to key the update, since a trader can hold more than one open position on the same symbol (e.g. positions opened at different times or on different strategies) — position_id is what disambiguates them. This confirms and supersedes any full-array-per-event or symbol-keyed reading of §3.2 — the existing snapshot render plus this row-level delta, keyed by position_id, is the update mechanism (Source: User instruction (BA session), 2026-08-31).
Row removal on full close: when a position closes out entirely (net_pos reaches 0 for that position_id), the affected-position update carries a closed flag for that position_id. Frontend removes the corresponding row from the table on that event, rather than leaving a stale zero-quantity row on screen. If this was the trader's last open position, the table then shows the empty state (Ref: BR_4.2.1.2).
Market closed / connection drop: while the market is closed or the WebSocket connection is temporarily unavailable, the table continues showing the latest snapshot/delta-updated state rather than resetting to empty (Ref: CR-14).
8. Screen Description
| No. | Field Name | Field Type | Displaying rule / Behaviour rule |
|---|---|---|---|
| 1 | Symbol | Label | Displaying rule:- The traded instrument symbol.- Overflow: Truncate with tooltip Ref: CR-10 if it exceeds the column width.Behaviour rule:- Read-only, display field. The row itself is keyed by position_id (not shown on screen), used to route each POSITION_METRICS delta update to its row and to remove the row on full close (Ref: BR_4.2.1.1, BR_4.2.1.3). |
| 2 | Side | Badge | Displaying rule:- Possible values: Long, Short. |
| 3 | Net Pos | Label | Displaying rule:- Net position size (integer, signed) for this symbol. |
| 4 | Unrealized PNL | Label | Displaying rule:- Formula: Σ((Current Price − Avg Entry Price) × Position Size × Tick Value) for this position (Source: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Logic Definitions "PnL Breakdown").- Monetary value display: Ref: CR-11. |
| 5 | Realized PNL | Label | Displaying rule:- Sum of Net PnL for this position's closed lots within the current session (Source: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Logic Definitions).- Monetary value display: Ref: CR-11. |
| 6 | Total PNL | Label | Displaying rule:- = Unrealized PNL + Realized PNL.- Monetary value display: Ref: CR-11. |
| 7 | Est Swap | Label | Displaying rule:- Forex-only, conditional visibility window (60 min before / 1 min after Forex rollover, 5:00 PM Eastern Time — DST-aware, shifts between 21:00/22:00 UTC). Ref: BR_4.2.1.1. |
UC_4.2.2 — The Pulse
1. Overview
| Field | Content |
|---|---|
| ID | UC_4.2.2 |
| Use Case | The Pulse |
| Description | A 0–100 discipline score displayed on the Overview screen, tracking trading behavior (not P&L) within the current trading session — deducting for averaging-down/increasing-risk/erratic-sizing and rewarding pyramiding/risk-reduction. Resets to 100 at the start of every trading session. Same UC for both SIM and LIVE account types. |
| Zapier Flow | — |
| Zapier Table | — |
| 3rd Party | — |
Dashboard.png Overview screen — "The Pulse" panel, populated state (score + trend chart). Dashboard - Day 0.png Overview screen — "The Pulse" panel, session-start state (score = 100, empty chart).
Motion reference: UC_4.2.2 motion clips
2. Trigger
The Overview screen (Ref: UC_4.1.1) has rendered.
3. Pre-conditions
- User is on the Overview screen (Ref: UC_4.1.1 §1 Overview).
4. Post-conditions
- The widget displays the current Pulse score for the active session, updating in real time as scoring events occur.
5. Basic Flow
- At the start of every trading session (Exchange Reset: 18:00 EST for Futures, 17:00 EST for Forex), the Pulse score resets to
100(Source: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Logic Definitions "Pulse Score"). - As the trader places/modifies orders during the session, the backend evaluates each action against the deduction/reward rules and recalculates
current_pulse_score(Ref: BR_4.2.2.1). - The backend pushes the updated
current_pulse_scorein real time via WebSocket (Ref: BR_4.2.2.2). - The widget renders the current score, updating live as new values arrive.
6. Exceptional Flow
- WebSocket connection drops, market is closed, or page reload (F5): the widget continues displaying the latest successfully captured score/snapshot rather than becoming empty (Ref: CR-14).
7. Business Rules
BR_4.2.2.1: Pulse Score — Reset, Deductions, Rewards
The Pulse Score is an integer, 0–100, reset to 100 at the start of every trading session (Exchange Reset: 18:00 EST for Futures, 17:00 EST for Forex).
Deductions:
- −25 — Averaging Down: submitting an order in the same direction on the same symbol while the current Unrealized P&L on that symbol is negative.
- −20 — Increasing Risk: modifying an existing Hard Stop order to move the price further away from the current market price.
- −15 — Erratic Sizing: submitting an order where the quantity exceeds 2× the average quantity of the last 10 fully filled or closed orders (partial fills ignored).
Rewards:
- +5 — Pyramiding: adding to a winning position in the same direction on the same symbol, while Unrealized P&L on that symbol is positive.
- +10 — Reducing Risk: modifying a Hard Stop to move it closer to the current market price, or locking in profit.
The score is clamped to the 0–100 range — deductions cannot push it below 0, rewards cannot push it above 100. (Source: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Logic Definitions "Pulse Score".)
BR_4.2.2.2: Real-Time Update via current_pulse_score
The Pulse widget updates in real time over WebSocket. The payload includes a current_pulse_score (integer) field (Source: BA confirmation, 2026-08-08, QnA_init_docs.md A-06 — supersedes the original architecture finding that no WebSocket event in the Customer supplies payload table carried a Pulse-related field).
BR_4.2.2.3: Widget Display — Score and Trend Chart
- The panel title shows the current score inline, e.g.
"The Pulse (42)". - Below the title, a trend-line/area chart plots the score's history within the session. At session start (no history yet) the chart area renders empty.
- Chart data source:
Get Pulse Historyendpoint (GET,user_id+limit, returnsArray[[timestamp_utc, integer_score]]) (Source: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Endpoints), refreshed live ascurrent_pulse_scoreupdates arrive (Ref: BR_4.2.2.2).
8. Screen Description
| No. | Field Name | Field Type | Displaying rule / Behaviour rule |
|---|---|---|---|
| 1 | Pulse Score | Label | Displaying rule:- Integer 0–100, shown inline in the panel title, e.g. "The Pulse (42)". Ref: BR_4.2.2.1, BR_4.2.2.3.Behaviour rule:- Read-only. Updates live via current_pulse_score (Ref: BR_4.2.2.2). |
| 2 | Trend chart | Label | Displaying rule:- Session trend-line/area chart of the score's history. Empty at session start. Ref: BR_4.2.2.3.Behaviour rule:- Read-only, appends new points as the score updates. |
UC_4.2.3 — Equity Curve
1. Overview
| Field | Content |
|---|---|
| ID | UC_4.2.3 |
| Use Case | Equity Curve |
| Description | [CR] A time-series chart of the trader's Net Equity (absolute notional value — Initial Starting Capital + Realized PnL + Unrealized PnL — not a relative PnL delta) on the Overview screen, filterable by 1D/1W/1M timeframe. Same UC for both SIM and LIVE account types. (Source: CR-20260831-001 — see References/CR/2026-08-31_equity-curve-net-equity-formula/CR_summary.md) |
| Zapier Flow | — |
| Zapier Table | — |
| 3rd Party | — |
Dashboard.png Overview screen — "Equity Curve" panel, populated state.
Dashboard - Day 0.png Overview screen — "Equity Curve" panel, empty state (mockup shows a flat line at $0; [CR] per CR-20260831-001 this is now a flat line at the account's Initial Starting Capital baseline, not $0 — Ref: BR_4.2.3.1).
Motion reference: UC_4.2.3 motion clips
2. Trigger
The Overview screen (Ref: UC_4.1.1) has rendered, or the user changes the 1D/1W/1M filter.
3. Pre-conditions
- User is on the Overview screen (Ref: UC_4.1.1 §1 Overview).
4. Post-conditions
- The chart displays the account balance time series for the selected timeframe.
5. Basic Flow
- On Overview screen load, frontend calls
GET /equity-curve?timeframe=1D(default filter). [CR]Backend returnsArray[[timestamp_utc, float_value]]wherefloat_valueis the absolute Net Equity notional value at that timestamp (e.g.25500.00) — not a relative PnL delta (e.g.500.00). Computed per the formula and baseline in BR_4.2.3.1 (Source: CR-20260831-001).[CR]Frontend renders the time series as a chart, plotting the exact array of absolute values returned by the backend — no client-side math (no re-basing, no delta calculation, no re-scaling of the underlying values) is performed on the data before charting (Ref: BR_4.2.3.2; Source: CR-20260831-001).- User clicks the
1D/1W/1Mfilter → frontend re-callsGET /equity-curve?timeframe=with the new value (Ref: BR_4.2.3.1 for re-fetch behavior, BR_4.2.3.4 for the snapshot cadence/aggregation applied per timeframe).
6. Exceptional Flow
- No balance snapshot yet (new account, Day 0): chart displays a flat line at the baseline — the account's Initial Starting Capital (Ref: §8 Screen Description row 2, BR_4.2.3.1).
- WebSocket/API connection drops, market is closed, or page reload (F5): the chart continues displaying the latest successfully loaded series rather than becoming empty (Ref: CR-14).
[CR]Page reload (F5) after a snapshot has been captured but not yet pushed over WebSocket: if the backend has already captured a new 30-minute intraday snapshot (BR_4.2.3.4) but the corresponding WebSocket push to the chart has not fired yet, and the user reloads the page in that window, the reload must render the latest already-captured snapshot immediately (the page-loadGET /equity-curvecall always reads the backend's current persisted state, so it is not blocked on the socket push) — the chart must never show the older, pre-snapshot data merely because the socket event hasn't arrived (Ref: BR_4.2.3.5; Source: CR-20260831-001).
7. Business Rules
BR_4.2.3.1: 1D/1W/1M Filter — Re-fetch on Change; Net Equity Formula & Baseline
[CR] The chart calls GET /equity-curve?timeframe= on page load and every time the user changes the 1D/1W/1M filter — it does not pre-fetch or cache all 3 timeframes at once (Source: BA confirmation, 2026-08-08, QnA_init_docs.md A-09; re-fetch trigger unchanged by CR-20260831-001).
Baseline data source (Table A/B lookup): the chart's Initial Starting Capital baseline is not stored per-account — it is looked up live from the Zapier configuration matrix each time it's needed: the backend takes the account's asset class (Forex vs Futures) and its current level, finds the matching row in Table A (Forex Matrix) or Table B (Futures Matrix), and reads that row's Max_Forex_Notional / Max_Futures_Notional field as the baseline value. This is the same lookup used to derive the formula's Initial Starting Capital term below (Source: CR-20260831-001; BA confirmation 2026-09-03).
Formula (Source: CR-20260831-001 — see References/CR/2026-08-31_equity-curve-net-equity-formula/CR_summary.md):
Net Equity = Initial Starting Capital + (Realized PnL + Unrealized PnL) = Account Balance + Unrealized PnLNote: all terms on the right-hand side —
Account Balance(which already embedsInitial Starting Capital + Realized PnL), andUnrealized PnL— are read from the trader's own trading account (the live account/position data feed), not from a separate ledger or estimate. Only theInitial Starting Capitalbaseline itself comes from the Table A/B lookup above; everything else is the account's actual current balance and open-position PnL (Source: BA confirmation, 2026-09-03).
-
Initial Starting Capital (the baseline): looked up once per account from the Zapier configuration table matching the account's asset class:
- Forex accounts:
Max_Forex_Notional(Table A). This column is already documented (Source: Zapier Integration V7.pdf, Appendix A). - Futures accounts:
Max_Futures_Notional(Table B). ✅ Confirmed by client, 2026-08-31 (CR-20260831-001): this is a new column, distinct from the existingFutures_Capitalcolumn — the two represent different things.
- Forex accounts:
-
Each
[timestamp_utc, float_value]point in the returned array is one snapshot's Net Equity computed by this formula, at the cadence defined in BR_4.2.3.4. No additional transformation, smoothing, or interpolation is applied on top of this formula.
Previously (superseded by CR-20260831-001): the series was built directly from the account's Daily Balance snapshots only — float_value was the raw account balance, which excludes Unrealized PnL entirely. This under-represented a trader's true net worth while holding open positions. See Changelog.
BR_4.2.3.2: Frontend Renders Absolute Values As-Is — No Client-Side Math
[CR] The frontend plots the exact [timestamp_utc, float_value] array returned by GET /equity-curve with no local/client-side computation on the float_value series — no subtracting a baseline to derive a delta, no adding a locally-held Unrealized PnL figure, no re-scaling, no smoothing/interpolation. Every value already fully embeds the BR_4.2.3.1 formula on the backend, so the chart is a pure renderer of whatever the backend sends. This guarantees a single source of truth for the Net Equity calculation — if the formula ever changes, only the backend needs updating, not the frontend chart logic. (Source: CR-20260831-001.)
BR_4.2.3.3: Y-Axis Dynamic Scaling & Target/Stop Overlay
The Y-axis does not start at $0 and is not a fixed range — it auto-scales dynamically to the trader's actual data:
- Scale bounds: the Y-axis min/max are computed from the actual high and low Net Equity values (
float_value) among the data points in the currently selected window, not from $0 and not from the baseline alone (a profitable trader's whole chart may sit well above $0; a drawdown trader's chart may sit below the baseline without ever approaching $0). - Padding for Target/Stop visibility: the backend also fetches the trader's
Profit_TargetandMarket_Loss_When_Stoppedfor their current level viaGET /current-level-detail(Ref: UC_4.8.1 BR_4.8.1.2), and derives the two static overlay levels:- Target level = Initial Starting Capital +
Profit_Target. - Stop level = Initial Starting Capital −
Market_Loss_When_Stopped. - The Y-axis top/bottom bounds must extend far enough beyond the min/max Net Equity data points that both the Target level and the Stop level remain visible within the chart frame at all times — i.e.
axis_max ≥ max(high Net Equity, Target level)plus a visual padding margin, andaxis_min ≤ min(low Net Equity, Stop level)minus a visual padding margin. If the trader's actual Net Equity range already exceeds the Target/Stop levels, the axis still auto-scales to the data (the Target/Stop lines simply sit inside the frame rather than at its edges).
- Target level = Initial Starting Capital +
- Overlay rendering: Target and Stop are drawn as static horizontal reference lines across the chart (distinct styling from the Net Equity line itself), not as additional data points in the series.
Axis & color semantics:
| Element | Meaning |
|---|---|
| X-axis | Time. Tick granularity depends on the active filter —1D → hourly ticks; 1W/1M → daily ticks (see §8 row 2 for full tick rules, unchanged by this CR). |
| Y-axis | Net Equity in USD (absolute notional value perBR_4.2.3.1 — not starting at $0), auto-scaled per this BR, monetary formatting per CR-11. |
| Baseline | A horizontal reference line at the Initial Starting Capital value (Ref:BR_4.2.3.1 — Max_Forex_Notional or Max_Futures_Notional). This is the account's Day-0 equity, not $0. |
| Green segment/fill (above baseline) | Net Equity is currentlyabove the Initial Starting Capital — the trader is net profitable. |
| Red segment/fill (below baseline) | Net Equity is currentlybelow the Initial Starting Capital — the trader is net in a loss. |
| Line-color transition | Color is evaluated per-point against the baseline, so the line/fill switches between green and red at the exact point(s) where the Net Equity series crosses the baseline within the visible window. |
(Y-axis scaling/Target-Stop overlay is display/rendering behavior, not the CR itself — see BR_4.2.3.1 for the CR-tagged formula change. Target/Stop field names — Source: RFQ_ Stack Trading Prop Tech V7.pdf, per UC_4.8.1 BR_4.8.1.2.)
BR_4.2.3.4: Snapshot Cadence & 1W/1M Aggregation
1D timeframe — 30-minute intraday snapshots: a background cron captures a Net Equity snapshot every 30 minutes throughout the trading session, populating the array that powers the 1D chart. A single daily balance snapshot cannot render a 1-day time-series chart, so intraday snapshotting at this cadence is the implied technical mechanism required to satisfy the V7 spec's mandate that GET /equity-curve support a 1D timeframe (Source: CR-20260831-001 — "capturing a snapshot every 30 minutes is the correct technical approach to power the 1D chart... intraday snapshotting is an implied technical necessity"; cross-ref: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Endpoints, Get Equity Curve 1D timeframe support).
- 1W timeframe — aggregated by day: given the underlying data is stored at 30-minute granularity,
GET /equity-curve?timeframe=1Wdoes not return all ~48 points/day — it returns one aggregated point per calendar day within the 7-day window. The aggregated point'sfloat_valueis the last (most recent, i.e. end-of-day) snapshot's Net Equity for that day — consistent with the existing "daily ticks" display rule in §8 row 2. - 1M timeframe — aggregated by week: analogously,
GET /equity-curve?timeframe=1Mreturns one aggregated point per calendar week within the window, using the last snapshot of that week. - Current (in-progress) day/week stays live: the aggregation above applies to closed days/weeks only. The data point representing the current, still-in-progress day (for
1W) or current, still-in-progress week (for1M) continues to update in place with every new 30-minute snapshot — it is not frozen at the last aggregation boundary — so the chart accurately reflects "today" at all times, matching the existing real-time-update behavior already defined in §8 row 2.
(Source: CR-20260831-001.)
BR_4.2.3.5: F5/Reload Reflects the Latest Captured Snapshot Immediately
This rule is a chart-specific application of the shared CR-14 snapshot-persistence pattern, made explicit for the 30-minute snapshot cadence in BR_4.2.3.4: if a new 30-minute snapshot has already been captured and persisted on the backend, but the corresponding WebSocket push to update the live chart has not fired yet, and the user reloads the page (F5) during that window, the page-load GET /equity-curve call reads directly from the backend's already-persisted data — so the reloaded chart reflects the latest captured snapshot immediately, never the older pre-snapshot state. (Ref: §6 Exceptional Flow; Source: CR-20260831-001.)
8. Screen Description
| No. | Field Name | Field Type | Displaying rule / Behaviour rule |
|---|---|---|---|
| 1 | Timeframe filter | Tab | Displaying rule:- Options: 1D, 1W, 1M. Default: 1D.Behaviour rule:- On selection: re-fetches the chart data for the selected timeframe. Ref: BR_4.2.3.1. |
| 2 | Equity chart | Label | [CR] Displaying rule (Source: CR-20260831-001):- X-axis: time. Tick granularity depends on the active filter — 1D → hourly ticks (e.g. 09:00, 10:00, …, covering the current trading session); 1W/1M → daily ticks (one tick per calendar day in the window). Tick spacing is evenly distributed across the axis regardless of how many data points fall between ticks.- Y-axis: Net Equity in USD (absolute notional value, does not start at $0), auto-scaled to the trader's actual high/low Net Equity data points within the current window, with extra top/bottom padding so the static Target and Stop levels stay visible in-frame, monetary formatting per CR-11. Full scaling/padding rule and axis semantics: Ref: BR_4.2.3.3.- Baseline: horizontal reference line at the Initial Starting Capital (Max_Forex_Notional/Max_Futures_Notional). Green above baseline (profit), red below baseline (loss). Ref: BR_4.2.3.3.- Target/Stop overlay: two static horizontal reference lines — Target = Initial Starting Capital + Profit_Target; Stop = Initial Starting Capital − Market_Loss_When_Stopped (from GET /current-level-detail). Ref: BR_4.2.3.3.- Line: one point per snapshot returned by GET /equity-curve — each point is the absolute Net Equity value computed per BR_4.2.3.1; frontend performs no local math on these values (Ref: BR_4.2.3.2). Points connected in chronological order by a straight-line (linear) segment — no curve smoothing/interpolation. Snapshot cadence and 1W/1M aggregation: Ref: BR_4.2.3.4.- Empty state (Day 0, no snapshot yet): chart displays a flat line at the account's Initial Starting Capital (the baseline) across the full X-axis range, rather than an empty canvas.- Real-time behavior: as new snapshots arrive (Ref: CR-14 for the shared WebSocket resiliency pattern; Ref: BR_4.2.3.5 for the F5/reload edge case), the chart appends the new point and redraws the line — for the 1D filter this extends the line rightward within the current session; for 1W/1M the current (in-progress) day/week's point updates live in place per BR_4.2.3.4.Behaviour rule:- Read-only. |
UC_4.2.4 — Profit factor
1. Overview
| Field | Content |
|---|---|
| ID | UC_4.2.4 |
| Use Case | Profit factor |
| Description | The ratio of gross profit to gross loss across the trader's closed trades at their current level, shown on the Overview screen alongside the underlying Gross Profit / Gross Loss values and a proportional profit-vs-loss bar. Same UC for both SIM and LIVE account types. |
| Zapier Flow | — |
| Zapier Table | — |
| 3rd Party | — |
Dashboard.png Overview screen — "Profit Factor" panel, populated state.
Dashboard - Day 0.png Overview screen — "Profit Factor" panel, no-trades state (Profit Factor (0)).
Motion reference: UC_4.2.4 motion clips
2. Trigger
The Overview screen (Ref: UC_4.1.1) has rendered.
3. Pre-conditions
- User is on the Overview screen (Ref: UC_4.1.1 §1 Overview).
4. Post-conditions
- The widget displays the trader's current-level Profit Factor, Gross Profit, and Gross Loss.
5. Basic Flow
- On Overview screen load, frontend calls
GET /core-performancefor the trader's current level (Source: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Endpoints). - Backend returns
profit_factor, alongsidegross_profit/gross_lossinputs used to compute it (Ref: BR_4.2.4.1). - Frontend renders the score, the Gross Profit / Gross Loss values, and the proportional profit-vs-loss bar.
- On trade close, the
PERFORMANCE_UPDATEWebSocket event pushes an updatedprofit_factor, and the widget updates live.
6. Exceptional Flow
- WebSocket/API connection drops, market is closed, or page reload (F5): the widget continues displaying the latest successfully loaded values rather than becoming empty (Ref: CR-14).
7. Business Rules
BR_4.2.4.1: Profit Factor Formula, Scope, and Zero-Trade Clamp
Formula: Profit Factor = Gross Profit ÷ Gross Loss, where:
Gross Profit= sum of the absolute PnL of all winning closed trades at the trader's current level.Gross Loss= sum of the absolute PnL of all losing closed trades at the trader's current level.- If
Gross Loss = 0(no losing trades yet), the result isGross Profititself, to avoid a divide-by-zero error.
(Source: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Logic Definitions "Profit Factor".)
Scope: the widget uses the current level scope (all closed trades since the trader reached their current level) via GET /core-performance / PERFORMANCE_UPDATE — there is no lookback-window selector on this widget (Source: BA confirmation, 2026-08-08, QnA_init_docs.md A-12). This differs from the period-scoped profit_factor shown inside the Win Rate widget's stats row (Ref: BR_4.2.5.1), which uses a selectable lookback window — the two are separate values by design, not duplicates.
Zero-trade clamp: when the trader has not closed any trade yet at their current level (Gross Profit = Gross Loss = 0), the widget displays 0 rather than — (Source: BA confirmation, 2026-08-08, QnA_init_docs.md A-13; confirmed visually by wireframe Dashboard - Day 0.png, which shows "Profit Factor (0)").
8. Screen Description
| No. | Field Name | Field Type | Displaying rule / Behaviour rule |
|---|---|---|---|
| 1 | Profit Factor | Label | Displaying rule:- Shown inline in the panel title, e.g. "Profit Factor (1.85)". 0 when no trade has closed yet at the current level. Ref: BR_4.2.4.1.Behaviour rule:- Read-only. Updates live on trade close via PERFORMANCE_UPDATE. |
| 2 | Gross Profit | Label | Displaying rule:- Sum of absolute PnL of all winning closed trades at the current level. Monetary value display: Ref: CR-11. |
| 3 | Gross Loss | Label | Displaying rule:- Sum of absolute PnL of all losing closed trades at the current level. Monetary value display: Ref: CR-11. |
| 4 | Profit / Loss bar | Label | Displaying rule:- A single horizontal bar split into two proportional sections — PROFIT (green, left) and LOSS (red, right) — representing Gross Profit and Gross Loss (Ref: BR_4.2.4.1), with a needle/pointer marking the current split point between the two sections (Source: BA confirmation, 2026-09-03).- Split calculation: Profit section width % = Gross Profit ÷ (Gross Profit + Gross Loss) × 100; Loss section width % = Gross Loss ÷ (Gross Profit + Gross Loss) × 100. The two sections always sum to 100% of the bar's width; the needle sits at the boundary between them (i.e. at Profit section width % measured from the left edge).- First day / no trades yet (Gross Profit = Gross Loss = 0): the bar still renders as two sections — green (left) and red (right) — split evenly at the midpoint, with the needle centered at 50%, and both PROFIT/LOSS labeled $0, consistent with the "Profit Factor (0)" zero-trade clamp (Ref: BR_4.2.4.1). Behaviour rule:- Read-only. Section widths and needle position update live whenever gross_profit/gross_loss change via PERFORMANCE_UPDATE. |
UC_4.2.5 — Win Rate
1. Overview
| Field | Content |
|---|---|
| ID | UC_4.2.5 |
| Use Case | Win Rate |
| Description | Win-rate statistics for a selectable lookback window (10/30/50/100 trades) on the Overview screen — total trades, win rate %, period-scoped profit factor, average win/loss — plus a per-trade win/loss dot grid. Same UC for both SIM and LIVE account types. |
| Zapier Flow | — |
| Zapier Table | — |
| 3rd Party | — |
Dashboard.png Overview screen — "Win Rate" panel, populated state. Dashboard - Day 0.png Overview screen — "Win Rate" panel, no-trades state (empty dot grid).
Motion reference: UC_4.2.5 motion clips
2. Trigger
The Overview screen (Ref: UC_4.1.1) has rendered, or the user changes the lookback filter.
3. Pre-conditions
- User is on the Overview screen (Ref: UC_4.1.1 §1 Overview).
4. Post-conditions
- The widget displays win-rate statistics and the win/loss dot grid for the selected lookback window.
5. Basic Flow
- On Overview screen load, frontend calls
GET /win-rate-grid-data?lookback_count=100(default100, per wireframe default"Last 100 trades"). - Backend returns
{period_total_trades, period_win_rate, period_profit_factor, period_avg_win, period_avg_loss, trade_results: [{ticker, timestamp_utc, pnl, holding_time_seconds, is_winner}...]}—trade_resultsis an array of per-trade objects for the lookback window, not a flat array of booleans (Ref: BR_4.2.5.1). - Frontend renders the stats row (
Total trades,Win rate,Profit factor,Avg win/loss) and the dot grid fromtrade_results(Ref: BR_4.2.5.1). - User changes the lookback dropdown (
10/30/50/100) → frontend re-calls the endpoint with the newlookback_count. - User hovers a dot in the grid → frontend renders a tooltip built from that trade's object (Ref: BR_4.2.5.1).
- On trade close, the
PERFORMANCE_UPDATEWebSocket event pushes an updatedwin_rate_percent— alongside the other performance fields in that same event payload (profit_factor,avg_win,avg_loss,latest_trade,holding_time, etc.) — and the widget'sWin ratestat updates live, without waiting for the nextGET /win-rate-grid-datare-fetch (Source: RFQ_ Stack Trading Prop Tech V7.pdf §12 WebSocket Events,PERFORMANCE_UPDATEpayload; Ref: BR_4.2.5.2).
6. Exceptional Flow
- No closed trades yet: stats show
0/empty values and the dot grid renders with no colored dots (Ref: wireframeDashboard - Day 0.png). - WebSocket/API connection drops, market is closed, or page reload (F5): the widget continues displaying the latest successfully loaded values rather than becoming empty (Ref: CR-14).
7. Business Rules
BR_4.2.5.1: Win/Loss Dot Grid — Per-Trade Data, Dot Coloring, and Hover Tooltip
Data source: trade_results in the Get Win Rate Grid Data response (GET /win-rate-grid-data?lookback_count=) is a chronologically ordered array of per-trade objects — one object per closed trade within the selected lookback window — not a flat array of booleans:
{
"ticker": "ESZ5",
"timestamp_utc": "2026-03-09 09:45 AM",
"pnl": 450.00,
"holding_time_seconds": 870,
"is_winner": true
}(Source: BA-supplied backend spec update, chat, 2026-08-10 — supersedes the earlier trade_results: [boolean...] shape; the backend now delivers individual trade metrics per dot instead of a flat win/loss flag.)
Dot coloring — one dot per trade in the grid, colored per trade object, checked in this order:
- Grey —
pnl = 0(breakeven trade, no gain or loss), regardless ofis_winner. - Green —
is_winner = true(winning trade,pnlnot0). - Red —
is_winner = false(losing trade,pnlnot0).
Hover tooltip — hovering a dot renders a tooltip built directly from that trade's object, no separate lookup:
- Ticker:
ticker. - Trade time:
timestamp_utc. - PnL:
pnl, monetary value display per CR-11. - Holding time: frontend formats
holding_time_secondsinto aXm Ysstring (minutes =floor(seconds ÷ 60), remaining seconds =seconds mod 60) — e.g.870seconds displays as"14m 30s". - Tooltip trigger/dismiss behavior follows CR-10 Tooltip Interaction.
Grid layout: [ASSUMPTION] rows/columns, reading order follow the wireframe's column-major fill pattern (not confirmed by a data field in the response).
BR_4.2.5.2: Win Rate — Live Update via PERFORMANCE_UPDATE WebSocket Event
Per the client-supplied WebSocket events specification (Source: RFQ_ Stack Trading Prop Tech V7.pdf §12 WebSocket Events), the PERFORMANCE_UPDATE event fires on the closure of a trade and carries a JSON payload that includes win_rate_percent (alongside sharpe_ratio, sortino_ratio, profit_factor, avg_win, avg_loss, latest_trade, holding_time, etc.).
- On receiving this event, the frontend updates the Win rate label (§8 row 3) in place, using the event's
win_rate_percentvalue directly — no re-call ofGET /win-rate-grid-datais needed for this field to update. - Same event also drives the live update of BR_4.2.4.1's
profit_factorand appends thelatest_tradeobject as a new dot in the win/loss dot grid (Ref: BR_4.2.5.1) —win_rate_percentupdates in the same push, keeping the stats row and the dot grid in sync on every trade close. - This live-push update applies on top of the lookback-window fetch/re-fetch flow in §5 — it does not replace the
GET /win-rate-grid-data?lookback_count=call on page load or filter change, since only the WebSocket event carries the incremental per-close update (Source: BA confirmation, 2026-09-03).
8. Screen Description
| No. | Field Name | Field Type | Displaying rule / Behaviour rule |
|---|---|---|---|
| 1 | Lookback filter | Dropdown (Single-selection) | Displaying rule:- Options: 10, 30, 50, 100. Default: 100.Behaviour rule:- On selection: re-fetches GET /win-rate-grid-data?lookback_count=. |
| 2 | Total trades | Label | Displaying rule:- period_total_trades for the selected lookback window. |
| 3 | Win rate | Label | Displaying rule:- period_win_rate, shown as a percentage. Formula: (count of winning trades ÷ total trade count) × 100, over the selected lookback window (Source: RFQ_ Stack Trading Prop Tech V7.pdf §Risk Metrics Logic Definitions "Win Rate & RR").Behaviour rule:- Read-only. Updates live on trade close via the PERFORMANCE_UPDATE WebSocket event's win_rate_percent field, without waiting for a lookback-window re-fetch (Ref: BR_4.2.5.2). |
| 4 | Profit factor | Label | Displaying rule:- period_profit_factor for the selected lookback window — distinct from the current-level Profit factor widget. Ref: BR_4.2.4.1. |
| 5 | Avg win/loss | Label | Displaying rule:- period_avg_win / period_avg_loss. Monetary value display: Ref: CR-11. |
| 6 | Win/loss dot grid | Label | Displaying rule:- One dot per trade in trade_results, ordered chronologically. Dot coloring, hover tooltip content, and data source: Ref: BR_4.2.5.1. |
Changelog
| Date | Version | Updated item | Before | After | Notes |
|---|---|---|---|---|---|
| 2026-08-08 | v1 | Initial creation | — | UC_4.2.1 → UC_4.2.5 full SRS (Overview, Trigger, Pre/Post-conditions, Basic/Exceptional Flow, Business Rules, Screen Description) | Init Flow Step 3, Architect — seeQnA_init_docs.md for confirmed A-00→A-15 inputs |
| 2026-08-31 | v2 | UC_4.2.1 §5 Basic Flow, §6 Exceptional Flow, new BR_4.2.1.3 (Snapshot Persistence, Delta Update by Position Identifier, Row Removal on Close), §8 row 1 (Symbol) | POSITION_METRICS described as streaming the full Array<{symbol, ...}> of open positions on every event; no backend snapshot-persistence rule; no row-removal-on-close rule; Exceptional Flow only covered WebSocket disconnect | Backend now holds the latest snapshot so the table restores on load/reload (F5) instead of appearing empty; subsequentPOSITION_METRICS events carry only the affected position's delta, keyed by symbol (position identifier) — Frontend maps the delta into the matching row; a closed-position flag on the delta triggers row removal; Exceptional Flow extended to cover market-closed and F5 alongside disconnect | User instruction (BA session), 2026-08-31 |
| 2026-08-31 | v2 | UC_4.2.2 §6, UC_4.2.3 §6, UC_4.2.4 §6, UC_4.2.5 §6 (Exceptional Flow — WebSocket/API connection drop wording) | Only covered "WebSocket connection drops" / "WebSocket/API connection drops" (client reconnect case) | Extended to also cover "market is closed" and "page reload (F5)" — widget/chart continues showing the latest successfully loaded snapshot/values rather than resetting to empty in either case, per the shared rule now defined atCR-14 | User instruction (BA session), 2026-08-31 — aligned withcommon_rules.md v1.7 CR-14 expansion |
| 2026-08-31 | v3 | UC_4.2.1 §5 Basic Flow (steps 2-4), §7 BR_4.2.1.3, §8 row 1 (Symbol) — keying field for delta update / row removal | Delta update and row removal keyed bysymbol | Corrected: keyed byposition_id (the actual position identifier), not symbol — a trader can hold multiple open positions on the same symbol, so symbol cannot uniquely identify a row; symbol remains a display-only field | User instruction (BA session), 2026-08-31 |
| 2026-08-31 | v4 | UC Index (UC_4.2.3 row), UC_4.2.3 §1 Overview, §5 Basic Flow (steps 2-4), §6 Exceptional Flow, §7 BR_4.2.3.1 (rewritten), new BR_4.2.3.2/BR_4.2.3.3/BR_4.2.3.4/BR_4.2.3.5, §8 row 2 (Equity chart) | Chart plotted raw Daily Balance snapshots only (excludes Unrealized PnL);GET /equity-curve float_value = raw account balance; Y-axis started near/at $0 and auto-scaled to balance min/max only; no Target/Stop overlay; no documented intraday snapshot cadence; 1W/1M returned undefined granularity; no F5-vs-socket-push edge case | [CR] Reformulated as Net Equity = Initial Starting Capital + (Realized PnL + Unrealized PnL) (new BR_4.2.3.1 formula); backend float_value now the absolute Net Equity notional value, not a relative delta; baseline sourced from Max_Forex_Notional (Table A, existing) or Max_Futures_Notional (Table B, ⚠️ flagged as unconfirmed/not yet in documented Table B schema — see BR_4.2.3.1 action item); new BR_4.2.3.2 (frontend performs no client-side math, plots backend values as-is); new BR_4.2.3.3 (Y-axis dynamic auto-scale to actual high/low with padding to keep Target=Profit_Target/Stop=Market_Loss_When_Stopped from GET /current-level-detail visible; full X/Y-axis and baseline color semantics — green above baseline = profit, red below = loss); new BR_4.2.3.4 (30-min intraday snapshot cadence for 1D; 1W aggregates by day, 1M aggregates by week, both using the last snapshot of the closed period; current in-progress day/week stays live); new BR_4.2.3.5 (F5/reload during the gap between snapshot capture and WebSocket push must render the latest already-captured snapshot immediately, per CR-14 pattern) | User instruction (BA session), 2026-08-31 — formalized asCR-20260831-001 (see References/CR/2026-08-31_equity-curve-net-equity-formula/), since this changes the previously RFQ-V7-sourced Daily-Balance-only formula |
| 2026-08-31 | v5 | BR_4.2.3.1 (Max_Futures_Notional baseline sourcing); BR_4.2.3.3 (removed [CR] tag) | Max_Futures_Notional flagged ⚠️ as unconfirmed/not-yet-in-documented-Table-B-schema, with open action item to confirm with client whether new column or alias of Futures_Capital; BR_4.2.3.3 tagged [CR] | Client-confirmed:Max_Futures_Notional is a genuinely new Table B column, distinct from Futures_Capital — Futures_Capital = actual margin cash deposited at the FCM (e.g. $1,250), Max_Futures_Notional = face value/buying power (e.g. $25,000); baseline must use the notional field because prop traders track accounts by notional size, not margin cash. Flag/action item resolved — Table B schema migration (add column) still required before backend implementation. Removed [CR] tag from BR_4.2.3.3: Y-axis auto-scale/Target-Stop overlay is display/rendering behavior, not itself the CR — the CR is the Net Equity formula + baseline field change (BR_4.2.3.1) | Client decision, relayed by user (BA session), 2026-08-31 — resolves the flagged discrepancy from CR-20260831-001 v4 |
| 2026-09-03 | v6 | UC_4.2.4 §8 row 4 (Profit / Loss bar) — section color/position layout | Bar renderedLOSS (red) on the left and PROFIT (green) on the right; needle position measured from the left edge as Loss section width %; first-day/no-trade state showed red (left) / green (right) | Swapped layout:PROFIT (green) now on the left, LOSS (red) on the right; needle position now measured from the left edge as Profit section width %; first-day/no-trade state now shows green (left) / red (right). Formulas and zero-trade clamp behavior (BR_4.2.4.1) unchanged | Source: BA confirmation, 2026-09-03 |
| 2026-09-03 | v7 | UC_4.2.3 §7 BR_4.2.3.1 — Initial Starting Capital baseline sourcing for evaluation (SIM) accounts not yet promoted to LIVE | BR_4.2.3.1 only stated the Table A/Max_Forex_Notional vs. Table B/Max_Futures_Notional baseline split by asset class, without addressing evaluation (SIM) accounts specifically — left ambiguous whether an account still in evaluation used the same lookup | Clarified: evaluation (SIM) accounts use the identical Table A/B lookup — no separate baseline source. The account's current level while in evaluation is Level 1/2/5 per the purchased track (Associate/Accelerate/Advanced, Ref:UC_4.8.2 BR_4.8.2.2), and the baseline is Max_Forex_Notional/Max_Futures_Notional from the Table A/B row for that level | Source: BA confirmation, 2026-09-03 |
| 2026-09-03 | v8 | UC_4.2.3 §7 BR_4.2.3.1 — added explicit "Baseline data source (Table A/B lookup)" explanation; note under the formula on term provenance; formula expanded | The Table A/B lookup mechanism for the baseline was only described inline inside the bulleted list below the formula, with no standalone statement ofhow the chart fetches the baseline; formula had no note on where its other terms come from; formula showed only the 2-term expanded form | Added a lead-in paragraph stating the baseline is looked up live from Table A (Forex Matrix)/Table B (Futures Matrix) by asset class + current level, readingMax_Forex_Notional/Max_Futures_Notional. Added a note directly under the formula clarifying Account Balance and Unrealized PnL are read from the trader's own trading account (live account/position feed) — only Initial Starting Capital comes from the Table A/B lookup. Extended the formula to Net Equity = Initial Starting Capital + (Realized PnL + Unrealized PnL) = Account Balance + Unrealized PnL, with a bullet noting the two forms are equivalent since Account Balance already embeds Initial Starting Capital + Realized PnL | Source: BA confirmation, 2026-09-03 |
| 2026-09-03 | v9 | UC_4.2.5 §5 Basic Flow step 6 (new); new BR_4.2.5.2 (Win Rate — Live Update via PERFORMANCE_UPDATE); §8 row 3 (Win rate) Behaviour rule | Win rate stat (period_win_rate) was only ever fetched via GET /win-rate-grid-data on page load / lookback filter change — no rule stated it also updates live on trade close, even though the client-supplied PERFORMANCE_UPDATE WebSocket payload (RFQ Stack Trading Prop Tech V7.pdf) already carries a win_rate_percent field | Added Basic Flow step 6 and new BR_4.2.5.2 documenting that PERFORMANCE_UPDATE (fired on trade close) pushes win_rate_percent and the frontend updates the Win rate label in place from that field, in addition to the lookback-window fetch flow — same event that already drives BR_4.2.4.1 profit factor and the dot-grid append (BR_4.2.5.1), now confirmed to also carry win_rate_percent in sync. §8 row 3 Behaviour rule added, referencing BR_4.2.5.2 | Source: RFQ_ Stack Trading Prop Tech V7.pdf §12 WebSocket Events (PERFORMANCE_UPDATE payload); BA confirmation 2026-09-03 |