Analytics Suite¶
Comprehensive analytics framework for backtest analysis, risk assessment, performance attribution, and trade diagnostics.
Overview¶
Purpose: Understand strategy performance beyond basic returns: - Risk Metrics: VaR, CVaR, stress tests, tail risk, beta analysis - Performance Attribution: Alpha/beta, factor exposures, timing/selection skill - Trade Analysis: Entry/exit quality, MAE/MFE, holding periods, cost impact - Visualization: Charts, heatmaps, distributions, timelines - Report Generation: Comprehensive backtest reports
When to Use: - ✅ After backtest completion for comprehensive analysis - ✅ Before deployment to understand risk profile - ✅ For investor reporting and documentation - ✅ To diagnose strategy weaknesses and improve execution
Quick Start¶
Risk Analysis¶
from rustybt.analytics.risk import RiskAnalytics
import pandas as pd
# Load backtest results
backtest_df = pd.read_parquet("backtest_results.parquet") # Must have 'returns' or 'portfolio_value'
# Initialize risk analytics
risk = RiskAnalytics(
backtest_result=backtest_df,
confidence_levels=[0.95, 0.99], # VaR confidence levels
benchmark_returns=spy_returns, # Optional: for beta analysis
positions=positions_df # Optional: for risk decomposition
)
# Calculate Value at Risk
var_results = risk.calculate_var(method='historical')
print(f"95% VaR: {var_results['var_95']}") # Max expected loss at 95% confidence
print(f"99% VaR: {var_results['var_99']}") # Max expected loss at 99% confidence
# Calculate Conditional VaR (tail risk)
cvar_results = risk.calculate_cvar(method='historical')
print(f"95% CVaR: {cvar_results['cvar_95']}") # Average loss beyond VaR
# Run stress tests
stress_results = risk.run_stress_tests()
print(f"2008 Crisis Loss: {stress_results['2008_financial_crisis']}")
print(f"COVID Crash Loss: {stress_results['covid_crash']}")
# Comprehensive risk report
risk_report = risk.analyze_risk()
print(risk_report['var'])
print(risk_report['cvar'])
print(risk_report['tail_risk'])
print(risk_report['beta']) # If benchmark provided
# Visualize risk
risk.plot_var_distribution(method='historical', confidence=0.95)
risk.plot_stress_test_results()
Performance Attribution¶
from rustybt.analytics.attribution import PerformanceAttribution
# Initialize attribution analyzer
attrib = PerformanceAttribution(
backtest_result=backtest_df,
benchmark_returns=spy_returns,
factor_returns=ff_factors_df, # Optional: Fama-French factors
risk_free_rate=0.02 # Optional: risk-free rate (2%)
)
# Comprehensive attribution analysis
results = attrib.analyze_attribution()
# Alpha and beta
print(f"Alpha: {results['alpha_beta']['alpha']:.4f}")
print(f"Beta: {results['alpha_beta']['beta']:.4f}")
print(f"Alpha significant: {results['alpha_beta']['alpha_significant']}")
print(f"Information Ratio: {results['alpha_beta']['information_ratio']:.2f}")
# Factor attribution (if factors provided)
if 'factor_attribution' in results:
print("\nFactor Loadings:")
print(results['factor_attribution']['factor_loadings'])
print(f"\nR-squared: {results['factor_attribution']['r_squared']:.3f}")
# Timing attribution
if 'timing' in results:
print(f"\nTiming ability: {results['timing']['timing_coefficient']:.4f}")
print(f"Timing significant: {results['timing']['timing_significant']}")
# Visualize attribution
attrib.plot_alpha_over_time()
attrib.plot_factor_exposures()
Trade Analysis¶
from rustybt.analytics.trade_analysis import TradeAnalyzer
# Initialize trade analyzer
analyzer = TradeAnalyzer(backtest_result)
# Analyze all trades
analysis = analyzer.analyze_trades()
# Summary statistics
print(f"Total trades: {analysis['summary_stats']['total_trades']}")
print(f"Win rate: {analysis['summary_stats']['win_rate']:.2%}")
print(f"Profit factor: {analysis['summary_stats']['profit_factor']:.2f}")
print(f"Average win: ${analysis['summary_stats']['avg_win']}")
print(f"Average loss: ${analysis['summary_stats']['avg_loss']}")
print(f"Largest win: ${analysis['summary_stats']['largest_win']}")
print(f"Largest loss: ${analysis['summary_stats']['largest_loss']}")
# Entry/exit quality
print(f"\nAverage MAE: {analysis['mae_mfe']['avg_mae']:.2%}")
print(f"Average MFE: {analysis['mae_mfe']['avg_mfe']:.2%}")
# Holding period distribution
print(f"\nAverage holding period: {analysis['holding_period']['avg_holding_hours']} hours")
# Cost impact
print(f"\nTotal commission: ${analysis['costs']['total_commission']}")
print(f"Total slippage: ${analysis['costs']['total_slippage']}")
print(f"Commission impact: {analysis['costs']['commission_pct_of_pnl']:.2%}")
# Visualizations
analyzer.plot_mae_vs_pnl() # MAE scatter plot
analyzer.plot_mfe_vs_pnl() # MFE scatter plot
analyzer.plot_trade_timeline() # Trade timeline
analyzer.plot_holding_period_distribution() # Holding periods
Core Modules¶
Risk Analytics¶
- Module:
rustybt.analytics.risk - Purpose: VaR, CVaR, stress testing, beta analysis, tail risk
- Key Features: Multiple VaR methods, scenario analysis, risk decomposition
- Documentation: Risk Metrics
Performance Attribution¶
- Module:
rustybt.analytics.attribution - Purpose: Decompose returns into alpha, beta, factors, timing, selection
- Key Features: Multi-factor models, rolling attribution, timing tests
- Documentation: Performance Attribution
Trade Analysis¶
- Module:
rustybt.analytics.trade_analysis - Purpose: Trade-level diagnostics and execution quality analysis
- Key Features: MAE/MFE, entry/exit quality, cost impact, trade clustering
- Documentation: Trade Analysis
Visualization¶
- Module:
rustybt.analytics.visualization - Purpose: Charts, plots, heatmaps for backtest visualization
- Key Features: Returns distribution, equity curve, drawdown chart, correlation heatmap
- Documentation: Visualization Tools
Report Generation¶
- Module:
rustybt.analytics.reports - Purpose: Generate comprehensive backtest reports
- Key Features: PDF/HTML export, customizable templates, multi-strategy comparison
- Documentation: Report Generation
Complete Analysis Workflow¶
from rustybt.analytics.risk import RiskAnalytics
from rustybt.analytics.attribution import PerformanceAttribution
from rustybt.analytics.trade_analysis import TradeAnalyzer
from rustybt.analytics.reports import ReportGenerator
import pandas as pd
# Run backtest
backtest_result = run_backtest(strategy, data)
# 1. Risk Analysis
risk = RiskAnalytics(
backtest_result=backtest_result.to_dataframe(),
confidence_levels=[0.95, 0.99],
benchmark_returns=spy_returns
)
risk_report = risk.analyze_risk()
print("\n=== Risk Analysis ===")
print(f"95% VaR: {risk_report['var']['var_95']}")
print(f"95% CVaR: {risk_report['cvar']['cvar_95']}")
print(f"Beta: {risk_report['beta']['beta']}")
print(f"Max drawdown: {risk_report['tail_risk']['max_loss_1d']}")
# 2. Performance Attribution
attribution = PerformanceAttribution(
backtest_result=backtest_result.to_dataframe(),
benchmark_returns=spy_returns
)
attrib_report = attribution.analyze_attribution()
print("\n=== Attribution Analysis ===")
print(f"Alpha: {attrib_report['alpha_beta']['alpha']}")
print(f"Beta: {attrib_report['alpha_beta']['beta']}")
print(f"Information Ratio: {attrib_report['alpha_beta']['information_ratio']}")
# 3. Trade Analysis
trade_analyzer = TradeAnalyzer(backtest_result)
trade_report = trade_analyzer.analyze_trades()
print("\n=== Trade Analysis ===")
print(f"Win rate: {trade_report['summary_stats']['win_rate']:.2%}")
print(f"Profit factor: {trade_report['summary_stats']['profit_factor']}")
print(f"Average MAE: {trade_report['mae_mfe']['avg_mae']:.2%}")
print(f"Average MFE: {trade_report['mae_mfe']['avg_mfe']:.2%}")
# 4. Generate Comprehensive Report
report_gen = ReportGenerator(
backtest_result=backtest_result,
risk_analysis=risk_report,
attribution_analysis=attrib_report,
trade_analysis=trade_report
)
report_gen.generate_report(
output_path='backtest_report.html',
format='html',
include_plots=True
)
print("\n✅ Comprehensive report saved to backtest_report.html")
Key Metrics Reference¶
Risk Metrics¶
| Metric | Description | Interpretation |
|---|---|---|
| VaR | Maximum expected loss at confidence level | Lower = less downside risk |
| CVaR | Average loss beyond VaR threshold | More conservative than VaR |
| Beta | Market sensitivity | 1.0 = market, > 1.0 = more volatile |
| Skewness | Return distribution asymmetry | Negative = more extreme losses |
| Kurtosis | Fat tails indicator | High = more extreme events |
| Max Drawdown | Largest peak-to-trough decline | Lower = less severe losses |
| Downside Deviation | Volatility of negative returns | Lower = less downside risk |
Performance Metrics¶
| Metric | Description | Interpretation |
|---|---|---|
| Alpha | Excess return vs. benchmark | Positive = outperformance |
| Information Ratio | Alpha / tracking error | Higher = better risk-adjusted alpha |
| R-squared | Variance explained by benchmark | Higher = more market-driven |
| Tracking Error | Volatility of excess returns | Lower = closer to benchmark |
| Factor Loadings | Exposure to risk factors | Shows sources of return |
Trade Metrics¶
| Metric | Description | Interpretation |
|---|---|---|
| Win Rate | % of profitable trades | Higher = more consistent |
| Profit Factor | Gross profit / gross loss | > 1.0 = profitable |
| MAE | Max adverse excursion | Risk taken during trade |
| MFE | Max favorable excursion | Profit potential captured |
| Avg Win / Avg Loss | Reward/risk ratio | Higher = better risk/reward |
Statistical Foundations¶
VaR Calculation Methods¶
1. Parametric VaR (assumes normal distribution):
VaR = μ + z * σ
where:
μ = mean daily return
σ = standard deviation of daily returns
z = z-score for confidence level (e.g., -1.645 for 95%)
2. Historical VaR (empirical quantiles):
3. Monte Carlo VaR (simulation-based):
CVaR (Expected Shortfall)¶
Properties: - CVaR ≥ VaR (in absolute terms) - More conservative risk measure - Captures tail risk better than VaR
Alpha and Beta¶
Linear regression:
R_portfolio = α + β * R_benchmark + ε
where:
α (alpha) = intercept (excess return)
β (beta) = slope (market sensitivity)
ε = error term (idiosyncratic risk)
Information Ratio:
MAE and MFE¶
MAE (Maximum Adverse Excursion):
For long: MAE = max(0, (entry_price - min_price) / entry_price)
For short: MAE = max(0, (max_price - entry_price) / entry_price)
MFE (Maximum Favorable Excursion):
For long: MFE = max(0, (max_price - entry_price) / entry_price)
For short: MFE = max(0, (entry_price - min_price) / entry_price)
Best Practices¶
✅ DO¶
- Run comprehensive analysis after every backtest (risk + attribution + trade)
- Use multiple VaR methods (parametric, historical, Monte Carlo) for robustness
- Analyze tail risk (skewness, kurtosis) beyond VaR/CVaR
- Review MAE/MFE to improve stop-loss and take-profit levels
- Calculate beta to understand market dependency
- Examine trade distribution for clustering and concentration risk
- Document risk profile before deploying strategies
❌ DON'T¶
- Rely on VaR alone (use CVaR and stress tests for tail risk)
- Ignore statistical significance (check p-values for alpha)
- Skip trade-level analysis (aggregate metrics hide execution issues)
- Use parametric VaR for non-normal returns (use historical or Monte Carlo)
- Forget transaction costs (commission/slippage impact profitability)
- Ignore correlation risk (check asset correlation matrix)
Performance Considerations¶
Risk Analytics¶
- Time Complexity: O(n) for VaR/CVaR, O(n²) for correlation
- Memory: O(n) for returns storage
- Typical Runtime: < 1 second for 1000 observations
Performance Attribution¶
- Time Complexity: O(n) for alpha/beta, O(n × f) for factor models
- Memory: O(n × f) where f = number of factors
- Typical Runtime: 1-5 seconds for factor models with 1000 observations
Trade Analysis¶
- Time Complexity: O(t) where t = number of trades
- Memory: O(t) for trade storage
- Typical Runtime: < 1 second for 1000 trades
See Also¶
- Risk Metrics Documentation
- Performance Attribution
- Trade Analysis
- Visualization Tools
- Report Generation
Last Updated: 2025-10-16 | RustyBT v1.0