-
Trading evidence
-
Quotes, responses, and proven trades
-
- One place for quote truth: every row starts at the incoming quote, then shows whether we responded, why not, and whether any asset movement was proven.
-
+
+
+
+
Trading evidence
+
Quotes, responses, and proven trades
+
+ One place for quote truth: every row starts at the incoming quote, then shows whether we responded, why not, and whether any asset movement was proven.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Successful trades only
-
Trades with proven asset movement
-
- This table excludes submitted-only quote responses. Realized PnL remains unavailable until fees and venue-native terminal fills are stored.
-
-
-
-
0 ? 'healthy' : 'unknown'} />
-
- 0 ? 'warning' : 'unknown'} />
-
-
-
-
-
-
-
-
-
Quote lifecycle
-
Incoming quotes and what happened next
-
- Full-width quote table: incoming quote, response decision, result, decisive reason, and expandable lifecycle stages.
-
-
-
-
-
- >
+
+
+
+
+
+
+
+
+
+
+
);
}
+
+function SuccessfulTradesSection({ funnel, counts }) {
+ return (
+
+
+
+
Successful trades only
+
Trades with proven asset movement
+
+ This table excludes submitted-only quote responses. Realized PnL remains unavailable until fees and venue-native terminal fills are stored.
+
+
+
+
0 ? 'healthy' : 'unknown'} />
+
+ 0 ? 'warning' : 'unknown'} />
+
+
+
+
+ );
+}
+
+function QuoteLifecycleSection({ items }) {
+ return (
+
+
+
+
Quote lifecycle
+
Incoming quotes and what happened next
+
+ Full-width quote table: incoming quote, response decision, result, decisive reason, and expandable lifecycle stages.
+
+
+
+
+
+ );
+}
+
+export default function StrategyPage({ strategy, onControl, page = 'strategy' }) {
+ const funnel = strategy.strategy_state.trade_funnel || {};
+ const counts = funnel.counts || {};
+
+ switch (page) {
+ case 'strategy-pairs':
+ return
;
+ case 'strategy-competitiveness':
+ return (
+
+ );
+ case 'strategy-assets':
+ return
;
+ case 'strategy-trades':
+ return
;
+ case 'strategy-lifecycle':
+ return
;
+ case 'strategy':
+ default:
+ return
;
+ }
+}
diff --git a/test/operator-dashboard-ui-static.test.mjs b/test/operator-dashboard-ui-static.test.mjs
index c10aa0c..ae4ab6d 100644
--- a/test/operator-dashboard-ui-static.test.mjs
+++ b/test/operator-dashboard-ui-static.test.mjs
@@ -9,6 +9,7 @@ const serviceCardSource = readFileSync(new URL('../src/operator-dashboard/static
const statusBarSource = readFileSync(new URL('../src/operator-dashboard/static/components/StatusBar.jsx', import.meta.url), 'utf8');
const systemSource = readFileSync(new URL('../src/operator-dashboard/static/pages/SystemPage.jsx', import.meta.url), 'utf8');
const appSource = readFileSync(new URL('../src/operator-dashboard/static/App.jsx', import.meta.url), 'utf8');
+const navSource = readFileSync(new URL('../src/operator-dashboard/static/components/NavRail.jsx', import.meta.url), 'utf8');
test('strategy page owns consolidated quote lifecycle and successful trade tables', () => {
assert.match(strategySource, /Quote lifecycle/);
@@ -143,10 +144,25 @@ test('strategy page distinguishes configured bps from gross quote edge percent',
assert.doesNotMatch(strategySource, /Edge \$\{item\.edge_bps\}%/);
});
-test('pair controls are rendered before the long asset catalog table', () => {
- assert.ok(
- strategySource.indexOf('
{
+ assert.match(navSource, /Strategy overview/);
+ assert.match(navSource, /Pair config/);
+ assert.match(navSource, /Competitiveness/);
+ assert.match(navSource, /Asset registry/);
+ assert.match(navSource, /Successful trades/);
+ assert.match(navSource, /Quote lifecycle/);
+ assert.match(navSource, /page: 'strategy-pairs'/);
+ assert.match(navSource, /page: 'strategy-competitiveness'/);
+ assert.match(navSource, /page: 'strategy-assets'/);
+ assert.match(navSource, /page: 'strategy-trades'/);
+ assert.match(navSource, /page: 'strategy-lifecycle'/);
+ assert.match(appSource, /currentPage\.startsWith\('strategy-'\)/);
+ assert.match(appSource, /page=\{currentPage\}/);
+ assert.match(strategySource, /case 'strategy-pairs'/);
+ assert.match(strategySource, /case 'strategy-competitiveness'/);
+ assert.match(strategySource, /case 'strategy-assets'/);
+ assert.match(strategySource, /case 'strategy-trades'/);
+ assert.match(strategySource, /case 'strategy-lifecycle'/);
});