RaptorBT
Getting Started
RaptorBT is a high-performance backtesting engine written in Rust with Python bindings via PyO3, delivering HFT-grade compute efficiency with 30+ performance metrics.
- Sub-millisecond backtesting on 1K bars (0.25ms)
- <10 MB disk footprint
- 100% deterministic results with no JIT compilation variance
- 30+ performance metrics including Sharpe, Sortino, Calmar, Omega, SQN, and more
- 6 strategy types: single, basket, pairs, options, spreads, and multi-strategy
- Batch spread backtesting: Run multiple spread backtests in parallel via Rayon with GIL released
- Monte Carlo simulation: Correlated multi-asset forward projection via GBM + Cholesky decomposition
- 12 technical indicators: SMA, EMA, RSI, MACD, Stochastic, ATR, Bollinger Bands, ADX, VWAP, Supertrend, Rolling Min, Rolling Max
pip install raptorbt
import numpy as np import raptorbt config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) result = raptorbt.run_single_backtest( timestamps=timestamps, open=open, high=high, low=low, close=close, volume=volume, entries=entries, exits=exits, direction=1, weight=1.0, symbol="AAPL", config=config, ) print(f"Return: {result.metrics.total_return_pct:.2f}%") print(f"Sharpe: {result.metrics.sharpe_ratio:.2f}")
Installation
From Pre-built Wheel
pip install raptorbt
From Source
cd raptorbt maturin develop --release
Verify Installation
import raptorbt print("RaptorBT installed successfully!")
Quick Start
Basic Single Instrument Backtest
import numpy as np import pandas as pd import raptorbt # Prepare data df = pd.read_csv("your_data.csv", index_col=0, parse_dates=True) # Generate signals (SMA crossover example) sma_fast = df['close'].rolling(10).mean() sma_slow = df['close'].rolling(20).mean() entries = (sma_fast > sma_slow) & (sma_fast.shift(1) <= sma_slow.shift(1)) exits = (sma_fast < sma_slow) & (sma_fast.shift(1) >= sma_slow.shift(1)) # Configure backtest config = raptorbt.PyBacktestConfig( initial_capital=100000, fees=0.001, # 0.1% per trade slippage=0.0005, # 0.05% slippage upon_bar_close=True ) # Optional: Add stop-loss config.set_fixed_stop(0.02) # 2% stop-loss # Optional: Add take-profit config.set_fixed_target(0.04) # 4% take-profit # Run backtest result = raptorbt.run_single_backtest( timestamps=df.index.astype('int64').values, open=df['open'].values, high=df['high'].values, low=df['low'].values, close=df['close'].values, volume=df['volume'].values, entries=entries.values, exits=exits.values, direction=1, # 1 = Long, -1 = Short weight=1.0, symbol="AAPL", config=config, ) # Access results print(f"Total Return: {result.metrics.total_return_pct:.2f}%") print(f"Sharpe Ratio: {result.metrics.sharpe_ratio:.2f}") print(f"Max Drawdown: {result.metrics.max_drawdown_pct:.2f}%") print(f"Win Rate: {result.metrics.win_rate_pct:.2f}%") print(f"Total Trades: {result.metrics.total_trades}") # Get equity curve equity = result.equity_curve() # Returns numpy array # Get trades trades = result.trades() # Returns list of PyTrade objects
Performance
Benchmark Results
Tested on Apple Silicon M-series with random walk price data and SMA crossover strategy:
| Data Size | RaptorBT |
|---|---|
| 1,000 bars | 0.25 ms |
| 5,000 bars | 0.24 ms |
| 10,000 bars | 0.46 ms |
| 50,000 bars | 1.68 ms |
Metric Accuracy
RaptorBT produces 100% deterministic results with no JIT compilation variance between runs.
Architecture
raptorbt/
├── src/
│ ├── core/ # Core types and error handling
│ │ ├── types.rs # BacktestConfig, BacktestResult, Trade, Metrics
│ │ ├── error.rs # RaptorError enum
│ │ ├── session.rs # SessionTracker, SessionConfig (intraday sessions)
│ │ └── timeseries.rs # Time series utilities
│ │
│ ├── strategies/ # Strategy implementations
│ │ ├── single.rs # Single instrument backtest
│ │ ├── basket.rs # Basket/collective strategies
│ │ ├── pairs.rs # Pairs trading
│ │ ├── options.rs # Options strategies
│ │ ├── spreads.rs # Multi-leg spread strategies (straddles, iron condors, etc.)
│ │ └── multi.rs # Multi-strategy combining
│ │
│ ├── indicators/ # Technical indicators
│ │ ├── trend.rs # SMA, EMA, Supertrend
│ │ ├── momentum.rs # RSI, MACD, Stochastic
│ │ ├── volatility.rs # ATR, Bollinger Bands
│ │ ├── strength.rs # ADX
│ │ ├── volume.rs # VWAP
│ │ └── rolling.rs # Rolling Min/Max (LLV/HHV)
│ │
│ ├── portfolio/ # Portfolio-level analysis
│ │ ├── monte_carlo.rs # Monte Carlo forward simulation (GBM + Cholesky)
│ │ ├── allocation.rs # Capital allocation
│ │ ├── engine.rs # Portfolio engine
│ │ └── position.rs # Position management
│ │
│ ├── metrics/ # Performance metrics
│ │ ├── streaming.rs # Streaming metric calculations
│ │ ├── drawdown.rs # Drawdown analysis
│ │ └── trade_stats.rs # Trade statistics
│ │
│ ├── signals/ # Signal processing
│ │ ├── processor.rs # Entry/exit signal processing
│ │ ├── synchronizer.rs # Multi-instrument sync
│ │ └── expression.rs # Signal expressions
│ │
│ ├── stops/ # Stop-loss implementations
│ │ ├── fixed.rs # Fixed percentage stops
│ │ ├── atr.rs # ATR-based stops
│ │ └── trailing.rs # Trailing stops
│ │
│ ├── python/ # PyO3 bindings
│ │ ├── bindings.rs # Python function exports
│ │ └── numpy_bridge.rs # NumPy array conversion
│ │
│ └── lib.rs # Library entry point
│
├── Cargo.toml # Rust dependencies
└── pyproject.toml # Python package config
RaptorBT is built with native parallelism using Rayon for parallel processing and explicit SIMD optimizations for maximum throughput.
Strategy Types
Single Instrument
Basic long or short strategy on a single instrument.
result = raptorbt.run_single_backtest( timestamps=timestamps, open=open_prices, high=high_prices, low=low_prices, close=close_prices, volume=volume, entries=entries, exits=exits, direction=1, # 1=Long, -1=Short weight=1.0, symbol="SYMBOL", config=config, )
Basket/Collective
Trade multiple instruments with synchronized signals.
instruments = [ (timestamps, open1, high1, low1, close1, volume1, entries1, exits1, 1, 0.33, "AAPL"), (timestamps, open2, high2, low2, close2, volume2, entries2, exits2, 1, 0.33, "GOOGL"), (timestamps, open3, high3, low3, close3, volume3, entries3, exits3, 1, 0.34, "MSFT"), ] result = raptorbt.run_basket_backtest( instruments=instruments, config=config, sync_mode="all", # "all", "any", "majority", "master" )
Sync Modes:
all: Enter only when ALL instruments signalany: Enter when ANY instrument signalsmajority: Enter when >50% of instruments signalmaster: Follow the first instrument's signals
Pairs Trading
Long one instrument, short another with optional hedge ratio.
result = raptorbt.run_pairs_backtest( # Long leg leg1_timestamps=timestamps, leg1_open=long_open, leg1_high=long_high, leg1_low=long_low, leg1_close=long_close, leg1_volume=long_volume, # Short leg leg2_timestamps=timestamps, leg2_open=short_open, leg2_high=short_high, leg2_low=short_low, leg2_close=short_close, leg2_volume=short_volume, # Signals entries=entries, exits=exits, direction=1, symbol="TCS_INFY", config=config, hedge_ratio=1.5, # Short 1.5x the long position dynamic_hedge=False, # Use rolling hedge ratio )
Options
Backtest options strategies with strike selection.
result = raptorbt.run_options_backtest( timestamps=timestamps, open=underlying_open, high=underlying_high, low=underlying_low, close=underlying_close, volume=volume, option_prices=option_prices, # Option premium series entries=entries, exits=exits, direction=1, symbol="NIFTY_CE", config=config, option_type="call", # "call" or "put" strike_selection="atm", # "atm", "otm1", "otm2", "itm1", "itm2" size_type="percent", # "percent", "contracts", "notional", "risk" size_value=0.1, # 10% of capital lot_size=50, # Options lot size strike_interval=50.0, # Strike interval (e.g., 50 for NIFTY) )
Spread Backtest
Backtest multi-leg options spread strategies with coordinated entry/exit.
import numpy as np import raptorbt config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) # Define leg configs: (option_type, strike, quantity, lot_size) leg_configs = [ ("CE", 24000.0, -1, 50), # Short 1 lot Call at 24000 ("PE", 24000.0, -1, 50), # Short 1 lot Put at 24000 ] # Premium series for each leg (numpy arrays) legs_premiums = [call_premiums, put_premiums] result = raptorbt.run_spread_backtest( timestamps=timestamps, underlying_close=underlying_close, legs_premiums=legs_premiums, leg_configs=leg_configs, entries=entries, exits=exits, config=config, spread_type="straddle", # "straddle", "strangle", "vertical_call", "vertical_put", # "iron_condor", "iron_butterfly", "butterfly_call", # "butterfly_put", "calendar", "diagonal", "custom" max_loss=5000.0, # Optional: exit if net loss exceeds this target_profit=3000.0, # Optional: exit if net profit exceeds this ) print(f"Return: {result.metrics.total_return_pct:.2f}%")
Batch Spread Backtest
Run multiple spread backtests in parallel. Shared data (timestamps, underlying close) is converted once, then each item is backtested on its own Rayon thread with the GIL released for maximum throughput.
import numpy as np import raptorbt config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) # Create batch items — one per strategy variation items = [ raptorbt.PyBatchSpreadItem( strategy_id="straddle_24000", legs_premiums=[call_24000_premiums, put_24000_premiums], leg_configs=[("CE", 24000.0, -1, 50), ("PE", 24000.0, -1, 50)], entries=entries, exits=exits, spread_type="straddle", max_loss=5000.0, target_profit=3000.0, ), raptorbt.PyBatchSpreadItem( strategy_id="strangle_23500_24500", legs_premiums=[call_24500_premiums, put_23500_premiums], leg_configs=[("CE", 24500.0, -1, 50), ("PE", 23500.0, -1, 50)], entries=entries, exits=exits, spread_type="strangle", ), ] # Run all in parallel — returns list of (strategy_id, result) tuples results = raptorbt.batch_spread_backtest( timestamps=timestamps, underlying_close=underlying_close, items=items, config=config, ) for strategy_id, result in results: print(f"{strategy_id}: {result.metrics.total_return_pct:.2f}%")
Supported Spread Types:
straddle: ATM call + put (same strike)strangle: OTM call + put (different strikes)vertical_call/vertical_put: Bull/bear vertical spreadsiron_condor: OTM put spread + OTM call spreadiron_butterfly: ATM straddle + OTM wingsbutterfly_call/butterfly_put: Three-strike butterflycalendar/diagonal: Different expiry spreadslong_call: Buy a single call option (limited risk to premium)long_put: Buy a single put option (limited risk to premium)naked_call: Sell a single call option without a hedge (unlimited risk)naked_put: Sell a single put option without a hedge (high risk)custom: Any arbitrary multi-leg combination
Multi-Strategy
Combine multiple strategies on the same instrument.
strategies = [ (entries_sma, exits_sma, 1, 0.4, "SMA_Crossover"), # 40% weight (entries_rsi, exits_rsi, 1, 0.35, "RSI_MeanRev"), # 35% weight (entries_bb, exits_bb, 1, 0.25, "BB_Breakout"), # 25% weight ] result = raptorbt.run_multi_backtest( timestamps=timestamps, open=open_prices, high=high_prices, low=low_prices, close=close_prices, volume=volume, strategies=strategies, config=config, combine_mode="any", # "any", "all", "majority", "weighted", "independent" )
Combine Modes:
any: Enter when any strategy signalsall: Enter only when all strategies signalmajority: Enter when >50% of strategies signalweighted: Weight signals by strategy weightindependent: Run strategies independently (aggregate PnL)
Metrics
RaptorBT calculates 30+ performance metrics:
Core Performance
| Metric | Description |
|---|---|
total_return_pct | Total return as percentage |
sharpe_ratio | Risk-adjusted return (annualized) |
sortino_ratio | Downside risk-adjusted return |
calmar_ratio | Return / Max Drawdown |
omega_ratio | Probability-weighted gains/losses |
Drawdown
| Metric | Description |
|---|---|
max_drawdown_pct | Maximum peak-to-trough decline |
max_drawdown_duration | Longest drawdown period (bars) |
Trade Statistics
| Metric | Description |
|---|---|
total_trades | Total number of trades |
total_closed_trades | Number of closed trades |
total_open_trades | Number of open positions |
winning_trades | Number of profitable trades |
losing_trades | Number of losing trades |
win_rate_pct | Percentage of winning trades |
Trade Performance
| Metric | Description |
|---|---|
profit_factor | Gross profit / Gross loss |
expectancy | Average expected profit per trade |
sqn | System Quality Number |
avg_trade_return_pct | Average trade return |
avg_win_pct | Average winning trade return |
avg_loss_pct | Average losing trade return |
best_trade_pct | Best single trade return |
worst_trade_pct | Worst single trade return |
Duration
| Metric | Description |
|---|---|
avg_holding_period | Average trade duration (bars) |
avg_winning_duration | Average winning trade duration |
avg_losing_duration | Average losing trade duration |
Streaks
| Metric | Description |
|---|---|
max_consecutive_wins | Longest winning streak |
max_consecutive_losses | Longest losing streak |
Other
| Metric | Description |
|---|---|
start_value | Initial portfolio value |
end_value | Final portfolio value |
total_fees_paid | Total transaction costs |
open_trade_pnl | Unrealized PnL from open positions |
exposure_pct | Percentage of time in market |
Technical Indicators
RaptorBT includes optimized technical indicators computed in native Rust:
import raptorbt # Trend indicators sma = raptorbt.sma(close, period=20) ema = raptorbt.ema(close, period=20) supertrend, direction = raptorbt.supertrend(high, low, close, period=10, multiplier=3.0) # Momentum indicators rsi = raptorbt.rsi(close, period=14) macd_line, signal_line, histogram = raptorbt.macd(close, fast=12, slow=26, signal=9) stoch_k, stoch_d = raptorbt.stochastic(high, low, close, k_period=14, d_period=3) # Volatility indicators atr = raptorbt.atr(high, low, close, period=14) upper, middle, lower = raptorbt.bollinger_bands(close, period=20, std_dev=2.0) # Strength indicators adx = raptorbt.adx(high, low, close, period=14) # Volume indicators vwap = raptorbt.vwap(high, low, close, volume) # Rolling indicators (LLV/HHV) rolling_low = raptorbt.rolling_min(low, period=20) # Lowest Low Value rolling_high = raptorbt.rolling_max(high, period=20) # Highest High Value
All indicators operate on NumPy arrays and return NumPy arrays, making them compatible with pandas DataFrames.
Stop-Loss & Take-Profit
Fixed Percentage
config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) config.set_fixed_stop(0.02) # 2% stop-loss config.set_fixed_target(0.04) # 4% take-profit
ATR-Based
config.set_atr_stop(multiplier=2.0, period=14) # 2x ATR stop config.set_atr_target(multiplier=3.0, period=14) # 3x ATR target
Trailing Stop
config.set_trailing_stop(0.02) # 2% trailing stop
Risk-Reward Target
config.set_risk_reward_target(ratio=2.0) # 2:1 risk-reward ratio
Monte Carlo Portfolio Simulation
RaptorBT includes a high-performance Monte Carlo forward simulation engine for portfolio risk analysis. It uses Geometric Brownian Motion (GBM) with Cholesky decomposition for correlated multi-asset simulation, parallelized via Rayon.
import numpy as np import raptorbt # Historical daily returns per strategy/asset (numpy arrays) returns = [ np.array([0.001, -0.002, 0.003, ...]), # Strategy 1 returns np.array([0.002, 0.001, -0.001, ...]), # Strategy 2 returns ] # Portfolio weights (must sum to 1.0) weights = np.array([0.6, 0.4]) # Correlation matrix (N x N) correlation_matrix = [ np.array([1.0, 0.3]), np.array([0.3, 1.0]), ] # Run simulation result = raptorbt.simulate_portfolio_mc( returns=returns, weights=weights, correlation_matrix=correlation_matrix, initial_value=100000.0, n_simulations=10000, # Number of Monte Carlo paths (default: 10,000) horizon_days=252, # Forward projection horizon (default: 252) seed=42, # Random seed for reproducibility (default: 42) ) # Results print(f"Expected Return: {result['expected_return']:.2f}%") print(f"Probability of Loss: {result['probability_of_loss']:.2%}") print(f"VaR (95%): {result['var_95']:.2f}%") print(f"CVaR (95%): {result['cvar_95']:.2f}%") # Percentile paths: list of (percentile, path_values) # Percentiles: 5th, 25th, 50th, 75th, 95th for pct, path in result['percentile_paths']: print(f" P{pct:.0f} final value: {path[-1]:.2f}") # Final values: numpy array of terminal values for all simulations final_values = result['final_values'] # numpy array, length = n_simulations
Result Fields
| Field | Type | Description |
|---|---|---|
expected_return | float | Expected return as percentage over the horizon |
probability_of_loss | float | Probability that final value < initial value (0.0 to 1.0) |
var_95 | float | Value at Risk at 95% confidence (percentage) |
cvar_95 | float | Conditional VaR at 95% confidence (percentage) |
percentile_paths | List[Tuple[float, List]] | Portfolio paths at 5th, 25th, 50th, 75th, 95th percentiles |
final_values | numpy.ndarray | Terminal portfolio values for all simulations |
API Reference
PyBacktestConfig
config = raptorbt.PyBacktestConfig( initial_capital: float = 100000.0, fees: float = 0.001, slippage: float = 0.0, upon_bar_close: bool = True, ) # Stop methods config.set_fixed_stop(percent: float) config.set_atr_stop(multiplier: float, period: int) config.set_trailing_stop(percent: float) # Target methods config.set_fixed_target(percent: float) config.set_atr_target(multiplier: float, period: int) config.set_risk_reward_target(ratio: float)
run_spread_backtest
result = raptorbt.run_spread_backtest( timestamps: np.ndarray, # int64 nanosecond timestamps underlying_close: np.ndarray, # Underlying close prices legs_premiums: List[np.ndarray], # Premium series per leg leg_configs: List[Tuple[str, float, int, int]], # (option_type, strike, quantity, lot_size) entries: np.ndarray, # bool entry signals exits: np.ndarray, # bool exit signals config: PyBacktestConfig = None, # Optional config spread_type: str = "custom", # Spread type string max_loss: float = None, # Optional max loss exit target_profit: float = None, # Optional target profit exit leg_expiry_timestamps: List[int] = None, # Optional per-leg expiry timestamps (nanoseconds) )
When leg_expiry_timestamps is provided, positions are force-closed at settlement when any leg expires. The exit reason is recorded as "Settlement". No re-entry is allowed after all legs have expired.
PyBatchSpreadItem
item = raptorbt.PyBatchSpreadItem( strategy_id: str, # Unique identifier for this backtest legs_premiums: List[np.ndarray], # Premium series per leg leg_configs: List[Tuple[str, float, int, int]], # (option_type, strike, quantity, lot_size) entries: np.ndarray, # bool entry signals exits: np.ndarray, # bool exit signals spread_type: str = "custom", # Spread type string max_loss: float = None, # Optional max loss exit target_profit: float = None, # Optional target profit exit )
batch_spread_backtest
results = raptorbt.batch_spread_backtest( timestamps: np.ndarray, # int64 nanosecond timestamps (shared) underlying_close: np.ndarray, # Underlying close prices (shared) items: List[PyBatchSpreadItem], # List of spread backtest items config: PyBacktestConfig = None, # Optional shared config ) -> List[Tuple[str, PyBacktestResult]] # (strategy_id, result) pairs
Runs all spread backtests in parallel via Rayon. Timestamps and underlying close are shared across all items and converted once. The GIL is released during execution for maximum Python concurrency.
simulate_portfolio_mc
result = raptorbt.simulate_portfolio_mc( returns: List[np.ndarray], # Per-asset daily returns (N arrays) weights: np.ndarray, # Portfolio weights (length N, sum to 1) correlation_matrix: List[np.ndarray], # N x N correlation matrix initial_value: float, # Starting portfolio value n_simulations: int = 10000, # Number of Monte Carlo paths horizon_days: int = 252, # Forward projection horizon in days seed: int = 42, # Random seed for reproducibility ) -> dict
Returns a dictionary with keys: expected_return, probability_of_loss, var_95, cvar_95, percentile_paths, final_values.
PyBacktestResult
result = raptorbt.run_single_backtest(...) # Attributes result.metrics # PyBacktestMetrics object # Methods result.equity_curve() # numpy.ndarray result.drawdown_curve() # numpy.ndarray result.returns() # numpy.ndarray result.trades() # List[PyTrade]
PyBacktestMetrics
metrics = result.metrics # All available metrics metrics.total_return_pct metrics.sharpe_ratio metrics.sortino_ratio metrics.calmar_ratio metrics.omega_ratio metrics.max_drawdown_pct metrics.max_drawdown_duration metrics.win_rate_pct metrics.profit_factor metrics.expectancy metrics.sqn metrics.total_trades metrics.total_closed_trades metrics.total_open_trades metrics.winning_trades metrics.losing_trades metrics.start_value metrics.end_value metrics.total_fees_paid metrics.best_trade_pct metrics.worst_trade_pct metrics.avg_trade_return_pct metrics.avg_win_pct metrics.avg_loss_pct metrics.avg_holding_period metrics.avg_winning_duration metrics.avg_losing_duration metrics.max_consecutive_wins metrics.max_consecutive_losses metrics.exposure_pct metrics.open_trade_pnl metrics.payoff_ratio # avg win / avg loss (risk/reward per trade) metrics.recovery_factor # net profit / max drawdown (resilience) # Convert to dictionary stats_dict = metrics.to_dict()
PyTrade
for trade in result.trades(): print(trade.id) # Trade ID print(trade.symbol) # Symbol print(trade.entry_idx) # Entry bar index print(trade.exit_idx) # Exit bar index print(trade.entry_price) # Entry price print(trade.exit_price) # Exit price print(trade.size) # Position size print(trade.direction) # 1=Long, -1=Short print(trade.pnl) # Profit/Loss print(trade.return_pct) # Return percentage print(trade.fees) # Fees paid print(trade.exit_reason) # "Signal", "StopLoss", "TakeProfit", "TrailingStop", "EndOfData", "Settlement"
Examples
SMA Crossover with Stop-Loss
import numpy as np import pandas as pd import raptorbt df = pd.read_csv("AAPL.csv", index_col=0, parse_dates=True) # SMA crossover signals sma_10 = df['close'].rolling(10).mean() sma_30 = df['close'].rolling(30).mean() entries = (sma_10 > sma_30) & (sma_10.shift(1) <= sma_30.shift(1)) exits = (sma_10 < sma_30) & (sma_10.shift(1) >= sma_30.shift(1)) config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) config.set_trailing_stop(0.03) # 3% trailing stop result = raptorbt.run_single_backtest( timestamps=df.index.astype('int64').values, open=df['open'].values, high=df['high'].values, low=df['low'].values, close=df['close'].values, volume=df['volume'].values, entries=entries.values, exits=exits.values, direction=1, weight=1.0, symbol="AAPL", config=config, ) print(f"Total Return: {result.metrics.total_return_pct:.2f}%") print(f"Sharpe Ratio: {result.metrics.sharpe_ratio:.2f}") print(f"Max Drawdown: {result.metrics.max_drawdown_pct:.2f}%") print(f"Win Rate: {result.metrics.win_rate_pct:.2f}%")
RSI Mean Reversion
import raptorbt import numpy as np # Compute RSI using built-in indicator rsi = raptorbt.rsi(close_prices, period=14) # Mean reversion: buy oversold, sell overbought entries = rsi < 30 exits = rsi > 70 config = raptorbt.PyBacktestConfig(initial_capital=50000, fees=0.001) config.set_fixed_stop(0.015) # 1.5% stop config.set_fixed_target(0.03) # 3% target result = raptorbt.run_single_backtest( timestamps=timestamps, open=open_prices, high=high_prices, low=low_prices, close=close_prices, volume=volume, entries=entries, exits=exits, direction=1, weight=1.0, symbol="SPY", config=config, )
Multi-Strategy Portfolio
import raptorbt import numpy as np # Strategy 1: SMA crossover sma_fast = raptorbt.sma(close, period=10) sma_slow = raptorbt.sma(close, period=30) entries_sma = sma_fast > sma_slow exits_sma = sma_fast < sma_slow # Strategy 2: RSI rsi = raptorbt.rsi(close, period=14) entries_rsi = rsi < 30 exits_rsi = rsi > 70 # Strategy 3: Bollinger Bands breakout upper, middle, lower = raptorbt.bollinger_bands(close, period=20, std_dev=2.0) entries_bb = close < lower exits_bb = close > upper strategies = [ (entries_sma, exits_sma, 1, 0.4, "SMA"), (entries_rsi, exits_rsi, 1, 0.35, "RSI"), (entries_bb, exits_bb, 1, 0.25, "BB"), ] config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) result = raptorbt.run_multi_backtest( timestamps=timestamps, open=open_prices, high=high_prices, low=low_prices, close=close_prices, volume=volume, strategies=strategies, config=config, combine_mode="weighted", ) print(f"Combined Return: {result.metrics.total_return_pct:.2f}%") print(f"Sharpe: {result.metrics.sharpe_ratio:.2f}")
Single-Leg Option (Long Call)
Backtest buying a single call option with expiry settlement.
import numpy as np import raptorbt config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) # Single leg: buy 1 lot of NIFTY 25000 CE leg_configs = [("CE", 25000.0, 1, 50)] # (option_type, strike, quantity=+1 long, lot_size) legs_premiums = [call_premiums] # Premium series for the call # Expiry settlement: force-close at expiry with intrinsic value # Timestamp in nanoseconds (e.g., 2026-03-06 15:30 IST) import pandas as pd expiry_ts = pd.Timestamp("2026-03-06 15:30", tz="Asia/Kolkata").value result = raptorbt.run_spread_backtest( timestamps=timestamps, underlying_close=underlying_close, legs_premiums=legs_premiums, leg_configs=leg_configs, entries=entries, exits=exits, config=config, spread_type="long_call", leg_expiry_timestamps=[expiry_ts], # Settlement at expiry ) # Check if trade settled at expiry for trade in result.trades(): if trade.exit_reason == "Settlement": print(f"Trade settled at expiry: PnL={trade.pnl:.2f}") else: print(f"Trade exited via {trade.exit_reason}: PnL={trade.pnl:.2f}")
Naked Put with Settlement
Backtest selling a naked put — position settles at intrinsic value if held to expiry.
import raptorbt config = raptorbt.PyBacktestConfig(initial_capital=200000, fees=0.001) # Short 1 lot of NIFTY 24500 PE leg_configs = [("PE", 24500.0, -1, 50)] # quantity=-1 for short legs_premiums = [put_premiums] result = raptorbt.run_spread_backtest( timestamps=timestamps, underlying_close=underlying_close, legs_premiums=legs_premiums, leg_configs=leg_configs, entries=entries, exits=exits, config=config, spread_type="naked_put", max_loss=10000.0, # Stop out if loss exceeds 10K leg_expiry_timestamps=[expiry_ts], # Settle at expiry if not stopped out ) print(f"Return: {result.metrics.total_return_pct:.2f}%") print(f"Trades: {result.metrics.total_trades}")
Building from Source
Prerequisites
- Rust 1.70+ (install via rustup)
- Python 3.10+
- maturin (
pip install maturin)
Development Build
cd raptorbt maturin develop --release
Production Build
cd raptorbt maturin build --release pip install target/wheels/raptorbt-*.whl
Testing
Rust Unit Tests
cd raptorbt cargo test
Python Integration Tests
import raptorbt import numpy as np config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) result = raptorbt.run_single_backtest( timestamps=np.arange(100, dtype=np.int64), open=np.random.randn(100).cumsum() + 100, high=np.random.randn(100).cumsum() + 101, low=np.random.randn(100).cumsum() + 99, close=np.random.randn(100).cumsum() + 100, volume=np.ones(100), entries=np.array([i % 20 == 0 for i in range(100)]), exits=np.array([i % 20 == 10 for i in range(100)]), direction=1, weight=1.0, symbol='TEST', config=config, ) print(f'Total Return: {result.metrics.total_return_pct:.2f}%') print('RaptorBT is working correctly!')
Contributing
RaptorBT is open source and contributions are welcome.
Setup
- Fork the repository on GitHub
- Clone your fork locally
- Install Rust 1.70+ via rustup and Python 3.10+
- Install maturin:
pip install maturin - Build in development mode:
maturin develop --release
Development Workflow
- Run Rust tests:
cargo test - Run Python integration tests after building
- Format Rust code:
cargo fmt - Lint Rust code:
cargo clippy
Pull Request Guidelines
- Keep PRs focused on a single change
- Add tests for new functionality
- Ensure all existing tests pass
- Follow the existing code style and conventions
- Update documentation for any API changes
Reporting Issues
Found a bug or have a feature request? Open an issue on the GitHub repository.
License
MIT License. See LICENSE for details.
Changelog
v0.3.4
- Add single-leg option spread types:
LongCall,LongPut,NakedCall,NakedPuttoSpreadTypeenum - Add
ExitReason::Settlementfor option expiry settlement exits - Add
leg_expiry_timestampsparameter torun_spread_backtestfor per-leg expiry tracking - Positions are force-closed at settlement when any leg expires, with premiums replaced by intrinsic value
- Prevent re-entry after all legs have expired
v0.3.3
- Add
batch_spread_backtestfunction for running multiple spread backtests in parallel via Rayon - Add
PyBatchSpreadItemclass for defining individual items in a batch spread backtest - Shared data (timestamps, underlying close) is converted once and reused across all items
- GIL released during parallel execution for maximum Python concurrency
- Each item carries its own
strategy_id, leg configs, signals, spread type, and optional max loss / target profit - Returns a list of
(strategy_id, PyBacktestResult)tuples preserving result-to-input mapping
v0.3.2
- Add
payoff_ratiometric toBacktestMetrics— average winning trade return divided by average losing trade return (absolute), measures risk/reward per trade - Add
recovery_factormetric toBacktestMetrics— net profit divided by maximum drawdown in absolute terms, measures how many times over the strategy recovered from its worst drawdown - Both metrics computed in
StreamingMetrics::finalize()(single-instrument backtest) andPortfolioEngine(multi-strategy aggregation) - Both metrics exposed via PyO3 as
#[pyo3(get)]attributes onPyBacktestMetrics - Handles edge cases: returns
f64::INFINITYwhen denominator is zero with positive numerator,0.0otherwise
v0.3.1
- Add Monte Carlo portfolio simulation (
simulate_portfolio_mc) for forward risk projection - Geometric Brownian Motion (GBM) with Cholesky decomposition for correlated multi-asset simulation
- Rayon-parallelized simulation paths with deterministic seeding (xoshiro256**)
- Returns percentile paths (P5/P25/P50/P75/P95), VaR, CVaR, expected return, and probability of loss
- GIL released during simulation for maximum Python concurrency
v0.3.0
- Per-instrument configuration via
PyInstrumentConfig(lot_size, alloted_capital, stop/target overrides) - Position sizes now correctly rounded to lot_size multiples
- Support for per-instrument capital allocation in basket backtests
- Automatic lot_size lookup from database via
instr_repo.get_by_segment_symbol() - Future-ready fields: existing_qty, avg_price for live-to-backtest transitions
v0.2.2
- Export
run_spread_backtestPython binding for multi-leg options spread strategies - Export
rolling_minandrolling_maxindicator functions to Python
v0.2.1
- Add
rolling_minandrolling_maxindicators for LLV (Lowest Low Value) and HHV (Highest High Value) support - NaN handling for warmup period
v0.2.0
- Add multi-leg spread backtesting (
run_spread_backtest) supporting straddles, strangles, vertical spreads, iron condors, iron butterflies, butterfly spreads, calendar spreads, and diagonal spreads - Coordinated entry/exit across all legs with net premium P&L calculation
- Max loss and target profit exit thresholds for spreads
- Add
SessionTrackerfor intraday session management: market hours detection, squareoff time enforcement, session high/low/open tracking - Pre-built session configs for NSE equity (9:15-15:30), MCX commodity (9:00-23:30), and CDS currency (9:00-17:00)
- Extend
StreamingMetricswith equity/drawdown tracking, trade recording, andfinalize()method
v0.1.0
- Initial release
- 5 strategy types: single, basket, pairs, options, multi
- 30+ performance metrics
- 10 technical indicators (SMA, EMA, RSI, MACD, Stochastic, ATR, Bollinger Bands, ADX, VWAP, Supertrend)
- Stop-loss management: fixed, ATR-based, and trailing stops
- Take-profit management: fixed, ATR-based, and risk-reward targets
- PyO3 Python bindings for seamless Python integration