Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb5335e809 | ||
|
|
0ff67e7fe2 |
Generated
+1
-1
@@ -502,7 +502,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raptorbt"
|
name = "raptorbt"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"approx",
|
"approx",
|
||||||
"criterion",
|
"criterion",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "raptorbt"
|
name = "raptorbt"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "High-performance Rust backtesting engine with Python bindings. Drop-in VectorBT replacement with up insanely faster performance at fractional memory footprint."
|
description = "High-performance Rust backtesting engine with Python bindings. Drop-in VectorBT replacement with up insanely faster performance at fractional memory footprint."
|
||||||
authors = ["Alphabench <contact@alphabench.in>"]
|
authors = ["Alphabench <contact@alphabench.in>"]
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
[](https://www.rust-lang.org/)
|
[](https://www.rust-lang.org/)
|
||||||
[](https://pepy.tech/projects/raptorbt)
|
[](https://pepy.tech/projects/raptorbt)
|
||||||
|
|
||||||
|
<iframe src="https://clickhouse-analytics.metabaseapp.com/public/dashboard/daa27bf9-c01e-43fe-9260-c69b679cfe83?project_name=raptorbt#&theme=night" frameborder="0" width="100%" height="600"></iframe>
|
||||||
|
|
||||||
**Blazing-fast backtesting for the modern quant.**
|
**Blazing-fast backtesting for the modern quant.**
|
||||||
|
|
||||||
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.
|
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.
|
||||||
@@ -79,9 +81,10 @@ RaptorBT was built to address the performance limitations of VectorBT. Benchmark
|
|||||||
|
|
||||||
### Key Features
|
### Key Features
|
||||||
|
|
||||||
- **5 Strategy Types**: Single instrument, basket/collective, pairs trading, options, and multi-strategy
|
- **6 Strategy Types**: Single instrument, basket/collective, pairs trading, options, spreads, and multi-strategy
|
||||||
- **30+ Metrics**: Full parity with VectorBT including Sharpe, Sortino, Calmar, Omega, SQN, and more
|
- **Monte Carlo Simulation**: Correlated multi-asset forward projection via GBM + Cholesky decomposition
|
||||||
- **10 Technical Indicators**: SMA, EMA, RSI, MACD, Stochastic, ATR, Bollinger Bands, ADX, VWAP, Supertrend
|
- **33 Metrics**: Full parity with VectorBT including Sharpe, Sortino, Calmar, Omega, SQN, Payoff Ratio, Recovery Factor, and more
|
||||||
|
- **12 Technical Indicators**: SMA, EMA, RSI, MACD, Stochastic, ATR, Bollinger Bands, ADX, VWAP, Supertrend, Rolling Min, Rolling Max
|
||||||
- **Stop/Target Management**: Fixed, ATR-based, and trailing stops with risk-reward targets
|
- **Stop/Target Management**: Fixed, ATR-based, and trailing stops with risk-reward targets
|
||||||
- **100% Deterministic**: No JIT compilation variance between runs
|
- **100% Deterministic**: No JIT compilation variance between runs
|
||||||
- **Native Parallelism**: Rayon-based parallel processing with explicit SIMD optimizations
|
- **Native Parallelism**: Rayon-based parallel processing with explicit SIMD optimizations
|
||||||
@@ -127,6 +130,7 @@ raptorbt/
|
|||||||
│ ├── core/ # Core types and error handling
|
│ ├── core/ # Core types and error handling
|
||||||
│ │ ├── types.rs # BacktestConfig, BacktestResult, Trade, Metrics
|
│ │ ├── types.rs # BacktestConfig, BacktestResult, Trade, Metrics
|
||||||
│ │ ├── error.rs # RaptorError enum
|
│ │ ├── error.rs # RaptorError enum
|
||||||
|
│ │ ├── session.rs # SessionTracker, SessionConfig (intraday sessions)
|
||||||
│ │ └── timeseries.rs # Time series utilities
|
│ │ └── timeseries.rs # Time series utilities
|
||||||
│ │
|
│ │
|
||||||
│ ├── strategies/ # Strategy implementations
|
│ ├── strategies/ # Strategy implementations
|
||||||
@@ -134,6 +138,7 @@ raptorbt/
|
|||||||
│ │ ├── basket.rs # Basket/collective strategies
|
│ │ ├── basket.rs # Basket/collective strategies
|
||||||
│ │ ├── pairs.rs # Pairs trading
|
│ │ ├── pairs.rs # Pairs trading
|
||||||
│ │ ├── options.rs # Options strategies
|
│ │ ├── options.rs # Options strategies
|
||||||
|
│ │ ├── spreads.rs # Multi-leg spread strategies
|
||||||
│ │ └── multi.rs # Multi-strategy combining
|
│ │ └── multi.rs # Multi-strategy combining
|
||||||
│ │
|
│ │
|
||||||
│ ├── indicators/ # Technical indicators
|
│ ├── indicators/ # Technical indicators
|
||||||
@@ -141,7 +146,8 @@ raptorbt/
|
|||||||
│ │ ├── momentum.rs # RSI, MACD, Stochastic
|
│ │ ├── momentum.rs # RSI, MACD, Stochastic
|
||||||
│ │ ├── volatility.rs # ATR, Bollinger Bands
|
│ │ ├── volatility.rs # ATR, Bollinger Bands
|
||||||
│ │ ├── strength.rs # ADX
|
│ │ ├── strength.rs # ADX
|
||||||
│ │ └── volume.rs # VWAP
|
│ │ ├── volume.rs # VWAP
|
||||||
|
│ │ └── rolling.rs # Rolling Min/Max (LLV/HHV)
|
||||||
│ │
|
│ │
|
||||||
│ ├── metrics/ # Performance metrics
|
│ ├── metrics/ # Performance metrics
|
||||||
│ │ ├── streaming.rs # Streaming metric calculations
|
│ │ ├── streaming.rs # Streaming metric calculations
|
||||||
@@ -158,6 +164,12 @@ raptorbt/
|
|||||||
│ │ ├── atr.rs # ATR-based stops
|
│ │ ├── atr.rs # ATR-based stops
|
||||||
│ │ └── trailing.rs # Trailing 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
|
│ ├── python/ # PyO3 bindings
|
||||||
│ │ ├── bindings.rs # Python function exports
|
│ │ ├── bindings.rs # Python function exports
|
||||||
│ │ └── numpy_bridge.rs # NumPy array conversion
|
│ │ └── numpy_bridge.rs # NumPy array conversion
|
||||||
@@ -529,6 +541,68 @@ 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 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## VectorBT Comparison
|
## VectorBT Comparison
|
||||||
|
|
||||||
RaptorBT is designed as a drop-in replacement for VectorBT. Here's a side-by-side comparison:
|
RaptorBT is designed as a drop-in replacement for VectorBT. Here's a side-by-side comparison:
|
||||||
@@ -643,10 +717,27 @@ inst_config.set_fixed_target(0.05)
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Fields:**
|
**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.
|
- `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).
|
- `alloted_capital` - Per-instrument capital cap (capped at available cash).
|
||||||
- `existing_qty` / `avg_price` - Reserved for future live-to-backtest transitions.
|
- `existing_qty` / `avg_price` - Reserved for future live-to-backtest transitions.
|
||||||
|
|
||||||
|
### 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
|
### PyBacktestResult
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -699,6 +790,8 @@ metrics.max_consecutive_wins
|
|||||||
metrics.max_consecutive_losses
|
metrics.max_consecutive_losses
|
||||||
metrics.exposure_pct
|
metrics.exposure_pct
|
||||||
metrics.open_trade_pnl
|
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 (VectorBT format)
|
# Convert to dictionary (VectorBT format)
|
||||||
stats_dict = metrics.to_dict()
|
stats_dict = metrics.to_dict()
|
||||||
@@ -833,6 +926,22 @@ MIT License - see [LICENSE](LICENSE) for details.
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### 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
|
### v0.3.0
|
||||||
|
|
||||||
- Per-instrument configuration via `PyInstrumentConfig` (lot_size, alloted_capital, stop/target overrides)
|
- Per-instrument configuration via `PyInstrumentConfig` (lot_size, alloted_capital, stop/target overrides)
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "raptorbt"
|
name = "raptorbt"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
description = "High-performance Rust backtesting engine with Python bindings. Drop-in VectorBT replacement with up insanely faster performance at fractional memory footprint."
|
description = "High-performance Rust backtesting engine with Python bindings. Drop-in VectorBT replacement with up insanely faster performance at fractional memory footprint."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from raptorbt._raptorbt import (
|
|||||||
rolling_max,
|
rolling_max,
|
||||||
)
|
)
|
||||||
|
|
||||||
__version__ = "0.3.1"
|
__version__ = "0.3.2"
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Config classes
|
# Config classes
|
||||||
|
|||||||
@@ -376,6 +376,10 @@ pub struct BacktestMetrics {
|
|||||||
pub avg_holding_period: f64,
|
pub avg_holding_period: f64,
|
||||||
/// Exposure time percentage (time in market).
|
/// Exposure time percentage (time in market).
|
||||||
pub exposure_pct: f64,
|
pub exposure_pct: f64,
|
||||||
|
/// Payoff ratio (avg win / avg loss).
|
||||||
|
pub payoff_ratio: f64,
|
||||||
|
/// Recovery factor (net profit / max drawdown).
|
||||||
|
pub recovery_factor: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete backtest result.
|
/// Complete backtest result.
|
||||||
|
|||||||
@@ -513,6 +513,30 @@ impl StreamingMetrics {
|
|||||||
let worst_trade_pct =
|
let worst_trade_pct =
|
||||||
if self.worst_trade_pct == f64::INFINITY { 0.0 } else { self.worst_trade_pct };
|
if self.worst_trade_pct == f64::INFINITY { 0.0 } else { self.worst_trade_pct };
|
||||||
|
|
||||||
|
// Payoff ratio: average win / average loss (absolute value)
|
||||||
|
let payoff_ratio = if avg_loss_pct.abs() > 0.0 {
|
||||||
|
avg_win_pct / avg_loss_pct.abs()
|
||||||
|
} else if avg_win_pct > 0.0 {
|
||||||
|
f64::INFINITY
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Recovery factor: net profit / max drawdown (absolute value)
|
||||||
|
let net_profit = final_value - initial_capital;
|
||||||
|
let recovery_factor = if self.max_drawdown_pct > 0.0 && initial_capital > 0.0 {
|
||||||
|
let max_dd_absolute = self.max_drawdown_pct / 100.0 * initial_capital;
|
||||||
|
if max_dd_absolute > 0.0 {
|
||||||
|
net_profit / max_dd_absolute
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else if net_profit > 0.0 {
|
||||||
|
f64::INFINITY
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
BacktestMetrics {
|
BacktestMetrics {
|
||||||
total_return_pct,
|
total_return_pct,
|
||||||
sharpe_ratio,
|
sharpe_ratio,
|
||||||
@@ -545,6 +569,8 @@ impl StreamingMetrics {
|
|||||||
max_consecutive_losses: self.max_consecutive_losses,
|
max_consecutive_losses: self.max_consecutive_losses,
|
||||||
avg_holding_period,
|
avg_holding_period,
|
||||||
exposure_pct: 0.0, // TODO: calculate based on time in market
|
exposure_pct: 0.0, // TODO: calculate based on time in market
|
||||||
|
payoff_ratio,
|
||||||
|
recovery_factor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -591,6 +591,30 @@ impl PortfolioEngine {
|
|||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Payoff ratio: average win / average loss (absolute value)
|
||||||
|
let payoff_ratio = if avg_loss_pct.abs() > 0.0 {
|
||||||
|
avg_win_pct / avg_loss_pct.abs()
|
||||||
|
} else if avg_win_pct > 0.0 {
|
||||||
|
f64::INFINITY
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Recovery factor: net profit / max drawdown (absolute value)
|
||||||
|
let net_profit = end_value - start_value;
|
||||||
|
let recovery_factor = if max_drawdown_pct > 0.0 && start_value > 0.0 {
|
||||||
|
let max_dd_absolute = max_drawdown_pct / 100.0 * start_value;
|
||||||
|
if max_dd_absolute > 0.0 {
|
||||||
|
net_profit / max_dd_absolute
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else if net_profit > 0.0 {
|
||||||
|
f64::INFINITY
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
BacktestMetrics {
|
BacktestMetrics {
|
||||||
total_return_pct,
|
total_return_pct,
|
||||||
sharpe_ratio,
|
sharpe_ratio,
|
||||||
@@ -623,6 +647,8 @@ impl PortfolioEngine {
|
|||||||
max_consecutive_losses,
|
max_consecutive_losses,
|
||||||
avg_holding_period,
|
avg_holding_period,
|
||||||
exposure_pct,
|
exposure_pct,
|
||||||
|
payoff_ratio,
|
||||||
|
recovery_factor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -419,6 +419,10 @@ pub struct PyBacktestMetrics {
|
|||||||
pub avg_holding_period: f64,
|
pub avg_holding_period: f64,
|
||||||
#[pyo3(get)]
|
#[pyo3(get)]
|
||||||
pub exposure_pct: f64,
|
pub exposure_pct: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
|
pub payoff_ratio: f64,
|
||||||
|
#[pyo3(get)]
|
||||||
|
pub recovery_factor: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
@@ -1203,6 +1207,8 @@ fn convert_result(result: crate::core::types::BacktestResult) -> PyBacktestResul
|
|||||||
max_consecutive_losses: result.metrics.max_consecutive_losses,
|
max_consecutive_losses: result.metrics.max_consecutive_losses,
|
||||||
avg_holding_period: result.metrics.avg_holding_period,
|
avg_holding_period: result.metrics.avg_holding_period,
|
||||||
exposure_pct: result.metrics.exposure_pct,
|
exposure_pct: result.metrics.exposure_pct,
|
||||||
|
payoff_ratio: result.metrics.payoff_ratio,
|
||||||
|
recovery_factor: result.metrics.recovery_factor,
|
||||||
};
|
};
|
||||||
|
|
||||||
let trades: Vec<PyTrade> = result
|
let trades: Vec<PyTrade> = result
|
||||||
|
|||||||
Reference in New Issue
Block a user