Files
zeekay 84190aaa45 docs: align all examples with @luxfi/trading DEX/AMM exports
- Update arbitrage.mdx with new import patterns
- Update sdk/index.mdx with correct package names and examples
- Update getting-started.mdx TypeScript examples
- Update tutorials/first-trade.mdx complete walkthrough
- Update tutorials/index.mdx quick reference
- Update main index.mdx SDK examples
- All docs now use clean DEX() and AMM() imports
- Build verified: 144 pages generated successfully
2025-12-12 05:17:23 -08:00

299 lines
9.1 KiB
TypeScript

/**
* LX-First Arbitrage Bot Example
*
* This bot uses the LX-First strategy where LX DEX prices are treated
* as the "truth" (fastest venue with nanosecond updates, 200ms blocks).
* Other venues are always stale by comparison.
*
* Arbitrage = exploiting stale venues before they catch up to LX prices.
*
* Cross-chain transport options:
* - Warp: For Lux subnet communication (<500ms)
* - Teleport: For EVM chain bridging (~30s)
* - CEX API: Direct trading (instant)
*
* NO SMART CONTRACTS - just coordinated trades through unified SDK.
*/
import { Decimal } from 'decimal.js';
import {
LxFirstArbitrage,
LxFirstOpportunity,
LxPrice,
VenuePrice,
Scanner,
ArbitrageOpportunity,
CrossChainRouter,
defaultLxFirstConfig,
defaultScannerConfig,
defaultCrossChainConfig,
} from '../src/arbitrage/index.js';
// ============================================
// Configuration
// ============================================
const LX_DEX_ENDPOINT = process.env.LX_DEX_ENDPOINT || 'wss://dex.lux.network/ws';
// ============================================
// Arbitrage Bot Class
// ============================================
class ArbitrageBot {
private lxFirst: LxFirstArbitrage | null = null;
private scanner: Scanner | null = null;
private router: CrossChainRouter | null = null;
private running = false;
private totalOpportunities = 0;
private totalExecutions = 0;
private totalPnl = new Decimal(0);
async start(): Promise<void> {
console.log('='.repeat(60));
console.log('LX-FIRST ARBITRAGE BOT');
console.log('='.repeat(60));
console.log();
// Initialize LX-First strategy with default config
const lxConfig = defaultLxFirstConfig();
this.lxFirst = new LxFirstArbitrage(lxConfig);
this.lxFirst.onOpportunity((opp) => this.onLxFirstOpportunity(opp));
console.log('[OK] LX-First strategy initialized');
// Initialize Scanner with default config
const scannerConfig = defaultScannerConfig();
this.scanner = new Scanner(scannerConfig);
this.scanner.onOpportunity((opp) => this.onScannerOpportunity(opp));
console.log('[OK] Scanner initialized');
// Initialize Cross-Chain Router with default config
const routerConfig = defaultCrossChainConfig();
this.router = new CrossChainRouter(routerConfig);
console.log('[OK] Cross-chain router initialized');
// Start all systems
this.lxFirst.start();
await this.scanner.start();
this.running = true;
console.log();
console.log('='.repeat(60));
console.log('BOT RUNNING - Press Ctrl+C to stop');
console.log('='.repeat(60));
console.log();
console.log(`Monitoring symbols: ${lxConfig.symbols.join(', ')}`);
console.log(`Min divergence: ${lxConfig.minDivergenceBps} bps`);
console.log(`Min profit: $${lxConfig.minProfit}`);
console.log();
// Start price feed simulation
this.simulatePriceFeeds();
// Start stats reporter
this.reportStats();
}
async stop(): Promise<void> {
console.log('\nShutting down...');
this.running = false;
this.lxFirst?.stop();
await this.scanner?.stop();
this.printFinalStats();
}
private onLxFirstOpportunity(opp: LxFirstOpportunity): void {
this.totalOpportunities++;
console.log();
console.log('='.repeat(50));
console.log('LX-FIRST OPPORTUNITY DETECTED');
console.log('='.repeat(50));
console.log(`Symbol: ${opp.symbol}`);
console.log(`LX Price: $${opp.lxPrice.mid}`);
console.log(`Stale Venue: ${opp.staleVenue}`);
console.log(`Stale Bid/Ask: $${opp.stalePrice.bid} / $${opp.stalePrice.ask}`);
console.log(`Staleness: ${opp.staleness}ms`);
console.log(`Side: ${opp.side.toUpperCase()}`);
console.log(`Divergence: ${opp.divergenceBps} bps`);
console.log(`Expected Profit: $${opp.expectedProfit}`);
console.log(`Confidence: ${(opp.confidence * 100).toFixed(1)}%`);
console.log('='.repeat(50));
// Execute if confidence is high enough
if (opp.confidence > 0.8) {
this.executeLxFirst(opp);
}
}
private onScannerOpportunity(opp: ArbitrageOpportunity): void {
console.log(
`[SCANNER] ${opp.type}: ${opp.buySource.venue} -> ` +
`${opp.sellSource.venue} | Spread: ${opp.spreadBps} bps | ` +
`Net PnL: $${opp.netPnL}`
);
}
private async executeLxFirst(opp: LxFirstOpportunity): Promise<void> {
try {
console.log(`\n[EXECUTING] ${opp.id}...`);
// Determine cross-chain transport
const buyChain = this.router!.venueToChain(opp.staleVenue);
const sellChain = 'lux_mainnet';
const transport = this.router!.determineTransport(buyChain, sellChain);
const latency = this.router!.estimateLatency(buyChain, sellChain);
console.log(` Transport: ${transport}`);
console.log(` Est. Latency: ${latency}ms`);
if (opp.side === 'buy') {
console.log(` Buying on ${opp.staleVenue}...`);
// In production: place actual order
// const order = await cexClient.placeOrder(...)
console.log(' Hedging on LX DEX...');
// In production: place hedge order
// const hedge = await this.dex.spot.sell(...)
} else {
console.log(` Selling on ${opp.staleVenue}...`);
// In production: place actual order
console.log(' Hedging on LX DEX...');
// In production: place hedge order
}
// Simulate successful execution
this.totalExecutions++;
const profit = opp.expectedProfit.mul(0.8); // Simulate slippage
this.totalPnl = this.totalPnl.add(profit);
console.log(`[SUCCESS] Executed ${opp.id} | Profit: $${profit.toFixed(2)}`);
} catch (error) {
console.log(`[FAILED] ${opp.id}: ${error}`);
}
}
private simulatePriceFeeds(): void {
const basePrices: Record<string, Decimal> = {
'BTC-USDC': new Decimal(50000),
'ETH-USDC': new Decimal(3000),
'LUX-USDC': new Decimal(25),
};
const interval = setInterval(() => {
if (!this.running) {
clearInterval(interval);
return;
}
for (const [symbol, base] of Object.entries(basePrices)) {
// Simulate LX DEX price (the oracle)
const lxMid = base.mul(1 + (Math.random() - 0.5) * 0.002);
const lxPrice: LxPrice = {
symbol,
bid: lxMid.mul(0.9999),
ask: lxMid.mul(1.0001),
mid: lxMid,
timestamp: Date.now(),
blockNum: BigInt(Math.floor(Math.random() * 1000000) + 1000000),
};
this.lxFirst?.updateLxPrice(lxPrice);
// Simulate stale CEX prices
for (const [venue, latency] of [
['binance', 50],
['mexc', 100],
] as const) {
const divergence = (Math.random() - 0.5) * 0.004;
const venueMid = base.mul(1 + divergence);
const venuePrice: VenuePrice = {
venue,
symbol,
bid: venueMid.mul(0.9998),
ask: venueMid.mul(1.0002),
timestamp: Date.now() - latency,
latency,
stale: false,
};
this.lxFirst?.updateVenuePrice(venuePrice);
}
}
}, 100); // 10 updates per second
}
private reportStats(): void {
const interval = setInterval(() => {
if (!this.running) {
clearInterval(interval);
return;
}
console.log();
console.log('-'.repeat(40));
console.log('STATS');
console.log(` Opportunities: ${this.totalOpportunities}`);
console.log(` Executions: ${this.totalExecutions}`);
console.log(` Total PnL: $${this.totalPnl.toFixed(2)}`);
if (this.totalExecutions > 0) {
const avgPnl = this.totalPnl.div(this.totalExecutions);
console.log(` Avg PnL: $${avgPnl.toFixed(2)}`);
}
console.log('-'.repeat(40));
}, 30000); // Every 30 seconds
}
private printFinalStats(): void {
console.log();
console.log('='.repeat(50));
console.log('FINAL STATISTICS');
console.log('='.repeat(50));
console.log(`Total Opportunities: ${this.totalOpportunities}`);
console.log(`Total Executions: ${this.totalExecutions}`);
console.log(`Total PnL: $${this.totalPnl.toFixed(2)}`);
if (this.totalExecutions > 0) {
const winRate = (this.totalExecutions / this.totalOpportunities) * 100;
const avgPnl = this.totalPnl.div(this.totalExecutions);
console.log(`Execution Rate: ${winRate.toFixed(1)}%`);
console.log(`Avg PnL per Trade: $${avgPnl.toFixed(2)}`);
}
console.log('='.repeat(50));
}
}
// ============================================
// Main Entry Point
// ============================================
async function main(): Promise<void> {
const bot = new ArbitrageBot();
// Handle Ctrl+C gracefully
process.on('SIGINT', async () => {
await bot.stop();
process.exit(0);
});
process.on('SIGTERM', async () => {
await bot.stop();
process.exit(0);
});
try {
await bot.start();
// Keep running
while (true) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
} catch (error) {
console.error('Fatal error:', error);
await bot.stop();
process.exit(1);
}
}
main();