Package Exports
- @neural-trader/backtesting
- @neural-trader/backtesting/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@neural-trader/backtesting) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@neural-trader/backtesting
Lightning-fast backtesting engine powered by Rust with zero-overhead Node.js bindings. Test trading strategies against historical data with microsecond-level precision.
Introduction
Testing trading strategies on historical data is critical for validating performance before risking real capital. Traditional JavaScript-based backtesting engines are too slow for production use, often taking minutes or hours to process thousands of trades. @neural-trader/backtesting solves this with a native Rust implementation that delivers 8-19x faster performance than Python alternatives.
Built on the Rust nt-backtesting crate, this package provides comprehensive backtesting capabilities including commission modeling, slippage simulation, mark-to-market accounting, and detailed performance metrics. All calculations happen at native speed with automatic multi-threading for maximum performance.
The package seamlessly integrates with @neural-trader/strategies for signal generation, @neural-trader/neural for AI-powered predictions, and @neural-trader/risk for position sizing and risk management.
Features
- Lightning-Fast Execution - Process 10,000+ trades in milliseconds with Rust's native performance
- Multi-Threaded Processing - Automatic parallelization across CPU cores for walk-forward analysis
- Realistic Simulation - Commission, slippage, and mark-to-market P&L calculations
- Comprehensive Metrics - Sharpe ratio, Sortino ratio, max drawdown, win rate, profit factor, and more
- Equity Curve Tracking - Full trade history with timestamp-synchronized equity curves
- Walk-Forward Analysis - Built-in support for out-of-sample testing and parameter optimization
- CSV Export - Export trade history and equity curves for external analysis
- Zero-Copy NAPI - Efficient memory handling with minimal JavaScript overhead
- Self-Learning Integration - Works with @neural-trader/neural for adaptive strategy optimization
Installation
Via npm (Node.js)
# Core package
npm install @neural-trader/backtesting
# With related packages
npm install @neural-trader/backtesting @neural-trader/strategies @neural-trader/risk
# Full platform (all features)
npm install neural-traderVia Cargo (Rust)
# Add to Cargo.toml
[dependencies]
nt-backtesting = "1.0.0"
# Or install CLI tool
cargo install nt-backtesting-cliPackage Size: ~300 KB (compressed native binary)
See main packages documentation for all available packages.
Quick Start
30-second example showing basic backtesting:
const { BacktestEngine } = require('@neural-trader/backtesting');
async function quickStart() {
// Initialize backtest engine
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001, // 0.1% per trade
slippage: 0.0005, // 0.05% slippage
useMarkToMarket: true
});
// Generate signals from your strategy
const signals = [
{
id: '1',
strategyId: 'momentum-strategy',
symbol: 'AAPL',
direction: 'long',
confidence: 0.85,
entryPrice: 150.00,
stopLoss: 145.00,
takeProfit: 160.00,
reasoning: 'Strong momentum breakout',
timestampNs: Date.now() * 1_000_000
}
];
// Run backtest
const result = await engine.run(signals, 'data/market-data.csv');
console.log('Performance Metrics:', result.metrics);
console.log(`Total Return: ${(result.metrics.totalReturn * 100).toFixed(2)}%`);
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio.toFixed(2)}`);
console.log(`Max Drawdown: ${(result.metrics.maxDrawdown * 100).toFixed(2)}%`);
}
quickStart().catch(console.error);Expected Output:
Performance Metrics: {
totalReturn: 0.127,
sharpeRatio: 1.84,
sortinoRatio: 2.31,
maxDrawdown: 0.082,
winRate: 0.58,
profitFactor: 2.15,
totalTrades: 142
}
Total Return: 12.70%
Sharpe Ratio: 1.84
Max Drawdown: 8.20%Core Concepts
Signal-Based Backtesting
@neural-trader/backtesting uses a signal-based approach where your strategy generates trade signals that the engine executes against historical market data.
// Signals define entry/exit intentions
const signal = {
id: 'unique-id',
strategyId: 'your-strategy',
symbol: 'AAPL',
direction: 'long', // 'long' or 'short'
confidence: 0.75, // 0.0 to 1.0
entryPrice: 150.00,
stopLoss: 145.00, // Risk management
takeProfit: 160.00, // Profit target
reasoning: 'Why this trade',
timestampNs: 1234567890000000n // Nanosecond precision
};Realistic Trade Execution
The engine simulates realistic market conditions with commission and slippage:
const config = {
initialCapital: 100000,
commission: 0.001, // 0.1% per trade side
slippage: 0.0005, // 0.05% average slippage
useMarkToMarket: true // Daily P&L calculation
};
// Actual entry price includes slippage and commission
// Entry: $150.00 * (1 + 0.0005) * (1 + 0.001) = $150.23
// Commission: $150.23 * 0.001 = $0.15Performance Metrics
Comprehensive metrics automatically calculated after each backtest:
const metrics = {
totalReturn: 0.15, // 15% total return
annualReturn: 0.125, // Annualized
sharpeRatio: 1.8, // Risk-adjusted return
sortinoRatio: 2.3, // Downside risk-adjusted
maxDrawdown: 0.12, // Maximum peak-to-trough decline
winRate: 0.58, // 58% winning trades
profitFactor: 2.1, // Gross profit / gross loss
totalTrades: 150,
winningTrades: 87,
losingTrades: 63,
avgWin: 0.025, // 2.5% average win
avgLoss: 0.015, // 1.5% average loss
largestWin: 0.08,
largestLoss: 0.05,
finalEquity: 115000
};Key Terminology
- Signal: A trading intention generated by your strategy with entry/exit parameters
- Equity Curve: Portfolio value over time, tracking all P&L and positions
- Mark-to-Market: Daily portfolio valuation using current market prices
- Walk-Forward Analysis: Testing strategy on rolling out-of-sample periods
- Slippage: The difference between expected and actual execution prices
- Commission: Transaction costs per trade side (entry and exit)
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ @neural-trader/backtesting │
│ │
│ ┌────────────────────┐ ┌─────────────────────────┐ │
│ │ BacktestEngine │────────▶│ Rust nt-backtesting │ │
│ │ (Node.js API) │ NAPI │ (Native Performance) │ │
│ └────────────────────┘ └─────────────────────────┘ │
│ │ │ │
│ │ Signals │ Market Data │
│ ▼ ▼ │
│ ┌────────────────────┐ ┌─────────────────────────┐ │
│ │ Strategy Generator │ │ Multi-threaded Executor │ │
│ └────────────────────┘ └─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ Metrics Calculator │ │
│ │ - Sharpe/Sortino │ │
│ │ - Drawdown Analysis │ │
│ │ - Trade Statistics │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘API Reference
BacktestEngine
High-performance backtesting engine for executing strategy signals against historical market data.
Constructor
new BacktestEngine(options: BacktestConfig)Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| initialCapital | number | Yes | - | Starting portfolio value in dollars |
| startDate | string | Yes | - | Backtest start date (YYYY-MM-DD) |
| endDate | string | Yes | - | Backtest end date (YYYY-MM-DD) |
| commission | number | No | 0.001 | Commission per trade side (0.001 = 0.1%) |
| slippage | number | No | 0.0005 | Average slippage per trade (0.0005 = 0.05%) |
| useMarkToMarket | boolean | No | true | Enable daily mark-to-market P&L calculation |
Example:
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});Methods
run(signals: Signal[], marketDataPath: string): Promise<BacktestResult>
Execute backtest with strategy signals against historical market data.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| signals | Signal[] | Yes | Array of trading signals from your strategy |
| marketDataPath | string | Yes | Path to CSV file with historical OHLCV data |
Returns: Promise resolving to BacktestResult with metrics, trades, and equity curve
Example:
const result = await engine.run(signals, 'data/AAPL-2024.csv');
console.log(result.metrics);
// Output: { totalReturn: 0.15, sharpeRatio: 1.8, maxDrawdown: 0.12, ... }calculateMetrics(equityCurve: number[]): BacktestMetrics
Calculate performance metrics from an equity curve.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| equityCurve | number[] | Yes | Array of portfolio values over time |
Returns: BacktestMetrics object with all performance statistics
Example:
const metrics = engine.calculateMetrics([100000, 102000, 101500, 105000]);
console.log(`Sharpe: ${metrics.sharpeRatio.toFixed(2)}`);exportTradesCsv(trades: Trade[]): string
Export trade history to CSV format for external analysis.
Example:
const csv = engine.exportTradesCsv(result.trades);
require('fs').writeFileSync('trades.csv', csv);compareBacktests
Compare multiple backtest results side-by-side.
Signature:
function compareBacktests(results: BacktestResult[]): stringExample:
const { compareBacktests } = require('@neural-trader/backtesting');
const comparison = compareBacktests([result1, result2, result3]);
console.log(comparison);Type Definitions
BacktestResult
interface BacktestResult {
metrics: BacktestMetrics; // Performance statistics
trades: Trade[]; // All executed trades with P&L
equityCurve: number[]; // Portfolio value over time
dates: string[]; // Corresponding timestamps
}BacktestMetrics
interface BacktestMetrics {
totalReturn: number; // Total return (0.15 = 15%)
annualReturn: number; // Annualized return
sharpeRatio: number; // Risk-adjusted return
sortinoRatio: number; // Downside risk-adjusted return
maxDrawdown: number; // Maximum drawdown
winRate: number; // Winning trades / total trades
profitFactor: number; // Gross profit / gross loss
totalTrades: number; // Total number of trades
winningTrades: number; // Number of winning trades
losingTrades: number; // Number of losing trades
avgWin: number; // Average winning trade return
avgLoss: number; // Average losing trade return
largestWin: number; // Largest winning trade
largestLoss: number; // Largest losing trade
finalEquity: number; // Final portfolio value
}Detailed Tutorials
Tutorial 1: Backtest a Momentum Strategy
Goal: Test a simple momentum breakout strategy and analyze performance metrics
Prerequisites:
- Basic understanding of trading signals
- Historical market data in CSV format (OHLCV)
Step 1: Generate Momentum Signals
Create signals when price breaks above 20-day high:
const { BacktestEngine } = require('@neural-trader/backtesting');
function generateMomentumSignals(marketData) {
const signals = [];
const lookback = 20;
for (let i = lookback; i < marketData.length; i++) {
const recentPrices = marketData.slice(i - lookback, i).map(b => b.high);
const maxHigh = Math.max(...recentPrices);
const currentBar = marketData[i];
// Breakout signal
if (currentBar.close > maxHigh) {
signals.push({
id: `signal-${i}`,
strategyId: 'momentum-breakout',
symbol: currentBar.symbol,
direction: 'long',
confidence: 0.8,
entryPrice: currentBar.close,
stopLoss: currentBar.close * 0.95, // 5% stop loss
takeProfit: currentBar.close * 1.10, // 10% take profit
reasoning: `Breakout above ${lookback}-day high`,
timestampNs: BigInt(currentBar.timestamp) * 1_000_000n
});
}
}
return signals;
}Step 2: Run Backtest
Execute the backtest with realistic parameters:
async function backtestMomentum() {
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001, // 0.1% commission
slippage: 0.0005, // 0.05% slippage
useMarkToMarket: true
});
// Load market data
const marketData = loadMarketData('data/AAPL-2024.csv');
const signals = generateMomentumSignals(marketData);
console.log(`Generated ${signals.length} signals`);
// Run backtest
const result = await engine.run(signals, 'data/AAPL-2024.csv');
return result;
}Step 3: Analyze Results
Review performance metrics and equity curve:
async function analyzeResults() {
const result = await backtestMomentum();
const m = result.metrics;
console.log('=== Backtest Results ===');
console.log(`Total Return: ${(m.totalReturn * 100).toFixed(2)}%`);
console.log(`Annual Return: ${(m.annualReturn * 100).toFixed(2)}%`);
console.log(`Sharpe Ratio: ${m.sharpeRatio.toFixed(2)}`);
console.log(`Sortino Ratio: ${m.sortinoRatio.toFixed(2)}`);
console.log(`Max Drawdown: ${(m.maxDrawdown * 100).toFixed(2)}%`);
console.log('');
console.log(`Total Trades: ${m.totalTrades}`);
console.log(`Win Rate: ${(m.winRate * 100).toFixed(1)}%`);
console.log(`Profit Factor: ${m.profitFactor.toFixed(2)}`);
console.log(`Avg Win: ${(m.avgWin * 100).toFixed(2)}%`);
console.log(`Avg Loss: ${(m.avgLoss * 100).toFixed(2)}%`);
console.log('');
console.log(`Final Equity: $${m.finalEquity.toLocaleString()}`);
// Export trades for analysis
const csv = engine.exportTradesCsv(result.trades);
require('fs').writeFileSync('momentum-trades.csv', csv);
console.log('\nTrades exported to momentum-trades.csv');
}Expected Output:
=== Backtest Results ===
Total Return: 15.30%
Annual Return: 15.30%
Sharpe Ratio: 1.84
Sortino Ratio: 2.31
Max Drawdown: 8.20%
Total Trades: 42
Win Rate: 61.9%
Profit Factor: 2.15
Avg Win: 4.20%
Avg Loss: 2.30%
Final Equity: $115,300
Trades exported to momentum-trades.csvComplete Example:
const { BacktestEngine } = require('@neural-trader/backtesting');
const fs = require('fs');
async function completeMomentumBacktest() {
// 1. Load market data
const marketData = loadMarketData('data/AAPL-2024.csv');
// 2. Generate signals
const signals = generateMomentumSignals(marketData);
// 3. Configure engine
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
// 4. Run backtest
const result = await engine.run(signals, 'data/AAPL-2024.csv');
// 5. Analyze and export
console.log('Performance Metrics:', result.metrics);
fs.writeFileSync('trades.csv', engine.exportTradesCsv(result.trades));
return result;
}
completeMomentumBacktest().catch(console.error);Tutorial 2: Multi-Symbol Portfolio Backtest
Goal: Test a diversified portfolio strategy across multiple symbols
Prerequisites:
- Market data for multiple symbols
- Understanding of portfolio allocation
Step 1: Configure Multi-Symbol Backtest
const symbols = ['AAPL', 'MSFT', 'GOOGL', 'AMZN'];
async function multiSymbolBacktest() {
const engine = new BacktestEngine({
initialCapital: 500000, // Larger capital for diversification
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
// Generate signals for each symbol
const allSignals = [];
for (const symbol of symbols) {
const data = loadMarketData(`data/${symbol}-2024.csv`);
const signals = generateMomentumSignals(data);
allSignals.push(...signals);
}
// Sort signals by timestamp
allSignals.sort((a, b) => Number(a.timestampNs - b.timestampNs));
return engine.run(allSignals, 'data/combined-2024.csv');
}Step 2: Risk-Adjusted Position Sizing
const { RiskManager } = require('@neural-trader/risk');
function generateRiskAdjustedSignals(marketData, portfolioValue) {
const riskManager = new RiskManager({
confidenceLevel: 0.95,
lookbackPeriods: 252,
method: 'historical'
});
const signals = [];
for (let i = 60; i < marketData.length; i++) {
const bar = marketData[i];
// Calculate position size based on 2% risk per trade
const stopLoss = bar.close * 0.95; // 5% stop
const stopDistance = Math.abs(bar.close - stopLoss) / bar.close;
const positionSize = riskManager.calculatePositionSize(
portfolioValue,
bar.close,
0.02, // Risk 2% per trade
stopDistance
);
signals.push({
id: `signal-${i}`,
strategyId: 'risk-adjusted-momentum',
symbol: bar.symbol,
direction: 'long',
confidence: 0.75,
entryPrice: bar.close,
stopLoss: stopLoss,
takeProfit: bar.close * 1.10,
positionSize: positionSize.shares, // Number of shares
reasoning: `Risk-adjusted position: ${positionSize.shares} shares`,
timestampNs: BigInt(bar.timestamp) * 1_000_000n
});
}
return signals;
}Step 3: Compare Strategies
const { compareBacktests } = require('@neural-trader/backtesting');
async function compareMultipleStrategies() {
// Test different parameter sets
const results = [];
// Strategy 1: Aggressive (10% stop, 20% target)
results.push(await backtestStrategy(0.90, 1.20));
// Strategy 2: Moderate (5% stop, 10% target)
results.push(await backtestStrategy(0.95, 1.10));
// Strategy 3: Conservative (3% stop, 6% target)
results.push(await backtestStrategy(0.97, 1.06));
// Compare all strategies
const comparison = compareBacktests(results);
console.log(comparison);
// Find best strategy
const best = results.reduce((a, b) =>
a.metrics.sharpeRatio > b.metrics.sharpeRatio ? a : b
);
console.log('\nBest Strategy:');
console.log(`Sharpe Ratio: ${best.metrics.sharpeRatio.toFixed(2)}`);
console.log(`Total Return: ${(best.metrics.totalReturn * 100).toFixed(2)}%`);
}Complete Example:
async function completeMultiSymbolBacktest() {
const symbols = ['AAPL', 'MSFT', 'GOOGL', 'AMZN'];
const engine = new BacktestEngine({
initialCapital: 500000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
// Generate and combine signals
const allSignals = symbols.flatMap(symbol =>
generateRiskAdjustedSignals(
loadMarketData(`data/${symbol}-2024.csv`),
500000 / symbols.length // Equal allocation
)
).sort((a, b) => Number(a.timestampNs - b.timestampNs));
// Run backtest
const result = await engine.run(allSignals, 'data/combined-2024.csv');
console.log('Multi-Symbol Portfolio Results:');
console.log(`Total Return: ${(result.metrics.totalReturn * 100).toFixed(2)}%`);
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio.toFixed(2)}`);
console.log(`Max Drawdown: ${(result.metrics.maxDrawdown * 100).toFixed(2)}%`);
return result;
}Tutorial 3: Walk-Forward Analysis
Goal: Validate strategy robustness with rolling out-of-sample testing
Complete Implementation:
const { BacktestEngine } = require('@neural-trader/backtesting');
async function walkForwardAnalysis(marketData, windowDays = 252, stepDays = 63) {
const results = [];
for (let i = 0; i < marketData.length - windowDays; i += stepDays) {
// In-sample training window
const trainData = marketData.slice(i, i + windowDays * 0.7);
// Out-of-sample testing window
const testData = marketData.slice(
i + windowDays * 0.7,
i + windowDays
);
// Optimize parameters on training data
const params = optimizeParameters(trainData);
// Test on out-of-sample data
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: testData[0].date,
endDate: testData[testData.length - 1].date,
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
const signals = generateSignalsWithParams(testData, params);
const result = await engine.run(signals, `data/test-${i}.csv`);
results.push({
period: `${testData[0].date} to ${testData[testData.length - 1].date}`,
metrics: result.metrics,
params: params
});
console.log(`Period ${results.length}: Return ${(result.metrics.totalReturn * 100).toFixed(2)}%`);
}
// Aggregate results
const avgReturn = results.reduce((sum, r) => sum + r.metrics.totalReturn, 0) / results.length;
const avgSharpe = results.reduce((sum, r) => sum + r.metrics.sharpeRatio, 0) / results.length;
console.log('\nWalk-Forward Analysis Complete:');
console.log(`Average Return: ${(avgReturn * 100).toFixed(2)}%`);
console.log(`Average Sharpe: ${avgSharpe.toFixed(2)}`);
console.log(`Consistency: ${results.filter(r => r.metrics.totalReturn > 0).length}/${results.length} periods profitable`);
return results;
}
function optimizeParameters(trainData) {
// Simple parameter optimization
const lookbacks = [10, 20, 30, 50];
const results = lookbacks.map(lookback => ({
lookback,
return: calculateReturn(trainData, lookback)
}));
return results.sort((a, b) => b.return - a.return)[0];
}Tutorial 4: Neural Strategy Integration
Goal: Integrate AI-powered predictions with backtesting
Implementation:
const { BacktestEngine } = require('@neural-trader/backtesting');
const { NeuralModel } = require('@neural-trader/neural');
async function neuralStrategyBacktest() {
// 1. Train neural model
const model = new NeuralModel({
modelType: 'LSTMAttention',
inputSize: 50,
horizon: 5,
hiddenSize: 256,
numLayers: 3,
dropout: 0.2,
learningRate: 0.001
});
const trainData = prepareTrainingData('data/AAPL-train.csv');
await model.train(trainData.features, trainData.targets, {
epochs: 100,
batchSize: 32,
validationSplit: 0.2,
earlyStoppingPatience: 10,
useGpu: true
});
// 2. Generate AI-powered signals
async function generateNeuralSignals(marketData) {
const signals = [];
for (let i = 60; i < marketData.length; i++) {
const features = extractFeatures(marketData.slice(i - 60, i));
const prediction = await model.predict(features);
// Only trade on high-confidence predictions
if (Math.abs(prediction.predictions[0]) > 0.01) {
const direction = prediction.predictions[0] > 0 ? 'long' : 'short';
const confidence = Math.abs(prediction.predictions[0]);
signals.push({
id: `neural-${i}`,
strategyId: 'lstm-momentum',
symbol: marketData[i].symbol,
direction: direction,
confidence: confidence,
entryPrice: marketData[i].close,
stopLoss: marketData[i].close * (direction === 'long' ? 0.97 : 1.03),
takeProfit: marketData[i].close * (direction === 'long' ? 1.06 : 0.94),
reasoning: `AI predicted ${(prediction.predictions[0] * 100).toFixed(2)}% return`,
timestampNs: BigInt(marketData[i].timestamp) * 1_000_000n
});
}
}
return signals;
}
// 3. Backtest neural strategy
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
const testData = loadMarketData('data/AAPL-test.csv');
const signals = await generateNeuralSignals(testData);
const result = await engine.run(signals, 'data/AAPL-test.csv');
console.log('Neural Strategy Results:');
console.log(`Total Return: ${(result.metrics.totalReturn * 100).toFixed(2)}%`);
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio.toFixed(2)}`);
console.log(`Win Rate: ${(result.metrics.winRate * 100).toFixed(1)}%`);
return result;
}Tutorial 5: Live Trading Simulation
Goal: Simulate real-time trading with delayed data feeds
Advanced Example:
const { BacktestEngine } = require('@neural-trader/backtesting');
class LiveTradingSimulator {
constructor(config) {
this.engine = new BacktestEngine(config);
this.signals = [];
this.positions = new Map();
}
async onBar(bar) {
// Generate signal based on current bar
const signal = await this.analyzeBar(bar);
if (signal) {
this.signals.push(signal);
console.log(`Signal generated: ${signal.direction} at $${signal.entryPrice}`);
}
// Check for stop loss / take profit
for (const [id, position] of this.positions) {
if (bar.low <= position.stopLoss) {
console.log(`Stop loss hit for position ${id}`);
this.closePosition(id, position.stopLoss);
} else if (bar.high >= position.takeProfit) {
console.log(`Take profit hit for position ${id}`);
this.closePosition(id, position.takeProfit);
}
}
}
async analyzeBar(bar) {
// Your strategy logic here
// Return signal or null
}
async runSimulation(marketDataPath) {
// Simulate live trading bar-by-bar
const data = loadMarketData(marketDataPath);
for (const bar of data) {
await this.onBar(bar);
await sleep(100); // Simulate real-time delay
}
// Final backtest with all signals
return this.engine.run(this.signals, marketDataPath);
}
}
// Usage
const simulator = new LiveTradingSimulator({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
const result = await simulator.runSimulation('data/AAPL-2024.csv');Integration Examples
Integration with @neural-trader/strategies
Combine backtesting with pre-built trading strategies:
const { BacktestEngine } = require('@neural-trader/backtesting');
const { MomentumStrategy } = require('@neural-trader/strategies');
async function backtestStrategy() {
const strategy = new MomentumStrategy({
lookbackPeriod: 20,
stopLossPercent: 0.05,
takeProfitPercent: 0.10
});
// Generate signals from strategy
const marketData = loadMarketData('data/AAPL-2024.csv');
const signals = [];
for (const bar of marketData) {
const signal = await strategy.analyze(bar);
if (signal) signals.push(signal);
}
// Backtest signals
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
return engine.run(signals, 'data/AAPL-2024.csv');
}Integration with @neural-trader/risk
Add risk management to your backtesting:
const { BacktestEngine } = require('@neural-trader/backtesting');
const { RiskManager } = require('@neural-trader/risk');
async function riskManagedBacktest() {
const riskManager = new RiskManager({
confidenceLevel: 0.95,
lookbackPeriods: 252,
method: 'historical'
});
// Calculate position sizes with risk management
function generateRiskAdjustedSignals(marketData, portfolioValue) {
const signals = [];
for (let i = 20; i < marketData.length; i++) {
const bar = marketData[i];
const stopLoss = bar.close * 0.95;
const stopDistance = Math.abs(bar.close - stopLoss) / bar.close;
// Calculate position size for 2% risk per trade
const position = riskManager.calculatePositionSize(
portfolioValue,
bar.close,
0.02,
stopDistance
);
signals.push({
id: `signal-${i}`,
strategyId: 'risk-managed',
symbol: bar.symbol,
direction: 'long',
confidence: 0.8,
entryPrice: bar.close,
stopLoss: stopLoss,
takeProfit: bar.close * 1.10,
positionSize: position.shares,
reasoning: position.reasoning,
timestampNs: BigInt(bar.timestamp) * 1_000_000n
});
}
return signals;
}
const marketData = loadMarketData('data/AAPL-2024.csv');
const signals = generateRiskAdjustedSignals(marketData, 100000);
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
const result = await engine.run(signals, 'data/AAPL-2024.csv');
// Analyze risk metrics
const returns = calculateReturns(result.equityCurve);
const var95 = riskManager.calculateVar(returns, 100000);
const drawdown = riskManager.calculateDrawdown(result.equityCurve);
console.log(`VaR (95%): $${var95.varAmount.toLocaleString()}`);
console.log(`Max Drawdown: ${(drawdown.maxDrawdown * 100).toFixed(2)}%`);
return result;
}Integration with @neural-trader/neural
Combine neural networks with backtesting for AI-driven strategies:
const { BacktestEngine } = require('@neural-trader/backtesting');
const { NeuralModel } = require('@neural-trader/neural');
const { RiskManager } = require('@neural-trader/risk');
async function neuralBacktestWithRisk() {
// 1. Train neural model
const model = new NeuralModel({
modelType: 'LSTMAttention',
inputSize: 50,
horizon: 5,
hiddenSize: 256,
numLayers: 3,
dropout: 0.2,
learningRate: 0.001
});
const trainData = prepareTrainingData('data/train.csv');
await model.train(trainData.features, trainData.targets, {
epochs: 100,
batchSize: 32,
validationSplit: 0.2,
earlyStoppingPatience: 10,
useGpu: true
});
// 2. Generate signals with AI predictions and risk management
const riskManager = new RiskManager({
confidenceLevel: 0.95,
lookbackPeriods: 252,
method: 'historical'
});
async function generateSmartSignals(marketData, portfolioValue) {
const signals = [];
for (let i = 60; i < marketData.length; i++) {
const features = extractFeatures(marketData.slice(i - 60, i));
const prediction = await model.predict(features);
const expectedReturn = prediction.predictions[0];
// Only trade on confident predictions
if (Math.abs(expectedReturn) > 0.01) {
const bar = marketData[i];
const direction = expectedReturn > 0 ? 'long' : 'short';
// Calculate risk-adjusted position size
const stopLoss = bar.close * (direction === 'long' ? 0.97 : 1.03);
const stopDistance = Math.abs(bar.close - stopLoss) / bar.close;
const position = riskManager.calculatePositionSize(
portfolioValue,
bar.close,
0.02, // Risk 2% per trade
stopDistance
);
signals.push({
id: `ai-signal-${i}`,
strategyId: 'neural-risk-managed',
symbol: bar.symbol,
direction: direction,
confidence: Math.abs(expectedReturn),
entryPrice: bar.close,
stopLoss: stopLoss,
takeProfit: bar.close * (direction === 'long' ? 1.06 : 0.94),
positionSize: position.shares,
reasoning: `AI: ${(expectedReturn * 100).toFixed(2)}% expected, ${position.shares} shares`,
timestampNs: BigInt(bar.timestamp) * 1_000_000n
});
}
}
return signals;
}
// 3. Backtest with full integration
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
const testData = loadMarketData('data/test.csv');
const signals = await generateSmartSignals(testData, 100000);
const result = await engine.run(signals, 'data/test.csv');
console.log('AI + Risk-Managed Strategy Results:');
console.log(`Total Return: ${(result.metrics.totalReturn * 100).toFixed(2)}%`);
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio.toFixed(2)}`);
console.log(`Win Rate: ${(result.metrics.winRate * 100).toFixed(1)}%`);
console.log(`Max Drawdown: ${(result.metrics.maxDrawdown * 100).toFixed(2)}%`);
return result;
}Full Platform Integration
Complete workflow with all packages:
const { BacktestEngine, compareBacktests } = require('@neural-trader/backtesting');
const { MomentumStrategy, MeanReversionStrategy } = require('@neural-trader/strategies');
const { NeuralModel } = require('@neural-trader/neural');
const { RiskManager } = require('@neural-trader/risk');
async function fullPlatformBacktest() {
// Initialize all components
const riskManager = new RiskManager({
confidenceLevel: 0.95,
lookbackPeriods: 252,
method: 'historical'
});
const neuralModel = new NeuralModel({
modelType: 'LSTMAttention',
inputSize: 50,
horizon: 5,
hiddenSize: 256,
numLayers: 3,
dropout: 0.2,
learningRate: 0.001
});
// Train neural model
const trainData = prepareTrainingData('data/train.csv');
await neuralModel.train(trainData.features, trainData.targets, {
epochs: 100,
batchSize: 32,
validationSplit: 0.2,
earlyStoppingPatience: 10,
useGpu: true
});
// Test multiple strategies
const strategies = [
new MomentumStrategy({ lookbackPeriod: 20 }),
new MeanReversionStrategy({ lookbackPeriod: 20, threshold: 2.0 }),
{ name: 'neural', model: neuralModel }
];
const results = [];
for (const strategy of strategies) {
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});
const marketData = loadMarketData('data/test.csv');
const signals = await generateSignalsWithRisk(strategy, marketData, riskManager);
const result = await engine.run(signals, 'data/test.csv');
results.push(result);
}
// Compare all strategies
const comparison = compareBacktests(results);
console.log(comparison);
// Select best strategy
const best = results.reduce((a, b) =>
a.metrics.sharpeRatio > b.metrics.sharpeRatio ? a : b
);
console.log('\n=== Best Strategy ===');
console.log(`Sharpe Ratio: ${best.metrics.sharpeRatio.toFixed(2)}`);
console.log(`Total Return: ${(best.metrics.totalReturn * 100).toFixed(2)}%`);
console.log(`Max Drawdown: ${(best.metrics.maxDrawdown * 100).toFixed(2)}%`);
return best;
}Configuration Options
Basic Configuration
const config = {
// Core settings
initialCapital: 100000, // Starting portfolio value
startDate: '2024-01-01', // Backtest start date (YYYY-MM-DD)
endDate: '2024-12-31', // Backtest end date (YYYY-MM-DD)
// Optional settings
commission: 0.001, // Commission per trade side (0.1%)
slippage: 0.0005, // Average slippage (0.05%)
useMarkToMarket: true // Enable daily mark-to-market P&L
};Advanced Configuration
const advancedConfig = {
// Capital and dates
initialCapital: 500000,
startDate: '2023-01-01',
endDate: '2024-12-31',
// Transaction costs
commission: 0.001, // 0.1% commission
slippage: 0.0005, // 0.05% slippage
// P&L calculation
useMarkToMarket: true, // Daily P&L updates
// Position management
maxPositionsPerSymbol: 1, // One position per symbol
allowShortSelling: true, // Enable short positions
// Execution settings
fillOnOpen: false, // Fill at close instead of open
partialFills: true // Allow partial position fills
};Environment Variables
# Market data directory
NT_DATA_DIR=/path/to/market-data
# Performance tuning
NT_THREADS=8 # Number of threads for parallel processing
# Logging
NT_LOG_LEVEL=info # Logging level (debug, info, warn, error)Configuration File
Create a configuration file backtest-config.json:
{
"backtest": {
"initialCapital": 100000,
"commission": 0.001,
"slippage": 0.0005
},
"dateRange": {
"startDate": "2024-01-01",
"endDate": "2024-12-31"
},
"execution": {
"useMarkToMarket": true,
"fillOnOpen": false
}
}Load configuration:
const fs = require('fs');
const config = JSON.parse(fs.readFileSync('backtest-config.json', 'utf8'));
const engine = new BacktestEngine(config.backtest);Default Configuration Values
| Option | Type | Default | Description |
|---|---|---|---|
| initialCapital | number | required | Starting portfolio value in dollars |
| startDate | string | required | Backtest start date (YYYY-MM-DD format) |
| endDate | string | required | Backtest end date (YYYY-MM-DD format) |
| commission | number | 0.001 | Commission per trade side (0.001 = 0.1%) |
| slippage | number | 0.0005 | Average slippage per trade (0.0005 = 0.05%) |
| useMarkToMarket | boolean | true | Enable daily mark-to-market P&L calculation |
Performance Tips
1. Use Multi-Threaded Processing
Leverage Rust's parallel processing for walk-forward analysis:
❌ Inefficient:
// Sequential backtesting
for (const period of periods) {
const result = await engine.run(signals[period], data[period]);
results.push(result);
}✅ Optimized:
// Parallel backtesting with Promise.all
const results = await Promise.all(
periods.map(period =>
engine.run(signals[period], data[period])
)
);Performance Gain: 4-8x faster on multi-core systems
2. Batch Signal Generation
Generate all signals before backtesting:
❌ Inefficient:
// Generating signals during backtest
for (const bar of marketData) {
const signal = await strategy.analyze(bar);
if (signal) await engine.run([signal], dataPath);
}✅ Optimized:
// Pre-generate all signals
const signals = [];
for (const bar of marketData) {
const signal = await strategy.analyze(bar);
if (signal) signals.push(signal);
}
const result = await engine.run(signals, dataPath);Performance Gain: 10-20x faster
3. Efficient Market Data Loading
Use memory-mapped files for large datasets:
const { readFileSync } = require('fs');
// Load data once, reuse for multiple backtests
const marketData = readFileSync('data/large-dataset.csv', 'utf8');
// Run multiple backtests with same data
const results = await Promise.all([
engine1.run(signals1, marketData),
engine2.run(signals2, marketData),
engine3.run(signals3, marketData)
]);4. Minimize JavaScript-Rust Boundary Crossings
Batch operations to reduce NAPI overhead:
// ✅ Single backtest call with all signals
const result = await engine.run(allSignals, dataPath);
// ❌ Multiple calls for each signal
for (const signal of signals) {
await engine.run([signal], dataPath);
}5. Optimize Signal Timestamps
Pre-calculate timestamps to avoid conversion overhead:
Best Practices:
- Use BigInt for nanosecond precision timestamps
- Pre-sort signals by timestamp before backtesting
- Cache timestamp calculations for recurring operations
// Pre-calculate and cache timestamps
const timestamps = marketData.map(bar =>
BigInt(bar.timestamp) * 1_000_000n
);
signals.forEach((signal, i) => {
signal.timestampNs = timestamps[i];
});Performance Benchmarks
| Operation | Time | Memory | Throughput |
|---|---|---|---|
| 10,000 trades backtest | 45ms | 12MB | 222,000 trades/sec |
| 100,000 trades backtest | 380ms | 95MB | 263,000 trades/sec |
| Walk-forward (8 periods) | 125ms | 48MB | 64 periods/sec (parallel) |
| Equity curve calculation | 2ms | 1MB | 5,000,000 points/sec |
| CSV export (10K trades) | 18ms | 8MB | 555,000 trades/sec |
vs Python (pandas/numpy):
- 8-19x faster trade execution
- 12-24x faster metrics calculation
- 4-8x lower memory usage
Troubleshooting
Common Issue 1: Market Data Format Errors
Problem: Backtest fails with "Invalid market data format" error
Symptoms:
Error: Invalid market data format at line 42
Expected: timestamp,open,high,low,close,volumeCause: CSV file doesn't match expected OHLCV format
Solution:
// ✅ Correct CSV format
// timestamp,open,high,low,close,volume
// 2024-01-01 09:30:00,150.00,151.50,149.75,151.00,1000000
// 2024-01-01 09:31:00,151.00,152.00,150.50,151.75,950000
// Validate CSV format before backtesting
function validateMarketData(filePath) {
const fs = require('fs');
const lines = fs.readFileSync(filePath, 'utf8').split('\n');
const header = lines[0].toLowerCase();
if (!header.includes('timestamp') || !header.includes('close')) {
throw new Error('Invalid CSV format. Required: timestamp,open,high,low,close,volume');
}
return true;
}Prevention: Always validate market data format before running backtests
Common Issue 2: Signal Timestamp Mismatch
Problem: Trades not executing despite valid signals
Error:
Warning: Signal timestamp outside market data range
Signal: 2024-01-15 10:30:00
Market data: 2024-01-01 to 2024-01-14Solution:
Step 1: Verify date ranges match
# Check market data date range
head -n 2 data/market.csv
tail -n 1 data/market.csvStep 2: Align signal timestamps with market data
// Ensure signals are within data range
function alignSignalTimestamps(signals, startDate, endDate) {
const start = new Date(startDate).getTime() * 1_000_000;
const end = new Date(endDate).getTime() * 1_000_000;
return signals.filter(s =>
s.timestampNs >= start && s.timestampNs <= end
);
}
const validSignals = alignSignalTimestamps(signals, '2024-01-01', '2024-12-31');Common Issue 3: Performance Degradation
Problem: Backtest runs slowly with large datasets
Quick Fix:
// Enable parallel processing
process.env.NT_THREADS = '8'; // Use 8 threads
// Batch signals before backtesting
const batchSize = 1000;
const batches = [];
for (let i = 0; i < signals.length; i += batchSize) {
batches.push(signals.slice(i, i + batchSize));
}
// Process batches in parallel
const results = await Promise.all(
batches.map(batch => engine.run(batch, dataPath))
);Common Issue 4: Memory Errors with Large Equity Curves
Problem: Out of memory error during equity curve calculation
Diagnosis:
- Check equity curve length:
result.equityCurve.length - Calculate memory usage:
equityCurve.length * 8 bytes - Verify available memory:
process.memoryUsage()
Solution: Downsample equity curve for visualization
function downsampleEquityCurve(curve, targetPoints = 1000) {
if (curve.length <= targetPoints) return curve;
const step = Math.floor(curve.length / targetPoints);
return curve.filter((_, i) => i % step === 0);
}
const downsampled = downsampleEquityCurve(result.equityCurve);Common Issue 5: Inconsistent Backtest Results
Problem: Different results on repeated backtests with same data
Solution: Ensure deterministic signal ordering
// Sort signals by timestamp before backtesting
signals.sort((a, b) => Number(a.timestampNs - b.timestampNs));
// Use consistent random seed if using randomization
const deterministicSignals = signals.map(s => ({
...s,
id: `${s.symbol}-${s.timestampNs}` // Deterministic IDs
}));Debugging Tips
Enable Debug Logging:
process.env.NT_LOG_LEVEL = 'debug';
const engine = new BacktestEngine({
initialCapital: 100000,
startDate: '2024-01-01',
endDate: '2024-12-31',
commission: 0.001,
slippage: 0.0005,
useMarkToMarket: true
});Check Configuration:
console.log('Backtest Config:', {
initialCapital: engine.initialCapital,
commission: engine.commission,
slippage: engine.slippage
});Validate Signals:
function validateSignals(signals) {
const errors = [];
signals.forEach((signal, i) => {
if (!signal.entryPrice || signal.entryPrice <= 0) {
errors.push(`Signal ${i}: Invalid entry price`);
}
if (!signal.timestampNs) {
errors.push(`Signal ${i}: Missing timestamp`);
}
if (!['long', 'short'].includes(signal.direction)) {
errors.push(`Signal ${i}: Invalid direction`);
}
});
if (errors.length > 0) {
console.error('Signal validation errors:', errors);
return false;
}
return true;
}
if (!validateSignals(signals)) {
throw new Error('Invalid signals detected');
}Getting Help
If you encounter issues not covered here:
- Check the main documentation
- Search GitHub Issues
- Join our Discord community
- Email support at support@neural-trader.io
Related Packages
Core Packages
- @neural-trader/core - Core types and interfaces (required)
- @neural-trader/strategies - Pre-built trading strategies
- @neural-trader/risk - Risk management and position sizing
Recommended Combinations
For Strategy Development:
npm install @neural-trader/backtesting @neural-trader/strategies @neural-trader/riskFor AI-Powered Trading:
npm install @neural-trader/backtesting @neural-trader/neural @neural-trader/riskFor Production Trading:
npm install @neural-trader/backtesting @neural-trader/execution @neural-trader/riskComplementary Packages
- @neural-trader/neural - Neural network models for predictions
- @neural-trader/execution - Live trading execution
- @neural-trader/data - Market data management
Full Package List
See packages/README.md for all 18 available packages.
Contributing
Contributions are welcome! We follow the main Neural Trader contribution guidelines.
Development Setup
# Clone repository
git clone https://github.com/ruvnet/neural-trader.git
cd neural-trader/neural-trader-rust
# Install dependencies
npm install
# Build package
cd packages/backtesting
npm run build
# Run tests
npm test
# Run linter
npm run lintRunning Tests
# Unit tests
npm test
# Integration tests
npm run test:integration
# E2E tests
npm run test:e2e
# With coverage
npm run test:coverage
# Watch mode
npm run test:watchCode Style
This package follows the Neural Trader code style:
- TypeScript: Strict mode enabled
- Formatting: Prettier with 2-space indentation
- Linting: ESLint with recommended rules
- Testing: Jest with >80% coverage requirement
# Format code
npm run format
# Lint code
npm run lint
# Type check
npm run typecheckContribution Workflow
- Fork the repository
- Create a feature branch:
git checkout -b feat/amazing-feature - Make changes and add tests
- Run tests and linting:
npm test && npm run lint - Commit changes:
git commit -m 'feat: add amazing feature' - Push to branch:
git push origin feat/amazing-feature - Open a Pull Request
Pull Request Guidelines
- Add tests for new features
- Update documentation
- Follow existing code style
- Keep changes focused and atomic
- Write clear commit messages
- Update CHANGELOG.md if applicable
See CONTRIBUTING.md for detailed guidelines.
License
This project is dual-licensed under MIT OR Apache-2.0.
You may choose either license at your option:
- MIT License: See LICENSE-MIT
- Apache License 2.0: See LICENSE-APACHE
Third-Party Licenses
This package includes dependencies with the following licenses:
- Tokio: MIT
- Serde: MIT OR Apache-2.0
- Rayon: MIT OR Apache-2.0
See THIRD_PARTY_LICENSES.md for complete list.
Support
Documentation
- Package Docs: This README
- API Reference: API.md
- Examples: examples/
- Main Docs: https://neural-trader.ruv.io
Community
- GitHub Issues: https://github.com/ruvnet/neural-trader/issues
- Discord: https://discord.gg/neural-trader
- Twitter: @neural_trader
Professional Support
For enterprise support, training, or custom development:
- Email: support@neural-trader.io
- Website: https://neural-trader.io/support
Changelog
See CHANGELOG.md for version history and release notes.
Security
For security issues, please see SECURITY.md for our security policy and reporting process.
Do not report security vulnerabilities through public GitHub issues.
Acknowledgments
- Built with Rust for performance
- Node.js bindings via napi-rs
- Multi-threading with Rayon
- Neural Trader community contributors
Disclaimer: This software is for educational and research purposes only. Trading financial instruments carries risk. Past performance does not guarantee future results. Use at your own risk.