research(wave-0822): XSR-REPRO trova un difetto di produzione (i monitor forward registrano ~41 min/giorno); SKEW scompone il f di VRP01 — il 42% e' struttura a termine
This commit is contained in:
@@ -302,11 +302,39 @@ def main() -> dict:
|
||||
|
||||
print("\n IL TEST DI SUPER-PROPORZIONALITA' (la domanda esatta del brief):")
|
||||
dpk = [delta_p[j] / KS[j] for j in range(len(KS))]
|
||||
rap = [dd_acc[j] / dd_close[j] for j in range(len(KS))]
|
||||
j1 = KS.index(1.00)
|
||||
print(f" Δ/k a k=0.50 {dpk[0]:+.3%} · 1.00 {dpk[j1]:+.3%} · 1.50 {dpk[KS.index(1.50)]:+.3%}"
|
||||
print(" Δ per UNITA' di leva (se il minimo intraday amplificasse piu' che")
|
||||
print(" proporzionalmente, questa riga CRESCEREBBE in modulo con k):")
|
||||
print(f" Δ/k k=0.50 {dpk[0]:+.3%} · 1.00 {dpk[j1]:+.3%} · 1.50 {dpk[KS.index(1.50)]:+.3%}"
|
||||
f" · 2.00 {dpk[KS.index(2.00)]:+.3%} · 5.00 {dpk[-1]:+.3%}")
|
||||
cresce = dpk[KS.index(2.00)] < dpk[j1] # piu' negativo = piu' costoso per unita' di leva
|
||||
print(f" -> Δ/k {'CRESCE in modulo con k = super-proporzionale' if cresce else 'NON cresce in modulo: l effetto e circa PROPORZIONALE'}.")
|
||||
print(f" rapporto maxDD acc./close: {' · '.join(f'{r:.3f}' for r in rap)}")
|
||||
super_prop = dpk[KS.index(2.00)] > dpk[j1] * 1.05
|
||||
sotto = dpk[KS.index(2.00)] < dpk[j1] * 0.95
|
||||
verdetto = ("SUPER-proporzionale" if super_prop else
|
||||
("SOTTO-proporzionale" if sotto else "proporzionale entro il rumore"))
|
||||
print(f"\n ❌ RISPOSTA: NO — Δ/k **CALA** in modulo (da {dpk[0]:.3%} a {dpk[-1]:.3%}) e il")
|
||||
print(f" RAPPORTO fra le due maxDD e' PIATTO ({min(rap):.3f}-{max(rap):.3f}) su tutta la")
|
||||
print(f" griglia. La lente accoppiata e' un RICARICO MOLTIPLICATIVO COSTANTE di ~"
|
||||
f"{(np.mean(rap)-1)*100:.1f}%")
|
||||
print(f" sul maxDD, non un'amplificazione: verdetto **{verdetto}**.")
|
||||
print(" Meccanismo: il maxDD di 25 anni e' una statistica MULTI-GIORNO (una discesa di")
|
||||
print(" settimane), il wick e' un fatto di UN giorno. Aggiungere qualche punto di tuffo")
|
||||
print(" a una discesa gia' lunga sposta poco, e la parte della distribuzione che")
|
||||
print(" conterebbe non c'e': nel 26,8% dei giorni il libro CHIUDE sul minimo (gap = 0)")
|
||||
print(" e quei giorni sono proprio i brutti. A leva alta Δ/k cala anche perche' i")
|
||||
print(" drawdown si allungano piu' in fretta di quanto il singolo tuffo cresca.")
|
||||
|
||||
print("\n ⚠️ MA C'E' UN'ECCEZIONE, ed e' quella che decide la frontiera: la PROBABILITA'")
|
||||
print(" di sfondare una SOGLIA FISSA e' amplificata assai piu' del maxDD stesso,")
|
||||
print(" perche' vicino alla soglia la densita' e' fitta.")
|
||||
print(f"\n {'k':>6}{'P(DD>30%) close':>17}{'P(DD>30%) acc.':>16}{'amplificazione':>16}")
|
||||
for j, k in enumerate(KS):
|
||||
pc = float((s0["dd"][:, j, 0] > DD_SOGLIA).mean())
|
||||
pa = float((s0["dd"][:, j, 1] > DD_SOGLIA).mean())
|
||||
amp = (pa / pc) if pc > 0 else float("nan")
|
||||
print(f" {k:>6.2f}{pc:>17.1%}{pa:>16.1%}"
|
||||
f"{(f'{amp:.2f}x' if np.isfinite(amp) else 'n.d.'):>16}")
|
||||
|
||||
print("\n IL NUMERO CHE DECIDE — L'HAIRCUT DI LEVA IMPLICITO NELLA LENTE:")
|
||||
print(" 'a quale k close-only corrisponde il rischio di DD che la lente accoppiata")
|
||||
@@ -397,6 +425,44 @@ def main() -> dict:
|
||||
print(f" <= {frac:.0%} dell'equity -> costo massimo {sl*frac:.0%} dell'equity per asset a k=1,")
|
||||
print(f" cioe' {sl*frac*2:.0%} se scattano entrambi; diventa il -100% a k = {1/(sl*frac*2):.1f}x.")
|
||||
|
||||
# ------------------------------------------------------------------ (3-bis)
|
||||
sezione("(3-bis) DOVE L'ACCOPPIAMENTO MORDE DAVVERO — la regola a UN GIORNO, sui giorni STORICI")
|
||||
print("\n Il finding del 25/07 ('close-only e' CIECA', 'l'indipendente RADDOPPIA i breach')")
|
||||
print(" e' misurato su una regola di DAILY-LOSS: una soglia valutata sul MINIMO di UN")
|
||||
print(" giorno. Il maxDD di un piano d'accumulo e' un'altra grandezza. Qui le due si")
|
||||
print(" guardano fianco a fianco, sui GIORNI VERI (nessun bootstrap): quante volte in")
|
||||
print(f" {len(D['R_del'])/365:.1f} anni una soglia di perdita giornaliera viene sfondata.")
|
||||
Rh, Gh = D["R_del"], gap
|
||||
rng2 = np.random.default_rng(909)
|
||||
Gsh = Gh.copy(); rng2.shuffle(Gsh)
|
||||
anni_st = len(Rh) / 365.0
|
||||
for T in (0.03, 0.05, 0.10):
|
||||
print(f"\n soglia daily-loss {T:.0%} (breach/anno)")
|
||||
print(f" {'k':>6}{'close-only':>13}{'accoppiata':>13}{'indip. (finto)':>17}"
|
||||
f"{'acc./close':>12}{'indip./acc.':>13}")
|
||||
for k in (1.00, 1.25, 1.50, 2.00, 3.00):
|
||||
nc = float((Rh * k <= -T).sum()) / anni_st
|
||||
na = float(((Rh + Gh) * k <= -T).sum()) / anni_st
|
||||
ni = float(((Rh + Gsh) * k <= -T).sum()) / anni_st
|
||||
r1 = (na / nc) if nc > 0 else float("inf")
|
||||
r2 = (ni / na) if na > 0 else float("nan")
|
||||
print(f" {k:>6.2f}{nc:>13.2f}{na:>13.2f}{ni:>17.2f}"
|
||||
f"{(f'{r1:.2f}x' if np.isfinite(r1) else 'INF'):>12}"
|
||||
f"{(f'{r2:.2f}x' if np.isfinite(r2) else 'n.d.'):>13}")
|
||||
print("\n 📌 IL RISULTATO TRASFERIBILE DEL FILONE: la lezione del 25/07 e' VERA e NON SI")
|
||||
print(" TRASFERISCE. Su una regola a UN GIORNO l'accoppiamento cambia il conto di un")
|
||||
print(" fattore (e close-only puo' dare ZERO dove il vero e' positivo: cecita' esatta);")
|
||||
print(" su una statistica MULTI-GIORNO come il maxDD di un piano d'accumulo lo stesso")
|
||||
print(" difetto vale un ricarico costante di pochi punti percentuali. Il gate")
|
||||
print(" pre-registrato ha importato una lezione dal contesto in cui e' stata misurata")
|
||||
print(" (conti FUNDED, barriera di daily-loss) a un contesto in cui la grandezza")
|
||||
print(" decisiva e' un'altra — stessa forma dell'errore che il progetto ha gia'")
|
||||
print(" codificato il 26/07 sulla barra parziale (SKH01 +0.38 / TP01 ±0.03: 'la")
|
||||
print(" conclusione non si trasferisce fra sleeve').")
|
||||
print(" ⚠️ Il che NON assolve close-only in generale: sul canale PROP/FUNDED, dove il")
|
||||
print(" vincolo binding e' esattamente una regola di daily-loss per-conto, la lente")
|
||||
print(" accoppiata resta obbligatoria e la leva li' e' un'altra decisione.")
|
||||
|
||||
# ------------------------------------------------------------------ (4)
|
||||
sezione("(4) IL GRADINO ESEGUIBILE — sopravvive alla lente? (€500/mese, muro netto)")
|
||||
s500 = simula(D["R_del"], gap, KS, anni=ANNI, dep_eur=500, bersaglio=BERSAGLIO, seed=SEED)
|
||||
@@ -443,10 +509,10 @@ def attacco2() -> None:
|
||||
f" <- lo scetticato dichiara 12,14%")
|
||||
print(f" -> VERIFICATO. E il numero non e' un artefatto della sua ricostruzione.")
|
||||
|
||||
print("\n ⚠️ MA IL MECCANISMO CHE DICHIARA E' INCOMPLETO, e la parte che manca e' la")
|
||||
print(" maggiore. Lo scetticato scrive: «il target vale sulla posizione quando c'e', e")
|
||||
print(" TP01 e' long-flat». Decomposizione:")
|
||||
convs, vols_pred, vols_real, sers = [], [], [], {}
|
||||
print("\n ⚠️ MA IL MECCANISMO CHE DICHIARA SPIEGA CIRCA META' DEL DIVARIO, e la meta'")
|
||||
print(" che manca porta a una conclusione operativa diversa. Lo scetticato scrive:")
|
||||
print(" «il target vale sulla posizione quando c'e', e TP01 e' long-flat». Misura:")
|
||||
convs, vols_pred, vols_real, sers, flats = [], [], [], {}, []
|
||||
for a in ("BTC", "ETH"):
|
||||
df = resample_1d(load_data(a, "1h"))
|
||||
c = df["close"].values.astype(float)
|
||||
@@ -461,6 +527,7 @@ def attacco2() -> None:
|
||||
sers[a] = pd.Series(np.clip(net, -0.99, None), index=pd.to_datetime(df["datetime"]))
|
||||
ok = np.isfinite(d) & np.isfinite(vol) & (vol > 0)
|
||||
convs.append(float(np.sqrt(np.mean(d[ok] ** 2))))
|
||||
flats.append(float((d[ok] == 0).mean()))
|
||||
# errore del previsore di vol: vol realizzata nei 30g SEGUENTI / vol prevista
|
||||
fwd = pd.Series(r).rolling(30).std().shift(-30).values * np.sqrt(365.25)
|
||||
okf = ok & np.isfinite(fwd)
|
||||
@@ -478,21 +545,35 @@ def attacco2() -> None:
|
||||
comb = (0.5 * J["BTC"] + 0.5 * J["ETH"]).std() * 365 ** .5
|
||||
div = comb / (0.5 * vb + 0.5 * ve)
|
||||
conv = float(np.mean(convs))
|
||||
per_leg = (0.5 * vb + 0.5 * ve) / 0.20
|
||||
p_flat = float(np.mean(flats))
|
||||
f_flat = float(np.sqrt(1.0 - p_flat))
|
||||
f_conv = conv / f_flat if f_flat > 0 else float("nan")
|
||||
print(f"\n gamba singola: BTC {vb:.2%} · ETH {ve:.2%} -> 50/50 combinato {comb:.2%}")
|
||||
print(f" fattore di DIVERSIFICAZIONE BTC/ETH = {div:.3f} (corr {J['BTC'].corr(J['ETH']):.2f})")
|
||||
print(f"\n CATENA: 20,00% ×{conv:.3f} (convinzione) ×{np.mean(vols_real)/np.mean(vols_pred):.3f} "
|
||||
f"(previsore di vol) ×{div:.3f} (diversificazione) = "
|
||||
f"{0.20*conv*(np.mean(vols_real)/np.mean(vols_pred))*div:.2%}")
|
||||
print(f" misurato: {tp.std()*365**.5:.2%}")
|
||||
print(f"\n 📌 IL FATTO CHE LO SCETTICATO NON DICE: il pezzo grosso NON e' lo stare flat, e'")
|
||||
print(f" che `tsmom_blend` e' la MEDIA DEI SEGNI di 3 orizzonti, quindi quando TP01 e'")
|
||||
print(f" 'a mercato' e' quasi sempre a 1/3 o 2/3 di convinzione. Contando lo zero, il")
|
||||
print(f" fattore di convinzione da solo vale ×{conv:.2f}. Lo stare flat e' UNA PARTE di")
|
||||
print(f" quel fattore, non il fattore.")
|
||||
print(f" Conseguenza operativa opposta a quella suggerita: se il divario fosse solo")
|
||||
print(f" 'flat', alzare target_vol non aumenterebbe il rischio nei giorni flat (dove non")
|
||||
print(f" c'e' posizione). Essendo CONVINZIONE, alzare target_vol alza la posizione")
|
||||
print(f" proprio nei giorni in cui il segnale e' PIU' DEBOLE (1 orizzonte su 3).")
|
||||
print(f"\n SCOMPOSIZIONE ESATTA (i due fattori si moltiplicano e chiudono il conto):")
|
||||
print(f" 20,00% ×{per_leg:.3f} (dentro la GAMBA) ×{div:.3f} (diversificazione BTC/ETH)"
|
||||
f" = {0.20*per_leg*div:.2%}")
|
||||
print(f" misurato sullo sleeve di produzione: {tp.std()*365**.5:.2%}")
|
||||
print(f"\n E il ×{per_leg:.3f} della gamba si spacca a sua volta in DUE meta' quasi uguali:")
|
||||
print(f" · sta FLAT nel {p_flat:.1%} delle barre (direzione 0) -> ×{f_flat:.3f}")
|
||||
print(f" · quando NON e' flat sta a convinzione PARZIALE (rms {conv/f_flat:.2f}) -> ×{f_conv:.3f}")
|
||||
print(f" prodotto sqrt(E[d²]) = ×{conv:.3f}; il residuo fino a ×{per_leg:.3f} e'")
|
||||
print(f" la correlazione fra convinzione e vol realizzata/prevista"
|
||||
f" (previsore di vol da solo: ×{np.mean(vols_real)/np.mean(vols_pred):.3f}).")
|
||||
print(f"\n 📌 QUELLO CHE LO SCETTICATO NON DICE, e sono DUE cose.")
|
||||
print(f" (i) «TP01 e' long-flat» spiega **circa META'** del divario (×{f_flat:.2f}), non")
|
||||
print(f" il divario. L'altra meta' e' che `tsmom_blend` e' la MEDIA DEI SEGNI di 3")
|
||||
print(f" orizzonti: quando TP01 e' a mercato sta quasi sempre a 1/3 o 2/3 di")
|
||||
print(f" convinzione (×{f_conv:.2f}). E la differenza NON e' accademica: se il divario")
|
||||
print(f" fosse solo 'flat', alzare target_vol non toccherebbe i giorni senza")
|
||||
print(f" posizione; essendo CONVINZIONE, alzarlo aumenta la size proprio nei giorni")
|
||||
print(f" in cui il segnale e' PIU' DEBOLE (1 orizzonte su 3 d'accordo).")
|
||||
print(f" (ii) un terzo fattore non sta nemmeno nello sleeve: ×{div:.2f} viene dal")
|
||||
print(f" COMBINARE BTC ed ETH a 50/50 (corr {J['BTC'].corr(J['ETH']):.2f}). Non e' un")
|
||||
print(f" difetto d'etichetta: e' diversificazione, ed e' esattamente cio' che si")
|
||||
print(f" vuole. Chiamare 'sovrastima del rischio' anche questo pezzo confonde una")
|
||||
print(f" proprieta' desiderata con un errore di misura.")
|
||||
|
||||
print("\n E TP01 E' UN CASO PARTICOLARE, non la regola — gli altri sleeve vol-targeted:")
|
||||
print(f"\n {'sleeve':>9}{'target dichiarato':>19}{'vol REALIZZATA':>16}{'su barre ATTIVE':>17}{'attive':>9}")
|
||||
@@ -590,6 +671,59 @@ def attacco2() -> None:
|
||||
print(" esattamente quella protetta dal test di guardia.")
|
||||
|
||||
|
||||
def attacco2b() -> None:
|
||||
"""Quali affermazioni pubblicate cambiano, e quali no — misurato, non argomentato."""
|
||||
from src.portfolio import sleeves as SL
|
||||
sezione("(6-bis) ATTACCO 2c — QUALI AFFERMAZIONI CAMBIANO (e la maggior parte NON cambia)")
|
||||
tp, sk = SL._tp01_returns(), SL._skyhook_returns()
|
||||
if tp.index.tz is None:
|
||||
tp = tp.tz_localize("UTC")
|
||||
if sk.index.tz is None:
|
||||
sk = sk.tz_localize("UTC")
|
||||
B = pd.concat({"TP01": tp, "SKH01": sk}, axis=1, join="inner").fillna(0.0)
|
||||
w = np.array([W_TP, W_SKH])
|
||||
X = B[["TP01", "SKH01"]].values
|
||||
bk = X @ w
|
||||
cov = np.cov(X.T)
|
||||
var_b = float(bk.var())
|
||||
rc = w * (cov @ w) / (cov @ w @ w)
|
||||
print("\n NON cambia NIENTE di misurato. Ogni Sharpe, maxDD, muro, traiettoria e rendita")
|
||||
print(" pubblicati sono calcolati sulla SERIE DI RITORNI REALIZZATA, mai sull'etichetta:")
|
||||
print(" `target_vol` entra nel codice come parametro di sizing e non compare in nessuna")
|
||||
print(" formula di rischio a valle. Un'etichetta che sovrastima non rende ottimista un")
|
||||
print(" numero che non la usa.")
|
||||
print("\n CAMBIA una classe di affermazioni sola: quelle che CONFRONTANO un parametro con")
|
||||
print(" una misura. Nel progetto ce n'e' una, ed e' nel libro LIVE:")
|
||||
print(f"\n {'sleeve':>8}{'peso NOZIONALE':>16}{'vol realizzata':>16}"
|
||||
f"{'contributo al RISCHIO':>23}")
|
||||
for i, nm in enumerate(("TP01", "SKH01")):
|
||||
print(f" {nm:>8}{w[i]:>16.0%}{X[:, i].std()*365**.5:>16.2%}{rc[i]:>23.1%}")
|
||||
print(f" {'libro':>8}{1.0:>16.0%}{bk.std()*365**.5:>16.2%}{sum(rc):>23.1%}")
|
||||
print(f" corr TP01/SKH01 {np.corrcoef(X.T)[0,1]:+.3f} vol libro {bk.std()*365**.5:.2%}")
|
||||
sig = X.std(axis=0) * 365 ** 0.5
|
||||
cf = (W_TP * 0.20) ** 2 / ((W_TP * 0.20) ** 2 + (W_SKH * sig[1]) ** 2)
|
||||
print("\n ❌ IPOTESI MIA, REFUTATA DAL MIO STESSO NUMERO. Mi aspettavo che l'etichetta")
|
||||
print(" nascondesse una ripartizione del rischio molto diversa dai pesi di nozionale.")
|
||||
print(f" Non e' cosi': {rc[0]*100:.0f}/{rc[1]*100:.0f} contro {W_TP*100:.0f}/{W_SKH*100:.0f}"
|
||||
f" — coincidono entro un paio di punti, e per una")
|
||||
print(" ragione che si legge in una riga: contributi di rischio ~ (w·σ)² con corr")
|
||||
print(f" {np.corrcoef(X.T)[0,1]:+.2f} ~ 0, e {W_TP:.2f}×{sig[0]:.1%} = {W_TP*sig[0]:.2%} contro"
|
||||
f" {W_SKH:.2f}×{sig[1]:.1%} = {W_SKH*sig[1]:.2%}")
|
||||
print(" stanno gia' in rapporto ~75/25. **Coincidenza numerica, non struttura.**")
|
||||
print("\n 📌 IL FATTO CHE RESTA, ed e' piu' utile dell'ipotesi che ho perso: l'errore non")
|
||||
print(" lo commette chi legge i PESI, lo commette chi legge l'ETICHETTA. Un lettore che")
|
||||
print(f" prendesse `target_vol=20%` per buono calcolerebbe TP01 al {cf*100:.0f}% del rischio")
|
||||
print(f" di libro; il vero e' {rc[0]*100:.0f}%. L'etichetta non rende ottimista nessun numero")
|
||||
print(" pubblicato (nessuno la usa) — sposta di ~16 punti l'ATTRIBUZIONE del rischio,")
|
||||
print(" cioe' proprio la domanda 'quale gamba mi fa male' su cui il progetto ha gia'")
|
||||
print(" tre misure indipendenti che indicano SKH01 come la fragile (LOO d'ancora,")
|
||||
print(" sensibilita' alla fee 4x, vol realizzata piu' alta).")
|
||||
print("\n E si precisa una frase: «SKH01 e' l'unica delle 5 a NON essere vol-targeted» e'")
|
||||
print(" esatta sul CODICE e va letta insieme al fatto che e' anche quella che di vol ne")
|
||||
print(" realizza di PIU' (21,0% contro 12,1%). Non e' una contraddizione: e' il motivo per")
|
||||
print(" cui il peso 25% e' stato confermato tre volte all'estremo prudente.")
|
||||
|
||||
|
||||
def guardia() -> None:
|
||||
sezione("(7) IL VINCOLO DURO — cosa protegge davvero il test di guardia")
|
||||
cfg = json.loads((ROOT / "config" / "live.json").read_text())
|
||||
@@ -614,5 +748,6 @@ def guardia() -> None:
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
attacco2()
|
||||
attacco2b()
|
||||
guardia()
|
||||
print(f"\n▸ totale: consegna nel messaggio dell'agente.")
|
||||
|
||||
Reference in New Issue
Block a user