#!/usr/bin/env bun /** * Example of running a sophisticated backtest with all advanced features */ import { BacktestEngine } from '../src/backtest/BacktestEngine'; import { StrategyManager } from '../src/strategies/StrategyManager'; import { StorageService } from '../src/services/StorageService'; import { AnalyticsService } from '../src/services/AnalyticsService'; import { MeanReversionStrategy } from '../src/strategies/examples/MeanReversionStrategy'; import { MLEnhancedStrategy } from '../src/strategies/examples/MLEnhancedStrategy'; import { logger } from '@stock-bot/logger'; async function runSophisticatedBacktest() { // Initialize services const storageService = new StorageService(); await storageService.initialize({ mode: 'backtest' }); const analyticsService = new AnalyticsService({ analyticsUrl: process.env.ANALYTICS_SERVICE_URL || 'http://localhost:3003' }); const strategyManager = new StrategyManager(); // Create backtest engine const backtestEngine = new BacktestEngine(storageService, strategyManager); // Configure backtest with advanced options const config = { mode: 'backtest' as const, startDate: '2023-01-01T00:00:00Z', endDate: '2023-12-31T23:59:59Z', symbols: ['AAPL', 'GOOGL', 'MSFT', 'AMZN', 'TSLA'], initialCapital: 1_000_000, dataFrequency: '5m' as const, // 5-minute bars for detailed analysis // Advanced fill model configuration fillModel: { slippage: 'realistic' as const, marketImpact: true, partialFills: true, // Use sophisticated market impact models impactModel: 'AlmgrenChriss', // Model hidden liquidity and dark pools includeHiddenLiquidity: true, darkPoolParticipation: 0.2, // 20% of volume in dark pools // Realistic latency simulation latencyMs: { mean: 1, std: 0.5, tail: 10 // Occasional high latency } }, // Risk limits riskLimits: { maxPositionSize: 100_000, maxDailyLoss: 50_000, maxDrawdown: 0.20, // 20% max drawdown maxLeverage: 2.0, maxConcentration: 0.30 // Max 30% in single position }, // Transaction costs costs: { commission: 0.0005, // 5 bps borrowRate: 0.03, // 3% annual for shorts slippageModel: 'volumeDependent' }, // Strategies to test strategies: [ { id: 'mean_reversion_1', name: 'Mean Reversion Strategy', type: 'MeanReversion', enabled: true, allocation: 0.5, symbols: ['AAPL', 'GOOGL', 'MSFT'], parameters: { lookback: 20, entryZScore: 2.0, exitZScore: 0.5, minVolume: 1_000_000, stopLoss: 0.05 // 5% stop loss } }, { id: 'ml_enhanced_1', name: 'ML Enhanced Strategy', type: 'MLEnhanced', enabled: true, allocation: 0.5, symbols: ['AMZN', 'TSLA'], parameters: { modelPath: './models/ml_strategy_v1', updateFrequency: 1440, // Daily retraining minConfidence: 0.6 } } ] }; logger.info('Starting sophisticated backtest...'); try { // Run the backtest const result = await backtestEngine.runBacktest(config); logger.info('Backtest completed successfully'); logger.info(`Total Return: ${result.performance.totalReturn.toFixed(2)}%`); logger.info(`Sharpe Ratio: ${result.performance.sharpeRatio.toFixed(2)}`); logger.info(`Max Drawdown: ${result.performance.maxDrawdown.toFixed(2)}%`); // Run statistical validation logger.info('Running statistical validation...'); const validationResult = await analyticsService.validateBacktest({ backtestId: result.id, returns: result.dailyReturns, trades: result.trades, parameters: extractParameters(config.strategies) }); if (validationResult.is_overfit) { logger.warn('⚠️ WARNING: Backtest shows signs of overfitting!'); logger.warn(`Confidence Level: ${(validationResult.confidence_level * 100).toFixed(1)}%`); logger.warn('Recommendations:'); validationResult.recommendations.forEach(rec => { logger.warn(` - ${rec}`); }); } else { logger.info('✅ Backtest passed statistical validation'); logger.info(`PSR: ${validationResult.psr.toFixed(3)}`); logger.info(`DSR: ${validationResult.dsr.toFixed(3)}`); } // Generate comprehensive report logger.info('Generating performance report...'); const report = await backtestEngine.exportResults('html'); // Save report const fs = require('fs'); const reportPath = `./reports/backtest_${result.id}.html`; fs.writeFileSync(reportPath, report); logger.info(`Report saved to: ${reportPath}`); // Advanced analytics logger.info('Running advanced analytics...'); // Factor attribution const factorAnalysis = await analyticsService.analyzeFactors({ returns: result.dailyReturns, positions: result.finalPositions, marketReturns: await getMarketReturns(config.startDate, config.endDate) }); logger.info('Factor Attribution:'); logger.info(` Alpha: ${(factorAnalysis.alpha * 100).toFixed(2)}%`); logger.info(` Beta: ${factorAnalysis.beta.toFixed(2)}`); logger.info(` Information Ratio: ${factorAnalysis.information_ratio.toFixed(2)}`); // Transaction cost analysis const tcaReport = await analyticsService.analyzeTCA({ trades: result.trades, orders: result.orders }); logger.info('Transaction Cost Analysis:'); logger.info(` Total Costs: $${tcaReport.total_costs.toFixed(2)}`); logger.info(` Avg Cost per Trade: ${tcaReport.avg_cost_bps.toFixed(1)} bps`); logger.info(` Implementation Shortfall: ${tcaReport.implementation_shortfall_bps.toFixed(1)} bps`); // Performance by time period const periodAnalysis = analyzeByPeriod(result); logger.info('Performance by Period:'); Object.entries(periodAnalysis).forEach(([period, metrics]) => { logger.info(` ${period}: ${metrics.return.toFixed(2)}% (Sharpe: ${metrics.sharpe.toFixed(2)})`); }); // Strategy correlation analysis if (config.strategies.length > 1) { const correlations = await calculateStrategyCorrelations(result); logger.info('Strategy Correlations:'); correlations.forEach(({ pair, correlation }) => { logger.info(` ${pair}: ${correlation.toFixed(3)}`); }); } // Monte Carlo simulation logger.info('Running Monte Carlo simulation...'); const monteCarloResults = await runMonteCarloSimulation(result, 1000); logger.info(`Monte Carlo 95% VaR: ${monteCarloResults.var95.toFixed(2)}%`); logger.info(`Monte Carlo 95% CVaR: ${monteCarloResults.cvar95.toFixed(2)}%`); // Walk-forward analysis suggestion if (result.performance.totalTrades > 100) { logger.info('\n💡 Suggestion: Run walk-forward analysis for more robust validation'); logger.info('Example: bun run examples/walk-forward-analysis.ts'); } } catch (error) { logger.error('Backtest failed:', error); } finally { await storageService.shutdown(); } } // Helper functions function extractParameters(strategies: any[]): Record { const params: Record = {}; strategies.forEach(strategy => { Object.entries(strategy.parameters).forEach(([key, value]) => { params[`${strategy.id}_${key}`] = value; }); }); return params; } async function getMarketReturns(startDate: string, endDate: string): Promise { // In real implementation, would fetch SPY or market index returns // For demo, return synthetic market returns const days = Math.floor((new Date(endDate).getTime() - new Date(startDate).getTime()) / (1000 * 60 * 60 * 24)); return Array.from({ length: days }, () => (Math.random() - 0.5) * 0.02); } function analyzeByPeriod(result: any): Record { const periods = { 'Q1': { start: 0, end: 63 }, 'Q2': { start: 63, end: 126 }, 'Q3': { start: 126, end: 189 }, 'Q4': { start: 189, end: 252 } }; const analysis: Record = {}; Object.entries(periods).forEach(([name, { start, end }]) => { const periodReturns = result.dailyReturns.slice(start, end); if (periodReturns.length > 0) { const avgReturn = periodReturns.reduce((a, b) => a + b, 0) / periodReturns.length; const std = Math.sqrt(periodReturns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / periodReturns.length); analysis[name] = { return: avgReturn * periodReturns.length * 100, sharpe: std > 0 ? (avgReturn / std) * Math.sqrt(252) : 0 }; } }); return analysis; } async function calculateStrategyCorrelations(result: any): Promise> { // In real implementation, would calculate actual strategy return correlations // For demo, return sample correlations return [ { pair: 'mean_reversion_1 vs ml_enhanced_1', correlation: 0.234 } ]; } async function runMonteCarloSimulation(result: any, numSims: number): Promise<{ var95: number; cvar95: number }> { const returns = result.dailyReturns; const simulatedReturns: number[] = []; for (let i = 0; i < numSims; i++) { // Bootstrap resample returns let cumReturn = 0; for (let j = 0; j < returns.length; j++) { const randomIndex = Math.floor(Math.random() * returns.length); cumReturn += returns[randomIndex]; } simulatedReturns.push(cumReturn * 100); } // Calculate VaR and CVaR simulatedReturns.sort((a, b) => a - b); const index95 = Math.floor(numSims * 0.05); const var95 = Math.abs(simulatedReturns[index95]); const cvar95 = Math.abs(simulatedReturns.slice(0, index95).reduce((a, b) => a + b, 0) / index95); return { var95, cvar95 }; } // Run the backtest runSophisticatedBacktest().catch(console.error);