Proof: operator dashboard now labels configured strategy edge as bps plus percent and labels quote opportunity edge as gross edge percent, preventing configured edge_bps=20 from being read as 20%. Assumptions: this is a display-only fix; strategy decisions, relay submissions, pair enablement, edge thresholds, notional limits, inventory, arming, and response policy are unchanged. Still fake: venue-native terminal fill ids and fee-complete realized PnL remain unavailable.
This commit is contained in:
parent
b6646fb7a3
commit
748950a1d8
2 changed files with 30 additions and 7 deletions
|
|
@ -115,6 +115,18 @@ function grossEdgeEstimate(item) {
|
|||
return symbol ? `${item.gross_edge_value} ${symbol}` : item.gross_edge_value;
|
||||
}
|
||||
|
||||
function formatGrossEdgePct(value) {
|
||||
if (value == null || value === '') return 'Gross edge unavailable';
|
||||
return `Gross edge ${value}%`;
|
||||
}
|
||||
|
||||
function formatConfiguredEdgeBps(value, { prefix = true } = {}) {
|
||||
const number = Number(value);
|
||||
if (!Number.isFinite(number)) return prefix ? 'Configured edge unavailable' : 'Unavailable';
|
||||
const label = `${value} bps (${(number / 100).toFixed(2)}%)`;
|
||||
return prefix ? `Configured edge ${label}` : label;
|
||||
}
|
||||
|
||||
function notionalLabel(item) {
|
||||
if (item.notional_display) return item.notional_display;
|
||||
if (item.notional != null) return `${item.notional}${item.notional_symbol ? ` ${item.notional_symbol}` : ''}`;
|
||||
|
|
@ -212,7 +224,8 @@ function LifecycleDetails({ item }) {
|
|||
|
||||
<StageCard at={item.decision_at} status={strategyDecisionStatus(item.decision)} title="2. Strategy decided">
|
||||
<div>{plainCodeLabel(item.decision?.decision_reason || item.reason_code, 'No decision reason recorded')}</div>
|
||||
<div className="status-subtle">{item.gross_edge_pct ? `Edge ${item.gross_edge_pct}%` : 'Edge unavailable'}</div>
|
||||
<div className="status-subtle">{formatGrossEdgePct(item.gross_edge_pct)}</div>
|
||||
<div className="status-subtle">{formatConfiguredEdgeBps(item.edge_bps)}</div>
|
||||
<div className="status-subtle">{notionalLabel(item)}</div>
|
||||
</StageCard>
|
||||
|
||||
|
|
@ -532,7 +545,7 @@ function QuoteLifecycleTable({ items }) {
|
|||
<th>Responded?</th>
|
||||
<th>Result</th>
|
||||
<th>Reason</th>
|
||||
<th>Edge / notional</th>
|
||||
<th>Gross edge / notional</th>
|
||||
<th>Lifecycle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -563,7 +576,8 @@ function QuoteLifecycleTable({ items }) {
|
|||
<div className="status-subtle mono">{item.reason_code || 'reason_unknown'}</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className={Number(item.gross_edge_pct) > 0 ? 'value-positive' : Number(item.gross_edge_pct) < 0 ? 'value-negative' : ''}>{item.gross_edge_pct ? `${item.gross_edge_pct}%` : 'Unavailable'}</div>
|
||||
<div className={Number(item.gross_edge_pct) > 0 ? 'value-positive' : Number(item.gross_edge_pct) < 0 ? 'value-negative' : ''}>{formatGrossEdgePct(item.gross_edge_pct)}</div>
|
||||
<div className="status-subtle">{formatConfiguredEdgeBps(item.edge_bps)}</div>
|
||||
<div className="status-subtle">{notionalLabel(item)}</div>
|
||||
</td>
|
||||
<td>
|
||||
|
|
@ -614,7 +628,7 @@ function SuccessfulTradesTable({ items }) {
|
|||
<tr>
|
||||
<th>Completed</th>
|
||||
<th>Quote id</th>
|
||||
<th>Edge</th>
|
||||
<th>Gross edge</th>
|
||||
<th>Gross edge est.</th>
|
||||
<th>Settlement</th>
|
||||
<th>Realized PnL</th>
|
||||
|
|
@ -631,7 +645,8 @@ function SuccessfulTradesTable({ items }) {
|
|||
<td>{formatTimestamp(item.latest_stage_at)}</td>
|
||||
<td><IdentifierRow label="Quote" value={item.quote_id} /></td>
|
||||
<td>
|
||||
<div>{item.gross_edge_pct ? `${item.gross_edge_pct}%` : 'Unavailable'}</div>
|
||||
<div>{formatGrossEdgePct(item.gross_edge_pct)}</div>
|
||||
<div className="status-subtle">{formatConfiguredEdgeBps(item.edge_bps)}</div>
|
||||
<div className="status-subtle">{notionalLabel(item)}</div>
|
||||
</td>
|
||||
<td>
|
||||
|
|
@ -998,7 +1013,7 @@ function PairConfigSection({ assetCatalog, pairConfig, onControl }) {
|
|||
<tr>
|
||||
<th>Pair</th>
|
||||
<th>Mode</th>
|
||||
<th>Edge</th>
|
||||
<th>Configured edge</th>
|
||||
<th>Limits</th>
|
||||
<th>Route</th>
|
||||
<th>Blocked</th>
|
||||
|
|
@ -1025,7 +1040,7 @@ function PairConfigSection({ assetCatalog, pairConfig, onControl }) {
|
|||
<div className="status-subtle mono">{truncateMiddle(pairId, 42)}</div>
|
||||
</td>
|
||||
<td><Pill label={pair.mode || pair.status} stateLabel={pair.canTrade || pair.can_trade ? 'healthy' : 'warning'} /></td>
|
||||
<td>{strategyConfig.edge_bps ?? 'Unavailable'} bps</td>
|
||||
<td>{formatConfiguredEdgeBps(strategyConfig.edge_bps, { prefix: false })}</td>
|
||||
<td>
|
||||
<div>{strategyConfig.max_notional || 'Unavailable'} max</div>
|
||||
<div className="status-subtle">
|
||||
|
|
|
|||
|
|
@ -132,6 +132,14 @@ test('strategy page exposes maker timing waterfall and competitiveness summaries
|
|||
assert.match(stylesSource, /\.timing-waterfall-table/);
|
||||
});
|
||||
|
||||
test('strategy page distinguishes configured bps from gross quote edge percent', () => {
|
||||
assert.match(strategySource, /formatConfiguredEdgeBps/);
|
||||
assert.match(strategySource, /Configured edge/);
|
||||
assert.match(strategySource, /Gross edge/);
|
||||
assert.match(strategySource, /number \/ 100/);
|
||||
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'),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue