1dd26342ca
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>
74 lines
3.1 KiB
Python
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
|