from quantfinlab.options.fourier import direct_price, fft_grid, cos_prices, cos_density, tail_probability, model_cf
from quantfinlab.options.quote_cleaning import convert_quotes_to_usd_equivalent
from quantfinlab.options.bsm import bsm_price
from quantfinlab.dataio import load_option_chain, load_ohlcv
from quantfinlab.calibration.fft_cos import (calibration_grid_quotes, calibration_weights, fit_daily_models,
compare_fourier_models, family_winner, residual_by_bucket)
from quantfinlab.calibration.jump_models import fixed_delta_hedge_candidates, tail_hedge_candidates, tail_hedge_schedule, hedge_score_components
from quantfinlab.backtest.overlays import run_overlay_backtest, overlay_summary, mark_book_for_schedules
from quantfinlab.plotting.options import (cos_convergence, fft_fit, throughput, model_quality,
daily_calibration_error, model_tournament, residual_smiles, tail_density,
tail_probability_series, overlay_equity, hedge_selection, hedge_cost_payoff)
cache = cache_dir / "btc"
cache.mkdir(parents=True, exist_ok=True)
raw = load_option_chain(data_dir / "btc_options_chain.parquet", source="btc_deribit")
raw = convert_quotes_to_usd_equivalent(raw, contract_size=1.0)
raw["date"] = pd.to_datetime(raw["date"], errors="coerce").dt.normalize()
raw["expiry"] = pd.to_datetime(raw["expiry"], errors="coerce").dt.normalize()
raw["dte_days"] = raw["tau"] * 365.0
raw["moneyness"] = raw["strike"] / raw["spot"]
raw["log_moneyness"] = np.log(raw["moneyness"])
raw["relative_spread"] = raw.get("rel_spread", (raw["ask"] - raw["bid"]) / raw["mid"].replace(0, np.nan))
raw["iv_mid"] = pd.to_numeric(raw.get("iv", raw.get("mark_iv")), errors="coerce")
raw["rate"] = 0.04
raw["dividend_yield"] = 0.0
raw["discount_factor"] = np.exp(-raw["rate"] * raw["tau"])
quotes = raw[
(raw["bid"] >= 0)
& (raw["ask"] >= raw["bid"])
& (raw["mid"] > 0)
& raw["dte_days"].between(3, 120)
& raw["moneyness"].between(0.55, 1.60)
& raw["relative_spread"].between(0, 0.50)
& raw["iv_mid"].between(0.05, 3.00)
].copy()
quotes["half_spread"] = 0.5 * (pd.to_numeric(quotes["ask"], errors="coerce") - pd.to_numeric(quotes["bid"], errors="coerce"))
quotes["calib_scale_px"] = np.maximum.reduce([quotes["half_spread"].to_numpy(float),
0.001 * pd.to_numeric(quotes["mid"], errors="coerce").to_numpy(float),
np.full(len(quotes), 5.0)])
quotes["obs_weight"] = 1.0 / (quotes["relative_spread"].clip(lower=0.01) ** 2 + 0.05 ** 2)
quotes["obs_weight"] = quotes["obs_weight"] / quotes["obs_weight"].median()
quotes["contract_key"] = quotes["option_type"].astype(str) + "_" + quotes["expiry"].dt.strftime("%Y-%m-%d") + "_" + quotes["strike"].round(4).astype(str)
spot = load_ohlcv(data_dir / "btc_usd_ohlcv.csv", fields=("close",))
calib_path = cache / "btc_calibration_grid_quotes.parquet"
steps_path = cache / "btc_calibration_grid_steps.parquet"
calib_settings = {"version": 2, "rows": int(len(quotes)), "min_quotes_per_date": 100, "max_quotes_per_date": 160}
if calib_path.exists() and steps_path.exists() and same_settings(calib_path, calib_settings):
calibration = pd.read_parquet(calib_path)
selection_steps = pd.read_parquet(steps_path)
else:
calibration, selection_steps = calibration_grid_quotes(
quotes,
min_dte=7.0,
max_dte=120.0,
min_vega=0.0,
max_relative_spread=0.50,
max_abs_log_moneyness=0.45,
min_quotes_per_expiry=6,
min_expiries_per_date=4,
dte_targets=(7.0, 14.0, 21.0, 30.0, 45.0, 60.0, 75.0, 90.0, 120.0),
k_targets=(-0.40, -0.32, -0.25, -0.18, -0.12, -0.08, -0.04, 0.0, 0.04, 0.08, 0.12, 0.18, 0.25, 0.32, 0.40),
min_quotes_per_date=100,
max_quotes_per_date=160,
return_steps=True,
)
calibration.to_parquet(calib_path, index=False)
selection_steps.to_parquet(steps_path, index=False)
write_meta(calib_path, calib_settings, len(calibration))
dates = calibration.groupby("date").size()
fit_dates = dates[dates >= 80].index
sv_dates = fit_dates[::3]
jump_path = cache / "btc_jump_daily_fits_scale_v2.parquet"
sv_path = cache / "btc_sv_daily_fits_scale_v2.parquet"
if jump_path.exists():
jump_daily = pd.read_parquet(jump_path)
jump_fit = pd.read_parquet(cache / "btc_jump_daily_fit_prices_scale_v2.parquet")
else:
out = fit_daily_models(calibration, ["merton", "vg"], calibration_dates=fit_dates,
min_quotes=80, max_nfev=45, engine="cpp",
n_terms=128, truncation_width=16.0)
jump_daily, jump_fit = out["params"], out["fit"]
jump_daily.to_parquet(jump_path, index=False)
jump_fit.to_parquet(cache / "btc_jump_daily_fit_prices_scale_v2.parquet", index=False)
if sv_path.exists():
sv_daily = pd.read_parquet(sv_path)
sv_fit = pd.read_parquet(cache / "btc_sv_daily_fit_prices_scale_v2.parquet")
else:
out = fit_daily_models(calibration, ["heston", "bates"], calibration_dates=sv_dates,
min_quotes=80, max_nfev=40, engine="cpp",
n_terms=112, truncation_width=16.0)
sv_daily, sv_fit = out["params"], out["fit"]
sv_daily.to_parquet(sv_path, index=False)
sv_fit.to_parquet(cache / "btc_sv_daily_fit_prices_scale_v2.parquet", index=False)
jump_summary = compare_fourier_models(daily=jump_daily)
sv_summary = compare_fourier_models(daily=sv_daily)
jump_winner = family_winner(jump_summary)
sv_winner = family_winner(sv_summary)
bsm_path = cache / "btc_bsm_daily_fits_scale_v2.parquet"
if bsm_path.exists():
bsm_daily = pd.read_parquet(bsm_path)
bsm_fit = pd.read_parquet(cache / "btc_bsm_daily_fit_prices_scale_v2.parquet")
else:
out = fit_daily_models(calibration, ["bsm"], calibration_dates=fit_dates,
min_quotes=80, max_nfev=25, engine="cpp",
n_terms=112, truncation_width=14.0)
bsm_daily, bsm_fit = out["params"], out["fit"]
bsm_daily.to_parquet(bsm_path, index=False)
bsm_fit.to_parquet(cache / "btc_bsm_daily_fit_prices_scale_v2.parquet", index=False)
final_daily = pd.concat([bsm_daily, jump_daily[jump_daily["model"].eq(jump_winner)], sv_daily[sv_daily["model"].eq(sv_winner)]], ignore_index=True)
final_comparison = compare_fourier_models(daily=final_daily)
fit_prices = pd.concat([bsm_fit, jump_fit[jump_fit["model"].eq(jump_winner)], sv_fit[sv_fit["model"].eq(sv_winner)]], ignore_index=True)
residuals = residual_by_bucket(fit_prices)
sample = quotes.iloc[:80].copy()
spot0 = float(sample["spot"].median())
rate0 = float(sample["rate"].median())
tau0 = float(sample["tau"].median())
strikes = np.linspace(0.70, 1.30, 1000) * spot0
closed = np.asarray(bsm_price("call", spot0, strikes, tau0, float(sample["iv_mid"].median()), rate0, 0.0), dtype=float)
engine_settings = {"version": 2, "spot": round(spot0, 6), "tau": round(tau0, 8), "rows": int(len(sample))}
fft_path = cache / "btc_fft_validation_grid.parquet"
cos_path = cache / "btc_cos_validation_grid.parquet"
fft_fit_path = cache / "btc_fft_fit_grid.parquet"
if fft_path.exists() and cos_path.exists() and fft_fit_path.exists() and same_settings(fft_path, engine_settings):
fft_validation = pd.read_parquet(fft_path)
cos_validation = pd.read_parquet(cos_path)
btc_fft_fit = pd.read_parquet(fft_fit_path)
else:
fft_rows = []
fft_fit_rows = []
for engine in ["numba", "cpp"]:
for n in [4096, 8192, 16384]:
t0 = time.perf_counter()
grid = fft_grid("bsm", [float(sample["iv_mid"].median())], spot0, rate0, 0.0, tau0, n=n, eta=0.35, alpha=1.5, engine=engine)
runtime = time.perf_counter() - t0
interp = np.interp(strikes, grid["strike"], grid["price"])
fft_rows.append({"engine": engine, "n": n, "runtime_sec": runtime,
"median_abs_error": float(np.median(np.abs(interp - closed))),
"max_abs_error": float(np.max(np.abs(interp - closed)))})
if engine == "cpp" and n == 16384:
fft_fit_rows.extend([{"strike": float(k), "moneyness": float(k / spot0), "fft_price": float(px),
"reference_price": float(ref), "abs_error": float(abs(px - ref))} for k, px, ref in zip(strikes, interp, closed)])
fft_validation = pd.DataFrame(fft_rows)
cos_rows = []
for model, params, width in [("bsm", [float(sample["iv_mid"].median())], 12.0),
("merton", [0.65, 0.60, -0.08, 0.35], 16.0), ("vg", [0.65, -0.10, 0.35], 30.0),
("heston", [0.45, 2.0, 0.45, 0.80, -0.45], 16.0),
("bates", [0.45, 2.0, 0.45, 0.80, -0.45, 0.60, -0.08, 0.35], 16.0)]:
ref = cos_prices(model, params, strikes,
np.full_like(strikes, tau0), spot0, rate0,
0.0, n_terms=512, truncation_width=width,
option_type="call", engine="numba")
for n_terms in [32, 64, 128, 256]:
num = cos_prices(model, params, strikes,
np.full_like(strikes, tau0), spot0, rate0,
0.0, n_terms=n_terms, truncation_width=width,
option_type="call", engine="numba")
t0 = time.perf_counter()
cpp = cos_prices(model, params, strikes,
np.full_like(strikes, tau0), spot0, rate0,
0.0, n_terms=n_terms, truncation_width=width,
option_type="call", engine="cpp")
runtime = time.perf_counter() - t0
cos_rows.append({"model": model, "n_terms": n_terms, "runtime_sec": runtime,
"items": len(strikes), "max_abs_error": float(np.max(np.abs(num - ref))),
"median_abs_error": float(np.median(np.abs(num - ref))),
"cpp_numba_diff": float(np.max(np.abs(num - cpp)))})
cos_validation = pd.DataFrame(cos_rows)
btc_fft_fit = pd.DataFrame(fft_fit_rows)
fft_validation.to_parquet(fft_path, index=False)
cos_validation.to_parquet(cos_path, index=False)
btc_fft_fit.to_parquet(fft_fit_path, index=False)
write_meta(fft_path, engine_settings, len(fft_validation))
btc_throughput = pd.concat([
fft_validation.assign(label=fft_validation["engine"] + " FFT", items=fft_validation["n"]),
cos_validation.assign(label="cpp COS", items=cos_validation["items"]),
], ignore_index=True)
btc_model_quality = final_comparison.copy()
tail_path = cache / "btc_tail_series_grid.parquet"
density_path = cache / "btc_density_grid.parquet"
tail_settings = {"version": 2, "jump": jump_winner, "sv": sv_winner, "dates": int(final_daily["date"].nunique())}
if tail_path.exists() and density_path.exists() and same_settings(tail_path, tail_settings):
tail_series = pd.read_parquet(tail_path)
density = pd.read_parquet(density_path)
else:
tail_rows = []
density_rows = []
for d in pd.DatetimeIndex(sorted(final_daily["date"].drop_duplicates()))[::max(1, len(final_daily["date"].drop_duplicates()) // 18)]:
day = quotes[quotes["date"].eq(pd.Timestamp(d).normalize())]
if day.empty:
continue
s = float(day["spot"].median())
r = float(day["rate"].median())
x = np.linspace(np.log(s) - 0.9, np.log(s) + 0.6, 520)
for model in ["bsm", jump_winner, sv_winner]:
source = final_daily[final_daily["model"].eq(model)]
source = source[source["date"].le(pd.Timestamp(d).normalize())].sort_values("date")
if source.empty:
continue
params = source.iloc[-1][[c for c in source.columns if c.startswith("p")]].dropna().to_numpy(float)
den = cos_density(model, params, x, s, r, 0.0, 45 / 365.0)
rel = np.exp(x) / s
probs = {}
for level in [0.90, 0.85, 0.80]:
mask = rel <= level
probs[level] = float(np.trapezoid(den[mask], x[mask])) if mask.any() else 0.0
tail_rows.append({"date": pd.Timestamp(d).normalize(), "model": model, "level": level,
"left_tail_probability": probs[level], "tail_probability": probs[level],
"tail_prob_80": probs[0.80] if level == 0.80 else np.nan})
if len(density_rows) < 5000:
density_rows.extend([{"date": pd.Timestamp(d).normalize(), "model": model, "x": float(xi),
"density": float(yi)} for xi, yi in zip(rel, den)])
tail_series = pd.DataFrame(tail_rows)
density = pd.DataFrame(density_rows)
tail_series.to_parquet(tail_path, index=False)
density.to_parquet(density_path, index=False)
write_meta(tail_path, tail_settings, len(tail_series))
tail_wide = tail_series.pivot_table(index=["date", "model"], columns="level", values="tail_probability").reset_index().rename(columns={0.9: "p90", 0.85: "p85", 0.8: "p80"})
tail_plot = tail_series[tail_series["level"].eq(0.80)].copy()
hedge_input = quotes[quotes["option_type"].astype(str).str.lower().str.startswith("p")
& quotes["dte_days"].between(21, 120)
& quotes["moneyness"].between(0.70, 0.98)
& quotes["relative_spread"].le(0.40)].copy()
hedge_prices_path = cache / "btc_hedge_prices_grid.parquet"
hedge_price_settings = {"version": 4, "rows": int(len(hedge_input)), "jump": jump_winner,
"sv": sv_winner, "engine": "cpp date batches", "scale": "usd floor v2"}
if hedge_prices_path.exists() and same_settings(hedge_prices_path, hedge_price_settings):
hedge_input = pd.read_parquet(hedge_prices_path)
else:
hedge_input["jump_price"] = np.nan
hedge_input["sv_price"] = np.nan
for price_col, model, daily, width in [("jump_price", jump_winner, jump_daily, 30.0 if jump_winner == "vg" else 16.0), ("sv_price", sv_winner, sv_daily, 16.0)]:
values = np.full(len(hedge_input), np.nan)
daily_model = daily[daily["model"].eq(model)].copy()
for d, idx in hedge_input.groupby("date", sort=True).groups.items():
source = daily_model[daily_model["date"].le(pd.Timestamp(d).normalize())].sort_values("date")
if source.empty:
continue
params = source.iloc[-1][[c for c in source.columns if c.startswith("p")]].dropna().to_numpy(float)
g = hedge_input.loc[idx]
values[hedge_input.index.get_indexer(idx)] = cos_prices(model, params, g["strike"].to_numpy(float),
g["tau"].to_numpy(float),
float(g["spot"].median()),
float(g["rate"].median()), 0.0, option_type="put",
n_terms=112, truncation_width=width, engine="cpp")
hedge_input[price_col] = values
hedge_input.to_parquet(hedge_prices_path, index=False)
write_meta(hedge_prices_path, hedge_price_settings, len(hedge_input))
hedge_input = hedge_input.dropna(subset=["jump_price", "sv_price"]).copy()
hedge_input["model_uncertainty"] = (hedge_input["jump_price"] - hedge_input["sv_price"]).abs() / hedge_input["spot"].clip(lower=1e-8)
def tail_for(model, d):
q = tail_wide[tail_wide["model"].eq(model)].copy()
q = q[q["date"].le(pd.Timestamp(d).normalize())].sort_values("date")
if q.empty:
return pd.Series({"p90": 0.12, "p85": 0.07, "p80": 0.04})
return q.iloc[-1][["p90", "p85", "p80"]].fillna({"p90": 0.12, "p85": 0.07, "p80": 0.04})
def budget_for(model, d, edge_ratio=0.0, relative_spread=0.0, base_bps=35.0):
q = tail_wide[tail_wide["model"].eq(model)].copy()
q = q[q["date"].le(pd.Timestamp(d).normalize())].sort_values("date")
if len(q) < 8:
return base_bps
hist = q["p80"].tail(60).dropna()
if hist.empty:
return base_bps
p = float(hist.iloc[-1])
edge = float(edge_ratio) if np.isfinite(edge_ratio) else 0.0
spread = float(relative_spread) if np.isfinite(relative_spread) else 1.0
add = 0.0
if spread <= 0.30 and p > float(hist.quantile(0.70)) and edge > -0.05:
add += 25.0
if spread <= 0.30 and p > float(hist.quantile(0.85)) and edge > 0.0:
add += 40.0
if spread <= 0.30 and p > float(hist.quantile(0.95)) and edge > 0.0:
add += 60.0
return min(base_bps + add, 120.0)
def with_tail_budget(q, model, gated=False):
out = q.copy()
vals = out["date"].map(lambda d: tail_for(model, d))
out["p90"] = [float(v["p90"]) for v in vals]
out["p85"] = [float(v["p85"]) for v in vals]
out["p80"] = [float(v["p80"]) for v in vals]
if gated:
edge = pd.to_numeric(out.get("model_edge", 0.0), errors="coerce").fillna(0.0) / pd.to_numeric(out.get("mid", 1.0), errors="coerce").replace(0.0, np.nan).fillna(1.0)
spread = pd.to_numeric(out.get("relative_spread", 1.0), errors="coerce").fillna(1.0)
out["budget_bps"] = [
budget_for(model, d, edge_ratio=e, relative_spread=sp)
for d, e, sp in zip(out["date"], edge, spread)
]
else:
out["budget_bps"] = 35.0
return out
fixed = fixed_delta_hedge_candidates(with_tail_budget(hedge_input, sv_winner, False), top_n=None, top_n_per_date=1)
jump_fixed = tail_hedge_candidates(with_tail_budget(hedge_input.assign(model_price=hedge_input["jump_price"], model_edge=hedge_input["jump_price"] - hedge_input["mid"]), jump_winner, False),
top_n=None, top_n_per_date=1, crash_level=0.85)
jump_gated = tail_hedge_candidates(with_tail_budget(hedge_input.assign(model_price=hedge_input["jump_price"], model_edge=hedge_input["jump_price"] - hedge_input["mid"]), jump_winner, True),
top_n=None, top_n_per_date=1, crash_level=0.85)
sv_fixed = tail_hedge_candidates(with_tail_budget(hedge_input.assign(model_price=hedge_input["sv_price"], model_edge=hedge_input["sv_price"] - hedge_input["mid"]), sv_winner, False),
top_n=None, top_n_per_date=1, crash_level=0.80)
sv_gated = tail_hedge_candidates(with_tail_budget(hedge_input.assign(model_price=hedge_input["sv_price"], model_edge=hedge_input["sv_price"] - hedge_input["mid"]), sv_winner, True),
top_n=None, top_n_per_date=1, crash_level=0.80)
risk_price = 0.5 * (hedge_input["jump_price"] + hedge_input["sv_price"])
risk_edge = risk_price - hedge_input["mid"] - 0.5 * (hedge_input["jump_price"] - hedge_input["sv_price"]).abs()
risk_frame = hedge_input[hedge_input["moneyness"].le(0.93)].copy()
risk_price = 0.5 * (risk_frame["jump_price"] + risk_frame["sv_price"])
risk_edge = risk_price - risk_frame["mid"] - 0.5 * (risk_frame["jump_price"] - risk_frame["sv_price"]).abs()
risk_frame = risk_frame.assign(model_price=risk_price, model_edge=risk_edge, calibration_penalty=0.0)
risk_fixed = tail_hedge_candidates(with_tail_budget(risk_frame, sv_winner, False), top_n=None, top_n_per_date=1, crash_level=0.80)
risk_gated = tail_hedge_candidates(with_tail_budget(risk_frame, sv_winner, True), top_n=None, top_n_per_date=1, crash_level=0.80)
hedge_candidates = pd.concat([fixed.assign(selector="fixed_delta_35bps"),
jump_fixed.assign(selector=f"{jump_winner}_35bps"),
jump_gated.assign(selector=f"{jump_winner}_tail_gated"),
sv_fixed.assign(selector=f"{sv_winner}_35bps"),
sv_gated.assign(selector=f"{sv_winner}_tail_gated"),
risk_fixed.assign(selector="model_risk_35bps"),
risk_gated.assign(selector="model_risk_tail_gated")], ignore_index=True)
schedules = {}
for selector, group in hedge_candidates.groupby("selector"):
sched = tail_hedge_schedule(group, spacing_days=21, budget_notional=1_000_000.0,
premium_budget_bps=35.0, budget_col="budget_bps", contract_multiplier=1.0,
label="tail_put")
schedules[selector] = sched
schedules["unhedged"] = pd.DataFrame(columns=["entry_date", "contract_key", "expiry", "strike", "option_type", "quantity", "label"])
underlying = spot["close"].reindex(pd.DatetimeIndex(sorted(quotes["date"].unique()))).ffill()
shares = 1_000_000.0 / float(underlying.dropna().iloc[0])
hedge_book = mark_book_for_schedules(quotes, schedules)
hedge_nav_path = cache / "btc_hedge_nav_grid.parquet"
hedge_trades_path = cache / "btc_hedge_trades_grid.parquet"
hedge_settings = {"version": 8, "schedule_rows": int(sum(len(x) for x in schedules.values())),
"mark_rows": int(len(hedge_book)), "base_budget_bps": 35.0, "tail_gated_budget": "price_aware",
"risk_penalty": 0.0, "risk_moneyness_max": 0.93, "spacing_days": 21}
if hedge_nav_path.exists() and hedge_trades_path.exists() and same_settings(hedge_nav_path, hedge_settings):
hedge_nav = pd.read_parquet(hedge_nav_path).set_index("date")
hedge_nav.index = pd.to_datetime(hedge_nav.index, errors="coerce").normalize()
if not hedge_nav.empty and float((hedge_nav.iloc[0] - 1_000_000.0).abs().max()) > 1e-8:
hedge_nav = pd.concat([pd.DataFrame({c: 1_000_000.0 for c in hedge_nav.columns}, index=[hedge_nav.index.min()]), hedge_nav])
hedge_trades = pd.read_parquet(hedge_trades_path)
hedge_results = {"nav": hedge_nav, "drawdown": hedge_nav / hedge_nav.cummax() - 1.0, "trades": hedge_trades}
else:
hedge_results = run_overlay_backtest(schedules, hedge_book, underlying,
initial_nav=1_000_000.0, contract_multiplier=1.0, shares=shares)
hedge_results["nav"].reset_index(names="date").to_parquet(hedge_nav_path, index=False)
hedge_results["trades"].to_parquet(hedge_trades_path, index=False)
write_meta(hedge_nav_path, hedge_settings, len(hedge_results["nav"]))
hedge_summary = overlay_summary(hedge_results)
trade_check = hedge_results["trades"].copy()
if trade_check.empty:
hedge_check = pd.DataFrame([{"rows": 0, "open_trades": 0, "mark_rows": len(hedge_book),
"max_quantity": 0.0, "median_open_price": np.nan, "premium_spent": 0.0}])
else:
open = trade_check[trade_check["event"].eq("open")].copy()
hedge_check = open.groupby("strategy", as_index=False).agg(open_trades=("event", "size"),
max_quantity=("quantity", "max"),
median_quantity=("quantity", "median"),
median_open_price=("price", "median"),
premium_spent=("cashflow", lambda x: -float(np.sum(x))),
median_moneyness=("moneyness", "median"),
median_dte=("dte_days", "median"))
hedge_check["mark_rows"] = len(hedge_book)
display(pd.DataFrame([{"btc_rows": len(quotes), "calibration_rows": len(calibration),
"calibration_dates": calibration["date"].nunique(), "avg_quotes_per_date": dates.mean(),
"jump_winner": jump_winner, "sv_winner": sv_winner}]).round(4))
display(selection_steps)
display(final_comparison.round(6))
display(hedge_check.round(4))
display(hedge_summary.round(6))
fig, axes = plt.subplots(3, 4, figsize=(22, 15))
axes = axes.ravel()
cos_convergence(axes[0], cos_validation, "COS Convergence")
fft_fit(axes[1], btc_fft_fit, "FFT Fit")
throughput(axes[2], btc_throughput, "Throughput")
model_quality(axes[3], btc_model_quality, "Model Quality")
daily_calibration_error(axes[4], jump_daily, "Jump RMSE")
daily_calibration_error(axes[5], sv_daily, "SV RMSE")
model_tournament(axes[6], final_comparison, "Model Tournament")
residual_smiles(axes[7], residuals.assign(x=np.arange(len(residuals))), "Residual Smile")
tail_density(axes[8], density.rename(columns={"x": "x"}), "Tail Density")
tail_probability_series(axes[9], tail_plot, "Tail Probability")
overlay_equity(axes[10], hedge_results, "Hedge NAV")
hedge_cost_payoff(axes[11], hedge_results["trades"], "Hedge Cashflow")
fig.tight_layout(pad=1.25, h_pad=1.0, w_pad=1.0)
plt.show()