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:
+23
-2
@@ -19,6 +19,7 @@ from src.live.shadow import shadow_report, tp01_trades
|
|||||||
from src.version import APP_VERSION
|
from src.version import APP_VERSION
|
||||||
|
|
||||||
PAPER = PROJECT_ROOT / "data" / "paper_portfolio" / "state.json"
|
PAPER = PROJECT_ROOT / "data" / "paper_portfolio" / "state.json"
|
||||||
|
PREVDAY = PROJECT_ROOT / "data" / "paper_prevday" / "state.json"
|
||||||
_CACHE = {"t": 0.0, "data": None}
|
_CACHE = {"t": 0.0, "data": None}
|
||||||
_TTL = 120.0
|
_TTL = 120.0
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ def build():
|
|||||||
step = max(1, len(eq) // 400)
|
step = max(1, len(eq) // 400)
|
||||||
spark = [(str(idx[i].date()), float(eq[i])) for i in range(0, len(eq), step)]
|
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
|
paper = json.loads(PAPER.read_text()) if PAPER.exists() else None
|
||||||
|
prevday = json.loads(PREVDAY.read_text()) if PREVDAY.exists() else None
|
||||||
try:
|
try:
|
||||||
shadow = shadow_report() # mainnet sola lettura, best-effort
|
shadow = shadow_report() # mainnet sola lettura, best-effort
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -46,8 +48,8 @@ def build():
|
|||||||
last_data=str(idx[-1].date()),
|
last_data=str(idx[-1].date()),
|
||||||
full=bt["full"], holdout=bt["holdout"], weights=bt["weights"],
|
full=bt["full"], holdout=bt["holdout"], weights=bt["weights"],
|
||||||
per_sleeve=bt["per_sleeve"], yearly=bt["yearly"],
|
per_sleeve=bt["per_sleeve"], yearly=bt["yearly"],
|
||||||
positions=pf.current_positions(), spark=spark, paper=paper, shadow=shadow, trades=trades,
|
positions=pf.current_positions(), spark=spark, paper=paper, prevday=prevday,
|
||||||
bh=None,
|
shadow=shadow, trades=trades, bh=None,
|
||||||
)
|
)
|
||||||
_CACHE.update(t=time.time(), data=data)
|
_CACHE.update(t=time.time(), data=data)
|
||||||
return data
|
return data
|
||||||
@@ -86,6 +88,22 @@ def html():
|
|||||||
f"{pp['last'][:10]}, {days}g) ret <b>{ret*100:+.2f}%</b> maxDD {pp['max_dd']*100:.1f}%")
|
f"{pp['last'][:10]}, {days}g) ret <b>{ret*100:+.2f}%</b> maxDD {pp['max_dd']*100:.1f}%")
|
||||||
else:
|
else:
|
||||||
paper_html = "non inizializzato (gira <code>paper_portfolio.py</code>)"
|
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.) · "
|
||||||
|
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> · "
|
||||||
|
f"maxDD {pv['dd_modeled']*100:.1f}% / {pv['dd_real']*100:.1f}% · "
|
||||||
|
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> · "
|
||||||
|
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")
|
sh = d.get("shadow")
|
||||||
if sh and "error" not in sh:
|
if sh and "error" not in sh:
|
||||||
bits = " · ".join(
|
bits = " · ".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>
|
<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>
|
<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 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="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>
|
<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>
|
<h3 style="font-size:14px;color:#8a93a0">Trades REALI eseguiti su Deribit</h3>
|
||||||
|
|||||||
Reference in New Issue
Block a user