File size: 1,800 Bytes
b717bee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
"""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,
    }