feat(dashboard): mostra il forward-monitor PREVDAY (lead ortogonale a TP01)

Nuova sezione "FORWARD-MONITOR — lead paper (non deploy)" nel dashboard, tra PAPER e LIVE:
legge data/paper_prevday/state.json e mostra i due libri (modeled €2k nominale vs real-$600
con min-order $5), ret/maxDD di entrambi, il fill-haircut, le posizioni correnti BTC/ETH e
i giorni/flip forward. Nota esplicita: LEAD in osservazione, NON deployato.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-06-21 19:27:27 +00:00
parent 5cce7acfe1
commit b5db59bea9
+23 -2
View File
@@ -19,6 +19,7 @@ from src.live.shadow import shadow_report, tp01_trades
from src.version import APP_VERSION
PAPER = PROJECT_ROOT / "data" / "paper_portfolio" / "state.json"
PREVDAY = PROJECT_ROOT / "data" / "paper_prevday" / "state.json"
_CACHE = {"t": 0.0, "data": None}
_TTL = 120.0
@@ -33,6 +34,7 @@ def build():
step = max(1, len(eq) // 400)
spark = [(str(idx[i].date()), float(eq[i])) for i in range(0, len(eq), step)]
paper = json.loads(PAPER.read_text()) if PAPER.exists() else None
prevday = json.loads(PREVDAY.read_text()) if PREVDAY.exists() else None
try:
shadow = shadow_report() # mainnet sola lettura, best-effort
except Exception as e:
@@ -46,8 +48,8 @@ def build():
last_data=str(idx[-1].date()),
full=bt["full"], holdout=bt["holdout"], weights=bt["weights"],
per_sleeve=bt["per_sleeve"], yearly=bt["yearly"],
positions=pf.current_positions(), spark=spark, paper=paper, shadow=shadow, trades=trades,
bh=None,
positions=pf.current_positions(), spark=spark, paper=paper, prevday=prevday,
shadow=shadow, trades=trades, bh=None,
)
_CACHE.update(t=time.time(), data=data)
return data
@@ -86,6 +88,22 @@ def html():
f"{pp['last'][:10]}, {days}g) &nbsp; ret <b>{ret*100:+.2f}%</b> &nbsp; maxDD {pp['max_dd']*100:.1f}%")
else:
paper_html = "non inizializzato (gira <code>paper_portfolio.py</code>)"
pv = d.get("prevday")
if pv:
days = (pv["last_ts"] - pv["start_ts"]) / 86400_000
rm = pv["cap_modeled"] / 2000.0 - 1
rr = pv["cap_real"] / 600.0 - 1
prevday_html = (
f"<b>${pv['cap_modeled']:,.2f}</b> modeled (€2k nom.) &nbsp;·&nbsp; "
f"<b>${pv['cap_real']:,.2f}</b> real-$600 (min-order $5)<br>"
f"ret <b>{rm*100:+.2f}%</b> / <b>{rr*100:+.2f}%</b> &nbsp;·&nbsp; "
f"maxDD {pv['dd_modeled']*100:.1f}% / {pv['dd_real']*100:.1f}% &nbsp;·&nbsp; "
f"fill-haircut {(rm-rr)*100:+.2f}pp<br>"
f"posizione: BTC <b>{pv['pos_real']['BTC']:+.3f}</b> · ETH <b>{pv['pos_real']['ETH']:+.3f}</b> &nbsp;·&nbsp; "
f"{pv['n_bars']} barre 1h (~{days:.0f}g) · {pv['n_trades']} flip · "
f"forward da {pd.Timestamp(pv['start_ts'], unit='ms').date()}")
else:
prevday_html = "non inizializzato (gira <code>scripts/live/paper_prevday.py</code>)"
sh = d.get("shadow")
if sh and "error" not in sh:
bits = " &nbsp;·&nbsp; ".join(
@@ -157,6 +175,9 @@ th{{color:#8a93a0;font-weight:500}}.y{{display:inline-block;background:#161b22;b
<h3 style="font-size:14px;color:#8a93a0">Trades TP01 — entry/exit (segnale causale, ultimi 15)</h3>
<table><tr><th>data</th><th>asset</th><th>azione</th><th>posizione</th><th>prezzo</th></tr>{trows}</table>
<div style="margin-top:10px">{yrs}</div>
<div class="section">FORWARD-MONITOR — lead paper (non deploy)</div>
<div class=box><b>PREVDAY range-breakout</b> — lead ORTOGONALE a TP01 (corr ~0.15 full / ~0 hold; marginal ADDS, non-hedge, robusto allo shift del confine-giorno). Forward-only, <b>nessuna esecuzione reale</b>:<br>{prevday_html}</div>
<p class=warn>⚠️ LEAD in osservazione, NON deployato. Sopravvissuto alla verifica avversariale dell'onda intraday; lo teniamo in paper per validarlo fuori-campione-vero. I due libri (modeled vs real-$600) mostrano l'haircut di fill che lo scettico aveva segnalato.</p>
<div class="section live">LIVE — Deribit mainnet (conto reale, sola lettura)</div>
<div class=box><b>Shadow TP01</b> (cosa farebbe ORA sul conto reale, nessun ordine inviato):<br>{shadow_html}</div>
<h3 style="font-size:14px;color:#8a93a0">Trades REALI eseguiti su Deribit</h3>