import { createRustBacktest } from './src/backtest/RustBacktestEngine'; import { SimpleMovingAverageCrossoverRust } from './src/strategies/rust/SimpleMovingAverageCrossoverRust'; import { IServiceContainer } from '@stock-bot/di'; // Mock StorageService class MockStorageService { async getHistoricalBars(symbol: string, startDate: Date, endDate: Date, frequency: string) { // Generate mock data const bars = []; const msPerDay = 24 * 60 * 60 * 1000; let currentDate = new Date(startDate); let price = 100 + Math.random() * 50; // Start between 100-150 while (currentDate <= endDate) { // Random walk const change = (Math.random() - 0.5) * 2; // +/- 1% price *= (1 + change / 100); bars.push({ symbol, timestamp: new Date(currentDate), open: price * (1 + (Math.random() - 0.5) * 0.01), high: price * (1 + Math.random() * 0.02), low: price * (1 - Math.random() * 0.02), close: price, volume: 1000000 + Math.random() * 500000, }); currentDate = new Date(currentDate.getTime() + msPerDay); } return bars; } } async function testRustBacktest() { console.log('šŸš€ Testing Rust Backtest Engine with TypeScript Strategy\n'); // Create minimal container const container: IServiceContainer = { logger: { info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args), error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args), warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args), debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args), } as any, custom: { StorageService: new MockStorageService(), } }; // Backtest configuration const config = { mode: 'backtest' as const, name: 'Rust Engine Test', strategy: 'sma-crossover', symbols: ['AAPL'], // Just one symbol for testing startDate: '2023-01-01T00:00:00Z', endDate: '2023-01-31T00:00:00Z', // Just one month for testing initialCapital: 100000, commission: 0.001, slippage: 0.0001, dataFrequency: '1d', speed: 'max' as const, }; // Create strategy const strategy = new SimpleMovingAverageCrossoverRust({ fastPeriod: 10, slowPeriod: 30, minHoldingBars: 5, }); console.log('Configuration:'); console.log(` Symbols: ${config.symbols.join(', ')}`); console.log(` Period: ${config.startDate} to ${config.endDate}`); console.log(` Initial Capital: $${config.initialCapital.toLocaleString()}`); console.log(` Strategy: ${strategy.constructor.name}`); console.log(''); try { console.log('Running backtest in Rust engine...\n'); const startTime = Date.now(); try { const result = await createRustBacktest(container, config, [strategy]); console.log('Raw result:', result); const duration = (Date.now() - startTime) / 1000; console.log(`\nāœ… Backtest completed in ${duration.toFixed(2)} seconds`); if (!result || !result.metrics) { console.error('Invalid result structure:', result); return; } console.log('\n=== PERFORMANCE METRICS ==='); console.log(`Total Return: ${result.metrics.totalReturn?.toFixed(2) || 'N/A'}%`); console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio?.toFixed(2) || 'N/A'}`); console.log(`Max Drawdown: ${result.metrics.maxDrawdown ? (result.metrics.maxDrawdown * 100).toFixed(2) : 'N/A'}%`); console.log(`Win Rate: ${result.metrics.winRate?.toFixed(1) || 'N/A'}%`); console.log(`Total Trades: ${result.metrics.totalTrades || 0}`); console.log(`Profit Factor: ${result.metrics.profitFactor?.toFixed(2) || 'N/A'}`); console.log('\n=== TRADE STATISTICS ==='); console.log(`Profitable Trades: ${result.metrics.profitableTrades || 0}`); console.log(`Average Win: $${result.metrics.avgWin?.toFixed(2) || '0.00'}`); console.log(`Average Loss: $${result.metrics.avgLoss?.toFixed(2) || '0.00'}`); console.log(`Total P&L: $${result.metrics.totalPnl?.toFixed(2) || '0.00'}`); console.log('\n=== EQUITY CURVE ==='); if (result.equityCurve.length > 0) { const firstValue = result.equityCurve[0].value; const lastValue = result.equityCurve[result.equityCurve.length - 1].value; console.log(`Starting Value: $${firstValue.toLocaleString()}`); console.log(`Ending Value: $${lastValue.toLocaleString()}`); console.log(`Growth: ${((lastValue / firstValue - 1) * 100).toFixed(2)}%`); } console.log('\n=== FINAL POSITIONS ==='); const positions = Object.entries(result.finalPositions); if (positions.length > 0) { for (const [symbol, position] of positions) { console.log(`${symbol}: ${position.quantity} shares @ $${position.averagePrice}`); } } else { console.log('No open positions'); } // Compare with TypeScript engine performance console.log('\n=== PERFORMANCE COMPARISON ==='); console.log('TypeScript Engine: ~5-10 seconds for 1 year backtest'); console.log(`Rust Engine: ${duration.toFixed(2)} seconds`); console.log(`Speed Improvement: ${(10 / duration).toFixed(1)}x faster`); } catch (innerError) { console.error('Result processing error:', innerError); } } catch (error) { console.error('āŒ Backtest failed:', error); } } // Run the test testRustBacktest().catch(console.error);