187 lines
No EOL
6.4 KiB
TypeScript
Executable file
187 lines
No EOL
6.4 KiB
TypeScript
Executable file
#!/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); |