mirror of
https://github.com/mauricioabh/arbpulse.git
synced 2026-08-17 09:08:05 +00:00
Initial commit: Arb Pulse monolith with CI and optional Fly deploy.
Real-time BTC cross-exchange arbitrage detection (Kraken, Bybit, OKX, Binance) with React dashboard, GitHub Actions CI, and documented Fly.io deploy workflow. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
908aefb2d8
commit
2e8744ccf0
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* 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<Record<ExchangeId, boolean>>;
|
||||
}
|
||||
|
||||
/** Engine config surfaced to the UI (no secrets). */
|
||||
export interface PublicConfig {
|
||||
minNetProfitPct: number;
|
||||
maxTradeBtc: number;
|
||||
staleMs: number;
|
||||
flickerConfirmMs: number;
|
||||
latencyMs: number;
|
||||
activeExchanges: Record<ExchangeId, boolean>;
|
||||
defaults: {
|
||||
minNetProfitPct: number;
|
||||
maxTradeBtc: number;
|
||||
flickerConfirmMs: number;
|
||||
activeExchanges: Record<ExchangeId, boolean>;
|
||||
};
|
||||
takerFees: Record<ExchangeId, number>;
|
||||
withdrawalFeesBtc: Record<ExchangeId, number>;
|
||||
}
|
||||
Reference in New Issue
Block a user