"""Simple strategy performance metrics. Scores a ``signal`` column against forward returns: total return, annualized Sharpe, win rate and max drawdown. """ import numpy as np def compute_performance_metrics(data, signal_col="signal", forecast_periods=12, annualization=24 * 365, verbose=True): """Compute strategy performance from a signal column and forward returns. Parameters ---------- data : pd.DataFrame Must contain a ``close`` column and ``signal_col`` (values in {-1, 0, 1}). forecast_periods : int Holding horizon (in bars) used to compute the forward return. annualization : float Factor applied under the square root when annualizing the Sharpe ratio. Returns ------- dict total_return, sharpe_ratio, win_rate, max_drawdown and the augmented frame. """ df = data.copy() # Forward return over the forecast horizon. df["future_return"] = df["close"].pct_change(periods=forecast_periods).shift(-forecast_periods) # Strategy return: signal * future return (long: +return, short: -return). df["strategy_return"] = df[signal_col] * df["future_return"] df = df.dropna(subset=["strategy_return"]) # Cumulative return. df["cumulative_return"] = (1 + df["strategy_return"]).cumprod() - 1 total_return = df["cumulative_return"].iloc[-1] if len(df) else float("nan") sharpe_ratio = ( df["strategy_return"].mean() / df["strategy_return"].std() * np.sqrt(annualization) if df["strategy_return"].std() else float("nan") ) active = df[df["strategy_return"] != 0] win_rate = len(df[df["strategy_return"] > 0]) / len(active) if len(active) else float("nan") max_drawdown = (df["cumulative_return"].cummax() - df["cumulative_return"]).max() if verbose: print(f"Total Return: {total_return:.2%}") print(f"Sharpe Ratio: {sharpe_ratio:.2f}") print(f"Win Rate: {win_rate:.2%}") print(f"Max Drawdown: {max_drawdown:.2%}") return { "total_return": total_return, "sharpe_ratio": sharpe_ratio, "win_rate": win_rate, "max_drawdown": max_drawdown, "data": df, }