Keep dashboard updates live without jitter
All checks were successful
deploy / deploy (push) Successful in 1m3s

Proof: Quote lifecycle and competitiveness now update live while unpaused, with fixed row slots and clamped cells carrying the jitter prevention; static UI coverage, full npm test, and the operator dashboard build validate the correction.

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 18:15:05 +02:00
parent cf4160245f
commit 7b2f31fd4d
2 changed files with 29 additions and 34 deletions

View file

@ -8,11 +8,9 @@ import { formatAgeFromTimestamp, formatBoolean, formatEur, formatTimestamp, trun
const RESPONDED_STATES = new Set(['submitted', 'awaiting_outcome', 'not_filled', 'completed']);
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;
const QUOTE_LIFECYCLE_REFRESH_MS = 5_000;
const QUOTE_LIFECYCLE_ROW_COUNT = 20;
async function copyIdentifier(value) {
@ -24,6 +22,17 @@ async function copyIdentifier(value) {
}
}
function useNow(intervalMs = 1000) {
const [now, setNow] = useState(() => Date.now());
useEffect(() => {
const timer = window.setInterval(() => setNow(Date.now()), intervalMs);
return () => window.clearInterval(timer);
}, [intervalMs]);
return now;
}
function formatRelativeAge(value, now) {
const age = formatAgeFromTimestamp(value, now);
return age === 'Unavailable' ? 'Age unavailable' : `${age} ago`;
@ -274,7 +283,6 @@ function fixedRows(items, count) {
function MakerCompetitivenessSection({ summary, pairConfig }) {
const latestSummaryRef = useRef(summary || {});
const [displaySummary, setDisplaySummary] = useState(() => summary || {});
const [displayUpdatedAt, setDisplayUpdatedAt] = useState(() => new Date().toISOString());
const [updatesPaused, setUpdatesPaused] = useState(false);
const total = displaySummary?.total || {};
const groups = displaySummary?.groups || [];
@ -290,23 +298,14 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
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]);
if (!updatesPaused) setDisplaySummary(summary || {});
}, [summary, updatesPaused]);
function toggleUpdatesPaused() {
setUpdatesPaused((current) => {
const nextPaused = !current;
if (current) {
setDisplaySummary(latestSummaryRef.current || {});
setDisplayUpdatedAt(new Date().toISOString());
}
return nextPaused;
});
@ -325,7 +324,7 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
<div className="pills">
<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={updatesPaused ? 'Updates paused' : 'Timed snapshot'} stateLabel={updatesPaused ? 'warning' : 'healthy'} />
<Pill label={updatesPaused ? 'Updates paused' : 'Live updates'} stateLabel={updatesPaused ? 'warning' : 'healthy'} />
<button
aria-pressed={updatesPaused}
className="button secondary trace-copy-button"
@ -337,7 +336,7 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
</div>
</div>
<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.
Live rows update as they arrive. Fixed row slots and clamped cells keep the table height stable.
</div>
<div className="metric-stack">
<MetricCard label="Accepted" meta={formatRate(total.accepted_rate)} value={String(total.accepted_count || 0)} />
@ -564,23 +563,17 @@ function QuoteLifecycleTable({ items }) {
const latestItemsRef = useRef(items || []);
const [quoteDisplayPaused, setQuoteDisplayPaused] = useState(false);
const [displayItems, setDisplayItems] = useState(() => items || []);
const liveNow = useNow();
const [displayNow, setDisplayNow] = useState(() => Date.now());
const [displayUpdatedAt, setDisplayUpdatedAt] = useState(() => new Date().toISOString());
useEffect(() => {
latestItemsRef.current = items || [];
}, [items]);
if (!quoteDisplayPaused) setDisplayItems(items || []);
}, [items, quoteDisplayPaused]);
useEffect(() => {
if (quoteDisplayPaused) return undefined;
const timer = window.setInterval(() => {
const now = Date.now();
setDisplayItems(latestItemsRef.current || []);
setDisplayNow(now);
setDisplayUpdatedAt(new Date(now).toISOString());
}, QUOTE_LIFECYCLE_REFRESH_MS);
return () => window.clearInterval(timer);
}, [quoteDisplayPaused]);
if (!quoteDisplayPaused) setDisplayNow(liveNow);
}, [liveNow, quoteDisplayPaused]);
const rejectedCount = useMemo(
() => displayItems.filter((item) => isStrategyRejected(item)).length,
@ -607,10 +600,8 @@ function QuoteLifecycleTable({ items }) {
}
function applyLatestLifecycleDisplay() {
const now = Date.now();
setDisplayItems(latestItemsRef.current || []);
setDisplayNow(now);
setDisplayUpdatedAt(new Date(now).toISOString());
setDisplayNow(Date.now());
}
function toggleQuoteDisplayPaused() {
@ -637,10 +628,10 @@ function QuoteLifecycleTable({ items }) {
>
{quoteDisplayPaused ? 'Resume display' : 'Pause display'}
</button>
<Pill label={quoteDisplayPaused ? 'Display paused' : 'Timed snapshot'} stateLabel={quoteDisplayPaused ? 'warning' : 'healthy'} />
<Pill label={quoteDisplayPaused ? 'Display paused' : 'Live updates'} stateLabel={quoteDisplayPaused ? 'warning' : 'healthy'} />
</div>
<div className="status-subtle quote-lifecycle-snapshot-note">
Display snapshot {formatTimestamp(displayUpdatedAt)}. Live rows are applied every {QUOTE_LIFECYCLE_REFRESH_MS / 1000}s to keep table height stable.
Live rows update as they arrive. Fixed row slots and clamped cells keep the table height stable.
</div>
<TableFrame className="quote-lifecycle-frame">

View file

@ -27,9 +27,12 @@ test('strategy page owns consolidated quote lifecycle and successful trade table
assert.match(strategySource, /quoteDisplayPaused/);
assert.match(strategySource, /Pause display/);
assert.match(strategySource, /Resume display/);
assert.match(strategySource, /QUOTE_LIFECYCLE_REFRESH_MS/);
assert.match(strategySource, /QUOTE_LIFECYCLE_ROW_COUNT/);
assert.match(strategySource, /latestItemsRef/);
assert.match(strategySource, /setDisplayItems\(items \|\| \[\]\)/);
assert.match(strategySource, /useNow\(\)/);
assert.match(strategySource, /Live updates/);
assert.match(strategySource, /Live rows update as they arrive/);
assert.match(strategySource, /visibleRows/);
assert.match(strategySource, /quote-lifecycle-placeholder-row/);
assert.match(stylesSource, /\.quote-lifecycle-table tbody tr\.quote-lifecycle-row/);
@ -144,7 +147,8 @@ test('strategy page exposes maker timing waterfall and competitiveness summaries
assert.match(strategySource, /Resume updates/);
assert.match(strategySource, /displaySummary/);
assert.match(strategySource, /fixedRows/);
assert.match(strategySource, /COMPETITIVENESS_REFRESH_MS/);
assert.match(strategySource, /latestSummaryRef/);
assert.match(strategySource, /Live updates/);
assert.match(strategySource, /latencyStageRows/);
assert.match(stylesSource, /\.metric-stack/);
assert.match(stylesSource, /\.competitiveness-table-stack/);