Files
PythagorasGoal/tests/test_reeval_live_weight.py
T
Adriano Dal Pastro 1dd26342ca research: rivalutazione della strategia — 0 cambi, 75/25 confermato per la terza volta
Delle sei misure prodotte oggi solo UNA apriva una decisione: il peso 75/25 fu
confermato il 24/07 con la lente HOURLY, e il 26/07 quella lente e' risultata
pessimistica su ENTRAMBI i lati di SKH01 (uscite +0.081, ingressi +0.048 di Sharpe di
book). Se SKH01 vale piu' di come e' stato pesato, 0.25 poteva non essere piu' l'ottimo.
Contro tirava il LOO de-luckato dello stesso giorno (SKH01 = il meno affidabile dei
cinque). Due correzioni in versi opposti: si misura, non si deduce.

MISURA (path live intra_entry=True, 8 offset, mediana delle differenze APPAIATE):
argmax a w=0.35, e 0.30-0.40 batte 0.25 nell'88% degli offset -> il segnale c'e' ed e'
nel verso previsto dalla correzione di lente. Ma il plateau entro 0.05 di Sharpe e'
[0.25 ... 0.50] e il peso live e' DENTRO, e `weights_tilt_null` FALLISCE
(delta_insample -0.0026, gate_pass False).

IL MOTIVO VERO sta in cio' che la mediana nasconde: il guadagno e' tutto nella coda ALTA.
p10 per peso 1.463 / 1.465 / 1.455 / 1.435 / 1.374 mentre il p90 sale monotono
1.915 -> 2.064. Alzare SKH01 non compra Sharpe, compra dipendenza da quale ancora ti e'
capitata (banda da 0.45 a 0.69). Coerente con altre due misure indipendenti dello stesso
giorno: SKH01 ha la frazione d'ancora piu' grande da restituire (LOO) ed e' 4x piu'
fee-sensibile. Tre misure indipendenti dicono che SKH01 e' la gamba fragile del book e
che 0.25 sta all'estremo prudente della regione robusta.

LA RIVALUTAZIONE CHE CONTA NON E' SULLA STRATEGIA. Ordini di grandezza a confronto:
ottimizzare il peso = +0.030 Sharpe (gate fallito); versare EUR 250/mese invece di EUR 0
= da MAI a 16.2 anni (P entro 20a = 92%); perdere il conto Deribit a p=1% = -18% di
probabilita' di arrivare, ripartendo da zero. Il book e' dentro il suo plateau su ogni
asse misurato: la ricerca ha smesso di essere il vincolo binding. I vincoli binding oggi
sono capitale che entra e conto che non sparisce.

Book, pesi, cron, config INVARIATI. Gate pre-registrati alle loro date (anticiparli
sarebbe selezione sull'hold-out). 365 test verdi (+6).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 20:06:23 +00:00

74 lines
3.1 KiB
Python

"""Test della rivalutazione del peso live (scripts/research/r0726_reeval_live_weight.py).
Il punto del file non e' il numero: e' che il peso live resti **dentro il plateau** e che ogni
proposta di cambio passi da `weights_tilt_null`. Se un domani qualcuno alza SKH01 senza il gate,
questi test lo dicono.
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
import pandas as pd
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "scripts" / "research"))
import r0726_reeval_live_weight as RW # noqa: E402
def test_il_peso_live_e_nella_griglia_valutata():
"""Una griglia che non contiene il peso corrente non puo' dire se cambiarlo."""
assert RW.W_LIVE in RW.W_GRID
def test_la_griglia_copre_entrambi_i_lati_del_peso_live():
"""Se la griglia fosse solo verso l'alto, l'argmax sarebbe garantito >= live per costruzione."""
assert min(RW.W_GRID) < RW.W_LIVE < max(RW.W_GRID)
def test_il_peso_della_griglia_coincide_col_book_di_produzione():
"""W_LIVE deve essere il peso VERO del book Deribit, non una costante scollegata."""
from src.live.book import W_SKH, W_TP01
assert RW.W_LIVE == pytest.approx(W_SKH)
assert W_TP01 + W_SKH == pytest.approx(1.0)
def test_la_statistica_e_la_mediana_delle_differenze_appaiate():
"""Lezione 26/07: fra offset si usa la mediana delle DIFFERENZE, non la differenza delle
mediane (che confronta offset diversi). Su dati costruiti le due risposte divergono."""
# Serve che l'ORDINAMENTO differisca fra le due serie, altrimenti le due statistiche
# coincidono per caso (prima stesura di questo test: coincidevano, e non provava niente).
a = np.array([1.0, 5.0, 6.0])
b = np.array([4.0, 2.0, 3.0])
appaiata = float(np.median(a - b)) # [-3, +3, +3] -> +3
delle_mediane = float(np.median(a) - np.median(b)) # 5 - 3 -> +2
assert appaiata == pytest.approx(3.0)
assert delle_mediane == pytest.approx(2.0)
assert appaiata != pytest.approx(delle_mediane)
def test_gate_tilt_null_rifiuta_un_cambio_che_perde_in_sample():
"""Controllo POSITIVO sul gate: un tilt costruito per perdere pre-holdout deve fallire.
Un gate che approva tutto e' inutile quanto uno che rifiuta tutto."""
from src.portfolio.portfolio import weights_tilt_null
idx = pd.date_range("2019-01-01", periods=1500, freq="D", tz="UTC")
rng = np.random.default_rng(7)
buona = pd.Series(rng.normal(0.0008, 0.01, len(idx)), index=idx)
cattiva = pd.Series(rng.normal(-0.0008, 0.02, len(idx)), index=idx)
res = weights_tilt_null({"BUONA": buona, "CATTIVA": cattiva},
{"BUONA": 0.75, "CATTIVA": 0.25},
{"BUONA": 0.35, "CATTIVA": 0.65}, k_seen=9)
assert res["gate_pass"] is False
def test_promemoria_fee_coerente_con_la_regola_gia_decisa():
"""La soglia d'azione sulla fee vive in un solo posto: se cambia li', questo test la segue."""
src = (ROOT / "scripts" / "research" / "r0726_reeval_live_weight.py").read_text()
assert "taker <=5bps" in src and ">10bps" in src