finished initial backtest / engine
This commit is contained in:
parent
55b4ca78c9
commit
c106a719e8
18 changed files with 1571 additions and 180 deletions
187
apps/stock/orchestrator/test-predictable-backtest.ts
Executable file
187
apps/stock/orchestrator/test-predictable-backtest.ts
Executable file
|
|
@ -0,0 +1,187 @@
|
|||
#!/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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue