705 lines
22 KiB
JavaScript
705 lines
22 KiB
JavaScript
/**
|
||
* Cross-Exchange Atomic Arbitrage
|
||
*
|
||
* EXOTIC: Flash loan arbitrage with MEV protection
|
||
*
|
||
* Uses @neural-trader/execution with RuVector for:
|
||
* - Multi-exchange price monitoring
|
||
* - Atomic execution via flash loans (DeFi)
|
||
* - MEV (Miner Extractable Value) protection
|
||
* - Latency-aware order routing
|
||
* - Triangular and cross-chain arbitrage
|
||
*
|
||
* WARNING: This is for educational purposes.
|
||
* Real arbitrage requires sophisticated infrastructure.
|
||
*/
|
||
|
||
// Arbitrage configuration
|
||
const arbitrageConfig = {
|
||
// Exchange configuration
|
||
exchanges: {
|
||
binance: { fee: 0.001, latency: 5, liquidity: 'high' },
|
||
coinbase: { fee: 0.005, latency: 8, liquidity: 'high' },
|
||
kraken: { fee: 0.002, latency: 12, liquidity: 'medium' },
|
||
ftx: { fee: 0.0007, latency: 3, liquidity: 'medium' },
|
||
uniswap: { fee: 0.003, latency: 15000, liquidity: 'medium', type: 'dex' },
|
||
sushiswap: { fee: 0.003, latency: 15000, liquidity: 'low', type: 'dex' }
|
||
},
|
||
|
||
// Arbitrage parameters
|
||
params: {
|
||
minProfitBps: 5, // Minimum profit in basis points
|
||
maxSlippage: 0.002, // 20 bps max slippage
|
||
maxPositionUSD: 100000, // Max position size
|
||
gasPrice: 50, // Gwei
|
||
gasLimit: 500000, // Gas units for DeFi
|
||
flashLoanFee: 0.0009 // 9 bps flash loan fee
|
||
},
|
||
|
||
// MEV protection
|
||
mev: {
|
||
usePrivatePool: true,
|
||
maxPriorityFee: 2, // Gwei
|
||
bundleTimeout: 2000 // ms
|
||
},
|
||
|
||
// Monitoring
|
||
monitoring: {
|
||
updateIntervalMs: 100,
|
||
priceHistorySize: 1000,
|
||
alertThresholdBps: 10
|
||
}
|
||
};
|
||
|
||
// Price Feed Simulator
|
||
class PriceFeed {
|
||
constructor(config) {
|
||
this.config = config;
|
||
this.prices = new Map();
|
||
this.orderBooks = new Map();
|
||
this.lastUpdate = Date.now();
|
||
}
|
||
|
||
// Simulate price update
|
||
updatePrices(basePrice, volatility = 0.0001) {
|
||
const now = Date.now();
|
||
|
||
for (const [exchange, params] of Object.entries(this.config.exchanges)) {
|
||
// Each exchange has slightly different price (market inefficiency)
|
||
const noise = (Math.random() - 0.5) * volatility * 2;
|
||
const exchangeSpecificBias = (Math.random() - 0.5) * 0.0005;
|
||
|
||
const price = basePrice * (1 + noise + exchangeSpecificBias);
|
||
|
||
// Simulate spread
|
||
const spread = params.type === 'dex' ? 0.002 : 0.0005;
|
||
|
||
this.prices.set(exchange, {
|
||
bid: price * (1 - spread / 2),
|
||
ask: price * (1 + spread / 2),
|
||
mid: price,
|
||
timestamp: now,
|
||
latency: params.latency
|
||
});
|
||
|
||
// Simulate order book depth
|
||
this.orderBooks.set(exchange, this.generateOrderBook(price, spread, params.liquidity));
|
||
}
|
||
|
||
this.lastUpdate = now;
|
||
}
|
||
|
||
generateOrderBook(midPrice, spread, liquidityLevel) {
|
||
const depths = { high: 5, medium: 3, low: 1 };
|
||
const baseDepth = depths[liquidityLevel] || 1;
|
||
|
||
const bids = [];
|
||
const asks = [];
|
||
|
||
for (let i = 0; i < 10; i++) {
|
||
const bidPrice = midPrice * (1 - spread / 2 - i * 0.0001);
|
||
const askPrice = midPrice * (1 + spread / 2 + i * 0.0001);
|
||
|
||
bids.push({
|
||
price: bidPrice,
|
||
quantity: baseDepth * 100000 * Math.exp(-i * 0.3) * (0.5 + Math.random())
|
||
});
|
||
|
||
asks.push({
|
||
price: askPrice,
|
||
quantity: baseDepth * 100000 * Math.exp(-i * 0.3) * (0.5 + Math.random())
|
||
});
|
||
}
|
||
|
||
return { bids, asks };
|
||
}
|
||
|
||
getPrice(exchange) {
|
||
return this.prices.get(exchange);
|
||
}
|
||
|
||
getOrderBook(exchange) {
|
||
return this.orderBooks.get(exchange);
|
||
}
|
||
|
||
getAllPrices() {
|
||
return Object.fromEntries(this.prices);
|
||
}
|
||
}
|
||
|
||
// Arbitrage Detector
|
||
class ArbitrageDetector {
|
||
constructor(config, priceFeed) {
|
||
this.config = config;
|
||
this.priceFeed = priceFeed;
|
||
this.opportunities = [];
|
||
this.history = [];
|
||
}
|
||
|
||
// Find simple arbitrage (buy low, sell high)
|
||
findSimpleArbitrage() {
|
||
const prices = this.priceFeed.getAllPrices();
|
||
const exchanges = Object.keys(prices);
|
||
const opportunities = [];
|
||
|
||
for (let i = 0; i < exchanges.length; i++) {
|
||
for (let j = i + 1; j < exchanges.length; j++) {
|
||
const ex1 = exchanges[i];
|
||
const ex2 = exchanges[j];
|
||
|
||
const p1 = prices[ex1];
|
||
const p2 = prices[ex2];
|
||
|
||
if (!p1 || !p2) continue;
|
||
|
||
// Check buy on ex1, sell on ex2
|
||
const profit1 = this.calculateProfit(ex1, ex2, p1.ask, p2.bid);
|
||
if (profit1.profitBps > this.config.params.minProfitBps) {
|
||
opportunities.push({
|
||
type: 'simple',
|
||
buyExchange: ex1,
|
||
sellExchange: ex2,
|
||
buyPrice: p1.ask,
|
||
sellPrice: p2.bid,
|
||
...profit1
|
||
});
|
||
}
|
||
|
||
// Check buy on ex2, sell on ex1
|
||
const profit2 = this.calculateProfit(ex2, ex1, p2.ask, p1.bid);
|
||
if (profit2.profitBps > this.config.params.minProfitBps) {
|
||
opportunities.push({
|
||
type: 'simple',
|
||
buyExchange: ex2,
|
||
sellExchange: ex1,
|
||
buyPrice: p2.ask,
|
||
sellPrice: p1.bid,
|
||
...profit2
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
return opportunities;
|
||
}
|
||
|
||
// Calculate profit after fees
|
||
calculateProfit(buyExchange, sellExchange, buyPrice, sellPrice) {
|
||
const buyFee = this.config.exchanges[buyExchange].fee;
|
||
const sellFee = this.config.exchanges[sellExchange].fee;
|
||
|
||
const effectiveBuy = buyPrice * (1 + buyFee);
|
||
const effectiveSell = sellPrice * (1 - sellFee);
|
||
|
||
const grossProfit = (effectiveSell - effectiveBuy) / effectiveBuy;
|
||
const profitBps = grossProfit * 10000;
|
||
|
||
// Estimate gas cost for DeFi exchanges
|
||
let gasCostBps = 0;
|
||
if (this.config.exchanges[buyExchange].type === 'dex' ||
|
||
this.config.exchanges[sellExchange].type === 'dex') {
|
||
const gasCostUSD = this.config.params.gasPrice * this.config.params.gasLimit * 1e-9 * 2000; // ETH price
|
||
const tradeSize = this.config.params.maxPositionUSD;
|
||
gasCostBps = (gasCostUSD / tradeSize) * 10000;
|
||
}
|
||
|
||
const netProfitBps = profitBps - gasCostBps;
|
||
|
||
return {
|
||
grossProfitBps: profitBps,
|
||
profitBps: netProfitBps,
|
||
fees: { buy: buyFee, sell: sellFee },
|
||
gasCostBps,
|
||
totalLatencyMs: this.config.exchanges[buyExchange].latency +
|
||
this.config.exchanges[sellExchange].latency
|
||
};
|
||
}
|
||
|
||
// Find triangular arbitrage
|
||
findTriangularArbitrage(pairs = ['BTC/USD', 'ETH/USD', 'ETH/BTC']) {
|
||
// Simulate exchange rates
|
||
const rates = {
|
||
'BTC/USD': 50000,
|
||
'ETH/USD': 3000,
|
||
'ETH/BTC': 0.06
|
||
};
|
||
|
||
// Add some inefficiency
|
||
const noisyRates = {};
|
||
for (const [pair, rate] of Object.entries(rates)) {
|
||
noisyRates[pair] = rate * (1 + (Math.random() - 0.5) * 0.002);
|
||
}
|
||
|
||
// Check triangular opportunity
|
||
// USD → BTC → ETH → USD
|
||
const path1 = {
|
||
step1: 1 / noisyRates['BTC/USD'], // USD to BTC
|
||
step2: noisyRates['ETH/BTC'], // BTC to ETH
|
||
step3: noisyRates['ETH/USD'] // ETH to USD
|
||
};
|
||
|
||
const return1 = path1.step1 * path1.step2 * path1.step3;
|
||
const profit1 = (return1 - 1) * 10000; // in bps
|
||
|
||
// USD → ETH → BTC → USD
|
||
const path2 = {
|
||
step1: 1 / noisyRates['ETH/USD'],
|
||
step2: 1 / noisyRates['ETH/BTC'],
|
||
step3: noisyRates['BTC/USD']
|
||
};
|
||
|
||
const return2 = path2.step1 * path2.step2 * path2.step3;
|
||
const profit2 = (return2 - 1) * 10000;
|
||
|
||
const opportunities = [];
|
||
|
||
if (profit1 > this.config.params.minProfitBps) {
|
||
opportunities.push({
|
||
type: 'triangular',
|
||
path: 'USD → BTC → ETH → USD',
|
||
profitBps: profit1,
|
||
rates: path1
|
||
});
|
||
}
|
||
|
||
if (profit2 > this.config.params.minProfitBps) {
|
||
opportunities.push({
|
||
type: 'triangular',
|
||
path: 'USD → ETH → BTC → USD',
|
||
profitBps: profit2,
|
||
rates: path2
|
||
});
|
||
}
|
||
|
||
return opportunities;
|
||
}
|
||
|
||
// Find flash loan arbitrage opportunity
|
||
findFlashLoanArbitrage() {
|
||
const dexExchanges = Object.entries(this.config.exchanges)
|
||
.filter(([_, params]) => params.type === 'dex')
|
||
.map(([name]) => name);
|
||
|
||
const cexExchanges = Object.entries(this.config.exchanges)
|
||
.filter(([_, params]) => params.type !== 'dex')
|
||
.map(([name]) => name);
|
||
|
||
const opportunities = [];
|
||
const prices = this.priceFeed.getAllPrices();
|
||
|
||
// DEX to DEX arbitrage with flash loan
|
||
for (let i = 0; i < dexExchanges.length; i++) {
|
||
for (let j = i + 1; j < dexExchanges.length; j++) {
|
||
const dex1 = dexExchanges[i];
|
||
const dex2 = dexExchanges[j];
|
||
|
||
const p1 = prices[dex1];
|
||
const p2 = prices[dex2];
|
||
|
||
if (!p1 || !p2) continue;
|
||
|
||
// Flash loan cost
|
||
const flashFee = this.config.params.flashLoanFee;
|
||
|
||
const minMid = Math.min(p1.mid, p2.mid);
|
||
const spread = minMid > 0 ? Math.abs(p1.mid - p2.mid) / minMid : 0;
|
||
const profitBps = (spread - flashFee) * 10000;
|
||
|
||
if (profitBps > this.config.params.minProfitBps) {
|
||
opportunities.push({
|
||
type: 'flash_loan',
|
||
buyDex: p1.mid < p2.mid ? dex1 : dex2,
|
||
sellDex: p1.mid < p2.mid ? dex2 : dex1,
|
||
spread: spread * 10000,
|
||
flashFee: flashFee * 10000,
|
||
profitBps,
|
||
atomic: true
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
return opportunities;
|
||
}
|
||
|
||
// Scan all arbitrage types
|
||
scanAll() {
|
||
const simple = this.findSimpleArbitrage();
|
||
const triangular = this.findTriangularArbitrage();
|
||
const flashLoan = this.findFlashLoanArbitrage();
|
||
|
||
this.opportunities = [...simple, ...triangular, ...flashLoan]
|
||
.sort((a, b) => b.profitBps - a.profitBps);
|
||
|
||
this.history.push({
|
||
timestamp: Date.now(),
|
||
count: this.opportunities.length,
|
||
bestProfit: this.opportunities[0]?.profitBps || 0
|
||
});
|
||
|
||
return this.opportunities;
|
||
}
|
||
}
|
||
|
||
// Execution Engine
|
||
class ExecutionEngine {
|
||
constructor(config) {
|
||
this.config = config;
|
||
this.pendingOrders = [];
|
||
this.executedTrades = [];
|
||
this.mevProtection = config.mev.usePrivatePool;
|
||
}
|
||
|
||
// Simulate execution
|
||
async execute(opportunity) {
|
||
const startTime = Date.now();
|
||
|
||
// Check for MEV risk
|
||
if (opportunity.type === 'flash_loan' && this.mevProtection) {
|
||
return this.executeWithMEVProtection(opportunity);
|
||
}
|
||
|
||
// Simulate latency
|
||
await this.simulateLatency(opportunity.totalLatencyMs || 50);
|
||
|
||
// Check slippage
|
||
const slippage = Math.random() * this.config.params.maxSlippage;
|
||
const adjustedProfit = opportunity.profitBps - slippage * 10000;
|
||
|
||
const result = {
|
||
success: adjustedProfit > 0,
|
||
opportunity,
|
||
actualProfitBps: adjustedProfit,
|
||
slippage,
|
||
executionTimeMs: Date.now() - startTime,
|
||
timestamp: Date.now()
|
||
};
|
||
|
||
this.executedTrades.push(result);
|
||
return result;
|
||
}
|
||
|
||
// Execute with MEV protection (Flashbots-style)
|
||
async executeWithMEVProtection(opportunity) {
|
||
const startTime = Date.now();
|
||
|
||
// Bundle transactions
|
||
const bundle = {
|
||
transactions: [
|
||
{ type: 'flash_loan_borrow', amount: this.config.params.maxPositionUSD },
|
||
{ type: 'swap', dex: opportunity.buyDex, direction: 'buy' },
|
||
{ type: 'swap', dex: opportunity.sellDex, direction: 'sell' },
|
||
{ type: 'flash_loan_repay' }
|
||
],
|
||
priorityFee: this.config.mev.maxPriorityFee
|
||
};
|
||
|
||
// Simulate private pool submission
|
||
await this.simulateLatency(this.config.mev.bundleTimeout);
|
||
|
||
// Check if bundle was included
|
||
const included = Math.random() > 0.2; // 80% success rate
|
||
|
||
if (!included) {
|
||
return {
|
||
success: false,
|
||
reason: 'bundle_not_included',
|
||
executionTimeMs: Date.now() - startTime
|
||
};
|
||
}
|
||
|
||
const result = {
|
||
success: true,
|
||
opportunity,
|
||
actualProfitBps: opportunity.profitBps * 0.95, // Some slippage
|
||
mevProtected: true,
|
||
executionTimeMs: Date.now() - startTime,
|
||
timestamp: Date.now()
|
||
};
|
||
|
||
this.executedTrades.push(result);
|
||
return result;
|
||
}
|
||
|
||
simulateLatency(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, Math.min(ms, 100)));
|
||
}
|
||
|
||
getStats() {
|
||
const successful = this.executedTrades.filter(t => t.success);
|
||
const totalProfit = successful.reduce((s, t) => s + (t.actualProfitBps || 0), 0);
|
||
const avgProfit = successful.length > 0 ? totalProfit / successful.length : 0;
|
||
|
||
return {
|
||
totalTrades: this.executedTrades.length,
|
||
successfulTrades: successful.length,
|
||
successRate: this.executedTrades.length > 0
|
||
? successful.length / this.executedTrades.length
|
||
: 0,
|
||
totalProfitBps: totalProfit,
|
||
avgProfitBps: avgProfit,
|
||
avgExecutionTimeMs: this.executedTrades.length > 0
|
||
? this.executedTrades.reduce((s, t) => s + t.executionTimeMs, 0) / this.executedTrades.length
|
||
: 0
|
||
};
|
||
}
|
||
}
|
||
|
||
// Latency Monitor
|
||
class LatencyMonitor {
|
||
constructor() {
|
||
this.measurements = new Map();
|
||
}
|
||
|
||
record(exchange, latencyMs) {
|
||
if (!this.measurements.has(exchange)) {
|
||
this.measurements.set(exchange, []);
|
||
}
|
||
|
||
const measurements = this.measurements.get(exchange);
|
||
measurements.push({ latency: latencyMs, timestamp: Date.now() });
|
||
|
||
// Keep last 100 measurements
|
||
if (measurements.length > 100) {
|
||
measurements.shift();
|
||
}
|
||
}
|
||
|
||
getStats(exchange) {
|
||
const measurements = this.measurements.get(exchange);
|
||
if (!measurements || measurements.length === 0) {
|
||
return null;
|
||
}
|
||
|
||
const latencies = measurements.map(m => m.latency);
|
||
const avg = latencies.reduce((a, b) => a + b, 0) / latencies.length;
|
||
const sorted = [...latencies].sort((a, b) => a - b);
|
||
const p50 = sorted[Math.floor(latencies.length * 0.5)];
|
||
const p99 = sorted[Math.floor(latencies.length * 0.99)];
|
||
|
||
return { avg, p50, p99, count: latencies.length };
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
console.log('═'.repeat(70));
|
||
console.log('CROSS-EXCHANGE ATOMIC ARBITRAGE');
|
||
console.log('═'.repeat(70));
|
||
console.log();
|
||
|
||
// 1. Initialize components
|
||
console.log('1. System Initialization:');
|
||
console.log('─'.repeat(70));
|
||
|
||
const priceFeed = new PriceFeed(arbitrageConfig);
|
||
const detector = new ArbitrageDetector(arbitrageConfig, priceFeed);
|
||
const executor = new ExecutionEngine(arbitrageConfig);
|
||
const latencyMonitor = new LatencyMonitor();
|
||
|
||
console.log(` Exchanges: ${Object.keys(arbitrageConfig.exchanges).length}`);
|
||
console.log(` CEX: ${Object.entries(arbitrageConfig.exchanges).filter(([_, p]) => p.type !== 'dex').length}`);
|
||
console.log(` DEX: ${Object.entries(arbitrageConfig.exchanges).filter(([_, p]) => p.type === 'dex').length}`);
|
||
console.log(` Min profit: ${arbitrageConfig.params.minProfitBps} bps`);
|
||
console.log(` Max position: $${arbitrageConfig.params.maxPositionUSD.toLocaleString()}`);
|
||
console.log(` MEV protection: ${arbitrageConfig.mev.usePrivatePool ? 'Enabled' : 'Disabled'}`);
|
||
console.log();
|
||
|
||
// 2. Exchange fees
|
||
console.log('2. Exchange Configuration:');
|
||
console.log('─'.repeat(70));
|
||
console.log(' Exchange │ Fee │ Latency │ Liquidity │ Type');
|
||
console.log('─'.repeat(70));
|
||
|
||
for (const [exchange, params] of Object.entries(arbitrageConfig.exchanges)) {
|
||
const type = params.type === 'dex' ? 'DEX' : 'CEX';
|
||
console.log(` ${exchange.padEnd(11)} │ ${(params.fee * 100).toFixed(2)}% │ ${String(params.latency).padStart(5)}ms │ ${params.liquidity.padEnd(9)} │ ${type}`);
|
||
}
|
||
console.log();
|
||
|
||
// 3. Price simulation
|
||
console.log('3. Price Feed Simulation:');
|
||
console.log('─'.repeat(70));
|
||
|
||
const basePrice = 50000; // BTC price
|
||
priceFeed.updatePrices(basePrice);
|
||
|
||
console.log(' Exchange │ Bid │ Ask │ Spread');
|
||
console.log('─'.repeat(70));
|
||
|
||
for (const [exchange, price] of priceFeed.prices) {
|
||
const spread = ((price.ask - price.bid) / price.mid * 10000).toFixed(1);
|
||
console.log(` ${exchange.padEnd(11)} │ $${price.bid.toFixed(2).padStart(9)} │ $${price.ask.toFixed(2).padStart(9)} │ ${spread.padStart(5)} bps`);
|
||
}
|
||
console.log();
|
||
|
||
// 4. Arbitrage detection
|
||
console.log('4. Arbitrage Opportunity Scan:');
|
||
console.log('─'.repeat(70));
|
||
|
||
// Run multiple scans
|
||
let allOpportunities = [];
|
||
for (let i = 0; i < 10; i++) {
|
||
priceFeed.updatePrices(basePrice, 0.0005); // Add more volatility
|
||
const opportunities = detector.scanAll();
|
||
allOpportunities.push(...opportunities);
|
||
}
|
||
|
||
// Deduplicate and sort
|
||
const uniqueOpps = allOpportunities
|
||
.filter((opp, idx, arr) =>
|
||
arr.findIndex(o => o.type === opp.type &&
|
||
o.buyExchange === opp.buyExchange &&
|
||
o.sellExchange === opp.sellExchange) === idx
|
||
)
|
||
.sort((a, b) => b.profitBps - a.profitBps);
|
||
|
||
console.log(` Scans performed: 10`);
|
||
console.log(` Total found: ${uniqueOpps.length}`);
|
||
console.log();
|
||
|
||
if (uniqueOpps.length > 0) {
|
||
console.log(' Top Opportunities:');
|
||
console.log(' Type │ Route │ Profit │ Details');
|
||
console.log('─'.repeat(70));
|
||
|
||
for (const opp of uniqueOpps.slice(0, 5)) {
|
||
let route = '';
|
||
let details = '';
|
||
|
||
if (opp.type === 'simple') {
|
||
route = `${opp.buyExchange} → ${opp.sellExchange}`;
|
||
details = `lat=${opp.totalLatencyMs}ms`;
|
||
} else if (opp.type === 'triangular') {
|
||
route = opp.path.substring(0, 22);
|
||
details = '';
|
||
} else if (opp.type === 'flash_loan') {
|
||
route = `${opp.buyDex} ⚡ ${opp.sellDex}`;
|
||
details = 'atomic';
|
||
}
|
||
|
||
console.log(` ${opp.type.padEnd(12)} │ ${route.padEnd(24)} │ ${opp.profitBps.toFixed(1).padStart(5)} bps │ ${details}`);
|
||
}
|
||
} else {
|
||
console.log(' No profitable opportunities found');
|
||
}
|
||
console.log();
|
||
|
||
// 5. Execute opportunities
|
||
console.log('5. Execution Simulation:');
|
||
console.log('─'.repeat(70));
|
||
|
||
for (const opp of uniqueOpps.slice(0, 5)) {
|
||
const result = await executor.execute(opp);
|
||
|
||
if (result.success) {
|
||
console.log(` ✓ ${opp.type.padEnd(12)} +${result.actualProfitBps.toFixed(1)} bps (${result.executionTimeMs}ms)${result.mevProtected ? ' [MEV-protected]' : ''}`);
|
||
} else {
|
||
console.log(` ✗ ${opp.type.padEnd(12)} Failed: ${result.reason || 'slippage'}`);
|
||
}
|
||
}
|
||
console.log();
|
||
|
||
// 6. Execution stats
|
||
console.log('6. Execution Statistics:');
|
||
console.log('─'.repeat(70));
|
||
|
||
const stats = executor.getStats();
|
||
|
||
console.log(` Total trades: ${stats.totalTrades}`);
|
||
console.log(` Successful: ${stats.successfulTrades}`);
|
||
console.log(` Success rate: ${(stats.successRate * 100).toFixed(1)}%`);
|
||
console.log(` Total profit: ${stats.totalProfitBps.toFixed(1)} bps`);
|
||
console.log(` Avg profit: ${stats.avgProfitBps.toFixed(1)} bps`);
|
||
console.log(` Avg exec time: ${stats.avgExecutionTimeMs.toFixed(0)}ms`);
|
||
console.log();
|
||
|
||
// 7. Order book depth analysis
|
||
console.log('7. Order Book Depth Analysis:');
|
||
console.log('─'.repeat(70));
|
||
|
||
const sampleExchange = 'binance';
|
||
const orderBook = priceFeed.getOrderBook(sampleExchange);
|
||
|
||
console.log(` ${sampleExchange.toUpperCase()} Order Book (Top 5 levels):`);
|
||
console.log(' Bids │ Asks');
|
||
console.log('─'.repeat(70));
|
||
|
||
for (let i = 0; i < 5; i++) {
|
||
const bid = orderBook.bids[i];
|
||
const ask = orderBook.asks[i];
|
||
console.log(` $${bid.price.toFixed(2)} × ${(bid.quantity / 1000).toFixed(0)}k │ $${ask.price.toFixed(2)} × ${(ask.quantity / 1000).toFixed(0)}k`);
|
||
}
|
||
console.log();
|
||
|
||
// 8. Latency importance
|
||
console.log('8. Latency Analysis:');
|
||
console.log('─'.repeat(70));
|
||
|
||
console.log(' In arbitrage, latency is critical:');
|
||
console.log();
|
||
console.log(' CEX latency: ~5-15ms (colocation advantage)');
|
||
console.log(' DEX latency: ~15,000ms (block time)');
|
||
console.log();
|
||
console.log(' Opportunity lifetime:');
|
||
console.log(' - Crypto CEX-CEX: 10-100ms');
|
||
console.log(' - DEX-DEX: 1-2 blocks (~15-30s)');
|
||
console.log(' - CEX-DEX: Limited by block time');
|
||
console.log();
|
||
|
||
// 9. Risk factors
|
||
console.log('9. Risk Factors:');
|
||
console.log('─'.repeat(70));
|
||
|
||
console.log(' Key risks in atomic arbitrage:');
|
||
console.log();
|
||
console.log(' 1. Execution risk:');
|
||
console.log(' - Slippage exceeds expected');
|
||
console.log(' - Partial fills');
|
||
console.log(' - Network congestion');
|
||
console.log();
|
||
console.log(' 2. MEV risk (DeFi):');
|
||
console.log(' - Frontrunning');
|
||
console.log(' - Sandwich attacks');
|
||
console.log(' - Block builder extraction');
|
||
console.log();
|
||
console.log(' 3. Smart contract risk:');
|
||
console.log(' - Flash loan failures');
|
||
console.log(' - Reentrancy');
|
||
console.log(' - Oracle manipulation');
|
||
console.log();
|
||
|
||
// 10. RuVector integration
|
||
console.log('10. RuVector Vector Storage:');
|
||
console.log('─'.repeat(70));
|
||
console.log(' Arbitrage opportunities as feature vectors:');
|
||
console.log();
|
||
|
||
if (uniqueOpps.length > 0) {
|
||
const opp = uniqueOpps[0];
|
||
const featureVector = [
|
||
opp.profitBps / 100,
|
||
opp.type === 'simple' ? 1 : opp.type === 'triangular' ? 2 : 3,
|
||
(opp.totalLatencyMs || 50) / 1000,
|
||
opp.gasCostBps ? opp.gasCostBps / 100 : 0,
|
||
opp.atomic ? 1 : 0
|
||
];
|
||
|
||
console.log(` Opportunity vector:`);
|
||
console.log(` [${featureVector.map(v => v.toFixed(3)).join(', ')}]`);
|
||
console.log();
|
||
console.log(' Dimensions: [profit, type, latency, gas_cost, atomic]');
|
||
}
|
||
console.log();
|
||
console.log(' Use cases:');
|
||
console.log(' - Pattern recognition for recurring opportunities');
|
||
console.log(' - Similar opportunity retrieval');
|
||
console.log(' - Historical profitability analysis');
|
||
console.log();
|
||
|
||
console.log('═'.repeat(70));
|
||
console.log('Cross-exchange atomic arbitrage analysis completed');
|
||
console.log('═'.repeat(70));
|
||
}
|
||
|
||
main().catch(console.error);
|