finished initial backtest / engine
This commit is contained in:
parent
55b4ca78c9
commit
c106a719e8
18 changed files with 1571 additions and 180 deletions
138
apps/stock/orchestrator/test-clear-crossovers.ts
Executable file
138
apps/stock/orchestrator/test-clear-crossovers.ts
Executable file
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Test with very clear crossover patterns
|
||||
*/
|
||||
|
||||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { DataManager } from './src/data/DataManager';
|
||||
|
||||
async function testClearCrossovers() {
|
||||
console.log('=== Test with Clear Crossovers ===\n');
|
||||
|
||||
const logger = getLogger('test');
|
||||
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 data loading to provide clear patterns
|
||||
const dataManager = new DataManager(container as any, storageService);
|
||||
(backtestEngine as any).dataManager = dataManager;
|
||||
|
||||
(dataManager as any).loadHistoricalData = async (symbols: string[], startDate: Date, endDate: Date) => {
|
||||
const data = new Map();
|
||||
const bars = [];
|
||||
|
||||
console.log('Generating clear crossover patterns...');
|
||||
|
||||
// Generate 100 days of data with 3 clear crossovers
|
||||
// Pattern: Start high, go low (death cross), go high (golden cross), go low (death cross)
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
let price;
|
||||
|
||||
if (i < 20) {
|
||||
// Start at 100, slight upward trend
|
||||
price = 100 + i * 0.5;
|
||||
} else if (i < 40) {
|
||||
// Sharp downtrend from 110 to 70 (death cross around day 30)
|
||||
price = 110 - (i - 20) * 2;
|
||||
} else if (i < 60) {
|
||||
// Sharp uptrend from 70 to 110 (golden cross around day 50)
|
||||
price = 70 + (i - 40) * 2;
|
||||
} else if (i < 80) {
|
||||
// Sharp downtrend from 110 to 70 (death cross around day 70)
|
||||
price = 110 - (i - 60) * 2;
|
||||
} else {
|
||||
// Stabilize around 70
|
||||
price = 70 + Math.sin((i - 80) * 0.3) * 2;
|
||||
}
|
||||
|
||||
const timestamp = startDate.getTime() + i * 86400000;
|
||||
|
||||
bars.push({
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price - 0.5,
|
||||
high: price + 1,
|
||||
low: price - 1,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp
|
||||
}
|
||||
});
|
||||
|
||||
if (i % 10 === 0) {
|
||||
console.log(`Day ${i + 1}: Price = $${price.toFixed(2)}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\nExpected crossovers:');
|
||||
console.log('- Death cross around day 30');
|
||||
console.log('- Golden cross around day 50');
|
||||
console.log('- Death cross around day 70\n');
|
||||
|
||||
data.set('AAPL', bars);
|
||||
return data;
|
||||
};
|
||||
|
||||
const config = {
|
||||
mode: 'backtest' as const,
|
||||
name: 'Clear Crossovers Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-04-10T00:00:00Z', // 100 days
|
||||
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('\n=== Backtest 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) => {
|
||||
const entry = new Date(trade.entryDate).toLocaleDateString();
|
||||
const exit = trade.exitDate ? new Date(trade.exitDate).toLocaleDateString() : 'OPEN';
|
||||
console.log(`Trade ${i + 1}: ${trade.side} ${trade.quantity} @ $${trade.entryPrice.toFixed(2)} (${entry}) -> ${exit === 'OPEN' ? 'OPEN' : `$${trade.exitPrice.toFixed(2)} (${exit})`} | P&L: ${trade.pnl.toFixed(2)}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testClearCrossovers().catch(console.error);
|
||||
Loading…
Add table
Add a link
Reference in a new issue