research(wave-0822): GATE PROP-01 gamba (b) PASS 23/23 — e la distorsione su SKH01 dichiarata due volte e' refutata; P(50/g) 7,8% -> 4,4%
This commit is contained in:
@@ -697,6 +697,7 @@ stessi hanno nominato senza poterli eseguire**. Stesso contratto di consegna, st
|
|||||||
| 29 | PROP-RECAL | J rifatto con XS01 dalla storia lunga: il canale funded si apre? | **LEAD ridimensionato ~5x** — 42% → **7,8%** |
|
| 29 | PROP-RECAL | J rifatto con XS01 dalla storia lunga: il canale funded si apre? | **LEAD ridimensionato ~5x** — 42% → **7,8%** |
|
||||||
| 30 | LISTINO | `GATE PROP-01` gamba (a): la firm lista >=10 delle 13 gambe con short? | **PASS 13/13** — verificato sul venue, non sul sito |
|
| 30 | LISTINO | `GATE PROP-01` gamba (a): la firm lista >=10 delle 13 gambe con short? | **PASS 13/13** — verificato sul venue, non sul sito |
|
||||||
| 31 | GATE-RECON | cosa direbbero i 3 gate pre-registrati se le serie forward fossero riparate | **1 gate su 3 ribaltato** — e la premessa e' CONFERMATA |
|
| 31 | GATE-RECON | cosa direbbero i 3 gate pre-registrati se le serie forward fossero riparate | **1 gate su 3 ribaltato** — e la premessa e' CONFERMATA |
|
||||||
|
| 32 | SKH-ANCHOR-PROP | `GATE PROP-01` gamba (b), datata 31/10: girata oggi | **PASS 23/23** — e il numero scende ancora, a **4,4%** |
|
||||||
| 22 | MAKER | l'esecuzione passiva e' una fonte di ritorno, al netto del costo di non essere eseguiti? | **SCARTATO** — il segno dipende da `<` contro `<=` |
|
| 22 | MAKER | l'esecuzione passiva e' una fonte di ritorno, al netto del costo di non essere eseguiti? | **SCARTATO** — il segno dipende da `<` contro `<=` |
|
||||||
| 23 | BOCPD | un rilevatore di cambio di regime vero batte il miglior lookback COSTANTE? | **SCARTATO** — filone chiuso definitivamente |
|
| 23 | BOCPD | un rilevatore di cambio di regime vero batte il miglior lookback COSTANTE? | **SCARTATO** — filone chiuso definitivamente |
|
||||||
| 24 | CRITICO | cosa NON e' stato misurato, e quale singola misura mancante vale di piu' | **3 correzioni all'ondata**, 1 regola mia ritirata |
|
| 24 | CRITICO | cosa NON e' stato misurato, e quale singola misura mancante vale di piu' | **3 correzioni all'ondata**, 1 regola mia ritirata |
|
||||||
@@ -1431,3 +1432,59 @@ riparando oggi, il 27/09 si leggerebbe una finestra **piu' corta ma VERA** (SE(S
|
|||||||
⚠️ `implausible_sharpe` **girato per la prima volta sulle serie forward**: chiede n≥30, quindi **2
|
⚠️ `implausible_sharpe` **girato per la prima volta sulle serie forward**: chiede n≥30, quindi **2
|
||||||
serie su 3 non sono giudicabili**, e su statarb dice "ok" **in entrambe le lenti** — **il gate non
|
serie su 3 non sono giudicabili**, e su statarb dice "ok" **in entrambe le lenti** — **il gate non
|
||||||
vede un segno ribaltato**.
|
vede un segno ribaltato**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 32 — SKH-ANCHOR-PROP (`GATE PROP-01` gamba (b), chiusa oggi invece che il 31/10)
|
||||||
|
|
||||||
|
`scripts/research/r0822c_skh_anchor_prop.py` (391 s, nessuna rete). **5.127 celle**, contate al
|
||||||
|
rialzo, con **griglia congiunta PIENA 23 offset × 10 fasi = 230 configurazioni** × 8 leve — **non** un
|
||||||
|
campionamento e **non** una somma di marginali (lezione `r0726_loo_deluck`: *la somma delle fortune
|
||||||
|
marginali non e' la mediana congiunta*).
|
||||||
|
|
||||||
|
📌 **La data non aspettava un dato: aspettava che qualcuno misurasse il costo.** Il gate descriveva il
|
||||||
|
lavoro come *"rifare `pc._hourly_legs` ai 23 offset"*, che costa **0,6 s per offset**. I "~9 min per
|
||||||
|
asset×offset" sono il costo di `r0726_skh_sigcache` — **una computazione che questa misura non usa**.
|
||||||
|
*Secondo gate d'attesa eliminato oggi dopo quello di XS01-OOS, e per la stessa ragione: la stima del
|
||||||
|
costo non era stata fatta.*
|
||||||
|
|
||||||
|
**`GATE PROP-01 GAMBA (b): PASS`** — delta appaiato mediano sui 23 offset **+0,1162**, banda p10-p90
|
||||||
|
**[+0,087, +0,152]**, **23/23 offset positivi**, **230/230 celle congiunte positive** (soglia
|
||||||
|
pre-registrata +0,05). Regione: 38/38/25 +0,116 · **38/25/38 +0,120** · 25/38/38 +0,109.
|
||||||
|
✅ **Riproduzione 7/7 esatta** dei numeri di PROP-RECAL all'ancora canonica; **bit-exact 3/3 a 0,0**
|
||||||
|
(gambe orarie contro `pc._hourly_legs()` su 65.219 ore; costruttore di libro contro `PR.rg` a due
|
||||||
|
pesi), piu' la guardia "confini HTF ⊂ LTF" a ogni offset e l'assert che **l'indice giornaliero e'
|
||||||
|
invariante all'offset** — senza il quale il bootstrap non sarebbe appaiato.
|
||||||
|
|
||||||
|
🚨 **MA il numero operativo scende di nuovo:** l'offset canonico 0 e' al **91° pctl dei 23** e regala
|
||||||
|
**+0,059 sul delta** e **+0,112 sul livello J**. J del candidato: canonico 0,453 → **mediana d'ancora
|
||||||
|
~0,347**; maxDD mediano **−10,8%** contro −9,6% pubblicato.
|
||||||
|
**P(≥50 €/g) da €600 in 36 mesi = mediana 4,4% [banda d'ancora 2,5%–8,2%], P(zero) 35,3%** — contro
|
||||||
|
7,8% / 25,5% di stamattina. ✅ All'ancora canonica la macchina **ridice 7,7%**: il crollo e' **fortuna
|
||||||
|
d'ancora, non disaccordo fra implementazioni**.
|
||||||
|
|
||||||
|
📌 **IL RISULTATO INTERPRETATIVO, piu' importante del PASS: la distorsione dichiarata due volte —
|
||||||
|
*"l'ottimo si appoggia a SKH01, lo sleeve con la fortuna d'ancora piu' grande da restituire"* — e'
|
||||||
|
MISURATA E REFUTATA nella sua forma forte.** A **`38/25/38` SKH01 sta al medesimo peso del LIVE
|
||||||
|
(25%)**, la sua ancora si cancella quasi esattamente nella differenza appaiata, e li' il delta e' **il
|
||||||
|
piu' alto dei tre (+0,120)** con la banda **piu' stretta** (ampiezza 0,032 contro 0,065 e 0,057 delle
|
||||||
|
due che portano SKH a 38%). **L'ampiezza della banda scala col differenziale di peso su SKH01** — come
|
||||||
|
l'agente aveva previsto **prima** di misurare. **Cio' che paga e' XS01, non un sovrappeso dello sleeve
|
||||||
|
fragile.**
|
||||||
|
|
||||||
|
⚠️ **Due cose che il PASS non deve nascondere.**
|
||||||
|
1. **Il gate e' su una DIFFERENZA, il numero operativo e' un LIVELLO:** nella prima l'ancora si
|
||||||
|
cancella in parte, nel secondo **per niente** → passano insieme e si muovono in modo diverso
|
||||||
|
(delta −33%, P(≥50/g) −44%). *Un gate superato non e' un numero confermato.*
|
||||||
|
2. **Il surrogato del 22/08 e' finito vicino al vero per la CANCELLAZIONE DI DUE ASIMMETRIE:**
|
||||||
|
ricalcolato a peso fisso il taglio del drift da' **+0,076/+0,045** (sottostima il vero +0,116), ma
|
||||||
|
PROP-RECAL lo calcolava **ri-cercando l'argmax sui 10 pesi migliori** mentre il LIVE resta
|
||||||
|
inchiodato a 75/25 — sotto stress il candidato puo' scappare da SKH01 e il LIVE no → si gonfia a
|
||||||
|
+0,124/+0,113. **Il numero era giusto, il metodo no.**
|
||||||
|
|
||||||
|
⚠️ **Sensibilita' dichiarata:** il de-luck ×0,89 **su questa dimensione e' un doppio conteggio** (fu
|
||||||
|
misurato proprio come mediana-ancore/canonica del drift). A ×1,00 il delta sale a **+0,148**, 23/23 →
|
||||||
|
**tenerlo e' conservativo**.
|
||||||
|
**Smentitore residuo, fuori dal perimetro della gamba (b):** l'ancora **giornaliera di TP01** (24 ore),
|
||||||
|
che questa misura **non tocca** e che resta canonica 00:00 UTC = **la piu' fortunata delle 24** per
|
||||||
|
l'hold-out di TP01. E' la **terza** dimensione, e l'agente la dichiara.
|
||||||
|
|||||||
@@ -0,0 +1,604 @@
|
|||||||
|
"""r0822c_skh_anchor_prop — LA GAMBA (b) DEL `GATE PROP-01`, GIRATA DAVVERO (2026-08-22, ondata 3).
|
||||||
|
|
||||||
|
COSA CHIUDE
|
||||||
|
-----------
|
||||||
|
`r0822b_prop_recal` (RESULTS-0822 §29) ha registrato un gate a tre gambe e ne ha DATATA una al
|
||||||
|
**2026-10-31**:
|
||||||
|
|
||||||
|
(b) BANDA D'ANCORA DI SKH01 SOTTO LA LENTE PROP — MISURA. Data: 2026-10-31.
|
||||||
|
Costo: rifare `pc._hourly_legs` ai 23 offset della griglia 230m/690m e rigirare §3.
|
||||||
|
Metrica: MEDIANA DELLE DIFFERENZE APPAIATE J(peso nella regione w_XS 25-38%) -
|
||||||
|
J(libro LIVE 75/25) sui 23 offset, finestra W-LONG. PASS se > +0.05.
|
||||||
|
Surrogato di oggi (taglio del drift SKH x0.50 / x0.33): +0.124 e +0.113.
|
||||||
|
Mi aspetto un PASS e lo dichiaro prima.
|
||||||
|
|
||||||
|
E' l'unica distorsione che il progetto ha dichiarato DUE VOLTE (PROP-ALLOC §1 e PROP-RECAL §29)
|
||||||
|
e mai rimossa: *l'ottimo gira sull'ancora canonica e si appoggia a SKH01, che il progetto misura
|
||||||
|
come lo sleeve con la fortuna d'ancora piu' grande da restituire*.
|
||||||
|
|
||||||
|
⚠️ **LA DATA ERA UN BUDGET, E IL BUDGET ERA ANCORATO ALLA COMPUTAZIONE SBAGLIATA.**
|
||||||
|
Il costo temuto ("~9 min per asset x offset") e' quello di `r0726_skh_sigcache`, che ricostruisce
|
||||||
|
il segnale intra-bin a OGNI osservazione oraria dentro ogni bin 230m (~82.000 righe per cella).
|
||||||
|
Questa misura non ne ha bisogno: serve `pc._hourly_legs` all'offset, cioe' resample + entries +
|
||||||
|
MTM per-trade, **misurato 0,6 s per offset** (entrambi gli asset). I 23 offset costano ~15 s.
|
||||||
|
La gamba (b) non aspettava un dato ne' una macchina: aspettava che qualcuno misurasse il costo.
|
||||||
|
|
||||||
|
IL PROBLEMA VERO: LE ANCORE SONO DUE
|
||||||
|
------------------------------------
|
||||||
|
SKH01 ha 23 offset di griglia (230m/690m, uno ogni 30m su [0,690)); XS01 ha 10 fasi del ciclo
|
||||||
|
H=10. PROP-RECAL ha girato la banda di FASE (a offset SKH canonico); PROP-ALLOC nessuna delle due.
|
||||||
|
La lezione registrata di `r0726_loo_deluck` e' che **la somma delle fortune marginali NON e' la
|
||||||
|
mediana congiunta** (sbagliava di +0,82 di Sharpe hold-out).
|
||||||
|
|
||||||
|
**SCELTA DICHIARATA: griglia congiunta PIENA 23 x 10 = 230 configurazioni**, non un campionamento
|
||||||
|
e non una somma di marginali. E' possibile perche' il costo e' stato MISURATO prima di scegliere
|
||||||
|
(~0,06 s per coppia config-leva): con 23 offset x 10 fasi x 8 leve la spesa e' ~110 s.
|
||||||
|
**Perche' questa scelta NON e' quella che mi conviene:** la scelta comoda sarebbe fissare la fase
|
||||||
|
di XS01 a 0, che PROP-RECAL misura fortunata di +0,019 di J; il collasso per MEDIANA sulle fasi la
|
||||||
|
restituisce. Entrambe sono riportate, cosi' si vede da che parte taglia.
|
||||||
|
|
||||||
|
CONVENZIONI (tutte ereditate, nessuna nuova)
|
||||||
|
--------------------------------------------
|
||||||
|
* Statistica = **MEDIANA DELLE DIFFERENZE APPAIATE**, mai differenza delle mediane (26/07: con
|
||||||
|
quella correzione un verdetto si ribalto'). Ogni offset e' valutato con ENTRAMBE le
|
||||||
|
configurazioni (candidato e LIVE) alla STESSA ancora e sugli STESSI indici di bootstrap.
|
||||||
|
* Lente **ACCOPPIATA** ovunque (su una regola di DD a UN GIORNO la close-only e' cieca).
|
||||||
|
* Pesi FISSI, mai ri-ottimizzati per offset: ri-cercare l'argmax a ogni offset sarebbe un
|
||||||
|
max-of-23, cioe' esattamente il difetto sotto audit. La leva si', ma su ENTRAMBI i lati.
|
||||||
|
* de-luck sul drift x0,89 tenuto **per comparabilita' con PROP-RECAL**. ⚠️ Su questa dimensione
|
||||||
|
e' un DOPPIO CONTEGGIO conservativo: lo 0,89 fu misurato (26/07) proprio come rapporto
|
||||||
|
mediana-ancore / ancora-canonica del drift. §6 rigira la misura a fattore 1,00.
|
||||||
|
* Universo XS01 = **U13 Binance** (la versione piu' DEBOLE, conservativa), finestra **W-LONG
|
||||||
|
2021+**, meccanismo congelato. Identici a PROP-RECAL.
|
||||||
|
|
||||||
|
COSA QUESTA MISURA NON TOGLIE (dichiarato, non nascosto)
|
||||||
|
--------------------------------------------------------
|
||||||
|
L'ancora giornaliera di **TP01** (24 ore possibili) resta canonica 00:00 UTC, che il progetto
|
||||||
|
misura come la migliore delle 24 per l'hold-out di TP01. E' una TERZA dimensione, fuori dal
|
||||||
|
perimetro della gamba (b), e la sua fortuna resta dentro questi numeri.
|
||||||
|
|
||||||
|
ESITO (2026-08-22, corsa completa 391 s, 5.127 celle dichiarate)
|
||||||
|
----------------------------------------------------------------
|
||||||
|
* RIPRODUZIONE all'ancora canonica **7/7 esatta** (J LIVE 0,281 · fase 0 0,472 · mediana 0,453 ·
|
||||||
|
banda [0,431 0,488] · delta +0,175 · 10/10 fasi) e **3 controlli bit-exact a 0,0** (gambe orarie
|
||||||
|
a off=0 contro `pc._hourly_legs()` su 4 colonne; costruttore di libro contro `PR.rg` a due pesi).
|
||||||
|
* **GAMBA (b): PASS.** Mediana delle differenze appaiate sui 23 offset = **+0,1162** contro la
|
||||||
|
soglia pre-registrata +0,05; banda p10-p90 **[+0,087, +0,152]**; **23/23 offset positivi**,
|
||||||
|
**230/230 celle** dello spazio congiunto positive. La regione tiene tutta: 38/38/25 +0,116 ·
|
||||||
|
38/25/38 +0,120 · 25/38/38 +0,109.
|
||||||
|
* **La fortuna d'ancora c'e' ed e' quella attesa, ma non e' dove si temeva.** L'offset canonico 0
|
||||||
|
sta al **91° pctl** dei 23 su tutte e tre le grandezze e regala **+0,059 sul delta**, +0,112 sul
|
||||||
|
livello J del candidato, +0,050 su quello del LIVE. Il delta e' MENO ancorato dei livelli perche'
|
||||||
|
il LIVE contiene SKH01 al 25% e la fortuna si cancella in parte nella differenza appaiata.
|
||||||
|
* 📌 **IL RISULTATO INTERPRETATIVO: il vantaggio NON e' un tilt su SKH01.** A `38/25/38` SKH01 sta
|
||||||
|
al **medesimo peso del LIVE (25%)**, quindi la sua ancora si cancella quasi esattamente — e li'
|
||||||
|
il delta e' il **piu' alto dei tre (+0,120)** con la banda **piu' stretta** (ampiezza 0,032
|
||||||
|
contro 0,065 di W_A e 0,057 di W_C, che portano SKH a 38%). **L'ampiezza della banda scala col
|
||||||
|
DIFFERENZIALE di peso su SKH01**, esattamente come previsto prima di misurare. Cio' che paga e'
|
||||||
|
XS01, non un sovrappeso dello sleeve fragile.
|
||||||
|
* ⚠️ **Il numero OPERATIVO invece si dimezza, e per una ragione diversa dal gate.** P(>=50 EUR/g)
|
||||||
|
da EUR 600 in 36 mesi: mediana della banda d'ancora **4,4% [p10-p90 2,5%-8,2%]** contro il
|
||||||
|
**7,8%** pubblicato; P(zero) **35,3%** contro 25,5%. All'ancora canonica la macchina ridice
|
||||||
|
**7,7%** (= replica di PROP-RECAL): il crollo e' la fortuna d'ancora, non un disaccordo.
|
||||||
|
**Il gate e' su una DIFFERENZA (dove l'ancora si cancella in parte), il numero operativo e' un
|
||||||
|
LIVELLO (dove non si cancella affatto) — passano insieme e si muovono in modo diverso.**
|
||||||
|
* ⚠️ **Il surrogato del 22/08 e' finito vicino al vero per la CANCELLAZIONE DI DUE ASIMMETRIE.**
|
||||||
|
Ricalcolato a peso FISSO il taglio del drift da' **+0,076 (x0,50)** e **+0,045 (x0,33)**, cioe'
|
||||||
|
SOTTOSTIMA il vero (+0,116); ma PROP-RECAL lo calcolava ri-cercando l'argmax sui 10 pesi
|
||||||
|
migliori mentre il LIVE resta inchiodato a 75/25 — sotto stress il candidato puo' scappare da
|
||||||
|
SKH01 e il LIVE no -> il surrogato si gonfia a +0,124/+0,113. **Due errori in versi opposti che
|
||||||
|
quasi si annullano: il numero era giusto, il metodo no.**
|
||||||
|
* Sensibilita' dichiarata: a de-luck **x1,00** (senza il doppio conteggio dell'ancora) il delta
|
||||||
|
sale a **+0,148**, 23/23 positivi -> tenere lo 0,89 e' conservativo su questa dimensione.
|
||||||
|
* Banda del maxDD del candidato sui 23 offset: **-8,5% ... -13,0%**, mediana **-10,8%** (il
|
||||||
|
pubblicato a peso 25% era -9,6%): su un conto a barriera la coda peggiora quanto il livello.
|
||||||
|
* **RESTA FUORI, dichiarato:** l'ancora giornaliera di **TP01** (24 ore) e' una TERZA dimensione,
|
||||||
|
fuori dal perimetro della gamba (b), e la sua fortuna e' ancora dentro questi numeri.
|
||||||
|
|
||||||
|
Uso: `nice -n 19 timeout 900 uv run python scripts/research/r0822c_skh_anchor_prop.py`
|
||||||
|
Rete: NESSUNA (parquet certificati + cache Binance di r0822b_xs01_oos).
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
for _p in (ROOT, ROOT / "scripts" / "research", ROOT / "scripts" / "research" / "alt"):
|
||||||
|
sys.path.insert(0, str(_p))
|
||||||
|
|
||||||
|
import r0702_anchor_skh01 as AN # noqa: E402 resample con fase spostata (02/07)
|
||||||
|
import r0724_goal50_intraday_mc as mc # noqa: E402 recon orario per-sleeve (24/07)
|
||||||
|
import r0725_prop_coupled as pc # noqa: E402 lente accoppiata (25/07)
|
||||||
|
import r0725_prop_ladder as pl # noqa: E402 regole firm + scala di conti
|
||||||
|
import r0822_prop_alloc as PA # noqa: E402 J, bootstrap, simulatore (22/08)
|
||||||
|
import r0822b_prop_recal as PR # noqa: E402 IL file di cui giro la gamba (b)
|
||||||
|
import r0822b_xs01_oos as X # noqa: E402 motore XS01 + cache Binance
|
||||||
|
from src.data.downloader import load_data # noqa: E402
|
||||||
|
from src.strategies.skyhook import HTF_MIN, LTF_MIN # noqa: E402
|
||||||
|
|
||||||
|
ASSETS = ("BTC", "ETH")
|
||||||
|
OFFSETS = tuple(range(0, 690, 30)) # 23 offset a priori, identici a r0702 (0 = canonico)
|
||||||
|
PHASES = tuple(range(10)) # 10 fasi del ciclo H=10 di XS01
|
||||||
|
PHASES_SUB = (0, 3, 7) # sottocampione a priori per la scala di conti
|
||||||
|
LEVS = PR.LEVS
|
||||||
|
N_PATHS = PA.N_PATHS
|
||||||
|
EV_H = FU_H = 365
|
||||||
|
SEED_EV, SEED_FU = 8220, 8221
|
||||||
|
HYRO = "HYRO"
|
||||||
|
GATE_B = 0.05 # soglia PRE-REGISTRATA da PROP-RECAL
|
||||||
|
|
||||||
|
# pesi FISSI che coprono la regione robusta w_XS in [25%, 38%] (griglia simplesso a passo 1/8)
|
||||||
|
W_LIVE = {"TP01": 0.75, "SKH01": 0.25}
|
||||||
|
W_A = {"TP01": 0.38, "SKH01": 0.38, "XS01": 0.25} # centro dichiarato da PROP-RECAL (headline)
|
||||||
|
W_B = {"TP01": 0.38, "SKH01": 0.25, "XS01": 0.38} # w_XS all'estremo alto, TP-pesante
|
||||||
|
W_C = {"TP01": 0.25, "SKH01": 0.38, "XS01": 0.38} # w_XS all'estremo alto, SKH-pesante
|
||||||
|
|
||||||
|
# numeri PUBBLICATI da riprodurre prima di produrne di nuovi (RESULTS-0822 §29)
|
||||||
|
PUB = dict(j_med=0.453, j_ph0=0.472, j_p10=0.431, j_p90=0.488, j_live=0.281,
|
||||||
|
dmed=0.175, npos=10, surr_50=0.124, surr_33=0.113, p50g=0.078)
|
||||||
|
|
||||||
|
TRIALS = 0
|
||||||
|
|
||||||
|
|
||||||
|
def bump(n: int = 1) -> None:
|
||||||
|
global TRIALS
|
||||||
|
TRIALS += n
|
||||||
|
|
||||||
|
|
||||||
|
def hr(t: str = "") -> None:
|
||||||
|
print("\n" + "-" * 104)
|
||||||
|
if t:
|
||||||
|
print(f" {t}")
|
||||||
|
print("-" * 104)
|
||||||
|
|
||||||
|
|
||||||
|
# ==================================================================== 1. GAMBE ORARIE ALL'OFFSET
|
||||||
|
|
||||||
|
_LEGS: dict[int, pd.DataFrame] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def hourly_legs_off(off: int) -> pd.DataFrame:
|
||||||
|
"""`pc._hourly_legs()` con la griglia 230m/690m di SKH01 spostata di `off` minuti.
|
||||||
|
|
||||||
|
NON riscrive la macchineria: rimpiazza temporaneamente `mc.build_frames` con il resample
|
||||||
|
a fase spostata di `r0702_anchor_skh01` e richiama `mc.skh_hourly_contrib` — cosi' la
|
||||||
|
logica trade-per-trade (entry a close del bin, exit AL LIVELLO, SL prioritario, fee
|
||||||
|
nell'ora d'uscita) resta letteralmente quella del 24/07. La fedelta' della sostituzione e'
|
||||||
|
provata dal controllo bit-exact a off=0 (§1).
|
||||||
|
|
||||||
|
La gamba TP01 NON viene toccata: l'ancora di TP01 e' un'ALTRA dimensione (24 ore giornaliere)
|
||||||
|
e la gamba (b) del gate riguarda i 23 offset di SKH01.
|
||||||
|
"""
|
||||||
|
if off in _LEGS:
|
||||||
|
return _LEGS[off]
|
||||||
|
tp_r = tp_w = sk_r = sk_w = None
|
||||||
|
orig = mc.build_frames
|
||||||
|
|
||||||
|
def patched(df5: pd.DataFrame, _off: int = off):
|
||||||
|
ltf = AN.resample_off(df5, LTF_MIN, _off)
|
||||||
|
htf = AN.resample_off(df5, HTF_MIN, _off)
|
||||||
|
# confini HTF sottoinsieme dei LTF (690 = 3 x 230): la stessa guardia di r0702.
|
||||||
|
assert np.isin(htf["timestamp"].values[1:], ltf["timestamp"].values).all(), \
|
||||||
|
f"confini HTF non sottoinsieme dei LTF (off={_off})"
|
||||||
|
return ltf, htf
|
||||||
|
|
||||||
|
mc.build_frames = patched
|
||||||
|
try:
|
||||||
|
for a in ASSETS:
|
||||||
|
df = load_data(a, "1h")
|
||||||
|
idx = pd.DatetimeIndex(pd.to_datetime(df["datetime"], utc=True))
|
||||||
|
close = df["close"].values.astype(float)
|
||||||
|
low = df["low"].values.astype(float)
|
||||||
|
high = df["high"].values.astype(float)
|
||||||
|
prev = np.concatenate(([np.nan], close[:-1]))
|
||||||
|
r_close = close / prev - 1.0
|
||||||
|
tpf = mc.tp01_daily_frame(a)
|
||||||
|
day_of = idx.floor("D")
|
||||||
|
w_tp = np.nan_to_num(tpf["w"].reindex(day_of).values)
|
||||||
|
fee_tp = np.nan_to_num(tpf["fee"].reindex(day_of).values)
|
||||||
|
first = np.concatenate(([True], day_of[1:] != day_of[:-1]))
|
||||||
|
r_wick = np.where(w_tp >= 0, low / prev - 1.0, high / prev - 1.0)
|
||||||
|
t_r = pd.Series(w_tp * r_close - np.where(first, fee_tp, 0.0), index=idx)
|
||||||
|
t_w = pd.Series(w_tp * r_wick - np.where(first, fee_tp, 0.0), index=idx)
|
||||||
|
sa, sb = mc.skh_hourly_contrib(a, idx, close, low, high)
|
||||||
|
s_r = pd.Series(sa / mc.W_SKH, index=idx)
|
||||||
|
s_w = pd.Series(sb / mc.W_SKH, index=idx)
|
||||||
|
tp_r = t_r if tp_r is None else tp_r.add(t_r, fill_value=np.nan)
|
||||||
|
tp_w = t_w if tp_w is None else tp_w.add(t_w, fill_value=np.nan)
|
||||||
|
sk_r = s_r if sk_r is None else sk_r.add(s_r, fill_value=np.nan)
|
||||||
|
sk_w = s_w if sk_w is None else sk_w.add(s_w, fill_value=np.nan)
|
||||||
|
finally:
|
||||||
|
mc.build_frames = orig
|
||||||
|
J = pd.concat({"tp_r": tp_r, "tp_w": tp_w, "skh_r": sk_r, "skh_w": sk_w}, axis=1).dropna()
|
||||||
|
_LEGS[off] = J
|
||||||
|
return J
|
||||||
|
|
||||||
|
|
||||||
|
_CRY: dict = {}
|
||||||
|
|
||||||
|
|
||||||
|
def crypto_off(off: int, w_tp: float, w_skh: float) -> pd.DataFrame:
|
||||||
|
"""`pc.crypto_daily_tuples` con le gambe orarie all'offset `off` (minimo ESATTO sul path
|
||||||
|
orario condiviso: si compone l'ora per ora e si guarda il minimo, wick incluso)."""
|
||||||
|
key = (off, round(w_tp, 6), round(w_skh, 6))
|
||||||
|
if key in _CRY:
|
||||||
|
return _CRY[key]
|
||||||
|
J = hourly_legs_off(off)
|
||||||
|
r_h = (w_tp * J["tp_r"] + w_skh * J["skh_r"]).values
|
||||||
|
w_h = (w_tp * J["tp_w"] + w_skh * J["skh_w"]).values
|
||||||
|
idx = J.index
|
||||||
|
days = idx.floor("D")
|
||||||
|
rows = []
|
||||||
|
for day, pos in pd.Series(np.arange(len(idx)), index=days).groupby(level=0):
|
||||||
|
sl = pos.values
|
||||||
|
r = r_h[sl]
|
||||||
|
wk = w_h[sl]
|
||||||
|
cum = np.cumprod(1.0 + r)
|
||||||
|
cum_prev = np.concatenate(([1.0], cum[:-1]))
|
||||||
|
m = min(cum.min(), (cum_prev * (1.0 + wk)).min()) - 1.0
|
||||||
|
rows.append((day, cum[-1] - 1.0, min(m, cum[-1] - 1.0)))
|
||||||
|
out = pd.DataFrame(rows, columns=["day", "R", "m"]).set_index("day")
|
||||||
|
_CRY[key] = out
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def rg_off(w: dict, idx: pd.DatetimeIndex, xs: pd.DataFrame, off: int) -> tuple:
|
||||||
|
"""`PR.rg` con il sub-book crypto all'offset `off`. Validato bit-exact a off=0 in §1."""
|
||||||
|
w_tp, w_sk = float(w.get("TP01", 0.0)), float(w.get("SKH01", 0.0))
|
||||||
|
w_xs = float(w.get("XS01", 0.0))
|
||||||
|
R = np.zeros(len(idx))
|
||||||
|
M = np.zeros(len(idx))
|
||||||
|
if w_tp > 0 or w_sk > 0:
|
||||||
|
c = crypto_off(off, w_tp, w_sk).reindex(idx)
|
||||||
|
R += np.nan_to_num(c["R"].values)
|
||||||
|
M += np.nan_to_num(c["m"].values)
|
||||||
|
if w_xs > 0:
|
||||||
|
f = xs.reindex(idx)
|
||||||
|
R += w_xs * np.nan_to_num(f["R"].values)
|
||||||
|
M += w_xs * np.nan_to_num(f["m"].values)
|
||||||
|
return R, np.minimum(M, R) - R
|
||||||
|
|
||||||
|
|
||||||
|
def best_over_levers(R, G, ev, fu, factor: float = PA.DELUCK) -> tuple[float, float, np.ndarray]:
|
||||||
|
"""(J, leva, vettore per-percorso) alla leva che massimizza J. La ricerca sulla leva e'
|
||||||
|
applicata SIMMETRICAMENTE al candidato e al LIVE (convenzione di PROP-RECAL)."""
|
||||||
|
jb, lb, vb = -1.0, None, None
|
||||||
|
for lv in LEVS:
|
||||||
|
o = PR.obj_rg(R, G, lv, ev, fu, factor=factor)
|
||||||
|
if o["J"] > jb:
|
||||||
|
jb, lb, vb = o["J"], lv, o["jvec"]
|
||||||
|
bump(len(LEVS))
|
||||||
|
return jb, lb, vb
|
||||||
|
|
||||||
|
|
||||||
|
# ==================================================================== main
|
||||||
|
def main() -> None:
|
||||||
|
try:
|
||||||
|
sys.stdout.reconfigure(line_buffering=True)
|
||||||
|
except Exception: # pragma: no cover
|
||||||
|
pass
|
||||||
|
t0 = time.time()
|
||||||
|
|
||||||
|
def tick(lab: str) -> None:
|
||||||
|
print(f" [{time.time()-t0:6.0f}s] {lab}")
|
||||||
|
|
||||||
|
print("=" * 104)
|
||||||
|
print(" SKH-ANCHOR-PROP — la gamba (b) del `GATE PROP-01`, girata invece che aspettata")
|
||||||
|
print("=" * 104)
|
||||||
|
print(" DOMANDA: la mediana delle differenze appaiate J(regione w_XS 25-38%) - J(LIVE 75/25)")
|
||||||
|
print(f" sui 23 offset della griglia 230m/690m di SKH01, W-LONG, supera +{GATE_B:.2f}?")
|
||||||
|
print(" ASPETTATIVA REGISTRATA PRIMA DI MISURARE (mia, non ereditata): mi aspetto un PASS ma")
|
||||||
|
print(" con una taglia SENSIBILMENTE minore del surrogato (+0.124/+0.113), per un motivo")
|
||||||
|
print(" strutturale: il LIVE 75/25 contiene SKH01 al 25% e il candidato al 38%, quindi la")
|
||||||
|
print(" fortuna d'ancora si cancella in PARTE nella differenza appaiata — il residuo pesa")
|
||||||
|
print(" ~13 punti di peso, non 38. Se il delta uscisse >= al surrogato, il surrogato non era")
|
||||||
|
print(" conservativo e lo dico.")
|
||||||
|
|
||||||
|
# ============================================ §0. RIPRODUZIONE
|
||||||
|
hr("§0. RIPRODUZIONE — la macchina deve dare i numeri di PROP-RECAL prima di darne di nuovi")
|
||||||
|
Cb, Ob, Hb, Lb = PR.ohlc_panels(PR.bin_loader, PR.XS_UNIVERSE)
|
||||||
|
p13 = Cb[X.U13].dropna()
|
||||||
|
o13 = Ob.reindex(p13.index)[X.U13]
|
||||||
|
h13 = Hb.reindex(p13.index)[X.U13]
|
||||||
|
l13 = Lb.reindex(p13.index)[X.U13]
|
||||||
|
XSP = {ph: PR.xs_tuples(p13, o13, h13, l13, phase=ph) for ph in PHASES}
|
||||||
|
bump(len(PHASES))
|
||||||
|
cry_idx = pc.crypto_daily_tuples(1.0, 0.0).index
|
||||||
|
base_idx = cry_idx.intersection(XSP[0].index)
|
||||||
|
first13 = XSP[0].index[np.flatnonzero(XSP[0]["R"].values != 0.0)[0]]
|
||||||
|
I_LONG = base_idx[base_idx >= max(first13, pd.Timestamp("2021-01-01", tz="UTC"))]
|
||||||
|
ev = PA.boot_idx(len(I_LONG), EV_H, N_PATHS, seed=SEED_EV)
|
||||||
|
fu = PA.boot_idx(len(I_LONG), FU_H, N_PATHS, seed=SEED_FU)
|
||||||
|
print(f" W-LONG: {len(I_LONG)} giorni {I_LONG[0].date()} -> {I_LONG[-1].date()} | "
|
||||||
|
f"de-luck x{PA.DELUCK} | {N_PATHS} percorsi | semi {SEED_EV}/{SEED_FU} | firm {HYRO}")
|
||||||
|
|
||||||
|
RL0, GL0 = PR.rg(W_LIVE, I_LONG, XSP[0])
|
||||||
|
jL0, lvL0, vL0 = best_over_levers(RL0, GL0, ev, fu)
|
||||||
|
js0, ds0 = [], []
|
||||||
|
for ph in PHASES:
|
||||||
|
Rp, Gp = PR.rg(W_A, I_LONG, XSP[ph])
|
||||||
|
jb, _, vb = best_over_levers(Rp, Gp, ev, fu)
|
||||||
|
js0.append(jb)
|
||||||
|
ds0.append(float((vb - vL0).mean()))
|
||||||
|
print(f" {'grandezza':>42} {'mio':>9} {'PROP-RECAL':>11} esito")
|
||||||
|
checks = [("J libro LIVE 75/25 (ancora canonica)", jL0, PUB["j_live"]),
|
||||||
|
("J candidato 38/38/25 fase 0", js0[0], PUB["j_ph0"]),
|
||||||
|
("J candidato MEDIANA su 10 fasi", float(np.median(js0)), PUB["j_med"]),
|
||||||
|
("banda di fase p10", float(np.percentile(js0, 10)), PUB["j_p10"]),
|
||||||
|
("banda di fase p90", float(np.percentile(js0, 90)), PUB["j_p90"]),
|
||||||
|
("delta appaiato MEDIANO (10 fasi)", float(np.median(ds0)), PUB["dmed"])]
|
||||||
|
ok = True
|
||||||
|
for lab, mine, pub in checks:
|
||||||
|
good = abs(mine - pub) <= 0.005
|
||||||
|
ok &= good
|
||||||
|
print(f" {lab:>42} {mine:>9.3f} {pub:>11.3f} {'OK' if good else '<-- DIVERGE'}")
|
||||||
|
npos0 = sum(1 for d in ds0 if d > 0)
|
||||||
|
print(f" {'fasi con delta > 0':>42} {npos0:>9d} {PUB['npos']:>11d} "
|
||||||
|
f"{'OK' if npos0 == PUB['npos'] else '<-- DIVERGE'}")
|
||||||
|
ok &= npos0 == PUB["npos"]
|
||||||
|
print(f"\n >>> RIPRODUZIONE ALL'ANCORA CANONICA: "
|
||||||
|
f"{'OK — si procede' if ok else 'FALLITA — questo E il risultato'}")
|
||||||
|
if not ok:
|
||||||
|
print(" La macchina non riproduce PROP-RECAL: qualunque numero sugli offset sarebbe di")
|
||||||
|
print(" un'altra macchina. Mi fermo qui, come impone la disciplina di riproduzione.")
|
||||||
|
return
|
||||||
|
tick("§0 riproduzione")
|
||||||
|
|
||||||
|
# ============================================ §1. LA MACCHINERIA ALL'OFFSET
|
||||||
|
hr("§1. LA MACCHINERIA ALL'OFFSET — e il controllo che prova che la sostituzione e' fedele")
|
||||||
|
t = time.time()
|
||||||
|
J0 = hourly_legs_off(0)
|
||||||
|
(Jref,) = pc._hourly_legs()
|
||||||
|
same_idx = J0.index.equals(Jref.index)
|
||||||
|
dmax = max(float(np.abs(J0[c].values - Jref[c].values).max()) for c in J0.columns)
|
||||||
|
print(f" gambe orarie a off=0 contro `pc._hourly_legs()`: n={len(J0)} indice identico="
|
||||||
|
f"{same_idx} max|dif| su 4 colonne = {dmax:.2e} -> "
|
||||||
|
f"{'BIT-EXACT' if (same_idx and dmax == 0.0) else 'DIVERGE — STOP'}")
|
||||||
|
if not (same_idx and dmax == 0.0):
|
||||||
|
print(" La sostituzione di `build_frames` non riproduce il canonico: mi fermo.")
|
||||||
|
return
|
||||||
|
for wt in (W_A, W_LIVE):
|
||||||
|
Ra, Ga = rg_off(wt, I_LONG, XSP[0], 0)
|
||||||
|
Rb, Gb = PR.rg(wt, I_LONG, XSP[0])
|
||||||
|
d1 = float(np.abs(Ra - Rb).max())
|
||||||
|
d2 = float(np.abs(Ga - Gb).max())
|
||||||
|
print(f" costruttore di libro a off=0 contro `PR.rg` ({PA.wkey(wt)}): "
|
||||||
|
f"max|dR| {d1:.2e} max|dgap| {d2:.2e} -> "
|
||||||
|
f"{'BIT-EXACT' if max(d1, d2) == 0.0 else 'DIVERGE'}")
|
||||||
|
if max(d1, d2) != 0.0:
|
||||||
|
return
|
||||||
|
print(f" costo misurato: {time.time()-t:.1f}s per la prima cella (import+cache inclusi)")
|
||||||
|
t = time.time()
|
||||||
|
for off in OFFSETS:
|
||||||
|
hourly_legs_off(off)
|
||||||
|
assert hourly_legs_off(off).index.equals(J0.index), \
|
||||||
|
f"indice orario diverso a off={off}: il bootstrap non sarebbe appaiato"
|
||||||
|
print(f" 23 offset costruiti in {time.time()-t:.0f}s — l'indice giornaliero e' lo STESSO a")
|
||||||
|
print(" ogni offset, quindi gli indici di bootstrap sono condivisi e il confronto e' appaiato.")
|
||||||
|
print(" ⚠️ La data 2026-10-31 era un budget di calcolo ancorato al costo di `skh_sigcache`")
|
||||||
|
print(" (segnale intra-bin, ~9 min/cella), che questa misura non usa.")
|
||||||
|
n_tr = {}
|
||||||
|
for off in (0, 330, 660):
|
||||||
|
J = hourly_legs_off(off)
|
||||||
|
n_tr[off] = int((J["skh_r"].values != 0.0).sum())
|
||||||
|
print(f" ore con contributo SKH01 non nullo: " +
|
||||||
|
" · ".join(f"off{o}: {v}" for o, v in n_tr.items()) +
|
||||||
|
" (griglie diverse, non una traslazione)")
|
||||||
|
tick("§1 macchineria")
|
||||||
|
|
||||||
|
# ============================================ §2. LA GRIGLIA CONGIUNTA
|
||||||
|
hr("§2. LA GRIGLIA CONGIUNTA 23 OFFSET x 10 FASI — la gamba (b), headline W_A = 38/38/25")
|
||||||
|
print(" Per ogni offset: J del LIVE (che contiene SKH01 al 25% e quindi si muove anch'esso)")
|
||||||
|
print(" e J del candidato a ciascuna delle 10 fasi di XS01, alla leva che massimizza J.")
|
||||||
|
print(" Il delta e' appaiato sui MEDESIMI indici di bootstrap; il collasso sulle fasi e' la")
|
||||||
|
print(" MEDIANA (la stima onesta di PROP-RECAL), non la fase 0 (fortunata di +0.019).")
|
||||||
|
D = np.zeros((len(OFFSETS), len(PHASES)))
|
||||||
|
JC = np.zeros((len(OFFSETS), len(PHASES)))
|
||||||
|
JL = np.zeros(len(OFFSETS))
|
||||||
|
LVL = np.zeros(len(OFFSETS))
|
||||||
|
DDC = np.zeros((len(OFFSETS), len(PHASES)))
|
||||||
|
DDL = np.zeros(len(OFFSETS))
|
||||||
|
t = time.time()
|
||||||
|
print(f"\n {'off':>4} {'J LIVE':>7} {'levL':>5} {'J cand fase0':>12} {'MEDIANA':>8} "
|
||||||
|
f"{'p10-p90':>15} {'delta MEDIANO':>13} {'fasi>0':>7} {'maxDD cand':>10}")
|
||||||
|
for i, off in enumerate(OFFSETS):
|
||||||
|
RL, GL = rg_off(W_LIVE, I_LONG, XSP[0], off)
|
||||||
|
jL, lvL, vL = best_over_levers(RL, GL, ev, fu)
|
||||||
|
JL[i], LVL[i] = jL, lvL
|
||||||
|
DDL[i] = PR.maxdd(PA.deluck(RL))
|
||||||
|
for k, ph in enumerate(PHASES):
|
||||||
|
Rc, Gc = rg_off(W_A, I_LONG, XSP[ph], off)
|
||||||
|
jc, _, vc = best_over_levers(Rc, Gc, ev, fu)
|
||||||
|
JC[i, k] = jc
|
||||||
|
DDC[i, k] = PR.maxdd(PA.deluck(Rc))
|
||||||
|
D[i, k] = float((vc - vL).mean())
|
||||||
|
print(f" {off:>4} {jL:>7.3f} {lvL:>5.2f} {JC[i,0]:>12.3f} {np.median(JC[i]):>8.3f} "
|
||||||
|
f"[{np.percentile(JC[i],10):>6.3f},{np.percentile(JC[i],90):>6.3f}] "
|
||||||
|
f"{np.median(D[i]):>+13.3f} {int((D[i] > 0).sum()):>5}/10 "
|
||||||
|
f"{np.median(DDC[i])*100:>9.1f}%")
|
||||||
|
print(f" ({time.time()-t:.0f}s per 23 x (1 LIVE + 10 fasi) x 8 leve)")
|
||||||
|
|
||||||
|
d_per_off = np.median(D, axis=1) # collasso onesto sulle fasi
|
||||||
|
d_gate = float(np.median(d_per_off)) # <-- IL NUMERO DELLA GAMBA (b)
|
||||||
|
d_ph0 = float(np.median(D[:, 0])) # la scelta comoda, per confronto
|
||||||
|
print(f"\n {'':>52}{'mediana':>9}{'p10':>9}{'p90':>9}{'>0':>8}")
|
||||||
|
print(f" {'delta appaiato per OFFSET (fasi collassate a mediana)':>52}"
|
||||||
|
f"{d_gate:>+9.3f}{np.percentile(d_per_off,10):>+9.3f}"
|
||||||
|
f"{np.percentile(d_per_off,90):>+9.3f}{int((d_per_off>0).sum()):>5}/23")
|
||||||
|
print(f" {'delta appaiato, spazio CONGIUNTO 230 celle':>52}"
|
||||||
|
f"{np.median(D):>+9.3f}{np.percentile(D,10):>+9.3f}"
|
||||||
|
f"{np.percentile(D,90):>+9.3f}{int((D>0).sum()):>4}/230")
|
||||||
|
print(f" {'delta a fase 0 fissa (la scelta COMODA)':>52}"
|
||||||
|
f"{d_ph0:>+9.3f}{np.percentile(D[:,0],10):>+9.3f}"
|
||||||
|
f"{np.percentile(D[:,0],90):>+9.3f}{int((D[:,0]>0).sum()):>5}/23")
|
||||||
|
print(f" {'ancora canonica (off 0, fase mediana) = PROP-RECAL':>52}"
|
||||||
|
f"{np.median(D[0]):>+9.3f}{'':>9}{'':>9}{int((D[0]>0).sum()):>5}/10")
|
||||||
|
print(f"\n Fortuna d'ancora di SKH01 SOTTO LA LENTE PROP (canonico - mediana):")
|
||||||
|
print(f" sul DELTA : {np.median(D[0]) - d_gate:+.3f} "
|
||||||
|
f"(l'offset 0 sta al {(d_per_off < d_per_off[0]).mean()*100:.0f}° pctl dei 23)")
|
||||||
|
print(f" sul LIVELLO J del candidato : {np.median(JC[0]) - np.median(np.median(JC,axis=1)):+.3f} "
|
||||||
|
f"(off 0 al {(np.median(JC,axis=1) < np.median(JC[0])).mean()*100:.0f}° pctl)")
|
||||||
|
print(f" sul LIVELLO J del LIVE : {JL[0] - np.median(JL):+.3f} "
|
||||||
|
f"(off 0 al {(JL < JL[0]).mean()*100:.0f}° pctl)")
|
||||||
|
print(" ⚠️ La fortuna si cancella in PARTE perche' il LIVE contiene SKH01 al 25%: il delta")
|
||||||
|
print(" e' meno ancorato dei livelli. E' la ragione per cui la statistica giusta e' il")
|
||||||
|
print(" DELTA APPAIATO e non il livello.")
|
||||||
|
tick("§2 griglia congiunta")
|
||||||
|
|
||||||
|
# ============================================ §3. LA REGIONE, NON IL PUNTO
|
||||||
|
hr("§3. LA REGIONE w_XS 25-38% — il gate parla di una REGIONE, non di un punto")
|
||||||
|
print(" Tre pesi FISSI dichiarati che coprono la regione (nessuna ri-ottimizzazione per")
|
||||||
|
print(" offset: sarebbe un max-of-23, cioe' il difetto sotto audit). W_A gira sulle 10 fasi")
|
||||||
|
print(f" piene; i due pesi di sensibilita' sul sottocampione a priori {PHASES_SUB} (budget).")
|
||||||
|
print(f"\n {'peso':>12} {'w_XS':>5} {'J mediano':>10} {'delta MEDIANO':>13} "
|
||||||
|
f"{'p10':>8} {'p90':>8} {'offset>0':>9} {'celle>0':>9} {'gate':>6}")
|
||||||
|
reg = {}
|
||||||
|
for lab, wt, phs in (("38/38/25", W_A, PHASES), ("38/25/38", W_B, PHASES_SUB),
|
||||||
|
("25/38/38", W_C, PHASES_SUB)):
|
||||||
|
if wt is W_A:
|
||||||
|
Dw = D
|
||||||
|
Jw = JC
|
||||||
|
else:
|
||||||
|
Dw = np.zeros((len(OFFSETS), len(phs)))
|
||||||
|
Jw = np.zeros((len(OFFSETS), len(phs)))
|
||||||
|
for i, off in enumerate(OFFSETS):
|
||||||
|
RL, GL = rg_off(W_LIVE, I_LONG, XSP[0], off)
|
||||||
|
_, _, vL = best_over_levers(RL, GL, ev, fu)
|
||||||
|
for k, ph in enumerate(phs):
|
||||||
|
Rc, Gc = rg_off(wt, I_LONG, XSP[ph], off)
|
||||||
|
jc, _, vc = best_over_levers(Rc, Gc, ev, fu)
|
||||||
|
Jw[i, k] = jc
|
||||||
|
Dw[i, k] = float((vc - vL).mean())
|
||||||
|
dpo = np.median(Dw, axis=1)
|
||||||
|
dm = float(np.median(dpo))
|
||||||
|
reg[lab] = dm
|
||||||
|
print(f" {lab:>12} {int(round(100*wt['XS01'])):>4}% {np.median(Jw):>10.3f} "
|
||||||
|
f"{dm:>+13.3f} {np.percentile(dpo,10):>+8.3f} {np.percentile(dpo,90):>+8.3f} "
|
||||||
|
f"{int((dpo>0).sum()):>7}/23 {int((Dw>0).sum()):>6}/{Dw.size} "
|
||||||
|
f"{'PASS' if dm > GATE_B else 'FAIL':>6}")
|
||||||
|
worst = min(reg.values())
|
||||||
|
print(f"\n Peggiore della regione: {min(reg, key=reg.get)} con {worst:+.3f}; "
|
||||||
|
f"mediana dei tre {np.median(list(reg.values())):+.3f}")
|
||||||
|
tick("§3 regione")
|
||||||
|
|
||||||
|
# ============================================ §4. SURROGATO vs MISURA
|
||||||
|
hr("§4. IL SURROGATO CONTRO LA MISURA — quanto valeva il taglio del drift")
|
||||||
|
print(" PROP-RECAL surrogava la banda d'ancora con un taglio del DRIFT di SKH01 (x0.50 e")
|
||||||
|
print(" x0.33) e leggeva +0.124 / +0.113. Qui lo stesso surrogato e' ricalcolato al peso")
|
||||||
|
print(" FISSO W_A e con lo stesso collasso di fase, cosi' il confronto e' appaiato.")
|
||||||
|
print(f"\n {'lente':>44} {'delta MEDIANO':>13}")
|
||||||
|
for lab, dmt in (("misura vera: 23 offset x 10 fasi", None),
|
||||||
|
("surrogato SKH x0.50 (ancora canonica)", (("SKH01", 0.50),)),
|
||||||
|
("surrogato SKH x0.33 (ancora canonica)", (("SKH01", 0.33),))):
|
||||||
|
if dmt is None:
|
||||||
|
print(f" {lab:>44} {d_gate:>+13.3f}")
|
||||||
|
continue
|
||||||
|
RLs, GLs = PR.rg(W_LIVE, I_LONG, XSP[0], dm=dmt)
|
||||||
|
_, _, vLs = best_over_levers(RLs, GLs, ev, fu)
|
||||||
|
dsx = []
|
||||||
|
for ph in PHASES:
|
||||||
|
Rs, Gs = PR.rg(W_A, I_LONG, XSP[ph], dm=dmt)
|
||||||
|
_, _, vs = best_over_levers(Rs, Gs, ev, fu)
|
||||||
|
dsx.append(float((vs - vLs).mean()))
|
||||||
|
print(f" {lab:>44} {np.median(dsx):>+13.3f}")
|
||||||
|
print("\n Un surrogato che taglia il DRIFT non e' la stessa cosa di una griglia diversa:")
|
||||||
|
print(" cambiare l'ancora cambia QUALI trade esistono, non solo quanto rendono.")
|
||||||
|
tick("§4 surrogato")
|
||||||
|
|
||||||
|
# ============================================ §5. IL NUMERO OPERATIVO
|
||||||
|
hr("§5. IL NUMERO OPERATIVO — P(>=50 EUR/g) da EUR 600 in 36 mesi, sulla banda d'ancora")
|
||||||
|
print(f" Sottocampione DICHIARATO: 23 offset x {len(PHASES_SUB)} fasi {PHASES_SUB} (uniformi a")
|
||||||
|
print(" priori sul ciclo H=10) = 69 simulazioni da 1500 percorsi. La scala di conti costa")
|
||||||
|
print(" ~1.7 s a simulazione: la griglia piena 23x10 costerebbe ~7 min in piu' e la mediana")
|
||||||
|
print(" di 69 estrazioni basta per una banda al punto percentuale.")
|
||||||
|
pc.DELUCK = PA.DELUCK
|
||||||
|
W3 = PA.CRYPTO_PROP
|
||||||
|
pt, pz, med_eur = [], [], []
|
||||||
|
t = time.time()
|
||||||
|
if time.time() - t0 < 620:
|
||||||
|
for i, off in enumerate(OFFSETS):
|
||||||
|
for ph in PHASES_SUB:
|
||||||
|
Rc, Gc = rg_off(W_A, I_LONG, XSP[ph], off)
|
||||||
|
jc, lvc, _ = best_over_levers(Rc, Gc, ev, fu)
|
||||||
|
cfg = tuple(W_A.get(nm, 0.0) for nm in W3)
|
||||||
|
key = (cfg[0], cfg[1], cfg[2], "shared")
|
||||||
|
saved = pc._TUP_CACHE.pop(key, None)
|
||||||
|
pc._TUP_CACHE[key] = (Rc, Gc, I_LONG)
|
||||||
|
r = pc.simulate([cfg], [0] * pl.MAX_CONCURRENT, n_paths=1500, lev=lvc,
|
||||||
|
seed=20260822, lens="coupled", start=str(I_LONG[0].date()))
|
||||||
|
pc._TUP_CACHE.pop(key, None)
|
||||||
|
if saved is not None:
|
||||||
|
pc._TUP_CACHE[key] = saved
|
||||||
|
pt.append(r["p_target"]); pz.append(r["p_zero"]); med_eur.append(r["med"])
|
||||||
|
bump(len(OFFSETS) * len(PHASES_SUB))
|
||||||
|
print(f"\n P(>=50 EUR/g) su {len(pt)} celle (offset x fase): mediana "
|
||||||
|
f"{np.median(pt):.1%} banda p10-p90 [{np.percentile(pt,10):.1%}, "
|
||||||
|
f"{np.percentile(pt,90):.1%}] min {min(pt):.1%} max {max(pt):.1%}")
|
||||||
|
print(f" P(zero) mediana {np.median(pz):.1%} | EUR/g mediana {np.median(med_eur):.2f}")
|
||||||
|
print(f" All'ancora CANONICA (off 0, fasi {PHASES_SUB}): "
|
||||||
|
f"{np.median(pt[:len(PHASES_SUB)]):.1%}")
|
||||||
|
print(f" PROP-RECAL pubblicava {PUB['p50g']:.1%} [7.2-9.1%] (ancora canonica, 10 fasi)")
|
||||||
|
print(f" ({time.time()-t:.0f}s)")
|
||||||
|
else:
|
||||||
|
print("\n NON GIRATO: budget di tempo esaurito prima di §5. Si dichiara, non si stima.")
|
||||||
|
pt = []
|
||||||
|
tick("§5 numero operativo")
|
||||||
|
|
||||||
|
# ============================================ §6. SENSIBILITA' AL DE-LUCK
|
||||||
|
hr("§6. SENSIBILITA' — il de-luck x0.89 su questa dimensione e' un doppio conteggio")
|
||||||
|
print(" Lo 0.89 fu MISURATO (26/07) come rapporto mediana-ancore / ancora-canonica del")
|
||||||
|
print(" drift: applicarlo mentre si gira la banda d'ancora conta due volte la stessa")
|
||||||
|
print(" correzione. E' tenuto per comparabilita' con PROP-RECAL; qui la stessa misura a")
|
||||||
|
print(" fattore 1.00 (nessun de-luck), sulle stesse 23 x 3 celle.")
|
||||||
|
d_nd = []
|
||||||
|
for i, off in enumerate(OFFSETS):
|
||||||
|
RL, GL = rg_off(W_LIVE, I_LONG, XSP[0], off)
|
||||||
|
_, _, vL = best_over_levers(RL, GL, ev, fu, factor=1.0)
|
||||||
|
row = []
|
||||||
|
for ph in PHASES_SUB:
|
||||||
|
Rc, Gc = rg_off(W_A, I_LONG, XSP[ph], off)
|
||||||
|
_, _, vc = best_over_levers(Rc, Gc, ev, fu, factor=1.0)
|
||||||
|
row.append(float((vc - vL).mean()))
|
||||||
|
d_nd.append(float(np.median(row)))
|
||||||
|
print(f"\n delta appaiato mediano de-luck x0.89 (headline) : {d_gate:+.3f}")
|
||||||
|
print(f" de-luck x1.00 (no double) : {np.median(d_nd):+.3f}"
|
||||||
|
f" ({int(sum(1 for d in d_nd if d > 0))}/23 > 0)")
|
||||||
|
print(" Il verso di questa differenza dice se la scelta conservativa lo era davvero.")
|
||||||
|
tick("§6 sensibilita'")
|
||||||
|
|
||||||
|
# ============================================ §7. VERDETTO
|
||||||
|
hr("§7. VERDETTO SULLA GAMBA (b)")
|
||||||
|
passed = d_gate > GATE_B
|
||||||
|
print(f" Metrica pre-registrata: mediana delle differenze appaiate J(w_XS 25-38%) - J(LIVE)")
|
||||||
|
print(f" sui 23 offset, W-LONG. Soglia: > +{GATE_B:.2f}")
|
||||||
|
print(f"\n MISURA (headline W_A 38/38/25, fasi collassate a mediana): {d_gate:+.4f}")
|
||||||
|
print(f" banda p10-p90 sui 23 offset: [{np.percentile(d_per_off,10):+.3f}, "
|
||||||
|
f"{np.percentile(d_per_off,90):+.3f}] offset positivi: "
|
||||||
|
f"{int((d_per_off>0).sum())}/23 celle positive: {int((D>0).sum())}/230")
|
||||||
|
print(f" regione: peggiore {worst:+.3f} ({min(reg, key=reg.get)}), "
|
||||||
|
f"mediana dei tre pesi {np.median(list(reg.values())):+.3f}")
|
||||||
|
print(f"\n >>> GAMBA (b): {'PASS' if passed else 'FAIL'} "
|
||||||
|
f"({d_gate:+.4f} {'>' if passed else '<='} +{GATE_B:.2f})")
|
||||||
|
if passed:
|
||||||
|
print(" L'aspettativa registrata da PROP-RECAL (un PASS) e' confermata.")
|
||||||
|
else:
|
||||||
|
print(" L'aspettativa registrata da PROP-RECAL (un PASS) e' SMENTITA: il vantaggio")
|
||||||
|
print(" era in parte calendario.")
|
||||||
|
if pt:
|
||||||
|
print(f"\n COSA RESTA del risultato funded, ri-ancorato: P(>=50 EUR/g) mediana "
|
||||||
|
f"{np.median(pt):.1%} [banda d'ancora {np.percentile(pt,10):.1%}-"
|
||||||
|
f"{np.percentile(pt,90):.1%}] contro il {PUB['p50g']:.1%} pubblicato.")
|
||||||
|
print(f"\n RESTA FUORI (dichiarato): l'ancora giornaliera di TP01 (24 ore) — terza dimensione,")
|
||||||
|
print(" non nel perimetro della gamba (b), e la sua fortuna e' ancora dentro questi numeri.")
|
||||||
|
print(f"\n TRIAL/CELLE DICHIARATI (al rialzo): {TRIALS}")
|
||||||
|
print(f" fatto in {time.time()-t0:.0f}s")
|
||||||
|
print("=" * 104)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user