research(gtaa): il gate (A) non misurava cio' che dichiarava — e TLT ha 13.5 anni in meno
Il test del gate sulla banda GTAA01 aveva smesso di passare senza che il codice fosse cambiato (data/raw/ e' gitignored, IB rivede ADJUSTED_LAST all'indietro ogni notte). Invece di allentare la soglia, misurata la risoluzione del criterio. `rank_in <= rank_oos` lo passano 14/30 celle (47%) PER COSTRUZIONE: la somma dei ranghi e' la stessa nelle due finestre. E sulla proposta si decideva su 0.00116 di Sharpe contro uno spread di griglia di 0.3124. Il 27/07 quel criterio passava, e passava per caso. Criterio sostituito con quello decidibile: la proposta e' una BANDA (la cadenza settimanale e' gia' produzione), quindi a cadenza fissa la banda scelta sui soli dati pre-2015 e' 25% = la proposta, margine +0.0151 (13x il vecchio); chi avesse scelto sull'hold-out avrebbe preso 40%. Controllo positivo incluso. Regge sull'universo coerente a 5 gambe. TROVATO PER STRADA: TLT parte dal 2016-02-03 invece che dalla quotazione (2002-07-22) → GTAA01 gira su CINQUE gambe prima del 2016, e quella assente e' la gamba obbligazionaria. Non e' di oggi (cosi' dal primo giro nel cron log del 24/06, prima della validazione) e non e' un fetch da rifare: una richiesta retro esplicita a IB ritorna 0 barre. Nessuna certificazione l'aveva visto perche' tutte guardano DENTRO la serie: una serie troncata e' integra, senza gap, senza spike, senza duplicati. Cablate due guardie in fetch_ib_equities.certify — TRONCATO (storia persa vs disco; il file NON viene sovrascritto ne' fuso, ADJUSTED_LAST e' ri-aggiustato all'indietro e il giunto creerebbe un salto) e STORIA-CORTA (parte dopo la quotazione, con distinzione dal tetto della richiesta 30Y). Controlli negativi obbligatori: giro normale, serie al cap, ETF giovane, simbolo fuori tabella. Produzione INVARIATA: REBAL_BAND_USD resta $50, GTAA01 resta non deployabile (PRIIPs). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,9 +4,19 @@ Congela i tre esiti del gate e — soprattutto — l'ANNUALIZZAZIONE. Una serie
|
||||
passata a un annualizzatore a 365 esce con lo Sharpe ×1.20 e il CAGR ×1.45: e' l'errore del
|
||||
25/07, ed e' l'unico difetto qui che produrrebbe numeri sbagliati ma plausibili.
|
||||
|
||||
`test_la_proposta_non_e_selezionata_sull_hold_out` e' il test di metodo: se un giorno la cella
|
||||
proposta risultasse in rango MIGLIORE sull'hold-out che in-sample, sarebbe la firma della
|
||||
selezione-sull'hold-out e la validazione andrebbe rifatta.
|
||||
Il test di METODO e' `test_la_banda_proposta_e_quella_scelta_al_buio` (+ il suo controllo
|
||||
positivo): se un giorno la banda che si sceglie sui soli dati pre-2015 non fosse piu' il 25%, la
|
||||
validazione del 27/07 andrebbe rifatta su quella cella.
|
||||
|
||||
⚠️ 2026-08-07 — CRITERIO SOSTITUITO. Fino a oggi il gate (A) era congelato come
|
||||
`rank_in <= rank_oos` sulla proposta, e ha iniziato a fallire (9ª in-sample, 8ª sull'hold-out)
|
||||
senza che nessuno toccasse il codice: `data/raw/` e' gitignored e IB rivede `ADJUSTED_LAST`
|
||||
all'indietro ogni notte. Misurato in `scripts/research/r0807_gtaa_gate_resolution.py`, quel
|
||||
criterio non poteva reggere: lo passa ~meta' della griglia PER COSTRUZIONE (la somma dei ranghi
|
||||
e' la stessa nelle due finestre) e sulla proposta si decideva su 0.001 di Sharpe contro uno
|
||||
spread di griglia di 0.31. Il motivo del ritiro e' congelato in
|
||||
`test_il_confronto_fra_ranghi_e_una_moneta_ed_e_per_questo_che_e_stato_RITIRATO` — non l'esito,
|
||||
che dipende dai dati e si muove da solo.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -76,9 +86,8 @@ def test_la_produzione_usa_ancora_la_banda_ASSOLUTA():
|
||||
# ===========================================================================
|
||||
# (A) selezione — il gate di metodo
|
||||
# ===========================================================================
|
||||
def test_la_proposta_non_e_selezionata_sull_hold_out():
|
||||
"""Rango in-sample <= rango hold-out: la proposta non migliora passando all'hold-out.
|
||||
Il contrario sarebbe la firma della selezione-sull'hold-out."""
|
||||
@pytest.fixture(scope="module")
|
||||
def griglia_piena():
|
||||
rows = []
|
||||
for every in BG.EVERY_GRID:
|
||||
for frac in BG.FRAC_GRID:
|
||||
@@ -87,12 +96,62 @@ def test_la_proposta_non_e_selezionata_sull_hold_out():
|
||||
sh_in=BG.met(s.loc[: BG.HOLDOUT])["sharpe"],
|
||||
sh_oos=BG.met(s.loc[BG.HOLDOUT:])["sharpe"]))
|
||||
G = pd.DataFrame(rows)
|
||||
p = G[(G["every"] == BG.PROPOSTA[0]) & (G["frac"] == BG.PROPOSTA[1])].iloc[0]
|
||||
rank_in = int((G["sh_in"] > p["sh_in"]).sum()) + 1
|
||||
rank_oos = int((G["sh_oos"] > p["sh_oos"]).sum()) + 1
|
||||
assert rank_in <= rank_oos, (
|
||||
f"la proposta e' {rank_oos}a sull'hold-out ma {rank_in}a in-sample: "
|
||||
"sta meglio dove non doveva essere guardata")
|
||||
G["rank_in"] = G["sh_in"].rank(ascending=False, method="min").astype(int)
|
||||
G["rank_oos"] = G["sh_oos"].rank(ascending=False, method="min").astype(int)
|
||||
return G
|
||||
|
||||
|
||||
def test_la_banda_proposta_e_quella_scelta_al_buio(griglia_piena):
|
||||
"""Il gate (A), nella forma decidibile (2026-08-07).
|
||||
|
||||
La proposta del 27/07 e' una BANDA: la cadenza settimanale e' gia' `REBAL_EVERY=5` in
|
||||
produzione e non era in discussione. Quindi la domanda sulla provenienza della scelta e'
|
||||
«a cadenza di produzione, quale banda si sceglie guardando SOLO il pre-2015?».
|
||||
"""
|
||||
G = griglia_piena
|
||||
riga = G[G["every"] == BG.PROPOSTA[0]]
|
||||
blind = riga.sort_values("sh_in", ascending=False).iloc[0]
|
||||
assert blind["frac"] == BG.PROPOSTA[1], (
|
||||
f"al buio si sceglierebbe la banda {blind['frac']:.0%}, non {BG.PROPOSTA[1]:.0%}: "
|
||||
"la validazione del 27/07 andrebbe rifatta su quella cella")
|
||||
|
||||
|
||||
def test_chi_guardasse_l_hold_out_sceglierebbe_una_banda_DIVERSA(griglia_piena):
|
||||
"""CONTROLLO POSITIVO del test precedente. Se le due selezioni coincidessero, «la proposta e'
|
||||
la scelta in-sample» sarebbe vero anche per una proposta selezionata sull'hold-out, e il gate
|
||||
non direbbe nulla. Un gate che non puo' fallire e' indistinguibile da uno rotto."""
|
||||
G = griglia_piena
|
||||
riga = G[G["every"] == BG.PROPOSTA[0]]
|
||||
hold = riga.sort_values("sh_oos", ascending=False).iloc[0]
|
||||
assert hold["frac"] != BG.PROPOSTA[1], (
|
||||
"in-sample e hold-out scelgono la stessa banda: su questa griglia il gate (A) non ha "
|
||||
"potenza e non va citato come validazione")
|
||||
|
||||
|
||||
def test_il_margine_del_blind_non_e_un_arrotondamento(griglia_piena):
|
||||
"""La ragione per cui questo criterio ha sostituito il confronto fra ranghi: decide su
|
||||
~0.015 di Sharpe invece che su ~0.001. Se il margine scendesse sotto un centesimo, anche
|
||||
questo criterio smetterebbe di essere una misura e andrebbe ripensato a sua volta."""
|
||||
G = griglia_piena
|
||||
riga = G[G["every"] == BG.PROPOSTA[0]]
|
||||
blind = riga.sort_values("sh_in", ascending=False).iloc[0]
|
||||
secondo = riga[riga["frac"] != blind["frac"]]["sh_in"].max()
|
||||
assert blind["sh_in"] - secondo >= 0.01
|
||||
|
||||
|
||||
def test_il_confronto_fra_ranghi_e_una_moneta_ed_e_per_questo_che_e_stato_RITIRATO(griglia_piena):
|
||||
"""Congela il MOTIVO del ritiro, non l'esito (che dipende dai dati e cambia da solo).
|
||||
|
||||
Fino al 2026-08-07 il gate (A) asseriva `rank_in <= rank_oos` sulla proposta. Quel criterio
|
||||
non puo' distinguere una proposta onesta da una selezionata sull'hold-out: la somma dei
|
||||
ranghi e' la stessa nelle due finestre, quindi lo passa circa META' della griglia per
|
||||
costruzione, qualunque cosa contenga. Se qualcuno lo rimettesse, questo test spiega perche' no.
|
||||
"""
|
||||
G = griglia_piena
|
||||
passa = int((G["rank_in"] <= G["rank_oos"]).sum())
|
||||
assert 0.3 * len(G) <= passa <= 0.7 * len(G), (
|
||||
f"passa {passa}/{len(G)} celle: se fosse molto lontano da meta', il criterio "
|
||||
"porterebbe informazione e questa motivazione andrebbe riletta")
|
||||
|
||||
|
||||
def test_l_hold_out_usato_e_quello_documentato_di_gtaa01():
|
||||
|
||||
Reference in New Issue
Block a user