audit+fix: anti-look-ahead audit, migrate deployable config to >=12h
- trackD_lookahead_audit.py: relabel test (left==right, no labeling leak) + execution-lag stress -> our trend pipeline is CLEAN (4h Sharpe 1.36 robust to +1 bar lag, label-invariant) - ADOPT conservative conclusion: deploy at 12h (sub-12h: costs/overfit dominate, slight Sharpe bump unreliable). 12h: Sharpe 1.32, DD 13.3%, CAGR 16.2% ~ identical and robust - trend_portfolio: DEPLOY_TF=12h, resample_tf(rule); paper trader + tests on 12h - calendar research (NEGATIVE, both): trackF seasonality (spurious), trackG prior-levels (breakouts continue, fade dead; only long-drift survivor, redundant with TP01) - gitignore data/paper_trend runtime state
This commit is contained in:
@@ -10,16 +10,16 @@ sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from src.backtest.harness import load
|
||||
from src.strategies.trend_portfolio import (
|
||||
TrendPortfolio, CANONICAL, resample_4h, simple_returns, tsmom_blend)
|
||||
TrendPortfolio, CANONICAL, resample_tf, simple_returns, tsmom_blend)
|
||||
|
||||
|
||||
def _dfs():
|
||||
return {a: resample_4h(load(a, "1h")) for a in ("BTC", "ETH")}
|
||||
return {a: resample_tf(load(a, "1h")) for a in ("BTC", "ETH")}
|
||||
|
||||
|
||||
def test_no_lookahead_target_is_causal():
|
||||
"""target_series[:k] non deve cambiare se aggiungo barre future."""
|
||||
df = resample_4h(load("BTC", "1h"))
|
||||
df = resample_tf(load("BTC", "1h"))
|
||||
tp = TrendPortfolio(**CANONICAL)
|
||||
full = tp.target_series(df)
|
||||
k = len(df) - 500
|
||||
@@ -41,7 +41,7 @@ def test_canonical_backtest_is_profitable_and_robust():
|
||||
|
||||
|
||||
def test_long_only_never_short():
|
||||
df = resample_4h(load("ETH", "1h"))
|
||||
df = resample_tf(load("ETH", "1h"))
|
||||
tp = TrendPortfolio(**CANONICAL) # long_only=True
|
||||
assert (tp.target_series(df) >= 0).all()
|
||||
|
||||
@@ -59,12 +59,11 @@ def test_paper_advance_matches_backtest_slice():
|
||||
combo = 0.5 * J["BTC"].values + 0.5 * J["ETH"].values
|
||||
# equity sull'ultimo tratto (skip warmup)
|
||||
tail = combo[-500:]
|
||||
eq_ref = np.cumprod(1.0 + np.clip(tail, -0.99, None))
|
||||
# ricostruzione "alla paper" deve dare lo stesso fattore
|
||||
factor = float(eq_ref[-1] / eq_ref[0])
|
||||
assert factor > 0
|
||||
# sanity: il fattore equivale al prodotto dei (1+combo)
|
||||
assert np.isclose(factor, np.prod(1.0 + np.clip(tail, -0.99, None)) / (1.0), rtol=1e-9)
|
||||
steps = 1.0 + np.clip(tail, -0.99, None)
|
||||
eq_ref = np.cumprod(steps)
|
||||
# il loop paper accumula moltiplicando i (1+net) barra per barra -> stesso prodotto
|
||||
assert np.isclose(eq_ref[-1], np.prod(steps), rtol=1e-9)
|
||||
assert eq_ref[-1] > 0
|
||||
|
||||
|
||||
def test_tsmom_blend_range():
|
||||
|
||||
Reference in New Issue
Block a user