research(onchain): prima ondata on-chain/sentiment — 0/6 slot; l'on-chain tradabile e' prezzo travestito (corr TP01 0.5-0.8)
6 famiglie mai testate (NET Liu-Tsyvinski, MVRV, exchange-supply, hash ribbons, F&G, stablecoin-supply-growth) su segnali CoinMetrics community + alternative.me + DefiLlama, ritorni SOLO dal feed certificato, study_family_honest su 32 celle. EXS hold-out -0.58 (claim outflow=bullish decaduto), HASH=HEDGE, FNG corr 0.82 (trend travestito), MVRV DILUTES. Unico lead: STABLE thr=10% (DSR 0.998, ADDS persistente) ma robust_oos=False + caveat VINTAGE (storia DefiLlama ricostruita) -> WATCH, no paper. Regola nuova: classificare il rischio-vintage di ogni fonte esterna prima del backtest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -70,3 +70,6 @@ data/paper_statarb/
|
|||||||
|
|
||||||
# log esecuzioni del book live (stato runtime, contiene fill/fee del conto reale)
|
# log esecuzioni del book live (stato runtime, contiene fill/fee del conto reale)
|
||||||
data/live/
|
data/live/
|
||||||
|
|
||||||
|
# dati esterni di ricerca (on-chain CoinMetrics community, F&G) — non certificati, non in git
|
||||||
|
data/external/
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# 2026-07-24 — Ondata ON-CHAIN / SENTIMENT: la famiglia era vergine, il verdetto è 0/6 slot
|
||||||
|
|
||||||
|
**Goal**: "trova altre strategie, sfruttiamo anche internet". Prima parte (questa): la
|
||||||
|
famiglia on-chain/sentiment — l'unica grande famiglia di INFORMAZIONE (non di prezzo)
|
||||||
|
mai testata dal progetto — meccanizzata onestamente su dati gratuiti scaricati stasera.
|
||||||
|
Script `scripts/research/r0724_onchain_wave.py`. In parallelo girano 3 agenti di ricerca
|
||||||
|
web (letteratura accademica, segnali on-chain/fonti dati, pratica non convenzionale) —
|
||||||
|
sintesi in un diario/addendum separato quando completano.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
- **Segnali**: CoinMetrics Community (CSV daily dal genesis, gratuito, tokenless:
|
||||||
|
AdrActCnt, TxTfrCnt, CapMVRVCur, SplyExNtv, HashRate) + Fear&Greed alternative.me
|
||||||
|
(2018-02→oggi) + supply stablecoin totale DefiLlama (2017-11→oggi). Staging in
|
||||||
|
`data/external/coinmetrics/` (gitignored — NON certificato, mai usato come prezzo).
|
||||||
|
- **Ritorni**: SOLO feed Deribit certificato (lezione v2.0.0). Harness: `altlib`,
|
||||||
|
`study_family_honest` (selezione cella IN-SAMPLE, deflated-Sharpe sull'intera griglia,
|
||||||
|
marginal scorer indurito vs TP01). 32 celle totali su 6 famiglie, tutte contate.
|
||||||
|
- **Causalità**: doppio lag — segnale by-day shiftato di 1 giorno (la riga CM del giorno
|
||||||
|
d si completa a EOD d + processing) + lo shift di 1 barra di `eval_weights` ⇒ lag
|
||||||
|
attività→posizione = 2 giorni. tz-aware su entrambi i lati del reindex (la trappola
|
||||||
|
naive/aware ha già colpito stasera nel MC intraday).
|
||||||
|
|
||||||
|
## Verdetti (0/6 earns_slot_honest)
|
||||||
|
|
||||||
|
| Famiglia | Cella IS | DSR | Marginale | Perché muore |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **STABLE**-supply-growth | L=30 LF thr=10% | **0.998** | **ADDS** | robust_oos=False (uplift concentrato, 2026 domina) |
|
||||||
|
| **NET**-growth (Liu-Tsyvinski) | TxTfrCnt L=30 LF | 0.916 | ADDS | DSR<0.95 E jackknife −0.12 |
|
||||||
|
| **MVRV**-valuation | long-salvo-top p90 | 0.968 | DILUTES | hold-out −0.08, blend peggiora sempre |
|
||||||
|
| **EXS**-exchange-supply | L=90 LF | 0.903 | NEUTRAL | hold-out **−0.58**: il claim "outflow=bullish" è DECADUTO |
|
||||||
|
| **HASH**-ribbons (BTC-only) | 30/60 hold120 | 0.984 | **HEDGE** | paga solo quando TP01 è debole (corr −0.67) |
|
||||||
|
| **FNG**-fear-greed | regime media7>50 | 0.955 | NEUTRAL | corr→TP01 **0.82**: è trend travestito |
|
||||||
|
|
||||||
|
## Il finding strutturale
|
||||||
|
|
||||||
|
**L'on-chain tradabile è in larga parte PREZZO TRAVESTITO**: corr→TP01 0.50-0.82 su
|
||||||
|
tutte le famiglie. L'attività di rete, il sentiment e la supply su exchange *seguono*
|
||||||
|
il prezzo; quando ne estrai un segnale direzionale daily ottieni un trend-follower
|
||||||
|
peggiore di TP01 con un vocabolario diverso. È la stessa firma delle onde macro-gate
|
||||||
|
(corr 0.989) e funding: la barra per un nuovo sleeve non è "informazione nuova" ma
|
||||||
|
"informazione nuova NON già incorporata nel prezzo che TP01 segue".
|
||||||
|
|
||||||
|
## Il lead (debole): STABLE-supply-growth
|
||||||
|
|
||||||
|
L'unico sopra la linea su quasi tutti i gate: la crescita 30g della supply stablecoin
|
||||||
|
totale (soglia 10% annualizzato) come gate risk-on. Standalone FULL 1.25/HOLD 0.42,
|
||||||
|
DSR 0.998, ADDS persistente multi-cut, non-hedge, blend w25 hold +0.12. Muore SOLO su
|
||||||
|
robust_oos (drop-best-month −0.06; l'uplift 2026 = 1.32 domina la persistenza).
|
||||||
|
|
||||||
|
⚠️ **Caveat VINTAGE che declassa il lead a WATCH**: la storia DefiLlama è RICOSTRUITA
|
||||||
|
retroattivamente (chain e coin aggiunte nel tempo) — il "totale supply" visto oggi per
|
||||||
|
il 2019-2021 NON era osservabile allora. Un backtest su serie vintage-contaminate può
|
||||||
|
inventare edge. Idem (più mite) per le metriche exchange-flow CM (labeling dei wallet
|
||||||
|
rivisto ex-post). **Regola nuova da questa ondata: ogni segnale da aggregatori esterni
|
||||||
|
va classificato per rischio-vintage prima del backtest; un PASS su serie ricostruita
|
||||||
|
vale al massimo WATCH.**
|
||||||
|
|
||||||
|
Decisione: **WATCH, niente paper-monitor** (corr 0.61-0.67 a TP01 = il beneficio
|
||||||
|
marginale reale è piccolo; il vintage-risk non è sanabile retroattivamente). Criterio
|
||||||
|
di rivisita: se una fonte point-in-time della supply stablecoin (snapshot propri da
|
||||||
|
oggi, o fonte con vintage garantito) accumula ≥12 mesi, ritestare il gate thr=10%.
|
||||||
|
|
||||||
|
## Note dati (per il futuro)
|
||||||
|
|
||||||
|
- CM community: fermo al **2026-05-24** (~2 mesi di lag) → inutilizzabile live senza
|
||||||
|
fonte fresca; per backtest ok.
|
||||||
|
- ETH HashRate muore al Merge (2022-09) → hash-famiglia solo BTC.
|
||||||
|
- F&G è DERIVATO da vol+momentum di prezzo → la ridondanza col trend era attesa e
|
||||||
|
lo scorer l'ha confermata.
|
||||||
|
|
||||||
|
Book/pesi INVARIATI. Test suite invariata (172 pass).
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
"""r0724_onchain_wave — prima ondata ON-CHAIN / SENTIMENT (famiglia MAI testata) (2026-07-24).
|
||||||
|
|
||||||
|
Goal "trova altre strategie": la famiglia on-chain e' l'unica grande famiglia di
|
||||||
|
INFORMAZIONE (non di prezzo) mai toccata dal progetto. Fonte segnali: CoinMetrics
|
||||||
|
Community (github.com/coinmetrics/data, CSV daily dal genesis, gratuito) + Fear&Greed
|
||||||
|
(alternative.me, dal 2018-02). I RITORNI restano SOLO dal feed certificato Deribit
|
||||||
|
(lezione v2.0.0: i dati esterni sono segnale, mai prezzo).
|
||||||
|
|
||||||
|
5 famiglie, ognuna giudicata con `study_family_honest` (selezione cella IN-SAMPLE,
|
||||||
|
deflated-Sharpe sull'INTERA griglia, marginal scorer indurito vs TP01):
|
||||||
|
NET — network-growth momentum (AdrActCnt / TxTfrCnt, stile Liu-Tsyvinski)
|
||||||
|
MVRV — valuation gate su CapMVRVCur (percentile causale espandente)
|
||||||
|
EXS — supply su exchange (SplyExNtv: accumulo = coin che LASCIANO gli exchange)
|
||||||
|
HASH — hash ribbons BTC-only (capitolazione/recovery miner; ETH post-Merge = 0)
|
||||||
|
FNG — Fear&Greed contrarian (long dopo paura estrema)
|
||||||
|
|
||||||
|
CAUSALITA' (doppio lag): la riga CoinMetrics del giorno d si completa a fine giorno d
|
||||||
|
(+ ore di processing) -> al close del bar daily d (=00:00 UTC di d+1) l'ultima riga
|
||||||
|
SICURAMENTE nota e' d-1 => segnale shiftato di 1 GIORNO prima del mapping sui bar;
|
||||||
|
eval_weights shifta di un altro bar (decisione a close[i], hold i+1) => lag totale
|
||||||
|
attivita'->posizione = 2 giorni. F&G: pubblicato ~00:00 UTC del giorno stesso ->
|
||||||
|
stesso trattamento conservativo.
|
||||||
|
|
||||||
|
CAVEAT DATI (dichiarati): CM community aggiornato al 2026-05-24 (~2 mesi di lag: ok
|
||||||
|
per ricerca, NON per un deploy senza fonte fresca); metriche exchange-flow = stima
|
||||||
|
CM dei wallet exchange (proxy, non verita'); F&G e' in parte DERIVATO dal prezzo
|
||||||
|
(vol+momentum) -> rischio ridondanza col trend, il marginal scorer lo vede.
|
||||||
|
|
||||||
|
Uso: `uv run python scripts/research/r0724_onchain_wave.py`
|
||||||
|
Dati attesi in data/external/coinmetrics/ (cm_btc.csv, cm_eth.csv, fng.json);
|
||||||
|
per aggiornare: scaricare di nuovo dalle fonti (URL nei commenti di _load_cm/_load_fng).
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
||||||
|
|
||||||
|
import altlib # noqa: E402
|
||||||
|
from altlib import study_family_honest, fmt_marginal # noqa: E402
|
||||||
|
|
||||||
|
EXT = ROOT / "data" / "external" / "coinmetrics"
|
||||||
|
AVAIL_LAG_D = 1 # giorni di lag di disponibilita' del segnale PRIMA del mapping sui bar
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ dati segnale
|
||||||
|
|
||||||
|
def _load_cm(asset: str) -> pd.DataFrame:
|
||||||
|
"""CoinMetrics community: https://raw.githubusercontent.com/coinmetrics/data/master/csv/{btc,eth}.csv"""
|
||||||
|
df = pd.read_csv(EXT / f"cm_{asset.lower()}.csv", low_memory=False)
|
||||||
|
idx = pd.DatetimeIndex(pd.to_datetime(df["time"], utc=True)).floor("D")
|
||||||
|
out = df.drop(columns=["time"]).apply(pd.to_numeric, errors="coerce")
|
||||||
|
out.index = idx
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _load_fng() -> pd.Series:
|
||||||
|
"""alternative.me: https://api.alternative.me/fng/?limit=0&format=json"""
|
||||||
|
d = json.loads((EXT / "fng.json").read_text())["data"]
|
||||||
|
ts = pd.to_datetime([int(x["timestamp"]) for x in d], unit="s", utc=True).floor("D")
|
||||||
|
return pd.Series([float(x["value"]) for x in d], index=ts).sort_index()
|
||||||
|
|
||||||
|
|
||||||
|
def _load_stables() -> pd.Series:
|
||||||
|
"""DefiLlama: https://stablecoins.llama.fi/stablecoincharts/all -> supply USD totale
|
||||||
|
stablecoin per giorno (dal 2017-11). Liquidita' 'dry powder' NON derivata dal prezzo BTC."""
|
||||||
|
d = json.loads((EXT / "stables.json").read_text())
|
||||||
|
ts = pd.to_datetime([int(x["date"]) for x in d], unit="s", utc=True).floor("D")
|
||||||
|
v = [float(x.get("totalCirculating", {}).get("peggedUSD", np.nan)) for x in d]
|
||||||
|
return pd.Series(v, index=ts).sort_index()
|
||||||
|
|
||||||
|
|
||||||
|
_CM = {a: _load_cm(a) for a in ("BTC", "ETH")}
|
||||||
|
_FNG = _load_fng()
|
||||||
|
_STB = _load_stables()
|
||||||
|
|
||||||
|
|
||||||
|
def _to_target(df: pd.DataFrame, sig_by_day: pd.Series) -> np.ndarray:
|
||||||
|
"""Mappa un segnale by-day sui bar del df certificato con lag di disponibilita'.
|
||||||
|
tz-aware su entrambi i lati (lezione della sera: naive-vs-aware nel reindex = NaN->0
|
||||||
|
silenziosi)."""
|
||||||
|
days = pd.DatetimeIndex(pd.to_datetime(df["datetime"], utc=True)).floor("D")
|
||||||
|
known = sig_by_day.shift(AVAIL_LAG_D)
|
||||||
|
return np.nan_to_num(known.reindex(days).values.astype(float))
|
||||||
|
|
||||||
|
|
||||||
|
def _expanding_pctl(x: pd.Series, minp: int = 365) -> pd.Series:
|
||||||
|
"""Percentile causale espandente di x[t] nella storia fino a t (incluso)."""
|
||||||
|
v = x.values.astype(float)
|
||||||
|
out = np.full(len(v), np.nan)
|
||||||
|
order: list[float] = []
|
||||||
|
import bisect
|
||||||
|
for i, xi in enumerate(v):
|
||||||
|
if np.isfinite(xi):
|
||||||
|
bisect.insort(order, xi)
|
||||||
|
if len(order) >= minp:
|
||||||
|
out[i] = bisect.bisect_left(order, xi) / len(order)
|
||||||
|
return pd.Series(out, index=x.index)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ famiglie
|
||||||
|
|
||||||
|
def net_factory(tf: str, metric: str = "AdrActCnt", L: int = 30, mode: str = "LF"):
|
||||||
|
def fn(df, asset):
|
||||||
|
m = _CM[asset][metric]
|
||||||
|
sm = np.log(m.rolling(7, min_periods=4).mean())
|
||||||
|
sig = sm - sm.shift(L)
|
||||||
|
pos = np.sign(sig) if mode == "LS" else (sig > 0).astype(float)
|
||||||
|
return _to_target(df, pos)
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
def mvrv_factory(tf: str, lo: float = 0.3, hi: float = 1.1, mode: str = "LF"):
|
||||||
|
"""LF lo: long solo se MVRV-pctl < lo (compra paura di valuation).
|
||||||
|
LF hi: long salvo top (pctl < hi). LS: +1 sotto lo, -1 sopra hi."""
|
||||||
|
def fn(df, asset):
|
||||||
|
p = _expanding_pctl(_CM[asset]["CapMVRVCur"])
|
||||||
|
if mode == "LS":
|
||||||
|
pos = pd.Series(np.where(p < lo, 1.0, np.where(p > hi, -1.0, 0.0)), index=p.index)
|
||||||
|
else:
|
||||||
|
pos = (p < (lo if hi > 1.0 else hi)).astype(float) if hi > 1.0 else (p < hi).astype(float)
|
||||||
|
return _to_target(df, pos)
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
def exs_factory(tf: str, L: int = 30, mode: str = "LF"):
|
||||||
|
def fn(df, asset):
|
||||||
|
s = np.log(_CM[asset]["SplyExNtv"].where(_CM[asset]["SplyExNtv"] > 0))
|
||||||
|
sig = -(s - s.shift(L)) # supply che LASCIA gli exchange = accumulo = +
|
||||||
|
pos = np.sign(sig) if mode == "LS" else (sig > 0).astype(float)
|
||||||
|
return _to_target(df, pos)
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
def hash_factory(tf: str, fast: int = 30, slow: int = 60, mode: str = "ribbon"):
|
||||||
|
"""BTC-only. ribbon: long da recovery-cross (fast riattraversa sopra slow) fino alla
|
||||||
|
prossima capitolazione (fast sotto slow). holdH: long per H giorni dal recovery."""
|
||||||
|
def fn(df, asset):
|
||||||
|
if asset != "BTC":
|
||||||
|
return np.zeros(len(df))
|
||||||
|
h = _CM[asset]["HashRate"]
|
||||||
|
f, s = h.rolling(fast).mean(), h.rolling(slow).mean()
|
||||||
|
above = (f > s).astype(float)
|
||||||
|
if mode == "ribbon":
|
||||||
|
pos = above # long quando il ribbon e' sano
|
||||||
|
else: # recovery: long H giorni dal cross-up
|
||||||
|
H = int(mode[4:])
|
||||||
|
cross_up = (above.diff() > 0)
|
||||||
|
pos = cross_up.rolling(H, min_periods=1).max().fillna(0.0)
|
||||||
|
return _to_target(df, pos)
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
def fng_factory(tf: str, lo: int = 20, H: int = 30, mode: str = "fear"):
|
||||||
|
"""fear: long H giorni dopo F&G < lo (contrarian). regime: long quando media7 > 50."""
|
||||||
|
def fn(df, asset):
|
||||||
|
g = _FNG
|
||||||
|
if mode == "regime":
|
||||||
|
pos = (g.rolling(7, min_periods=4).mean() > 50).astype(float)
|
||||||
|
else:
|
||||||
|
trig = (g < lo)
|
||||||
|
pos = trig.rolling(H, min_periods=1).max().fillna(0.0)
|
||||||
|
return _to_target(df, pos)
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
def stable_factory(tf: str, L: int = 30, mode: str = "LF", thr: float = 0.0):
|
||||||
|
"""Crescita della supply stablecoin totale (liquidita' in ingresso nel sistema).
|
||||||
|
LF: long se crescita L-giorni > thr (annualizzata), flat altrimenti. LS: segno."""
|
||||||
|
def fn(df, asset):
|
||||||
|
s = np.log(_STB.where(_STB > 0))
|
||||||
|
sig = (s - s.shift(L)) * (365.0 / L) - thr
|
||||||
|
pos = np.sign(sig) if mode == "LS" else (sig > 0).astype(float)
|
||||||
|
return _to_target(df, pos)
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
FAMILIES = [
|
||||||
|
("STABLE-supply-growth", stable_factory, [
|
||||||
|
dict(L=30, mode="LF", thr=0.0), dict(L=90, mode="LF", thr=0.0),
|
||||||
|
dict(L=30, mode="LF", thr=0.10), dict(L=90, mode="LF", thr=0.10),
|
||||||
|
dict(L=30, mode="LS", thr=0.0), dict(L=90, mode="LS", thr=0.0),
|
||||||
|
]),
|
||||||
|
("NET-growth", net_factory, [
|
||||||
|
dict(metric="AdrActCnt", L=30, mode="LF"), dict(metric="AdrActCnt", L=90, mode="LF"),
|
||||||
|
dict(metric="AdrActCnt", L=30, mode="LS"), dict(metric="AdrActCnt", L=90, mode="LS"),
|
||||||
|
dict(metric="TxTfrCnt", L=30, mode="LF"), dict(metric="TxTfrCnt", L=90, mode="LF"),
|
||||||
|
dict(metric="TxTfrCnt", L=30, mode="LS"), dict(metric="TxTfrCnt", L=90, mode="LS"),
|
||||||
|
]),
|
||||||
|
("MVRV-valuation", mvrv_factory, [
|
||||||
|
dict(lo=0.2, hi=9.9, mode="LF"), dict(lo=0.3, hi=9.9, mode="LF"),
|
||||||
|
dict(lo=0.0, hi=0.8, mode="LF"), dict(lo=0.0, hi=0.9, mode="LF"),
|
||||||
|
dict(lo=0.2, hi=0.8, mode="LS"), dict(lo=0.3, hi=0.9, mode="LS"),
|
||||||
|
]),
|
||||||
|
("EXS-exchange-supply", exs_factory, [
|
||||||
|
dict(L=30, mode="LF"), dict(L=90, mode="LF"),
|
||||||
|
dict(L=30, mode="LS"), dict(L=90, mode="LS"),
|
||||||
|
]),
|
||||||
|
("HASH-ribbons-BTC", hash_factory, [
|
||||||
|
dict(fast=30, slow=60, mode="ribbon"),
|
||||||
|
dict(fast=30, slow=60, mode="hold60"), dict(fast=30, slow=60, mode="hold120"),
|
||||||
|
]),
|
||||||
|
("FNG-fear-greed", fng_factory, [
|
||||||
|
dict(lo=15, H=10, mode="fear"), dict(lo=15, H=30, mode="fear"),
|
||||||
|
dict(lo=25, H=10, mode="fear"), dict(lo=25, H=30, mode="fear"),
|
||||||
|
dict(mode="regime"),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("=" * 100)
|
||||||
|
print(" ONDATA ON-CHAIN / SENTIMENT — 5 famiglie via study_family_honest (gate completi)")
|
||||||
|
print(f" CM: BTC {_CM['BTC'].index[0].date()}->{_CM['BTC'].index[-1].date()}, "
|
||||||
|
f"ETH {_CM['ETH'].index[0].date()}->{_CM['ETH'].index[-1].date()} | "
|
||||||
|
f"F&G {_FNG.index[0].date()}->{_FNG.index[-1].date()}")
|
||||||
|
print("=" * 100)
|
||||||
|
n_cells_tot = sum(len(g) for _, _, g in FAMILIES)
|
||||||
|
print(f" trial totali dichiarati: {n_cells_tot} celle su 5 famiglie "
|
||||||
|
"(tutte contate nel deflated-Sharpe di famiglia)\n")
|
||||||
|
results = []
|
||||||
|
for name, factory, grid in FAMILIES:
|
||||||
|
print("-" * 100)
|
||||||
|
rep = study_family_honest(name, factory, grid, tfs=("1d",))
|
||||||
|
results.append(rep)
|
||||||
|
if rep.get("chosen") is None:
|
||||||
|
print(f"=== {name}: nessuna cella valida in-sample")
|
||||||
|
continue
|
||||||
|
ch = rep["chosen"]
|
||||||
|
print(f"=== {name}: cella IS {ch['params']} (IS Sh {ch['insample_sharpe']}, "
|
||||||
|
f"full {ch['full_sharpe']}) su {rep['n_cells']} celle")
|
||||||
|
print(f" deflated-Sharpe {rep['deflated_sharpe']} (null-max atteso "
|
||||||
|
f"{rep['expected_null_max']}) dsr_pass={rep['dsr_pass']}")
|
||||||
|
print(fmt_marginal(rep["marginal"]) if isinstance(rep["marginal"], str)
|
||||||
|
else fmt_marginal(rep["marginal"]))
|
||||||
|
print(f" >>> EARNS_SLOT_HONEST = {rep['earns_slot_honest']}")
|
||||||
|
print("\n" + "=" * 100)
|
||||||
|
print(" SINTESI")
|
||||||
|
print("=" * 100)
|
||||||
|
for rep in results:
|
||||||
|
ch = rep.get("chosen")
|
||||||
|
lab = "no-cell" if ch is None else (
|
||||||
|
f"IS{ch['params']} dsr={rep.get('deflated_sharpe')} "
|
||||||
|
f"marg={rep['marginal'].get('marginal_verdict') if isinstance(rep.get('marginal'), dict) else rep['marginal']['marginal_verdict']}")
|
||||||
|
print(f" {rep['name']:<24} earns_slot_honest={rep.get('earns_slot_honest')} {lab}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user