# RaptorBT [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![PyPI version](https://img.shields.io/pypi/v/raptorbt.svg)](https://pypi.org/project/raptorbt/) [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) [![Rust](https://img.shields.io/badge/rust-1.70+-red.svg)](https://www.rust-lang.org/) [![PyPI Downloads](https://static.pepy.tech/personalized-badge/raptorbt?period=total&units=INTERNATIONAL_SYSTEM&left_color=GRAY&right_color=ORANGE&left_text=downloads)](https://pepy.tech/projects/raptorbt) **Blazing-fast backtesting for the modern quant.** RaptorBT is a high-performance backtesting engine written in Rust with Python bindings via PyO3. Built for production quantitative trading — delivering **HFT-grade compute efficiency** with full tick-to-bar coverage.

5,800x faster · 45x smaller · 100% deterministic

--- ### Quick Install ```bash pip install raptorbt ``` ### 30-Second Example ```python import numpy as np import raptorbt # Configure config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) # Run backtest 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, ) # Results print(f"Return: {result.metrics.total_return_pct:.2f}%") print(f"Sharpe: {result.metrics.sharpe_ratio:.2f}") ``` --- Developed and maintained by the [Alphabench](https://alphabench.in) team. ## Table of Contents - [Overview](#overview) - [Performance](#performance) - [Architecture](#architecture) - [Installation](#installation) - [Quick Start](#quick-start) - [Strategy Types](#strategy-types) - [Metrics](#metrics) - [Indicators](#indicators) - [Stop-Loss & Take-Profit](#stop-loss--take-profit) - [API Reference](#api-reference) - [Building from Source](#building-from-source) - [Testing](#testing) --- ## Overview RaptorBT is benchmarked by the Alphabench team on Apple Silicon M-series: | Metric | RaptorBT | | ----------------------------- | ------------ | | **Disk Footprint** | <10MB | | **Startup Latency** | <10ms | | **Backtest Speed (1K bars)** | 0.25ms | | **Backtest Speed (50K bars)** | 1.7ms | | **Memory Usage** | Low (native) | ### Key Features - **7 Strategy Types**: Single instrument, basket/collective, pairs trading, options, spreads, multi-strategy, and tick-level - **Tick-Level Simulation**: Full tick resolution for intraday options momentum, scalping, and microstructure strategies - **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 - **33 Metrics**: Sharpe, Sortino, Calmar, Omega, SQN, Payoff Ratio, Recovery Factor, and more - **Technical Indicators**: SMA, EMA, RSI, MACD, Stochastic, ATR, Bollinger Bands, ADX, VWAP, Supertrend, Rolling Min/Max, and tick feature functions - **Stop/Target Management**: Fixed, ATR-based, and trailing stops with risk-reward targets - **100% Deterministic**: No JIT compilation variance between runs - **Native Parallelism**: Rayon-based parallel processing with explicit SIMD optimizations --- ## 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 deterministic, reproducible results across runs: ``` RaptorBT Total Return: 7.2764% (seed=42, 500 bars, SMA crossover) Difference between runs: 0.0000% ✓ ``` --- ## 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 │ │ └── 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) │ │ │ ├── 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 │ │ │ ├── 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 │ │ │ ├── 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 ``` --- ## Installation ### From Pre-built Wheel ```bash pip install raptorbt ``` ### From Source ```bash cd raptorbt maturin develop --release ``` ### Verify Installation ```python import raptorbt print("RaptorBT installed successfully!") ``` --- ## Quick Start ### Basic Single Instrument Backtest ```python 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 ``` --- ## Strategy Types ### 1. Single Instrument Basic long or short strategy on a single instrument. ```python # Optional: Instrument-specific configuration inst_config = raptorbt.PyInstrumentConfig(lot_size=1.0) 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, instrument_config=inst_config, # Optional: lot_size rounding, capital caps ) ``` ### 2. Basket/Collective Trade multiple instruments with synchronized signals. ```python 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"), ] # Optional: Per-instrument configs for lot_size and capital allocation instrument_configs = { "AAPL": raptorbt.PyInstrumentConfig(lot_size=1.0, alloted_capital=33000), "GOOGL": raptorbt.PyInstrumentConfig(lot_size=1.0, alloted_capital=33000), "MSFT": raptorbt.PyInstrumentConfig(lot_size=1.0, alloted_capital=34000), } result = raptorbt.run_basket_backtest( instruments=instruments, config=config, sync_mode="all", # "all", "any", "majority", "master" instrument_configs=instrument_configs, # Optional ) ``` **Sync Modes:** - `all`: Enter only when ALL instruments signal - `any`: Enter when ANY instrument signals - `majority`: Enter when >50% of instruments signal - `master`: Follow the first instrument's signals ### 3. Pairs Trading Long one instrument, short another with optional hedge ratio. ```python 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 ) ``` ### 4. Options Backtest options strategies with strike selection. ```python 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) ) ``` ### 5. Multi-Strategy Combine multiple strategies on the same instrument. ```python 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 signals - `all`: Enter only when all strategies signal - `majority`: Enter when >50% of strategies signal - `weighted`: Weight signals by strategy weight - `independent`: Run strategies independently (aggregate PnL) ### 6. 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. ```python 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}%") ``` ### 7. Tick-Level Backtest Simulate intraday strategies at full tick resolution — no bar resampling, no intra-bar path approximation. Designed for options momentum, scalping, and any setup where the exact fill tick matters. ```python import numpy as np import raptorbt # Raw tick arrays (one element per tick, same length N) # buy_qty_delta / sell_qty_delta must be per-tick deltas, NOT Zerodha cumulative sums result = raptorbt.run_tick_backtest( timestamps=timestamps_ns, # int64 nanoseconds-since-epoch ltp=ltp_arr, # last traded price bid=bid_arr, ask=ask_arr, buy_qty_delta=buy_delta, # pre-converted from cumulative: np.diff(buy_cum).clip(0) sell_qty_delta=sell_delta, oi=oi_arr, entries=entry_signals, # bool array — True where entry is allowed exits=exit_signals, # bool array — True where position should exit symbol="NIFTY26APR24600PE", initial_capital=100_000.0, fees=0.001, slippage=0.0005, stop_loss_pct=5.0, take_profit_pct=10.0, max_hold_seconds=1800, # 30-minute maximum hold entry_cooldown_ticks=10, # minimum ticks between entries max_trades=50, ) print(f"trades: {result.metrics.total_trades}") print(f"profit_factor: {result.metrics.profit_factor:.2f}") print(f"win_rate: {result.metrics.win_rate_pct:.1f}%") ``` #### Tick Signal & Feature Helpers Precompute entry/exit signal arrays and tick microstructure features before calling `run_tick_backtest`: ```python # Signal arrays entries = raptorbt.compute_tick_entry_signals( spread_pct=raptorbt.tick_spread_pct(bid, ask), bsi_delta=raptorbt.buy_sell_imbalance_delta(buy_cum, sell_cum), # pass raw cumulative return_1m=raptorbt.return_window(timestamps_ns, ltp, window_seconds=60.0), spread_pct_max=3.0, bsi_min=0.55, # minimum buy-side delta fraction return_1m_min_abs=0.3, # minimum 1-min return % (abs) return_direction=1, # +1 long, -1 short cooldown_ticks=10, ) exits = raptorbt.compute_tick_exit_signals( timestamps_ns=timestamps_ns, eod_exit_time_ns=eod_ns, # force exit at/after this timestamp; 0 = disabled ) # Feature arrays (all return Vec of same length as input) spread = raptorbt.tick_spread_pct(bid, ask) # (ask-bid)/mid * 100 bsi = raptorbt.buy_sell_imbalance_delta(buy_cum, sell_cum) # delta BSI per tick ret_1m = raptorbt.return_window(ts_ns, ltp, 60.0) # 1-min lookback return % vol = raptorbt.realized_vol_rolling(ts_ns, ltp, 300.0) # 5-min realized vol % oi_pos = raptorbt.oi_position_pct(oi, oi_day_high, oi_day_low) # [0, 100] velocity = raptorbt.tick_velocity(ts_ns, 60.0) # ticks/min over last 60s ``` **Important for Zerodha data:** `total_buy_qty` and `total_sell_qty` from KiteTicker are cumulative session running sums, not per-tick values. Pass them as-is to `buy_sell_imbalance_delta` (it computes deltas internally). For `run_tick_backtest`, convert first: `buy_delta = np.diff(buy_cum, prepend=0).clip(min=0)`. --- ## 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 | --- ## Indicators RaptorBT includes optimized technical indicators: ```python 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) ``` --- ## Stop-Loss & Take-Profit ### Fixed Percentage ```python 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 ```python 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 ```python config.set_trailing_stop(0.02) # 2% trailing stop ``` ### Risk-Reward Target ```python 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. ```python 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 ```python 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) ``` ### PyInstrumentConfig Per-instrument configuration for position sizing and risk management. ```python inst_config = raptorbt.PyInstrumentConfig( lot_size=1.0, # Min tradeable quantity (1 for equity, 50 for NIFTY F&O) alloted_capital=50000.0, # Capital allocated to this instrument (optional) existing_qty=None, # Existing position quantity (future use) avg_price=None, # Existing position avg price (future use) ) # Optional: per-instrument stop/target overrides inst_config.set_fixed_stop(0.02) inst_config.set_trailing_stop(0.03) inst_config.set_fixed_target(0.05) ``` **Fields:** - `lot_size` - Minimum tradeable quantity. Position sizes are rounded down to nearest lot_size multiple. Use `1.0` for equities, `50.0` for NIFTY F&O, `0.01` for forex. - `alloted_capital` - Per-instrument capital cap (capped at available cash). - `existing_qty` / `avg_price` - Reserved for future live-to-backtest transitions. ### PyBatchSpreadItem ```python 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 ```python 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 ```python 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 ```python 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 ```python 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 ```python 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" ``` --- ## Building from Source ### Prerequisites - Rust 1.70+ (install via [rustup](https://rustup.rs/)) - Python 3.10+ - maturin (`pip install maturin`) ### Development Build ```bash cd raptorbt maturin develop --release ``` ### Production Build ```bash cd raptorbt maturin build --release pip install target/wheels/raptorbt-*.whl ``` --- ## Testing ### Rust Unit Tests ```bash cd raptorbt cargo test ``` ### Python Integration Tests ```python 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!') ``` ### Verification Test ```python import numpy as np import raptorbt np.random.seed(42) n = 500 close = np.cumprod(1 + np.random.randn(n) * 0.02) * 100 entries = np.zeros(n, dtype=bool) exits = np.zeros(n, dtype=bool) entries[::20] = True exits[10::20] = True config = raptorbt.PyBacktestConfig(initial_capital=100000, fees=0.001) result = raptorbt.run_single_backtest( timestamps=np.arange(n, dtype=np.int64), open=close, high=close, low=close, close=close, volume=np.ones(n), entries=entries, exits=exits, direction=1, weight=1.0, symbol="TEST", config=config ) print(f"Total Return: {result.metrics.total_return_pct:.4f}%") print(f"Sharpe Ratio: {result.metrics.sharpe_ratio:.4f}") print(f"Max Drawdown: {result.metrics.max_drawdown_pct:.4f}%") print("RaptorBT is working correctly!") ``` --- ## License MIT License - see [LICENSE](LICENSE) for details. --- ## Changelog ### v0.4.0 **Tick-level backtesting — full tick resolution, no bar resampling.** - Add `TickData` struct — parallel arrays of `timestamps`, `ltp`, `bid`, `ask`, `buy_qty_delta`, `sell_qty_delta`, `oi` (one element per tick). Callers must pre-convert Zerodha cumulative session totals to per-tick deltas before passing. - Add `ExitReason::TimeExit` — max hold-time exceeded exit for tick strategies. - Add `run_tick_backtest` — tick-native simulation engine. Entry fills at ask+slippage; stop/target checked against ltp on every tick (not OHLC approximation); max-hold-seconds time exit; configurable cooldown between entries. Returns the same `PyBacktestResult` / 27-metric `PyBacktestMetrics` as all other strategy types. - Add `compute_tick_entry_signals` — compute momentum entry bool array from precomputed feature arrays (spread gate, delta BSI gate, 1-min return gate, cooldown enforcement). O(N) single pass. - Add `compute_tick_exit_signals` — time-based (EOD) exit bool array from tick timestamps. - Add `tick_spread_pct` — per-tick bid/ask spread as percentage of mid price. - Add `buy_sell_imbalance_delta` — per-tick delta BSI from Zerodha cumulative running sums. Fixes the raw-cumulative BSI artefact (~0.95 all day regardless of order flow). - Add `return_window` — per-tick lookback return over a configurable time window using binary search (O(N log N)). Returns NaN where history is insufficient — correctly gates the entry filter rather than silently passing. - Add `realized_vol_rolling` — rolling realized volatility proxy (stddev of log-returns) over a time window. - Add `oi_position_pct` — OI position within the day's high/low range, per tick: [0, 100]. - Add `tick_velocity` — rolling tick count per minute over a configurable time window. - Expose `compute_backtest_metrics` as a public free function in `portfolio::engine` — non-OHLCV strategy types can produce identical metrics without duplicating the calculation logic. ### v0.3.4 - Add single-leg option spread types: `LongCall`, `LongPut`, `NakedCall`, `NakedPut` to `SpreadType` enum - Add `ExitReason::Settlement` for option expiry settlement exits - Add `leg_expiry_timestamps` parameter to `run_spread_backtest` for 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_backtest` function for running multiple spread backtests in parallel via Rayon - Add `PyBatchSpreadItem` class 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_ratio` metric to `BacktestMetrics` — average winning trade return divided by average losing trade return (absolute), measures risk/reward per trade - Add `recovery_factor` metric to `BacktestMetrics` — 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) and `PortfolioEngine` (multi-strategy aggregation) - Both metrics exposed via PyO3 as `#[pyo3(get)]` attributes on `PyBacktestMetrics` - Handles edge cases: returns `f64::INFINITY` when denominator is zero with positive numerator, `0.0` otherwise ### 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 - Future-ready fields: existing_qty, avg_price for live-to-backtest transitions ### v0.2.2 - Export `run_spread_backtest` Python binding for multi-leg options spread strategies - Export `rolling_min` and `rolling_max` indicator functions to Python ### v0.2.1 - Add `rolling_min` and `rolling_max` indicators 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 `SessionTracker` for 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 `StreamingMetrics` with equity/drawdown tracking, trade recording, and `finalize()` method ### v0.1.0 - Initial release - 5 strategy types: single, basket, pairs, options, multi - 30+ performance metrics: Sharpe, Sortino, Calmar, Omega, SQN, profit factor, drawdown duration, and more - 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