Stabilize competitiveness dashboard
All checks were successful
deploy / deploy (push) Successful in 1m3s

Proof: Competitiveness now renders from a timed display snapshot with fixed row slots, a pause/resume control, and full-width stacked cards and tables; static UI coverage, full npm test, and the operator dashboard build validate the cleanup.

Assumptions: this is a UI-only operator cleanup for the active maker timing and competitiveness turn; it does not change strategy decisions, execution, DB config, 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:
philipp 2026-05-19 17:52:33 +02:00
parent 686b922342
commit 49187379ef
3 changed files with 231 additions and 82 deletions

View file

@ -1,4 +1,4 @@
import { Fragment, useEffect, useMemo, useState } from 'react'; import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import EmptyState from '../components/EmptyState.jsx'; import EmptyState from '../components/EmptyState.jsx';
import MetricCard from '../components/MetricCard.jsx'; import MetricCard from '../components/MetricCard.jsx';
@ -8,6 +8,10 @@ import { formatAgeFromTimestamp, formatBoolean, formatEur, formatTimestamp, trun
const RESPONDED_STATES = new Set(['submitted', 'awaiting_outcome', 'not_filled', 'completed']); const RESPONDED_STATES = new Set(['submitted', 'awaiting_outcome', 'not_filled', 'completed']);
const TRADING_PAIR_MODES = new Set(['maker', 'taker', 'both']); const TRADING_PAIR_MODES = new Set(['maker', 'taker', 'both']);
const COMPETITIVENESS_REFRESH_MS = 5_000;
const COMPETITIVENESS_GROUP_ROW_COUNT = 12;
const COMPETITIVENESS_DETAIL_ROW_COUNT = 8;
const COMPETITIVENESS_LATENCY_ROW_COUNT = 6;
async function copyIdentifier(value) { async function copyIdentifier(value) {
if (!value || !navigator?.clipboard?.writeText) return; if (!value || !navigator?.clipboard?.writeText) return;
@ -270,12 +274,52 @@ function pairDisplayLabel(pairId, pairConfig) {
return `${pair.asset_in_symbol || pair.asset_in || pair.assetIn} -> ${pair.asset_out_symbol || pair.asset_out || pair.assetOut}`; return `${pair.asset_in_symbol || pair.asset_in || pair.assetIn} -> ${pair.asset_out_symbol || pair.asset_out || pair.assetOut}`;
} }
function fixedRows(items, count) {
const rows = (items || []).slice(0, count);
while (rows.length < count) rows.push(null);
return rows;
}
function MakerCompetitivenessSection({ summary, pairConfig }) { function MakerCompetitivenessSection({ summary, pairConfig }) {
const total = summary?.total || {}; const latestSummaryRef = useRef(summary || {});
const groups = summary?.groups || []; const [displaySummary, setDisplaySummary] = useState(() => summary || {});
const ageBuckets = summary?.age_buckets || []; const [displayUpdatedAt, setDisplayUpdatedAt] = useState(() => new Date().toISOString());
const latestErrors = summary?.latest_errors || []; const [updatesPaused, setUpdatesPaused] = useState(false);
const policySkips = summary?.policy_skips || []; const total = displaySummary?.total || {};
const groups = displaySummary?.groups || [];
const ageBuckets = displaySummary?.age_buckets || [];
const latestErrors = displaySummary?.latest_errors || [];
const policySkips = displaySummary?.policy_skips || [];
const latencyStages = displaySummary?.latency_stages || [];
const groupRows = fixedRows(groups, COMPETITIVENESS_GROUP_ROW_COUNT);
const ageBucketRows = fixedRows(ageBuckets, COMPETITIVENESS_GROUP_ROW_COUNT);
const latestErrorRows = fixedRows(latestErrors, COMPETITIVENESS_DETAIL_ROW_COUNT);
const policySkipRows = fixedRows(policySkips, COMPETITIVENESS_DETAIL_ROW_COUNT);
const latencyStageRows = fixedRows(latencyStages, COMPETITIVENESS_LATENCY_ROW_COUNT);
useEffect(() => {
latestSummaryRef.current = summary || {};
}, [summary]);
useEffect(() => {
if (updatesPaused) return undefined;
const timer = window.setInterval(() => {
setDisplaySummary(latestSummaryRef.current || {});
setDisplayUpdatedAt(new Date().toISOString());
}, COMPETITIVENESS_REFRESH_MS);
return () => window.clearInterval(timer);
}, [updatesPaused]);
function toggleUpdatesPaused() {
setUpdatesPaused((current) => {
const nextPaused = !current;
if (current) {
setDisplaySummary(latestSummaryRef.current || {});
setDisplayUpdatedAt(new Date().toISOString());
}
return nextPaused;
});
}
return ( return (
<section className="panel"> <section className="panel">
@ -290,17 +334,29 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
<div className="pills"> <div className="pills">
<Pill label={`${total.count || 0} rows`} stateLabel="info" /> <Pill label={`${total.count || 0} rows`} stateLabel="info" />
<Pill label={`${total.quote_not_found_or_finished_count || 0} stale/finished`} stateLabel={(total.quote_not_found_or_finished_count || 0) > 0 ? 'warning' : 'unknown'} /> <Pill label={`${total.quote_not_found_or_finished_count || 0} stale/finished`} stateLabel={(total.quote_not_found_or_finished_count || 0) > 0 ? 'warning' : 'unknown'} />
<Pill label={updatesPaused ? 'Updates paused' : 'Timed snapshot'} stateLabel={updatesPaused ? 'warning' : 'healthy'} />
<button
aria-pressed={updatesPaused}
className="button secondary trace-copy-button"
onClick={toggleUpdatesPaused}
type="button"
>
{updatesPaused ? 'Resume updates' : 'Pause updates'}
</button>
</div> </div>
</div> </div>
<div className="metric-grid"> <div className="status-subtle competitiveness-snapshot-note">
Display snapshot {formatTimestamp(displayUpdatedAt)}. Live updates are applied every {COMPETITIVENESS_REFRESH_MS / 1000}s to keep table height stable.
</div>
<div className="metric-stack">
<MetricCard label="Accepted" meta={formatRate(total.accepted_rate)} value={String(total.accepted_count || 0)} /> <MetricCard label="Accepted" meta={formatRate(total.accepted_rate)} value={String(total.accepted_count || 0)} />
<MetricCard label="Relay failed" meta="Executor reached result" value={String(total.relay_failed_count || 0)} /> <MetricCard label="Relay failed" meta="Executor reached result" value={String(total.relay_failed_count || 0)} />
<MetricCard label="Already finished" meta={formatRate(total.stale_or_finished_rate)} value={String(total.quote_not_found_or_finished_count || 0)} /> <MetricCard label="Already finished" meta={formatRate(total.stale_or_finished_rate)} value={String(total.quote_not_found_or_finished_count || 0)} />
<MetricCard label="Policy skips" meta="No relay submission" value={String(total.policy_skip_count || 0)} /> <MetricCard label="Policy skips" meta="No relay submission" value={String(total.policy_skip_count || 0)} />
</div> </div>
<TableFrame> <TableFrame className="competitiveness-frame">
<table> <table className="competitiveness-table competitiveness-groups-table">
<thead> <thead>
<tr> <tr>
<th>Pair</th> <th>Pair</th>
@ -315,10 +371,17 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{groups.length ? groups.slice(0, 12).map((group, index) => { {groupRows.map((group, index) => {
if (!group) {
return (
<tr className="competitiveness-placeholder-row" key={`group-placeholder:${index}`}>
<td colSpan={9}>{index === 0 && !groups.length ? 'No competitiveness rows are available yet.' : ''}</td>
</tr>
);
}
const quoteToRelay = group.latency_stages?.find((stage) => stage.stage === 'quote_to_relay_result_ms'); const quoteToRelay = group.latency_stages?.find((stage) => stage.stage === 'quote_to_relay_result_ms');
return ( return (
<tr key={`${group.pair}:${group.direction}:${group.request_kind}:${group.edge_bps ?? 'edge'}:${group.result_code}:${group.quote_age_bucket}:${index}`}> <tr key={`${group.pair}:${group.direction}:${group.request_kind}:${group.edge_bps ?? 'edge'}:${group.result_code}:${group.quote_age_bucket}:${group.notional_bucket}:${group.outcome_status}`}>
<td> <td>
<div>{pairDisplayLabel(group.pair, pairConfig)}</div> <div>{pairDisplayLabel(group.pair, pairConfig)}</div>
<div className="status-subtle mono">{truncateMiddle(group.pair || '', 42)}</div> <div className="status-subtle mono">{truncateMiddle(group.pair || '', 42)}</div>
@ -345,16 +408,14 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
</td> </td>
</tr> </tr>
); );
}) : ( })}
<tr><td colSpan={9}>No competitiveness rows are available yet.</td></tr>
)}
</tbody> </tbody>
</table> </table>
</TableFrame> </TableFrame>
<div className="two-column-grid"> <div className="stack-grid competitiveness-table-stack">
<TableFrame> <TableFrame className="competitiveness-frame">
<table> <table className="competitiveness-table competitiveness-latency-table">
<thead> <thead>
<tr> <tr>
<th>Latency stage</th> <th>Latency stage</th>
@ -364,22 +425,29 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{(summary?.latency_stages || []).length ? summary.latency_stages.map((stage) => ( {latencyStageRows.map((stage, index) => {
<tr key={stage.stage}> if (!stage) {
<td>{stageLabel(stage.stage)}</td> return (
<td>{formatTimingMs(stage.p50_ms)}</td> <tr className="competitiveness-placeholder-row" key={`latency-stage-placeholder:${index}`}>
<td>{formatTimingMs(stage.p90_ms)}</td> <td colSpan={4}>{index === 0 && !latencyStages.length ? 'No stage timing percentiles are available yet.' : ''}</td>
<td>{formatTimingMs(stage.p99_ms)}</td> </tr>
</tr> );
)) : ( }
<tr><td colSpan={4}>No stage timing percentiles are available yet.</td></tr> return (
)} <tr key={stage.stage}>
<td>{stageLabel(stage.stage)}</td>
<td>{formatTimingMs(stage.p50_ms)}</td>
<td>{formatTimingMs(stage.p90_ms)}</td>
<td>{formatTimingMs(stage.p99_ms)}</td>
</tr>
);
})}
</tbody> </tbody>
</table> </table>
</TableFrame> </TableFrame>
<TableFrame> <TableFrame className="competitiveness-frame">
<table> <table className="competitiveness-table competitiveness-age-table">
<thead> <thead>
<tr> <tr>
<th>Age bucket</th> <th>Age bucket</th>
@ -389,27 +457,34 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{ageBuckets.length ? ageBuckets.slice(0, 12).map((bucket, index) => ( {ageBucketRows.map((bucket, index) => {
<tr key={`${bucket.pair}:${bucket.quote_age_bucket}:${bucket.outcome_status}:${index}`}> if (!bucket) {
<td> return (
<div>{bucket.quote_age_bucket}</div> <tr className="competitiveness-placeholder-row" key={`age-bucket-placeholder:${index}`}>
<div className="status-subtle">{pairDisplayLabel(bucket.pair, pairConfig)}</div> <td colSpan={4}>{index === 0 && !ageBuckets.length ? 'No quote-age buckets are available yet.' : ''}</td>
</td> </tr>
<td>{plainCodeLabel(bucket.outcome_status)}</td> );
<td>{bucket.count}</td> }
<td>{bucket.accepted_count || 0}</td> return (
</tr> <tr key={`${bucket.pair}:${bucket.quote_age_bucket}:${bucket.outcome_status}:${bucket.notional_bucket || ''}`}>
)) : ( <td>
<tr><td colSpan={4}>No quote-age buckets are available yet.</td></tr> <div>{bucket.quote_age_bucket}</div>
)} <div className="status-subtle">{pairDisplayLabel(bucket.pair, pairConfig)}</div>
</td>
<td>{plainCodeLabel(bucket.outcome_status)}</td>
<td>{bucket.count}</td>
<td>{bucket.accepted_count || 0}</td>
</tr>
);
})}
</tbody> </tbody>
</table> </table>
</TableFrame> </TableFrame>
</div> </div>
<div className="two-column-grid"> <div className="stack-grid competitiveness-table-stack">
<TableFrame> <TableFrame className="competitiveness-frame">
<table> <table className="competitiveness-table competitiveness-detail-table">
<thead> <thead>
<tr> <tr>
<th>Latest relay errors</th> <th>Latest relay errors</th>
@ -418,28 +493,39 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{latestErrors.length ? latestErrors.map((error) => ( {latestErrorRows.map((error, index) => {
<tr key={`${error.quote_id}:${error.result_at}`}> if (!error) {
<td> return (
<IdentifierRow label="Quote" value={error.quote_id} /> <tr className="competitiveness-placeholder-row" key={`latest-error-placeholder:${index}`}>
<div className="status-subtle">{pairDisplayLabel(error.pair, pairConfig)}</div> <td colSpan={3}>{index === 0 && !latestErrors.length ? 'No relay errors are available yet.' : ''}</td>
<div className="status-subtle">{plainCodeLabel(error.failure_category || error.result_code)}</div> </tr>
</td> );
<td> }
<div>{formatTimingMs(error.quote_age_ms) || 'Unavailable'}</div> return (
<div className="status-subtle">{error.quote_age_bucket}</div> <tr key={`${error.quote_id}:${error.result_at}`}>
</td> <td>
<td>{error.error_message || 'Error text unavailable'}</td> <IdentifierRow label="Quote" value={error.quote_id} />
</tr> <div className="status-subtle">{pairDisplayLabel(error.pair, pairConfig)}</div>
)) : ( <div className="status-subtle">{plainCodeLabel(error.failure_category || error.result_code)}</div>
<tr><td colSpan={3}>No relay errors are available yet.</td></tr> </td>
)} <td>
<div>{formatTimingMs(error.quote_age_ms) || 'Unavailable'}</div>
<div className="status-subtle">{error.quote_age_bucket}</div>
</td>
<td>
<div className="competitiveness-error-text" title={error.error_message || ''}>
{error.error_message || 'Error text unavailable'}
</div>
</td>
</tr>
);
})}
</tbody> </tbody>
</table> </table>
</TableFrame> </TableFrame>
<TableFrame> <TableFrame className="competitiveness-frame">
<table> <table className="competitiveness-table competitiveness-detail-table">
<thead> <thead>
<tr> <tr>
<th>Policy skips</th> <th>Policy skips</th>
@ -448,24 +534,31 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{policySkips.length ? policySkips.map((skip) => ( {policySkipRows.map((skip, index) => {
<tr key={`${skip.quote_id}:${skip.decision_at}`}> if (!skip) {
<td> return (
<IdentifierRow label="Quote" value={skip.quote_id} /> <tr className="competitiveness-placeholder-row" key={`policy-skip-placeholder:${index}`}>
<div className="status-subtle">{plainCodeLabel(skip.reason_code)}</div> <td colSpan={3}>{index === 0 && !policySkips.length ? 'No policy skips are available yet.' : ''}</td>
</td> </tr>
<td> );
<div>{formatTimingMs(skip.quote_age_ms) || 'Unavailable'}</div> }
<div className="status-subtle">{`max ${formatTimingMs(skip.max_quote_age_ms) || 'Unavailable'}`}</div> return (
</td> <tr key={`${skip.quote_id}:${skip.decision_at}`}>
<td> <td>
<div>{skip.pair_config_version ? `v${skip.pair_config_version}` : 'Version unavailable'}</div> <IdentifierRow label="Quote" value={skip.quote_id} />
<div className="status-subtle mono">{truncateMiddle(skip.pair_config_id || '', 36)}</div> <div className="status-subtle">{plainCodeLabel(skip.reason_code)}</div>
</td> </td>
</tr> <td>
)) : ( <div>{formatTimingMs(skip.quote_age_ms) || 'Unavailable'}</div>
<tr><td colSpan={3}>No policy skips are available yet.</td></tr> <div className="status-subtle">{`max ${formatTimingMs(skip.max_quote_age_ms) || 'Unavailable'}`}</div>
)} </td>
<td>
<div>{skip.pair_config_version ? `v${skip.pair_config_version}` : 'Version unavailable'}</div>
<div className="status-subtle mono">{truncateMiddle(skip.pair_config_id || '', 36)}</div>
</td>
</tr>
);
})}
</tbody> </tbody>
</table> </table>
</TableFrame> </TableFrame>

View file

@ -216,6 +216,12 @@ select {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
} }
.metric-stack {
display: grid;
gap: 14px;
grid-template-columns: 1fr;
}
.metric-card { .metric-card {
min-width: 0; min-width: 0;
padding: 16px; padding: 16px;
@ -272,6 +278,45 @@ select {
margin-top: 14px; margin-top: 14px;
} }
.competitiveness-snapshot-note {
margin-bottom: 14px;
}
.competitiveness-table-stack {
margin-top: 14px;
}
.competitiveness-table {
table-layout: fixed;
min-width: 1040px;
}
.competitiveness-table tbody tr {
height: 64px;
}
.competitiveness-detail-table tbody tr {
height: 88px;
}
.competitiveness-table td {
overflow: hidden;
}
.competitiveness-placeholder-row td {
height: inherit;
color: var(--muted);
}
.competitiveness-error-text {
display: -webkit-box;
max-height: 60px;
overflow: hidden;
overflow-wrap: anywhere;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
.checkbox-row { .checkbox-row {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;

View file

@ -132,7 +132,18 @@ test('strategy page exposes maker timing waterfall and competitiveness summaries
assert.match(strategySource, /maker_competitiveness/); assert.match(strategySource, /maker_competitiveness/);
assert.match(strategySource, /pairDisplayLabel/); assert.match(strategySource, /pairDisplayLabel/);
assert.match(strategySource, /group\.edge_bps/); assert.match(strategySource, /group\.edge_bps/);
assert.match(stylesSource, /\.two-column-grid/); assert.match(strategySource, /Pause updates/);
assert.match(strategySource, /Resume updates/);
assert.match(strategySource, /displaySummary/);
assert.match(strategySource, /fixedRows/);
assert.match(strategySource, /COMPETITIVENESS_REFRESH_MS/);
assert.match(strategySource, /latencyStageRows/);
assert.match(stylesSource, /\.metric-stack/);
assert.match(stylesSource, /\.competitiveness-table-stack/);
assert.match(stylesSource, /\.competitiveness-table tbody tr/);
assert.match(stylesSource, /\.competitiveness-detail-table tbody tr/);
assert.match(stylesSource, /\.competitiveness-error-text/);
assert.match(stylesSource, /\.competitiveness-placeholder-row td/);
assert.match(stylesSource, /\.timing-waterfall-table/); assert.match(stylesSource, /\.timing-waterfall-table/);
}); });