feat(chart): candle build indicator as direction line #1

Open
u1 wants to merge 31 commits from feat/candle-build-indicator into main
Showing only changes of commit 2a158334bf - Show all commits

View File

@@ -0,0 +1,136 @@
import type { ReactNode } from 'react';
import type { DlobStats } from './useDlobStats';
import type { DlobDepthBandRow } from './useDlobDepthBands';
import type { DlobSlippageRow } from './useDlobSlippage';
import DlobDepthBandsPanel from './DlobDepthBandsPanel';
import DlobSlippageChart from './DlobSlippageChart';
function formatUsd(v: number | null | undefined): string {
if (v == null || !Number.isFinite(v)) return '—';
if (v >= 1_000_000) return `$${(v / 1_000_000).toFixed(2)}M`;
if (v >= 1000) return `$${(v / 1000).toFixed(0)}K`;
if (v >= 1) return `$${v.toFixed(2)}`;
return `$${v.toPrecision(4)}`;
}
function formatBps(v: number | null | undefined): string {
if (v == null || !Number.isFinite(v)) return '—';
return `${v.toFixed(1)} bps`;
}
function formatPct(v: number | null | undefined): string {
if (v == null || !Number.isFinite(v)) return '—';
return `${(v * 100).toFixed(0)}%`;
}
function statusLabel(connected: boolean, error: string | null): ReactNode {
if (error) return <span className="neg">{error}</span>;
return connected ? <span className="pos">live</span> : <span className="muted">offline</span>;
}
export default function DlobDashboard({
market,
stats,
statsConnected,
statsError,
depthBands,
depthBandsConnected,
depthBandsError,
slippageRows,
slippageConnected,
slippageError,
}: {
market: string;
stats: DlobStats | null;
statsConnected: boolean;
statsError: string | null;
depthBands: DlobDepthBandRow[];
depthBandsConnected: boolean;
depthBandsError: string | null;
slippageRows: DlobSlippageRow[];
slippageConnected: boolean;
slippageError: string | null;
}) {
const updatedAt = stats?.updatedAt || depthBands[0]?.updatedAt || slippageRows[0]?.updatedAt || null;
return (
<div className="dlobDash">
<div className="dlobDash__head">
<div className="dlobDash__title">DLOB</div>
<div className="dlobDash__meta">
<span className="dlobDash__market">{market}</span>
<span className="muted">{updatedAt ? `updated ${updatedAt}` : '—'}</span>
</div>
</div>
<div className="dlobDash__statuses">
<div className="dlobStatus">
<span className="dlobStatus__label">stats</span>
<span className="dlobStatus__value">{statusLabel(statsConnected, statsError)}</span>
</div>
<div className="dlobStatus">
<span className="dlobStatus__label">depth bands</span>
<span className="dlobStatus__value">{statusLabel(depthBandsConnected, depthBandsError)}</span>
</div>
<div className="dlobStatus">
<span className="dlobStatus__label">slippage</span>
<span className="dlobStatus__value">{statusLabel(slippageConnected, slippageError)}</span>
</div>
</div>
<div className="dlobDash__grid">
<div className="dlobKpi">
<div className="dlobKpi__label">Bid</div>
<div className="dlobKpi__value pos">{formatUsd(stats?.bestBid ?? null)}</div>
</div>
<div className="dlobKpi">
<div className="dlobKpi__label">Ask</div>
<div className="dlobKpi__value neg">{formatUsd(stats?.bestAsk ?? null)}</div>
</div>
<div className="dlobKpi">
<div className="dlobKpi__label">Mid</div>
<div className="dlobKpi__value">{formatUsd(stats?.mid ?? null)}</div>
</div>
<div className="dlobKpi">
<div className="dlobKpi__label">Spread</div>
<div className="dlobKpi__value">{formatBps(stats?.spreadBps ?? null)}</div>
<div className="dlobKpi__sub muted">{formatUsd(stats?.spreadAbs ?? null)}</div>
</div>
<div className="dlobKpi">
<div className="dlobKpi__label">Depth (bid/ask)</div>
<div className="dlobKpi__value">
<span className="pos">{formatUsd(stats?.depthBidUsd ?? null)}</span>{' '}
<span className="muted">/</span> <span className="neg">{formatUsd(stats?.depthAskUsd ?? null)}</span>
</div>
</div>
<div className="dlobKpi">
<div className="dlobKpi__label">Imbalance</div>
<div className="dlobKpi__value">{formatPct(stats?.imbalance ?? null)}</div>
<div className="dlobKpi__sub muted">[-1..1]</div>
</div>
</div>
<div className="dlobDash__panes">
<div className="dlobDash__pane">
<DlobDepthBandsPanel rows={depthBands} />
</div>
<div className="dlobDash__pane">
<div className="dlobSlippage">
<div className="dlobSlippage__head">
<div className="dlobSlippage__title">Slippage (impact bps)</div>
<div className="dlobSlippage__meta muted">by size (USD)</div>
</div>
{slippageRows.length ? (
<div className="dlobSlippage__chartWrap">
<DlobSlippageChart rows={slippageRows} />
</div>
) : (
<div className="dlobSlippage__empty muted">No slippage rows yet.</div>
)}
</div>
</div>
</div>
</div>
);
}