stock-bot/test-rust-strategies.js

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.');