from __future__ import annotations import math import pandas as pd def max_drawdown(equity: pd.Series) -> float: if equity.empty: return 0.0 running_max = equity.cummax() drawdown = equity / running_max - 1 return float(drawdown.min()) def backtest_realized_vol_signal( prices: pd.Series, short_window: int = 10, long_window: int = 30, holding_days: int = 5, signal: str = "long_vol", ) -> dict: close = prices.dropna().astype(float) returns = close.pct_change().dropna() short_rv = returns.rolling(short_window).std() * math.sqrt(252) long_rv = returns.rolling(long_window).std() * math.sqrt(252) trades = [] equity = [1.0] index = 0 dates = list(returns.index) while index + holding_days < len(returns): current_date = dates[index] if pd.isna(short_rv.iloc[index]) or pd.isna(long_rv.iloc[index]): index += 1 equity.append(equity[-1]) continue vol_expanding = short_rv.iloc[index] > long_rv.iloc[index] enter = vol_expanding if signal == "long_vol" else not vol_expanding if not enter: index += 1 equity.append(equity[-1]) continue period_returns = returns.iloc[index + 1:index + 1 + holding_days] realized_move = float(period_returns.abs().sum()) signed_pnl = realized_move if signal == "long_vol" else -realized_move equity.append(equity[-1] * (1 + signed_pnl)) trades.append( { "entry_date": str(current_date), "exit_date": str(dates[index + holding_days]), "short_rv": float(short_rv.iloc[index]), "long_rv": float(long_rv.iloc[index]), "realized_abs_move": realized_move, "pnl_proxy": signed_pnl, } ) index += holding_days equity_series = pd.Series(equity) wins = [trade for trade in trades if trade["pnl_proxy"] > 0] return { "signal": signal, "short_window": short_window, "long_window": long_window, "holding_days": holding_days, "trade_count": len(trades), "win_rate": len(wins) / len(trades) if trades else 0.0, "total_return_proxy": float(equity_series.iloc[-1] - 1) if not equity_series.empty else 0.0, "max_drawdown_proxy": max_drawdown(equity_series), "avg_trade_pnl_proxy": ( sum(trade["pnl_proxy"] for trade in trades) / len(trades) if trades else 0.0 ), "trades": trades[:100], "limitations": [ "This is an underlying-price realized-volatility signal backtest, not a true option PnL backtest.", "It does not use historical option-chain prices, bid/ask spreads, margin, assignment, or delta hedging costs.", ], }