import { DataFrame } from '@stock-bot/data-frame'; import { EventBus } from '@stock-bot/event-bus'; import { getLogger } from '@stock-bot/logger'; import { VectorEngine, VectorizedBacktestResult } from '@stock-bot/vector-engine'; import { BacktestContext, BacktestResult, ExecutionMode } from '../framework/execution-mode'; export interface VectorizedModeConfig { batchSize?: number; enableOptimization?: boolean; parallelProcessing?: boolean; } export class VectorizedMode extends ExecutionMode { private vectorEngine: VectorEngine; private config: VectorizedModeConfig; private logger = getLogger('vectorized-mode'); constructor(context: BacktestContext, eventBus: EventBus, config: VectorizedModeConfig = {}) { super(context, eventBus); this.vectorEngine = new VectorEngine(); this.config = { batchSize: 10000, enableOptimization: true, parallelProcessing: true, ...config, }; } async initialize(): Promise { await super.initialize(); this.logger.info('Vectorized mode initialized', { backtestId: this.context.backtestId, config: this.config, }); } async execute(): Promise { const startTime = Date.now(); this.logger.info('Starting vectorized backtest execution'); try { // Load all data at once for vectorized processing const data = await this.loadHistoricalData(); // Convert to DataFrame format const dataFrame = this.createDataFrame(data); // Execute vectorized strategy const strategyCode = this.generateStrategyCode(); const vectorResult = await this.vectorEngine.executeVectorizedStrategy( dataFrame, strategyCode ); // Convert to standard backtest result format const result = this.convertVectorizedResult(vectorResult, startTime); // Emit completion event await this.eventBus.publishBacktestUpdate(this.context.backtestId, 100, { status: 'completed', result, }); this.logger.info('Vectorized backtest completed', { backtestId: this.context.backtestId, duration: Date.now() - startTime, totalTrades: result.trades.length, }); return result; } catch (error) { this.logger.error('Vectorized backtest failed', { error, backtestId: this.context.backtestId, }); await this.eventBus.publishBacktestUpdate(this.context.backtestId, 0, { status: 'failed', error: error.message, }); throw error; } } private async loadHistoricalData(): Promise { // Load all historical data at once // This is much more efficient than loading tick by tick const data = []; // Simulate loading data (in production, this would be a bulk database query) const startTime = new Date(this.context.startDate).getTime(); const endTime = new Date(this.context.endDate).getTime(); const interval = 60000; // 1 minute intervals for (let timestamp = startTime; timestamp <= endTime; timestamp += interval) { // Simulate OHLCV data const basePrice = 100 + Math.sin(timestamp / 1000000) * 10; const volatility = 0.02; const open = basePrice + (Math.random() - 0.5) * volatility * basePrice; const close = open + (Math.random() - 0.5) * volatility * basePrice; const high = Math.max(open, close) + Math.random() * volatility * basePrice; const low = Math.min(open, close) - Math.random() * volatility * basePrice; const volume = Math.floor(Math.random() * 10000) + 1000; data.push({ timestamp, symbol: this.context.symbol, open, high, low, close, volume, }); } return data; } private createDataFrame(data: any[]): DataFrame { return new DataFrame(data, { columns: ['timestamp', 'symbol', 'open', 'high', 'low', 'close', 'volume'], dtypes: { timestamp: 'number', symbol: 'string', open: 'number', high: 'number', low: 'number', close: 'number', volume: 'number', }, }); } private generateStrategyCode(): string { // Convert strategy configuration to vectorized strategy code // This is a simplified example - in production you'd have a more sophisticated compiler const strategy = this.context.strategy; if (strategy.type === 'sma_crossover') { return 'sma_crossover'; } // Add more strategy types as needed return strategy.code || 'sma_crossover'; } private convertVectorizedResult( vectorResult: VectorizedBacktestResult, startTime: number ): BacktestResult { return { backtestId: this.context.backtestId, strategy: this.context.strategy, symbol: this.context.symbol, startDate: this.context.startDate, endDate: this.context.endDate, mode: 'vectorized', duration: Date.now() - startTime, trades: vectorResult.trades.map(trade => ({ id: `trade_${trade.entryIndex}_${trade.exitIndex}`, symbol: this.context.symbol, side: trade.side, entryTime: vectorResult.timestamps[trade.entryIndex], exitTime: vectorResult.timestamps[trade.exitIndex], entryPrice: trade.entryPrice, exitPrice: trade.exitPrice, quantity: trade.quantity, pnl: trade.pnl, commission: 0, // Simplified slippage: 0, })), performance: { totalReturn: vectorResult.metrics.totalReturns, sharpeRatio: vectorResult.metrics.sharpeRatio, maxDrawdown: vectorResult.metrics.maxDrawdown, winRate: vectorResult.metrics.winRate, profitFactor: vectorResult.metrics.profitFactor, totalTrades: vectorResult.metrics.totalTrades, winningTrades: vectorResult.trades.filter(t => t.pnl > 0).length, losingTrades: vectorResult.trades.filter(t => t.pnl <= 0).length, avgTrade: vectorResult.metrics.avgTrade, avgWin: vectorResult.trades.filter(t => t.pnl > 0).reduce((sum, t) => sum + t.pnl, 0) / vectorResult.trades.filter(t => t.pnl > 0).length || 0, avgLoss: vectorResult.trades.filter(t => t.pnl <= 0).reduce((sum, t) => sum + t.pnl, 0) / vectorResult.trades.filter(t => t.pnl <= 0).length || 0, largestWin: Math.max(...vectorResult.trades.map(t => t.pnl), 0), largestLoss: Math.min(...vectorResult.trades.map(t => t.pnl), 0), }, equity: vectorResult.equity, drawdown: vectorResult.metrics.drawdown, metadata: { mode: 'vectorized', dataPoints: vectorResult.timestamps.length, signals: Object.keys(vectorResult.signals), optimizations: this.config.enableOptimization ? ['vectorized_computation'] : [], }, }; } async cleanup(): Promise { await super.cleanup(); this.logger.info('Vectorized mode cleanup completed'); } // Batch processing capabilities async batchBacktest( strategies: Array<{ id: string; config: any }> ): Promise> { this.logger.info('Starting batch vectorized backtest', { strategiesCount: strategies.length, }); const data = await this.loadHistoricalData(); const dataFrame = this.createDataFrame(data); const strategyConfigs = strategies.map(s => ({ id: s.id, code: this.generateStrategyCode(), })); const batchResults = await this.vectorEngine.batchBacktest(dataFrame, strategyConfigs); const results: Record = {}; for (const [strategyId, vectorResult] of Object.entries(batchResults)) { results[strategyId] = this.convertVectorizedResult(vectorResult, Date.now()); } return results; } } export default VectorizedMode;