eth-usd-predictor / src /backtest_utils.py
Jony Ling
Clean initial commit for HF Space
b717bee
Raw
History Blame Contribute Delete
1.8 kB
"""Dual filter utilities — matches cleaned notebook implementation"""
import numpy as np
def dual_filter_backtest(preds, actuals, vols, ret30d, cost=0.0030, K=1.0,
vol_floor_pct=0.75, ret30d_thresh=0.05, extra_edge=0.0):
"""Exact dual filter logic from notebook"""
vols = np.asarray(vols, dtype=float)
ret30d = np.asarray(ret30d, dtype=float)
finite_vols = vols[np.isfinite(vols)]
vol_floor = np.nanpercentile(finite_vols, vol_floor_pct) if len(finite_vols) else np.nan
trade_mask = (
np.isfinite(vols) &
np.isfinite(ret30d) &
(np.abs(ret30d) > ret30d_thresh) &
(vols > vol_floor)
)
preds = np.asarray(preds, dtype=float)
actuals = np.asarray(actuals, dtype=float)
valid = np.isfinite(preds) & np.isfinite(actuals) & trade_mask
strong_signal = np.abs(preds) > (K * cost + extra_edge)
pos = np.where(valid & strong_signal, np.sign(preds), 0.0)
# Reuse notebook backtest result logic
changes = np.abs(np.diff(pos, prepend=0.0))
gross = pos * actuals
net = gross - changes * cost
eq = np.cumprod(1.0 + net) if len(net) else np.array([1.0])
n_trades = int(np.sum(changes > 0))
active = pos != 0
sharpe = np.mean(net) / np.std(net) * np.sqrt(365) if np.std(net) > 0 else 0.0
running_max = np.maximum.accumulate(eq)
max_dd = np.min((eq - running_max) / running_max) if len(eq) > 0 else 0.0
wr = np.mean(gross[active] > 0) if active.sum() else 0.0
return {
"sharpe": float(sharpe),
"cum_return": float(eq[-1] - 1),
"max_dd": float(max_dd),
"wr": float(wr),
"n_trades": n_trades,
"pct_active": float(active.mean()),
"eq": eq,
"net": net,
"pos": pos,
}