RaptorBT

High-performance backtesting engine written in Rust with Python bindings

PyPIGitHubMIT License

RaptorBT

Getting Started

RaptorBT is a high-performance backtesting engine written in Rust with Python bindings via PyO3. It serves as a drop-in replacement for VectorBT, delivering HFT-grade compute efficiency with full metric parity.

  • 5,800x faster than VectorBT on 1K bars
  • 45x smaller disk footprint (~10 MB vs ~450 MB)
  • 100% deterministic results with no JIT compilation variance
  • 30+ performance metrics with full VectorBT parity
  • 5 strategy types: single, basket, pairs, options, and multi-strategy
  • 10 technical indicators: SMA, EMA, RSI, MACD, Stochastic, ATR, Bollinger Bands, ADX, VWAP, Supertrend
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 SizeVectorBTRaptorBTSpeedup
1,000 bars1,460 ms0.25 ms5,827x
5,000 bars36 ms0.24 ms153x
10,000 bars37 ms0.46 ms80x
50,000 bars43 ms1.68 ms26x

Note: First VectorBT run includes Numba JIT compilation overhead. Subsequent runs are faster but still significantly slower than RaptorBT.

Metric Accuracy

RaptorBT produces identical results to VectorBT:

VectorBT Total Return: 7.2764%
RaptorBT Total Return: 7.2764%
Difference: 0.0000%

Architecture

raptorbt/
├── src/
│   ├── core/              # Core types and error handling
│   │   ├── types.rs       # BacktestConfig, BacktestResult, Trade, Metrics
│   │   ├── error.rs       # RaptorError enum
│   │   └── 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
│   │   └── 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
│   │
│   ├── 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 signal
  • any: Enter when ANY instrument signals
  • majority: Enter when >50% of instruments signal
  • master: 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)
)

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 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)

Metrics

RaptorBT calculates 30+ performance metrics:

Core Performance

MetricDescription
total_return_pctTotal return as percentage
sharpe_ratioRisk-adjusted return (annualized)
sortino_ratioDownside risk-adjusted return
calmar_ratioReturn / Max Drawdown
omega_ratioProbability-weighted gains/losses

Drawdown

MetricDescription
max_drawdown_pctMaximum peak-to-trough decline
max_drawdown_durationLongest drawdown period (bars)

Trade Statistics

MetricDescription
total_tradesTotal number of trades
total_closed_tradesNumber of closed trades
total_open_tradesNumber of open positions
winning_tradesNumber of profitable trades
losing_tradesNumber of losing trades
win_rate_pctPercentage of winning trades

Trade Performance

MetricDescription
profit_factorGross profit / Gross loss
expectancyAverage expected profit per trade
sqnSystem Quality Number
avg_trade_return_pctAverage trade return
avg_win_pctAverage winning trade return
avg_loss_pctAverage losing trade return
best_trade_pctBest single trade return
worst_trade_pctWorst single trade return

Duration

MetricDescription
avg_holding_periodAverage trade duration (bars)
avg_winning_durationAverage winning trade duration
avg_losing_durationAverage losing trade duration

Streaks

MetricDescription
max_consecutive_winsLongest winning streak
max_consecutive_lossesLongest losing streak

Other

MetricDescription
start_valueInitial portfolio value
end_valueFinal portfolio value
total_fees_paidTotal transaction costs
open_trade_pnlUnrealized PnL from open positions
exposure_pctPercentage 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)

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

VectorBT Comparison

RaptorBT is designed as a drop-in replacement for VectorBT. Here's a side-by-side comparison:

VectorBT (before)

import vectorbt as vbt
import pandas as pd

pf = vbt.Portfolio.from_signals(
    close=close_series,
    entries=entries,
    exits=exits,
    init_cash=100000,
    fees=0.001,
)

print(pf.stats()["Total Return [%]"])
print(pf.stats()["Sharpe Ratio"])
print(pf.stats()["Max Drawdown [%]"])

RaptorBT (after)

import raptorbt
import numpy as np

config = raptorbt.PyBacktestConfig(
    initial_capital=100000,
    fees=0.001,
)

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="SYMBOL",
    config=config,
)

print(f"Total Return: {result.metrics.total_return_pct}%")
print(f"Sharpe Ratio: {result.metrics.sharpe_ratio}")
print(f"Max Drawdown: {result.metrics.max_drawdown_pct}%")

Metric Mapping

VectorBT KeyRaptorBT Attribute
Total Return [%]metrics.total_return_pct
Sharpe Ratiometrics.sharpe_ratio
Sortino Ratiometrics.sortino_ratio
Max Drawdown [%]metrics.max_drawdown_pct
Win Rate [%]metrics.win_rate_pct
Profit Factormetrics.profit_factor
SQNmetrics.sqn
Omega Ratiometrics.omega_ratio
Total Tradesmetrics.total_trades
Expectancymetrics.expectancy

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)

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

# Convert to dictionary (VectorBT format)
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"

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}")

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

  1. Fork the repository on GitHub
  2. Clone your fork locally
  3. Install Rust 1.70+ via rustup and Python 3.10+
  4. Install maturin: pip install maturin
  5. 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.1.0

  • Initial release
  • 5 strategy types: single, basket, pairs, options, multi
  • 30+ performance metrics with full VectorBT parity
  • 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