Backtesting¶
qufin includes a walk-forward backtesting engine for comparing portfolio strategies with realistic assumptions.
Basic Usage¶
import numpy as np
from qufin.backtesting import BacktestEngine
# Simulated returns: 2 years, 10 assets
rng = np.random.default_rng(42)
returns = rng.normal(0.0003, 0.01, (504, 10))
# Create engine with 6-month train window, monthly rebalance
engine = BacktestEngine(
returns,
train_window=126, # 6 months lookback
test_window=21, # rebalance monthly
transaction_cost=0.001, # 10 bps per trade
)
Defining Strategies¶
A strategy is any callable that takes (mu, cov) and returns a weight vector:
def equal_weight(mu, cov):
n = len(mu)
return np.ones(n) / n
def min_variance(mu, cov):
from qufin.portfolio.classical.mean_variance import mean_variance
return mean_variance(mu, cov).weights
def risk_parity(mu, cov):
from qufin.portfolio.classical.risk_parity import risk_parity as rp
return rp(cov).weights
Running a Backtest¶
result = engine.run(equal_weight, strategy_name="Equal Weight")
print(f"Annualized Return: {result.summary.annualized_return:.2%}")
print(f"Annualized Vol: {result.summary.annualized_volatility:.2%}")
print(f"Sharpe Ratio: {result.summary.sharpe_ratio:.2f}")
print(f"Max Drawdown: {result.summary.max_drawdown:.2%}")
print(f"Calmar Ratio: {result.summary.calmar_ratio:.2f}")
print(f"VaR 95%: {result.summary.var_95:.4f}")
print(f"Hit Rate: {result.summary.hit_rate:.2%}")
Comparing Strategies¶
strategies = {
"Equal Weight": equal_weight,
"Min Variance": min_variance,
"Risk Parity": risk_parity,
}
results = engine.compare(strategies)
table = engine.comparison_table(results)
# Print formatted comparison
for row in table:
print(f"{row['Strategy']:15s} | Sharpe: {row['Sharpe']} | MaxDD: {row['Max DD']}")
Performance Metrics¶
The PerformanceSummary dataclass includes:
| Metric | Description |
|---|---|
total_return |
Cumulative return |
annualized_return |
CAGR |
annualized_volatility |
Annualized standard deviation |
sharpe_ratio |
Risk-adjusted return |
sortino_ratio |
Downside-risk-adjusted return |
max_drawdown |
Peak-to-trough decline |
calmar_ratio |
Return / max drawdown |
avg_turnover |
Average portfolio turnover |
hit_rate |
Fraction of positive-return days |
var_95 |
95% Value at Risk |
cvar_95 |
95% Conditional VaR |
skewness |
Return distribution skewness |
kurtosis |
Return distribution kurtosis |
Standalone Metrics¶
All metrics are also available as standalone functions: