Analysis Deep Dive:
portfolio calculations, metric configs and crisis analysis
This is a sample split feature page for the Analysis module. It explains where the analysis layer lives, which classes and functions matter, how metric configs become report tables and how crisis analysis turns historical regimes into reviewable evidence.
backtester/portfolio/calc/ Pure portfolio analytics
Side-effect-free calculators for returns, drawdowns, ratios, exposures, attribution, rolling windows, liquidity, PnL and Monte Carlo.
backtester/metrics/configs/ Report metric contract
Configuration maps human-readable report rows to nested calculation outputs and formatter rules.
backtester/portfolio/crisis_analysis.py Stress-period engine
Named crises and market regimes are loaded from JSON, matched to strategy dates and summarized for PDF and table output.
Analysis Flow
The analysis layer starts after the strategy produces returns, weights or order-derived positions. It keeps calculations inspectable: pandas objects go in, deterministic metric tables and report artifacts come out.
Portfolio Calc Modules
The `calc` package is the reusable core for analysis. It is intentionally split by concern so a report, strategy page or notebook can reuse one piece without depending on a full backtest run.
backtester/portfolio/calc/returns.py Return series and NAV construction
backtester/portfolio/calc/risk.py Volatility, drawdowns, VaR/CVaR and risk-adjusted ratios
backtester/portfolio/calc/metrics.py Benchmark-relative and market-sensitivity summaries
backtester/portfolio/calc/rolling_stats.py Windowed statistics for report charts and monitoring
backtester/portfolio/calc/exposures.py Position exposure, turnover and participation checks
backtester/portfolio/calc/attribution.py Factor exposure and performance attribution
backtester/portfolio/calc/liquidity.py Liquidity proxies for review and reporting
backtester/portfolio/calc/montecarlo.py Bootstrap path simulation for risk questions
backtester/portfolio/calc/scenario.py Historical and synthetic stress scenarios
backtester/portfolio/calc/round_trips.py FIFO trade grouping and round-trip analytics
backtester/portfolio/calc/pnl_multi_asset.py Contract-aware PnL, margin and notional exposure
import pandas as pd
from backtester.portfolio.calc import exposures, returns, risk, rolling_stats
asset_returns = prices.pct_change().dropna()
portfolio_returns = asset_returns.mul(target_weights.shift(1)).sum(axis=1)
nav = returns.convert_returns_to_nav(portfolio_returns.to_frame("strategy"))
drawdowns = risk.compute_drawdowns(portfolio_returns.to_frame("strategy"))
rolling_sharpe = rolling_stats.rolling_sharpe_ratio(
portfolio_returns.to_frame("strategy"),
window=126,
)
gross_exposure = exposures.compute_exposures(prices, units).abs().sum(axis=1) Main Classes And Entry Points
For a public feature page, these are the names worth explaining first. They are the objects a quant developer will search for when moving from marketing copy into source code.
| Name | What it does |
|---|---|
| CrisisPeriod | Dataclass for one named period: name, start, end and category. |
| CrisisPeriodResult | Dataclass with return, volatility, drawdown, Sharpe, worst day, best day and benchmark-relative fields. |
| load_crisis_periods() | Loads bundled or custom JSON definitions; optionally includes broader market regimes. |
| compute_crisis_analysis() | Main crisis entry point. Produces summary rows, details, overlap counts, hit rate and best/worst periods. |
| PORTFOLIO_PERF_METRICS | Report contract mapping visible metric names to result paths and formatter types. |
| MonteCarloSimulation | Bootstrap simulation object used when the report needs distribution and drawdown probability questions. |
| RoundTripAnalyzer | FIFO round-trip layer for trade-level review after order-aware runs. |
Metric Configs
`PORTFOLIO_PERF_METRICS` is the bridge between computed analytics and the visible report. Each row has a display name, a nested result path and a formatter type. That keeps the calculation layer separate from the reporting layout.
Why configs matter
Without a config layer, report code hard-codes metric names and formatting. Here the report can ask for sections like Executive Summary, Risk Metrics or Interesting Times and resolve each row from the same result object.
What gets configured
Paths such as `compute_crisis_analysis.worst_crisis` or `compute_advanced_sharpe_ratio.smart_sharpe` tell the report where to find data. Formatter keys such as `percentage`, `ratio`, `currency0`, `count` and `text` control presentation.
PORTFOLIO_PERF_METRICS = {
"Executive Summary": {
"CAGR": ("compute_annualized_return", "percentage"),
"Sharpe Ratio": ("compute_advanced_sharpe_ratio.smart_sharpe", "ratio"),
"Max Drawdown": ("compute_max_drawdown", "percentage"),
},
"Interesting Times": {
"Crises Evaluated": ("compute_crisis_analysis.overlapping_count", "count"),
"Worst Crisis": ("compute_crisis_analysis.worst_crisis", "text"),
"Crisis Hit Rate": ("compute_crisis_analysis.crisis_hit_rate", "percentage_raw"),
},
} Crisis Analysis
Crisis analysis is a separate module because it answers a different question from summary performance: how did this strategy behave during named stress windows and market regimes?
| Output | Meaning |
|---|---|
| summary | Rows ready for a report table: period, dates, days, return, volatility, max drawdown, Sharpe, worst day and benchmark fields. |
| details | Ordered per-period dictionaries for plots or deeper inspection. |
| overlapping_count | Number of configured periods that overlap the strategy date range. |
| total_defined | Total number of configured periods after optional regime inclusion. |
| avg_crisis_return | Mean strategy return across overlapping crisis windows. |
| worst_crisis / best_crisis | Names of the lowest and highest return stress windows. |
| crisis_hit_rate | Percent of overlapping crisis windows with positive strategy return. |
from backtester.portfolio.crisis_analysis import compute_crisis_analysis
analysis = compute_crisis_analysis(
returns=strategy_returns,
benchmark_returns=benchmark_returns,
include_regimes=True,
)
for row in analysis["summary"]:
print(row["Period"], row["Return"], row["Max DD"], row["Sharpe"]) `crisis_periods.json` ships named periods such as GFC / Lehman, Flash Crash, COVID Crash, 2022 Rate Shock, SVB / Banking Crisis, Japan Carry Unwind and DeepSeek Shock. The same file also contains broader regimes, and a custom JSON path can be passed at runtime.
Suggested Template For The Other Feature Pages
If this direction works, each feature page should follow the same pattern: what the module does, where it lives, main classes and functions, one short code example, output contract and a link to the strategy packet that uses it.