Files
PythagorasGoal/tests/test_prevday_breakout.py
Adriano Dal Pastro 5cce7acfe1 live(monitor): prevday-breakout in FORWARD-MONITOR (paper, non deploy)
Il lead ortogonale a TP01 sopravvissuto all'onda intraday entra in forward-monitor (stesso
trattamento di XS01 STAT-MODE / STA05), NON in esecuzione reale.

- src/strategies/prevday_breakout.py: segnale CONGELATO (params fissi anchor=1, k=0.30, simmetrico,
  vol-target 0.20/30/2.0), self-contained. Bit-identico all'agent di ricerca (max diff 0.0):
  BTC full Sh 1.18/hold 0.92, ETH 1.09/1.42; marginal ADDS, earns_slot, corr_hold -0.01, non-hedge.
- scripts/live/paper_prevday.py: forward-only paper, traccia DUE libri — MODELED ($2000 continuo)
  e REAL-$600 (salta i ribilanciamenti < min-order $5) -> il gap = haircut di fill reale che lo
  scettico aveva segnalato. Inizializzato forward-only da oggi.
- cron_daily.sh: avanza il monitor ogni giorno.
- test: param congelati + causale + bounded + long-short. Suite intera verde.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 15:37:41 +00:00

34 lines
1.2 KiB
Python

"""Locks the FROZEN prevday-breakout lead (src/strategies/prevday_breakout.py) so the
forward-monitor track record is against an immutable signal. Pins: causal, bounded,
non-trivial (long & short), and the frozen params unchanged.
"""
import sys
from pathlib import Path
import numpy as np
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from src.backtest.harness import load # noqa: E402
from src.strategies import prevday_breakout as pb # noqa: E402
def test_frozen_params_unchanged():
assert (pb.ANCHOR_DAYS, pb.BUFFER_K, pb.ALLOW_SHORT) == (1, 0.30, True)
assert (pb.TARGET_VOL, pb.VOL_WIN_DAYS, pb.LEV_CAP) == (0.20, 30, 2.0)
def test_target_causal_bounded_nontrivial():
df = load("BTC", "1h")
t = pb.target(df)
assert len(t) == len(df)
assert np.all(np.isfinite(t))
assert np.all(np.abs(t) <= pb.LEV_CAP + 1e-9)
# symmetric book actually takes BOTH sides over history
assert (t > 0).any() and (t < 0).any()
# CAUSAL: recomputing on a truncated prefix matches the full run on its tail
cut = int(len(df) * 0.85)
sub = pb.target(df.iloc[:cut].reset_index(drop=True))
assert np.max(np.abs(sub[cut - 120:cut] - t[cut - 120:cut])) < 1e-9