38 lines
1.3 KiB
Python
38 lines
1.3 KiB
Python
import numpy as np
|
|
import pandas as pd
|
|
|
|
|
|
TRADING_DAYS_PER_YEAR = 252
|
|
|
|
|
|
def summarize_equity_window(equity: pd.Series, strategy: str, window_years: int | float) -> dict:
|
|
"""Summarize a strategy equity curve over a trailing trading-day window."""
|
|
window_days = max(int(window_years * TRADING_DAYS_PER_YEAR), 1)
|
|
clean_equity = equity.dropna()
|
|
if len(clean_equity) < window_days + 1:
|
|
return {
|
|
"strategy": strategy,
|
|
"window_years": window_years,
|
|
"CAGR": np.nan,
|
|
"Sharpe": np.nan,
|
|
"MaxDD": np.nan,
|
|
"TotalRet": np.nan,
|
|
}
|
|
window_equity = clean_equity.tail(window_days + 1)
|
|
|
|
daily = window_equity.pct_change(fill_method=None).dropna()
|
|
total_ret = window_equity.iloc[-1] / window_equity.iloc[0] - 1
|
|
years = len(daily) / TRADING_DAYS_PER_YEAR
|
|
cagr = (window_equity.iloc[-1] / window_equity.iloc[0]) ** (1 / years) - 1 if years > 0 else np.nan
|
|
vol = daily.std() * np.sqrt(TRADING_DAYS_PER_YEAR)
|
|
sharpe = (daily.mean() * TRADING_DAYS_PER_YEAR) / vol if vol > 0 else 0.0
|
|
max_dd = (window_equity / window_equity.cummax() - 1).min()
|
|
return {
|
|
"strategy": strategy,
|
|
"window_years": window_years,
|
|
"CAGR": cagr,
|
|
"Sharpe": sharpe,
|
|
"MaxDD": max_dd,
|
|
"TotalRet": total_ret,
|
|
}
|