274 lines
No EOL
7.4 KiB
JavaScript
274 lines
No EOL
7.4 KiB
JavaScript
#!/usr/bin/env bun
|
|
|
|
import { BacktestEngine } from './apps/stock/core/index.js';
|
|
|
|
// Test configuration
|
|
const config = {
|
|
name: 'Native Rust Strategies Test',
|
|
symbols: ['AA', 'AAS'],
|
|
startDate: '2024-01-01T00:00:00Z',
|
|
endDate: '2024-01-31T00:00:00Z',
|
|
initialCapital: 100000,
|
|
commission: 0.001,
|
|
slippage: 0.0001,
|
|
dataFrequency: '1d',
|
|
};
|
|
|
|
// Create the Rust engine
|
|
const engine = new BacktestEngine(config);
|
|
|
|
// Test different native Rust strategies
|
|
console.log('Testing native Rust strategies...\n');
|
|
|
|
// 1. Test Mean Reversion Strategy
|
|
console.log('1. Mean Reversion Strategy');
|
|
engine.addNativeStrategy(
|
|
'mean_reversion',
|
|
'Mean Reversion AA/AAS',
|
|
'mean-rev-1',
|
|
{
|
|
lookbackPeriod: 20,
|
|
entryThreshold: 2.0,
|
|
positionSize: 1000,
|
|
}
|
|
);
|
|
|
|
// Generate synthetic market data with mean-reverting characteristics
|
|
const testData = [];
|
|
const startDate = new Date('2024-01-01');
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
const date = new Date(startDate);
|
|
date.setDate(date.getDate() + i);
|
|
|
|
// AA: Mean-reverting around 100
|
|
const aaMean = 100;
|
|
const aaPrice = aaMean + Math.sin(i / 5) * 10 + (Math.random() - 0.5) * 5;
|
|
|
|
// AAS: Mean-reverting around 50
|
|
const aasMean = 50;
|
|
const aasPrice = aasMean + Math.sin(i / 5) * 5 + (Math.random() - 0.5) * 2.5;
|
|
|
|
testData.push({
|
|
symbol: 'AA',
|
|
timestamp: date.getTime(),
|
|
type: 'bar',
|
|
open: aaPrice - 0.5,
|
|
high: aaPrice + 0.5,
|
|
low: aaPrice - 1,
|
|
close: aaPrice,
|
|
volume: 1000000,
|
|
vwap: aaPrice,
|
|
});
|
|
|
|
testData.push({
|
|
symbol: 'AAS',
|
|
timestamp: date.getTime(),
|
|
type: 'bar',
|
|
open: aasPrice - 0.25,
|
|
high: aasPrice + 0.25,
|
|
low: aasPrice - 0.5,
|
|
close: aasPrice,
|
|
volume: 500000,
|
|
vwap: aasPrice,
|
|
});
|
|
}
|
|
|
|
console.log(`Loading ${testData.length} market data points...`);
|
|
engine.loadMarketData(testData);
|
|
|
|
// Run the backtest
|
|
console.log('Running mean reversion backtest...');
|
|
try {
|
|
const resultJson = engine.run();
|
|
const result = JSON.parse(resultJson);
|
|
|
|
console.log('\nResults:');
|
|
console.log('Total trades:', result.trades?.length || 0);
|
|
console.log('Win rate:', result.metrics.win_rate?.toFixed(2) + '%');
|
|
console.log('Profit factor:', result.metrics.profit_factor?.toFixed(2));
|
|
console.log('Total PnL:', '$' + result.metrics.total_pnl?.toFixed(2));
|
|
console.log('Final equity:', '$' + result.equity_curve[result.equity_curve.length - 1]?.[1].toFixed(2));
|
|
|
|
// Show some trades
|
|
if (result.trades && result.trades.length > 0) {
|
|
console.log('\nFirst few trades:');
|
|
result.trades.slice(0, 5).forEach((trade, i) => {
|
|
console.log(` ${i + 1}. ${trade.symbol} ${trade.side} @ $${trade.price.toFixed(2)}`);
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Backtest failed:', error);
|
|
}
|
|
|
|
console.log('\n' + '='.repeat(50) + '\n');
|
|
|
|
// 2. Test Momentum Strategy
|
|
console.log('2. Momentum Strategy');
|
|
|
|
// Create a new engine for momentum strategy
|
|
const engine2 = new BacktestEngine(config);
|
|
|
|
engine2.addNativeStrategy(
|
|
'momentum',
|
|
'Momentum Trading',
|
|
'momentum-1',
|
|
{
|
|
lookbackPeriod: 10,
|
|
momentumThreshold: 5.0,
|
|
positionSize: 1000,
|
|
}
|
|
);
|
|
|
|
// Generate trending market data
|
|
const trendData = [];
|
|
for (let i = 0; i < 30; i++) {
|
|
const date = new Date(startDate);
|
|
date.setDate(date.getDate() + i);
|
|
|
|
// AA: Uptrend
|
|
const aaPrice = 100 + i * 2 + (Math.random() - 0.5) * 2;
|
|
|
|
// AAS: Downtrend then uptrend
|
|
const aasPrice = i < 15
|
|
? 50 - i * 1 + (Math.random() - 0.5) * 1
|
|
: 35 + (i - 15) * 1.5 + (Math.random() - 0.5) * 1;
|
|
|
|
trendData.push({
|
|
symbol: 'AA',
|
|
timestamp: date.getTime(),
|
|
type: 'bar',
|
|
open: aaPrice - 0.5,
|
|
high: aaPrice + 0.5,
|
|
low: aaPrice - 1,
|
|
close: aaPrice,
|
|
volume: 1000000,
|
|
vwap: aaPrice,
|
|
});
|
|
|
|
trendData.push({
|
|
symbol: 'AAS',
|
|
timestamp: date.getTime(),
|
|
type: 'bar',
|
|
open: aasPrice - 0.25,
|
|
high: aasPrice + 0.25,
|
|
low: aasPrice - 0.5,
|
|
close: aasPrice,
|
|
volume: 500000,
|
|
vwap: aasPrice,
|
|
});
|
|
}
|
|
|
|
engine2.loadMarketData(trendData);
|
|
|
|
console.log('Running momentum backtest...');
|
|
try {
|
|
const resultJson = engine2.run();
|
|
const result = JSON.parse(resultJson);
|
|
|
|
console.log('\nResults:');
|
|
console.log('Total trades:', result.trades?.length || 0);
|
|
console.log('Win rate:', result.metrics.win_rate?.toFixed(2) + '%');
|
|
console.log('Profit factor:', result.metrics.profit_factor?.toFixed(2));
|
|
console.log('Total PnL:', '$' + result.metrics.total_pnl?.toFixed(2));
|
|
console.log('Final equity:', '$' + result.equity_curve[result.equity_curve.length - 1]?.[1].toFixed(2));
|
|
|
|
} catch (error) {
|
|
console.error('Backtest failed:', error);
|
|
}
|
|
|
|
console.log('\n' + '='.repeat(50) + '\n');
|
|
|
|
// 3. Test Pairs Trading Strategy
|
|
console.log('3. Pairs Trading Strategy');
|
|
|
|
const engine3 = new BacktestEngine(config);
|
|
|
|
engine3.addNativeStrategy(
|
|
'pairs_trading',
|
|
'Pairs Trading AA/AAS',
|
|
'pairs-1',
|
|
{
|
|
pairA: 'AA',
|
|
pairB: 'AAS',
|
|
lookbackPeriod: 20,
|
|
entryThreshold: 2.0,
|
|
positionSize: 1000,
|
|
}
|
|
);
|
|
|
|
// Generate correlated market data with spread deviations
|
|
const pairsData = [];
|
|
for (let i = 0; i < 30; i++) {
|
|
const date = new Date(startDate);
|
|
date.setDate(date.getDate() + i);
|
|
|
|
// Base prices
|
|
const basePrice = 100 + Math.sin(i / 10) * 5;
|
|
|
|
// Spread oscillates around 50
|
|
const spread = 50 + Math.sin(i / 3) * 10 + (Math.random() - 0.5) * 2;
|
|
|
|
const aaPrice = basePrice;
|
|
const aasPrice = basePrice - spread;
|
|
|
|
pairsData.push({
|
|
symbol: 'AA',
|
|
timestamp: date.getTime(),
|
|
type: 'bar',
|
|
open: aaPrice - 0.5,
|
|
high: aaPrice + 0.5,
|
|
low: aaPrice - 1,
|
|
close: aaPrice,
|
|
volume: 1000000,
|
|
vwap: aaPrice,
|
|
});
|
|
|
|
pairsData.push({
|
|
symbol: 'AAS',
|
|
timestamp: date.getTime(),
|
|
type: 'bar',
|
|
open: aasPrice - 0.25,
|
|
high: aasPrice + 0.25,
|
|
low: aasPrice - 0.5,
|
|
close: aasPrice,
|
|
volume: 500000,
|
|
vwap: aasPrice,
|
|
});
|
|
}
|
|
|
|
engine3.loadMarketData(pairsData);
|
|
|
|
console.log('Running pairs trading backtest...');
|
|
try {
|
|
const resultJson = engine3.run();
|
|
const result = JSON.parse(resultJson);
|
|
|
|
console.log('\nResults:');
|
|
console.log('Total trades:', result.trades?.length || 0);
|
|
console.log('Win rate:', result.metrics.win_rate?.toFixed(2) + '%');
|
|
console.log('Profit factor:', result.metrics.profit_factor?.toFixed(2));
|
|
console.log('Total PnL:', '$' + result.metrics.total_pnl?.toFixed(2));
|
|
console.log('Final equity:', '$' + result.equity_curve[result.equity_curve.length - 1]?.[1].toFixed(2));
|
|
|
|
// Show paired trades
|
|
if (result.trades && result.trades.length > 0) {
|
|
console.log('\nPairs trades (showing pairs):');
|
|
for (let i = 0; i < result.trades.length && i < 6; i += 2) {
|
|
const trade1 = result.trades[i];
|
|
const trade2 = result.trades[i + 1];
|
|
if (trade2) {
|
|
console.log(` Pair ${Math.floor(i/2) + 1}: ${trade1.symbol} ${trade1.side} @ $${trade1.price.toFixed(2)}, ${trade2.symbol} ${trade2.side} @ $${trade2.price.toFixed(2)}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Backtest failed:', error);
|
|
}
|
|
|
|
console.log('\n' + '='.repeat(50));
|
|
console.log('\nNative Rust strategies test complete!');
|
|
console.log('\nThese strategies run at microsecond speeds in Rust,');
|
|
console.log('perfect for high-frequency trading and production use.'); |