/** * Domain types for the cross-exchange BTC arbitrage engine. * All prices are quoted in USDT (BTC/USDT on every exchange — apples-to-apples, * no USD/USDT basis distortion). */ export type ExchangeId = "kraken" | "bybit" | "okx" | "binance"; export const EXCHANGE_IDS: ExchangeId[] = ["kraken", "bybit", "okx", "binance"]; /** A single order book price level. */ export interface Level { price: number; qty: number; } /** Normalized order book for one exchange. Bids desc, asks asc. */ export interface OrderBook { exchange: ExchangeId; bids: Level[]; asks: Level[]; /** Local receive timestamp (ms epoch). */ recvTs: number; /** Exchange-provided timestamp if available (ms epoch). */ exchangeTs: number | null; } /** Connection status of an exchange feed. */ export type FeedStatus = "connecting" | "live" | "stale" | "down"; /** Best bid/ask snapshot exposed to the UI. */ export interface BestQuote { exchange: ExchangeId; bid: number | null; bidQty: number | null; ask: number | null; askQty: number | null; recvTs: number | null; status: FeedStatus; /** ms since last update. */ ageMs: number | null; } /** * Why an opportunity was or wasn't executed. * Exhaustive union — handle every case. */ export type OpportunityStatus = | "executed" | "executed_partial" | "rejected_fees" | "rejected_liquidity" | "rejected_risk" | "rejected_flicker" | "rejected_stale" | "pending_confirm"; export interface Opportunity { id: string; ts: number; buyExchange: ExchangeId; sellExchange: ExchangeId; /** Best (top-of-book) ask on the buy side, before walking depth. */ topBuyAsk: number; /** Best (top-of-book) bid on the sell side, before walking depth. */ topSellBid: number; /** Volume actually evaluated (BTC), after liquidity + wallet caps. */ volumeBtc: number; /** Volume-weighted average buy price for volumeBtc. */ buyVwap: number; /** Volume-weighted average sell price for volumeBtc. */ sellVwap: number; grossSpread: number; grossSpreadPct: number; feeBuy: number; feeSell: number; netProfit: number; netProfitPct: number; status: OpportunityStatus; reason: string; /** True when generated by the synthetic demo injector. */ demo: boolean; } /** An executed (simulated) trade. */ export interface Trade { id: string; ts: number; buyExchange: ExchangeId; sellExchange: ExchangeId; volumeBtc: number; requestedBtc: number; buyVwap: number; sellVwap: number; /** Buy VWAP after simulated latency drift. */ execBuyVwap: number; /** Sell VWAP after simulated latency drift. */ execSellVwap: number; feeBuy: number; feeSell: number; /** Net realized P&L in USDT (negative = loss). */ netProfit: number; netProfitPct: number; partial: boolean; demo: boolean; } /** Per-exchange simulated wallet (pre-positioned inventory model). */ export interface Wallet { exchange: ExchangeId; usdt: number; btc: number; } export interface RebalanceEvent { id: string; ts: number; fromExchange: ExchangeId; toExchange: ExchangeId; asset: "BTC" | "USDT"; amount: number; withdrawalFee: number; reason: string; } export type CircuitState = "running" | "paused" | "tripped"; export interface EngineStats { uptimeMs: number; ticksProcessed: number; opportunitiesDetected: number; tradesExecuted: number; tradesRejected: number; realizedPnl: number; consecutiveLosses: number; circuit: CircuitState; demoMode: boolean; /** Average engine processing time per tick (ms). */ avgTickMs: number; } /** Full snapshot pushed to the dashboard over SSE. */ export interface StateSnapshot { ts: number; quotes: BestQuote[]; wallets: Wallet[]; stats: EngineStats; recentOpportunities: Opportunity[]; recentTrades: Trade[]; rebalances: RebalanceEvent[]; pnlSeries: PnlPoint[]; config: PublicConfig; } export interface PnlPoint { ts: number; pnl: number; } /** Partial update body for PATCH /api/config. */ export interface ConfigPatch { minNetProfitPct?: number; maxTradeBtc?: number; flickerConfirmMs?: number; activeExchanges?: Partial>; } /** Engine config surfaced to the UI (no secrets). */ export interface PublicConfig { minNetProfitPct: number; maxTradeBtc: number; staleMs: number; flickerConfirmMs: number; latencyMs: number; activeExchanges: Record; defaults: { minNetProfitPct: number; maxTradeBtc: number; flickerConfirmMs: number; activeExchanges: Record; }; takerFees: Record; withdrawalFeesBtc: Record; }