research(wave-0822): TP01-SINISTRO — l'allarme e' falsificato nel verso; il rischio dell'ottimo funded e' XS01, non TP01

This commit is contained in:
Adriano Dal Pastro
2026-08-22 21:07:19 +00:00
parent 93ce12931a
commit 82ccff2553
5 changed files with 1430 additions and 67 deletions
+96 -40
View File
@@ -62,6 +62,7 @@ USO: nice -n 19 timeout 900 uv run python scripts/research/r0822b_bin_freq.py [
from __future__ import annotations
import argparse
import gc
import sys
import time
from pathlib import Path
@@ -132,6 +133,10 @@ class KeepCache:
self.rv: dict = {}
self.q: dict = {}
def clear(self) -> None:
self.rv.clear()
self.q.clear()
def keep(self, ex: dict, key, thr, w: int, h: float, sg: int) -> pd.Series:
base = ex["base_daily"]
k = (key, w)
@@ -152,6 +157,18 @@ class KeepCache:
KC = KeepCache()
def rss_mb() -> float:
"""RSS in MB, per non far morire il lavoro degli altri agenti (brief §8: 2 core, ~3 GB)."""
try:
with open("/proc/self/status") as f:
for ln in f:
if ln.startswith("VmRSS:"):
return float(ln.split()[1]) / 1024.0
except Exception:
pass
return float("nan")
def size_bin(thr, w, h, sg):
def f(ex, key):
return V.sizes_from_L(ex, KC.keep(ex, key, thr, w, h, sg))
@@ -175,7 +192,7 @@ def iso_pair(TP: pd.Series, leg: pd.Series, leg_base: pd.Series):
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--every", type=int, default=1)
ap.add_argument("--ndraw", type=int, default=200)
ap.add_argument("--ndraw", type=int, default=120)
args = ap.parse_args()
offs = OFFSETS_FULL[:: max(1, args.every)]
t0 = time.time()
@@ -208,8 +225,10 @@ def main() -> None:
print("\n estrazione tabelle trade (V2-DD) ...", flush=True)
EX = {o: {a: V.extract(a, o) for a in ASSETS} for o in offs}
ntr = {a: int(np.mean([len(EX[o][a]["i_ent"]) for o in offs])) for a in ASSETS}
R.get5m.cache_clear() # ~90 MB di 5m non servono durante la §4 (la VPS ha 2 core
gc.collect() # e ~3 GB liberi condivisi); si ricaricano in §9b in ~3 s
print(f" fatto in {time.time()-t0:.0f}s — trade medi/ancora: " +
", ".join(f"{a} {ntr[a]}" for a in ASSETS))
", ".join(f"{a} {ntr[a]}" for a in ASSETS) + f" (RSS {rss_mb():.0f} MB)")
TP = A.tp01_baseline_daily()
can = offs[0]
@@ -299,52 +318,71 @@ def main() -> None:
print(" 4. LA FAMIGLIA INTERA — ISO dShFULL, mediana delle differenze appaiate su "
f"{len(offs)} ancore")
print("-" * L)
# DUE PASSATE, per la RAM (7 GB sulla VPS, ~3 liberi, e siamo in tanti): la 1a calcola le
# metriche per-ancora di tutte le 144 celle SENZA conservare le serie (144 x 23 x 3 serie da
# 2717 punti sarebbero ~500 MB); la 2a ricalcola e CONSERVA le serie delle sole 3-4 celle che
# servono dopo (canonica / scelta al buio / argmax): 4 x 23 gambe, ~2 s.
# RAM (brief §8: 7 GB totali, ~3 liberi, e siamo in tanti). Tre accorgimenti, tutti
# necessari — la prima stesura di questo script e' stata UCCISA a meta' della §4:
# (a) DUE PASSATE: la 1a calcola le metriche per-ancora di tutte le 144 celle SENZA
# conservare le serie (144 x 23 x 3 serie da 2717 punti = ~500 MB); la 2a ricalcola e
# conserva le serie delle sole 3-4 celle che servono dopo (~2 s);
# (b) ciclo ANCORA-ESTERNO con la cache di rv/quantili SVUOTATA a ogni ancora: viva una sola
# ancora alla volta (72 serie invece di 1.656);
# (c) RSS stampata, cosi' se qualcuno la rilancia sa cosa aspettarsi.
BK, CT, LEG, MS = {}, {}, {}, {}
ACC: dict = {}
def eval_cell(thr, w, h, sg, store: bool = False) -> dict:
def _one(o, thr, w, h, sg, store: bool):
"""UNA cella a UNA ancora. Le size si calcolano una volta sola e si riusano (la prima
stesura le ricalcolava per il conteggio dei tenuti); la scomposizione per anno e il maxDD
si fanno SOLO nella seconda passata, perche' servono a 3 celle e costavano il 60% del
tempo su tutte e 144."""
nm = cname(thr, w, h, sg)
fn = size_bin(thr, w, h, sg)
dF, dH, dI, lF, lI, lH, ms, kp, ddv, ddc, dC_ = ([] for _ in range(11))
yr: dict = {}
for o in offs:
lg = leg_of(EX[o], o, fn)
b, c, m = iso_pair(TP, lg, base_leg[o])
dF.append(sh(b) - sh(c)); dH.append(sh(hold(b)) - sh(hold(c)))
dI.append(sh(ins(b)) - sh(ins(c))); dC_.append(cagr(b) - cagr(c))
lF.append(sh(lg)); lI.append(sh(ins(lg))); lH.append(sh(hold(lg)))
ms.append(m); ddv.append(maxdd(b)); ddc.append(maxdd(c))
kp.append(float(np.mean(np.concatenate([fn(EX[o][a], (o, a)) for a in ASSETS]))))
szs = {a: fn(EX[o][a], (o, a)) for a in ASSETS}
lg = V.leg_daily(EX[o], szs)
b, c, m = iso_pair(TP, lg, base_leg[o])
d = ACC.setdefault(nm, dict(thr=thr, w=w, h=h, sg=sg, dF=[], dH=[], dI=[], dC=[],
lF=[], lI=[], lH=[], ms=[], kp=[], ddv=[], ddc=[], yr={}))
d["dF"].append(sh(b) - sh(c)); d["dH"].append(sh(hold(b)) - sh(hold(c)))
d["dI"].append(sh(ins(b)) - sh(ins(c)))
d["lF"].append(sh(lg)); d["lI"].append(sh(ins(lg))); d["lH"].append(sh(hold(lg)))
d["ms"].append(m)
d["kp"].append(float(np.mean(np.concatenate(list(szs.values())))))
if store:
d["dC"].append(cagr(b) - cagr(c))
d["ddv"].append(maxdd(b)); d["ddc"].append(maxdd(c))
for y in {int(t) for t in b.index.year}:
by, cy = b[b.index.year == y], c[c.index.year == y]
if len(by) > 20:
yr.setdefault(y, []).append(sh(by) - sh(cy))
if store:
BK.setdefault(nm, {})[o] = b
CT.setdefault(nm, {})[o] = c
LEG.setdefault(nm, {})[o] = lg
MS.setdefault(nm, {})[o] = m
a_ = np.asarray(dF, float)
return dict(nm=nm, thr=thr, w=w, h=h, sg=sg,
dF=float(np.median(a_)), nF=int((a_ > 0).sum()),
lo=float(a_.min()), hi=float(a_.max()), arrF=a_,
dH=float(np.median(dH)), nH=int((np.asarray(dH) > 0).sum()),
isoI=float(np.median(dI)), dC=float(np.median(dC_)),
legF=float(np.median(lF)), legI=float(np.median(lI)),
legH=float(np.median(lH)), kept=float(np.median(kp)),
weff=float(np.median([W_SKH * x for x in ms])),
ddv=float(np.median(ddv)), ddc=float(np.median(ddc)),
yr={y: float(np.median(v)) for y, v in yr.items()})
d["yr"].setdefault(y, []).append(sh(by) - sh(cy))
BK.setdefault(nm, {})[o] = b
CT.setdefault(nm, {})[o] = c
LEG.setdefault(nm, {})[o] = lg
MS.setdefault(nm, {})[o] = m
RES = {}
for (thr, w, h, sg) in cells:
r = eval_cell(thr, w, h, sg)
RES[r["nm"]] = r
for o in offs:
KC.clear()
for (thr, w, h, sg) in cells:
_one(o, thr, w, h, sg, store=False)
KC.clear()
def _med(v, dflt=float("nan")):
return float(np.median(v)) if len(v) else dflt
def _fin(d: dict) -> dict:
a_ = np.asarray(d["dF"], float)
return dict(nm=cname(d["thr"], d["w"], d["h"], d["sg"]), thr=d["thr"], w=d["w"],
h=d["h"], sg=d["sg"], dF=float(np.median(a_)), nF=int((a_ > 0).sum()),
lo=float(a_.min()), hi=float(a_.max()), arrF=a_,
dH=float(np.median(d["dH"])), nH=int((np.asarray(d["dH"]) > 0).sum()),
isoI=float(np.median(d["dI"])), dC=_med(d["dC"]),
legF=float(np.median(d["lF"])), legI=float(np.median(d["lI"])),
legH=float(np.median(d["lH"])), kept=float(np.median(d["kp"])),
weff=float(np.median([W_SKH * x for x in d["ms"]])),
ddv=_med(d["ddv"]), ddc=_med(d["ddc"]),
yr={y: float(np.median(v)) for y, v in d["yr"].items()})
RES = {nm: _fin(d) for nm, d in ACC.items()}
print(f" ({len(cells)} celle x {len(offs)} ancore valutate in {time.time()-t0:.0f}s "
f"dall'avvio, prima passata senza conservare le serie)")
f"dall'avvio, prima passata senza conservare le serie; RSS {rss_mb():.0f} MB)")
def matrix(sg: int, h: float) -> None:
print(f"\n ISO dShFULL — segno {'+1 (tieni vol BASSA)' if sg>0 else '-1 (CONTROLLO)'}"
@@ -383,9 +421,19 @@ def main() -> None:
f"positive {sum(r['dF']>0 for r in NEG)}/{len(NEG)} <- controllo di segno")
# seconda passata: conserva le serie delle sole celle che servono dopo
sel_iso0 = max(ALLC, key=lambda r: r["isoI"])
ACC2 = ACC
for r in dict((x["nm"], x) for x in (RES[nmcan], argmax, sel_iso0,
max(ALLC, key=lambda z: z["legI"]))).values():
eval_cell(r["thr"], r["w"], r["h"], r["sg"], store=True)
ACC = {}
for o in offs:
_one(o, r["thr"], r["w"], r["h"], r["sg"], store=True)
KC.clear()
RES[r["nm"]] = _fin(ACC[r["nm"]]) # ora con dC / maxDD / per-anno
ACC = ACC2
POS = [RES[cname(t, w, h, +1)] for t in THRS for w in WS for h in HS]
NEG = [RES[cname(t, w, h, -1)] for t in THRS for w in WS for h in HS]
ALLC = POS + NEG
argmax = RES[argmax["nm"]]
dchk = A.anchor_luck_delta(lambda o: BK[nmcan][o], lambda o: CT[nmcan][o], offs, metric=sh)
assert abs(dchk["median_paired"] - RES[nmcan]["dF"]) < 1e-12 and \
dchk["n_positive"] == RES[nmcan]["nF"], "la statistica appaiata di §4 non coincide con A.anchor_luck_delta"
@@ -718,8 +766,10 @@ def main() -> None:
print(f" {tag:<40}{row}")
if fee != keep_fee:
del EXf, basef
gc.collect()
V.FEE_RT = keep_fee
assert V.FEE_RT == 0.001
print(f" (RSS {rss_mb():.0f} MB)")
f_mod = FEEDEC[(f"MODELLATA {keep_fee*1e4:.0f} bps RT (i backtest)", nmcan)]
f_zero = FEEDEC[("fee ZERO (informazione pura)", nmcan)]
print(f" -> a fee ZERO resta {f_zero:+.3f} di {f_mod:+.3f} = "
@@ -735,6 +785,8 @@ def main() -> None:
print(" temporale, ma e' l'unico campione indipendente disponibile a questo costo.")
print("-" * L)
EX1 = {o: {a: V.extract(a, o, SKH01_V1) for a in ASSETS} for o in offs}
R.get5m.cache_clear() # i 5m non servono piu': ~80 MB restituiti
gc.collect()
base1 = {o: V.leg_daily(EX1[o], {a: V.size_flat(EX1[o][a]) for a in ASSETS}) for o in offs}
print(f" {'cella':<26}{'ISO dShFULL':>13}{'pos/n':>8}{'ISO dShHOLD':>14}{'pos/n':>8}"
f"{'tenuti':>9}")
@@ -751,6 +803,10 @@ def main() -> None:
print(f" {cname(r['thr'], r['w'], r['h'], r['sg']):<26}{dF['median_paired']:>+13.3f}"
f"{dF['n_positive']:>5}/{len(offs):<3}{dH['median_paired']:>+14.3f}"
f"{dH['n_positive']:>5}/{len(offs):<3}{float(np.median(kp))*100:>8.0f}%")
del EX1, base1
KC.clear()
gc.collect()
print(f" (RSS {rss_mb():.0f} MB)")
# ---------------------------------------------------------- §11 per anno
print("\n" + "-" * L)