#!/usr/bin/env bun /** * Test with predictable data to ensure trades are generated */ import { TradingEngine } from '@stock-bot/core'; import { getLogger } from '@stock-bot/logger'; async function testPredictableBacktest() { console.log('=== Predictable Backtest Test ===\n'); const logger = getLogger('test'); // Create trading engine directly const tradingEngine = new TradingEngine('backtest', { startTime: new Date('2023-01-01').getTime(), endTime: new Date('2023-03-01').getTime(), speedMultiplier: 0 }); // Set initial capital await tradingEngine.setCapital(100000); // Generate predictable price data that will cause crossovers console.log('Generating predictable market data...'); // Phase 1: Downtrend (days 1-25) - prices fall from 100 to 75 for (let i = 0; i < 25; i++) { const price = 100 - i; const timestamp = new Date('2023-01-01').getTime() + i * 86400000; await tradingEngine.advanceTime(timestamp); await tradingEngine.updateQuote('AAPL', price - 0.01, price + 0.01, 10000, 10000); if (i % 5 === 0) { console.log(`Day ${i + 1}: Price = $${price}`); } } // Phase 2: Uptrend (days 26-50) - prices rise from 76 to 100 for (let i = 25; i < 50; i++) { const price = 76 + (i - 25); const timestamp = new Date('2023-01-01').getTime() + i * 86400000; await tradingEngine.advanceTime(timestamp); await tradingEngine.updateQuote('AAPL', price - 0.01, price + 0.01, 10000, 10000); if (i % 5 === 0) { console.log(`Day ${i + 1}: Price = $${price}`); } } // Phase 3: Another downtrend (days 51-60) - prices fall from 99 to 90 for (let i = 50; i < 60; i++) { const price = 100 - (i - 50); const timestamp = new Date('2023-01-01').getTime() + i * 86400000; await tradingEngine.advanceTime(timestamp); await tradingEngine.updateQuote('AAPL', price - 0.01, price + 0.01, 10000, 10000); if (i % 5 === 0) { console.log(`Day ${i + 1}: Price = $${price}`); } } // Test moving averages manually console.log('\n=== Expected Crossovers ==='); console.log('Around day 35-40: Golden cross (10 SMA crosses above 20 SMA)'); console.log('Around day 55-60: Death cross (10 SMA crosses below 20 SMA)'); // Get results const closedTrades = tradingEngine.getClosedTrades ? JSON.parse(tradingEngine.getClosedTrades()) : []; const tradeCount = tradingEngine.getTradeCount ? tradingEngine.getTradeCount() : 0; const [realizedPnl, unrealizedPnl] = tradingEngine.getTotalPnl(); console.log('\n=== Results ==='); console.log(`Total trades: ${tradeCount}`); console.log(`Closed trades: ${closedTrades.length}`); console.log(`Realized P&L: $${realizedPnl.toFixed(2)}`); console.log(`Unrealized P&L: $${unrealizedPnl.toFixed(2)}`); // Now let's test the full backtest with this data pattern console.log('\n=== Running Full Backtest with SMA Strategy ==='); const { BacktestEngine } = await import('./src/backtest/BacktestEngine'); const { StrategyManager } = await import('./src/strategies/StrategyManager'); const { StorageService } = await import('./src/services/StorageService'); const { ModeManager } = await import('./src/core/ModeManager'); const { MarketDataService } = await import('./src/services/MarketDataService'); const { ExecutionService } = await import('./src/services/ExecutionService'); const { DataManager } = await import('./src/data/DataManager'); const container = { logger, custom: {} }; const storageService = new StorageService(container as any); const marketDataService = new MarketDataService(container as any); const executionService = new ExecutionService(container as any); const modeManager = new ModeManager(container as any, marketDataService, executionService, storageService); container.custom = { ModeManager: modeManager, MarketDataService: marketDataService, ExecutionService: executionService }; const strategyManager = new StrategyManager(container as any); const backtestEngine = new BacktestEngine(container as any, storageService, strategyManager); // Override the data manager to return our predictable data const dataManager = new DataManager(container as any, storageService); (backtestEngine as any).dataManager = dataManager; // Mock the loadHistoricalData to return our pattern (dataManager as any).loadHistoricalData = async (symbols: string[], startDate: Date, endDate: Date) => { const data = new Map(); const bars = []; // Generate the same pattern as above for (let i = 0; i < 60; i++) { let price; if (i < 25) { price = 100 - i; } else if (i < 50) { price = 76 + (i - 25); } else { price = 100 - (i - 50); } const timestamp = startDate.getTime() + i * 86400000; bars.push({ type: 'bar', data: { symbol: 'AAPL', open: price - 0.5, high: price + 0.5, low: price - 0.5, close: price, volume: 1000000, timestamp } }); } data.set('AAPL', bars); return data; }; const config = { mode: 'backtest' as const, name: 'Predictable Test', strategy: 'sma-crossover', symbols: ['AAPL'], startDate: '2023-01-01', endDate: '2023-03-01', initialCapital: 100000, dataFrequency: '1d', commission: 0.001, slippage: 0.0001, speed: 'max' as const }; await modeManager.initializeMode(config); try { const result = await backtestEngine.runBacktest(config); console.log('\nBacktest Results:'); console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`); console.log(`Total Trades: ${result.metrics.totalTrades}`); console.log(`Trades in history: ${result.trades.length}`); console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`); console.log('\nTrade Details:'); result.trades.forEach((trade, i) => { console.log(`Trade ${i + 1}: ${trade.side} ${trade.quantity} @ $${trade.entryPrice.toFixed(2)} -> $${trade.exitPrice?.toFixed(2) || 'OPEN'}`); }); } catch (error) { console.error('Backtest failed:', error); } } testPredictableBacktest().catch(console.error);