Split strategy dashboard sections
All checks were successful
deploy / deploy (push) Successful in 58s
All checks were successful
deploy / deploy (push) Successful in 58s
Proof: operator dashboard Strategy panels now render as separate sidebar menu destinations for overview, pair config, competitiveness, asset registry, successful trades, and quote lifecycle; static UI coverage and the dashboard bundle verify the routing. Assumptions: this is a UI-only operator cleanup inside the active maker timing and competitiveness turn; it does not change strategy decisions, pair config, execution, persistence, live funds, or response policy. Still fake: venue-native terminal fill ids and fee-complete realized PnL remain unavailable.
This commit is contained in:
parent
04c59ee93d
commit
686b922342
4 changed files with 143 additions and 74 deletions
|
|
@ -31,6 +31,7 @@ function LoadingPanel({ error, onRetry }) {
|
|||
export default function App() {
|
||||
const [state, dispatch] = useReducer(dashboardReducer, initialDashboardState);
|
||||
const currentPage = state.page || state.dashboard?.default_page || 'funds';
|
||||
const isStrategyPage = currentPage === 'strategy' || currentPage.startsWith('strategy-');
|
||||
const isReadyForSocket = Boolean(state.session && state.dashboard);
|
||||
const criticalBanner = null;
|
||||
|
||||
|
|
@ -178,8 +179,12 @@ export default function App() {
|
|||
onControl={submitControl}
|
||||
/>
|
||||
) : null}
|
||||
{currentPage === 'strategy' ? (
|
||||
<StrategyPage onControl={submitControl} strategy={state.dashboard.strategy} />
|
||||
{isStrategyPage ? (
|
||||
<StrategyPage
|
||||
onControl={submitControl}
|
||||
page={currentPage}
|
||||
strategy={state.dashboard.strategy}
|
||||
/>
|
||||
) : null}
|
||||
{currentPage === 'system' ? (
|
||||
<SystemPage onControl={submitControl} system={state.dashboard.system} />
|
||||
|
|
|
|||
|
|
@ -6,8 +6,33 @@ const NAV_ITEMS = [
|
|||
},
|
||||
{
|
||||
page: 'strategy',
|
||||
title: 'Strategy',
|
||||
description: 'Trading state, decision flow, and guarded omissions.',
|
||||
title: 'Strategy overview',
|
||||
description: 'Quote, response, and settlement counters.',
|
||||
},
|
||||
{
|
||||
page: 'strategy-pairs',
|
||||
title: 'Pair config',
|
||||
description: 'Directed pairs, edges, limits, and response policy.',
|
||||
},
|
||||
{
|
||||
page: 'strategy-competitiveness',
|
||||
title: 'Competitiveness',
|
||||
description: 'Timing, quote-age, relay result, and outcome summaries.',
|
||||
},
|
||||
{
|
||||
page: 'strategy-assets',
|
||||
title: 'Asset registry',
|
||||
description: 'Supported-token import and deposit addresses.',
|
||||
},
|
||||
{
|
||||
page: 'strategy-trades',
|
||||
title: 'Successful trades',
|
||||
description: 'Only quotes with proven asset movement.',
|
||||
},
|
||||
{
|
||||
page: 'strategy-lifecycle',
|
||||
title: 'Quote lifecycle',
|
||||
description: 'Incoming quotes and what happened next.',
|
||||
},
|
||||
{
|
||||
page: 'system',
|
||||
|
|
|
|||
|
|
@ -1203,12 +1203,11 @@ function PairConfigSection({ assetCatalog, pairConfig, onControl }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default function StrategyPage({ strategy, onControl }) {
|
||||
function StrategyOverviewSection({ strategy }) {
|
||||
const funnel = strategy.strategy_state.trade_funnel || {};
|
||||
const counts = funnel.counts || {};
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="panel">
|
||||
<div className="panel-head">
|
||||
<div>
|
||||
|
|
@ -1234,16 +1233,11 @@ export default function StrategyPage({ strategy, onControl }) {
|
|||
<MetricCard label="Executor armed" meta={`Paused ${formatBoolean(strategy.executor_state.paused)}`} value={formatBoolean(strategy.executor_state.armed)} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
<PairConfigSection assetCatalog={strategy.asset_catalog} pairConfig={strategy.pair_config} onControl={onControl} />
|
||||
|
||||
<MakerCompetitivenessSection
|
||||
pairConfig={strategy.pair_config}
|
||||
summary={strategy.strategy_state.maker_competitiveness}
|
||||
/>
|
||||
|
||||
<AssetCatalogSection assetCatalog={strategy.asset_catalog} onControl={onControl} />
|
||||
|
||||
function SuccessfulTradesSection({ funnel, counts }) {
|
||||
return (
|
||||
<section className="panel">
|
||||
<div className="panel-head">
|
||||
<div>
|
||||
|
|
@ -1261,7 +1255,11 @@ export default function StrategyPage({ strategy, onControl }) {
|
|||
</div>
|
||||
<SuccessfulTradesTable items={funnel.successful_trades} />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function QuoteLifecycleSection({ items }) {
|
||||
return (
|
||||
<section className="panel full-width-evidence-panel">
|
||||
<div className="panel-head">
|
||||
<div>
|
||||
|
|
@ -1272,8 +1270,33 @@ export default function StrategyPage({ strategy, onControl }) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<QuoteLifecycleTable items={strategy.strategy_state.recent_lifecycle_rows} />
|
||||
<QuoteLifecycleTable items={items} />
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
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 <PairConfigSection assetCatalog={strategy.asset_catalog} pairConfig={strategy.pair_config} onControl={onControl} />;
|
||||
case 'strategy-competitiveness':
|
||||
return (
|
||||
<MakerCompetitivenessSection
|
||||
pairConfig={strategy.pair_config}
|
||||
summary={strategy.strategy_state.maker_competitiveness}
|
||||
/>
|
||||
);
|
||||
case 'strategy-assets':
|
||||
return <AssetCatalogSection assetCatalog={strategy.asset_catalog} onControl={onControl} />;
|
||||
case 'strategy-trades':
|
||||
return <SuccessfulTradesSection counts={counts} funnel={funnel} />;
|
||||
case 'strategy-lifecycle':
|
||||
return <QuoteLifecycleSection items={strategy.strategy_state.recent_lifecycle_rows} />;
|
||||
case 'strategy':
|
||||
default:
|
||||
return <StrategyOverviewSection strategy={strategy} />;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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('<PairConfigSection') < strategySource.indexOf('<AssetCatalogSection'),
|
||||
);
|
||||
test('strategy section panels are split into separate menu destinations', () => {
|
||||
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'/);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue