feat(visualizer): add DLOB slippage subscription hook
This commit is contained in:
137
apps/visualizer/src/features/market/useDlobSlippage.ts
Normal file
137
apps/visualizer/src/features/market/useDlobSlippage.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { subscribeGraphqlWs } from '../../lib/graphqlWs';
|
||||
|
||||
export type DlobSlippageRow = {
|
||||
marketName: string;
|
||||
side: 'buy' | 'sell';
|
||||
sizeUsd: number;
|
||||
midPrice: number | null;
|
||||
vwapPrice: number | null;
|
||||
worstPrice: number | null;
|
||||
filledUsd: number | null;
|
||||
filledBase: number | null;
|
||||
impactBps: number | null;
|
||||
levelsConsumed: number | null;
|
||||
fillPct: number | null;
|
||||
updatedAt: string | null;
|
||||
};
|
||||
|
||||
function toNum(v: unknown): number | null {
|
||||
if (v == null) return null;
|
||||
if (typeof v === 'number') return Number.isFinite(v) ? v : null;
|
||||
if (typeof v === 'string') {
|
||||
const s = v.trim();
|
||||
if (!s) return null;
|
||||
const n = Number(s);
|
||||
return Number.isFinite(n) ? n : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function toInt(v: unknown): number | null {
|
||||
if (v == null) return null;
|
||||
if (typeof v === 'number') return Number.isFinite(v) ? Math.trunc(v) : null;
|
||||
if (typeof v === 'string') {
|
||||
const s = v.trim();
|
||||
if (!s) return null;
|
||||
const n = Number.parseInt(s, 10);
|
||||
return Number.isFinite(n) ? n : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
type HasuraRow = {
|
||||
market_name: string;
|
||||
side: string;
|
||||
size_usd: unknown;
|
||||
mid_price?: unknown;
|
||||
vwap_price?: unknown;
|
||||
worst_price?: unknown;
|
||||
filled_usd?: unknown;
|
||||
filled_base?: unknown;
|
||||
impact_bps?: unknown;
|
||||
levels_consumed?: unknown;
|
||||
fill_pct?: unknown;
|
||||
updated_at?: string | null;
|
||||
};
|
||||
|
||||
type SubscriptionData = {
|
||||
dlob_slippage_latest: HasuraRow[];
|
||||
};
|
||||
|
||||
export function useDlobSlippage(marketName: string): { rows: DlobSlippageRow[]; connected: boolean; error: string | null } {
|
||||
const [rows, setRows] = useState<DlobSlippageRow[]>([]);
|
||||
const [connected, setConnected] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const normalizedMarket = useMemo(() => (marketName || '').trim(), [marketName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!normalizedMarket) {
|
||||
setRows([]);
|
||||
setError(null);
|
||||
setConnected(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
|
||||
const query = `
|
||||
subscription DlobSlippage($market: String!) {
|
||||
dlob_slippage_latest(
|
||||
where: { market_name: { _eq: $market } }
|
||||
order_by: [{ side: asc }, { size_usd: asc }]
|
||||
) {
|
||||
market_name
|
||||
side
|
||||
size_usd
|
||||
mid_price
|
||||
vwap_price
|
||||
worst_price
|
||||
filled_usd
|
||||
filled_base
|
||||
impact_bps
|
||||
levels_consumed
|
||||
fill_pct
|
||||
updated_at
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const sub = subscribeGraphqlWs<SubscriptionData>({
|
||||
query,
|
||||
variables: { market: normalizedMarket },
|
||||
onStatus: ({ connected }) => setConnected(connected),
|
||||
onError: (e) => setError(e),
|
||||
onData: (data) => {
|
||||
const out: DlobSlippageRow[] = [];
|
||||
for (const r of data?.dlob_slippage_latest || []) {
|
||||
if (!r?.market_name) continue;
|
||||
const side = String(r.side || '').trim();
|
||||
if (side !== 'buy' && side !== 'sell') continue;
|
||||
const sizeUsd = toInt(r.size_usd);
|
||||
if (sizeUsd == null || sizeUsd <= 0) continue;
|
||||
out.push({
|
||||
marketName: r.market_name,
|
||||
side,
|
||||
sizeUsd,
|
||||
midPrice: toNum(r.mid_price),
|
||||
vwapPrice: toNum(r.vwap_price),
|
||||
worstPrice: toNum(r.worst_price),
|
||||
filledUsd: toNum(r.filled_usd),
|
||||
filledBase: toNum(r.filled_base),
|
||||
impactBps: toNum(r.impact_bps),
|
||||
levelsConsumed: toInt(r.levels_consumed),
|
||||
fillPct: toNum(r.fill_pct),
|
||||
updatedAt: r.updated_at ?? null,
|
||||
});
|
||||
}
|
||||
setRows(out);
|
||||
},
|
||||
});
|
||||
|
||||
return () => sub.unsubscribe();
|
||||
}, [normalizedMarket]);
|
||||
|
||||
return { rows, connected, error };
|
||||
}
|
||||
Reference in New Issue
Block a user