feat(dashboard): riordino LIVE→trades→storico + trade/anno contati
- dashboard riorganizzata in 3 sezioni: ① LIVE (mainnet sola lettura) in alto, ② TRADES ESEGUITI (reali) + frequenza operativa al centro, ③ STORICO (backtest/forward simulato) in fondo (COMBO/BOOK/FORWARD come ③·a/b/c). - book_trade_frequency() in sleeves.py: trade/anno CONTATI sui dati certificati (cache di modulo, una volta per processo). SKH01 round-trip BTC ~37 / ETH ~43 -> ~75/anno combinato; TP01 turnover ~7x/anno. Card "trade/anno" + blocco freq. - fix: collisione var `pos` nel ramo shadow-online (-> shpos) che avrebbe rotto la tabella posizioni se il conto fosse leggibile dal container. - fix: turnover TP01 nan (disallineamento groupby) -> groupby posizionale, 7.2x/anno. - 56 test pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+45
-15
@@ -14,7 +14,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|||||||
sys.path.insert(0, str(PROJECT_ROOT))
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
import numpy as np, pandas as pd
|
import numpy as np, pandas as pd
|
||||||
from src.portfolio.portfolio import StrategyPortfolio, metrics, HOLDOUT
|
from src.portfolio.portfolio import StrategyPortfolio, metrics, HOLDOUT
|
||||||
from src.portfolio.sleeves import active_sleeves
|
from src.portfolio.sleeves import active_sleeves, book_trade_frequency
|
||||||
from src.portfolio.gtaa import gtaa_weights
|
from src.portfolio.gtaa import gtaa_weights
|
||||||
from src.live.shadow import shadow_report, tp01_trades
|
from src.live.shadow import shadow_report, tp01_trades
|
||||||
from src.version import APP_VERSION
|
from src.version import APP_VERSION
|
||||||
@@ -65,6 +65,10 @@ def build():
|
|||||||
trades = tp01_trades(limit=15) # entry/exit TP01 dal segnale causale
|
trades = tp01_trades(limit=15) # entry/exit TP01 dal segnale causale
|
||||||
except Exception:
|
except Exception:
|
||||||
trades = []
|
trades = []
|
||||||
|
try:
|
||||||
|
freq = book_trade_frequency() # trade/anno contati (SKH01 round-trip + TP01 turnover)
|
||||||
|
except Exception as e:
|
||||||
|
freq = {"error": f"{type(e).__name__}: {e}"}
|
||||||
data = dict(
|
data = dict(
|
||||||
version=APP_VERSION,
|
version=APP_VERSION,
|
||||||
last_data=str(idx[-1].date()),
|
last_data=str(idx[-1].date()),
|
||||||
@@ -72,7 +76,7 @@ def build():
|
|||||||
per_sleeve=bt["per_sleeve"], yearly=bt["yearly"],
|
per_sleeve=bt["per_sleeve"], yearly=bt["yearly"],
|
||||||
positions=pf.current_positions(), spark=spark, paper=paper, prevday=prevday,
|
positions=pf.current_positions(), spark=spark, paper=paper, prevday=prevday,
|
||||||
combo=combo, gtaa_weights=gtaa_w, deribit=deribit,
|
combo=combo, gtaa_weights=gtaa_w, deribit=deribit,
|
||||||
shadow=shadow, trades=trades, bh=None,
|
shadow=shadow, trades=trades, freq=freq, bh=None,
|
||||||
)
|
)
|
||||||
_CACHE.update(t=time.time(), data=data)
|
_CACHE.update(t=time.time(), data=data)
|
||||||
return data
|
return data
|
||||||
@@ -180,13 +184,13 @@ def html():
|
|||||||
f"{a['target']:+.2f}x" for a in sh["assets"])
|
f"{a['target']:+.2f}x" for a in sh["assets"])
|
||||||
if sh.get("online"):
|
if sh.get("online"):
|
||||||
eq = f"${sh['real_equity']:,.2f}" if sh.get("real_equity") else sh.get("eq_basis", "?")
|
eq = f"${sh['real_equity']:,.2f}" if sh.get("real_equity") else sh.get("eq_basis", "?")
|
||||||
pos = ", ".join(f"{a['asset']} ${a['position_usd']:,.0f}" for a in sh["assets"])
|
shpos = ", ".join(f"{a['asset']} ${a['position_usd']:,.0f}" for a in sh["assets"])
|
||||||
ordtxt = ("; ".join(f"{o['side'].upper()} {o['amount']:.0f} {o['instrument']}" for o in sh["orders"])
|
ordtxt = ("; ".join(f"{o['side'].upper()} {o['amount']:.0f} {o['instrument']}" for o in sh["orders"])
|
||||||
if sh.get("orders") else "nessuno (target flat / gia' a target)")
|
if sh.get("orders") else "nessuno (target flat / gia' a target)")
|
||||||
dsl = sh.get("disaster_sls") or []
|
dsl = sh.get("disaster_sls") or []
|
||||||
dsl_txt = (" · ".join(f"{x['asset']} stop <b>${x['stop']:,.0f}</b> ({x['amount']:.4f})" for x in dsl)
|
dsl_txt = (" · ".join(f"{x['asset']} stop <b>${x['stop']:,.0f}</b> ({x['amount']:.4f})" for x in dsl)
|
||||||
if dsl else "nessuno (flat)")
|
if dsl else "nessuno (flat)")
|
||||||
shadow_html = (f"mainnet · sola lettura · conto reale <b>{eq}</b> · pos {pos} · dato {sh['last_data']}<br>"
|
shadow_html = (f"mainnet · sola lettura · conto reale <b>{eq}</b> · pos {shpos} · dato {sh['last_data']}<br>"
|
||||||
f"TP01 target: {bits}<br>ordini-che-invierebbe (<b>NON inviati</b>): {ordtxt}<br>"
|
f"TP01 target: {bits}<br>ordini-che-invierebbe (<b>NON inviati</b>): {ordtxt}<br>"
|
||||||
f"🛡️ disaster-SL attivi (−30%): {dsl_txt}")
|
f"🛡️ disaster-SL attivi (−30%): {dsl_txt}")
|
||||||
else:
|
else:
|
||||||
@@ -212,6 +216,24 @@ def html():
|
|||||||
f"<td>{x['amount']:.4f}</td><td>${x['price']:,.1f}</td><td>{x['fee']:.5f}</td></tr>")
|
f"<td>{x['amount']:.4f}</td><td>${x['price']:,.1f}</td><td>{x['fee']:.5f}</td></tr>")
|
||||||
if not live_trows:
|
if not live_trows:
|
||||||
live_trows = "<tr><td colspan=6 style='color:#8a93a0'>nessun trade reale eseguito (o conto non leggibile dal container)</td></tr>"
|
live_trows = "<tr><td colspan=6 style='color:#8a93a0'>nessun trade reale eseguito (o conto non leggibile dal container)</td></tr>"
|
||||||
|
fq = d.get("freq")
|
||||||
|
if fq and "error" not in fq:
|
||||||
|
skh = fq["skh"]
|
||||||
|
by = lambda a: " ".join(f"{y}:{c}" for y, c in skh[a]["by_year"].items())
|
||||||
|
tpy = fq["skh_combo_per_year"]
|
||||||
|
freq_html = (
|
||||||
|
f"<b>SKH01</b> (round-trip = apri+chiudi, no-overlap): BTC <b>~{fq['skh_per_year']['BTC']:.0f}/anno</b> · "
|
||||||
|
f"ETH <b>~{fq['skh_per_year']['ETH']:.0f}/anno</b> → combinato <b>~{tpy:.0f}/anno</b> "
|
||||||
|
f"(~{2*tpy:.0f} esecuzioni)<br>"
|
||||||
|
f"<b>TP01</b> (posizione continua vol-target, NON trade discreti): turnover "
|
||||||
|
f"<b>~{fq['tp01_turnover_per_year']:.0f}×/anno</b> per asset · zero quando flat/risk-off<br>"
|
||||||
|
f"<span style='color:#8a93a0;font-size:13px'>→ book eseguibile ~{tpy:.0f}-{tpy+5:.0f} operazioni/anno: "
|
||||||
|
f"poche per design (regime AND breakout). Fee ~$45-60/anno, gia' nette nei numeri storici · min-order $5 OK a $600.<br>"
|
||||||
|
f"SKH01 round-trip/anno — BTC: {by('BTC')} · ETH: {by('ETH')}</span>")
|
||||||
|
tpy_card = f"~{tpy:.0f}"
|
||||||
|
else:
|
||||||
|
freq_html = "n/d" + (f" — {fq['error']}" if fq and fq.get("error") else "")
|
||||||
|
tpy_card = "~75"
|
||||||
return f"""<!doctype html><html><head><meta charset=utf-8>
|
return f"""<!doctype html><html><head><meta charset=utf-8>
|
||||||
<meta http-equiv=refresh content=300><title>PythagorasGoal — Portafoglio</title>
|
<meta http-equiv=refresh content=300><title>PythagorasGoal — Portafoglio</title>
|
||||||
<style>body{{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:#0e1116;color:#e6e6e6;margin:0;padding:24px;max-width:980px;margin:auto}}
|
<style>body{{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:#0e1116;color:#e6e6e6;margin:0;padding:24px;max-width:980px;margin:auto}}
|
||||||
@@ -226,14 +248,26 @@ th{{color:#8a93a0;font-weight:500}}.y{{display:inline-block;background:#161b22;b
|
|||||||
.section{{font-size:15px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;margin:34px 0 14px;padding:10px 14px;border-radius:9px;background:#12181f;border-left:5px solid #2ecc71;color:#d7dee6}}
|
.section{{font-size:15px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;margin:34px 0 14px;padding:10px 14px;border-radius:9px;background:#12181f;border-left:5px solid #2ecc71;color:#d7dee6}}
|
||||||
.section.live{{border-left-color:#e74c3c;background:#1c1316;color:#f0c4c4}}</style></head><body>
|
.section.live{{border-left-color:#e74c3c;background:#1c1316;color:#f0c4c4}}</style></head><body>
|
||||||
<h1>PythagorasGoal — Portafoglio attivo (TP01 + XS01 + VRP01 + SKH01)</h1>
|
<h1>PythagorasGoal — Portafoglio attivo (TP01 + XS01 + VRP01 + SKH01)</h1>
|
||||||
<div class=sub>monitor · v{d['version']} · ultimo dato {d['last_data']} · esecuzione REALE non attiva (solo micro-test)</div>
|
<div class=sub>monitor · v{d['version']} · ultimo dato {d['last_data']} · esecuzione REALE non attiva (solo micro-test) · ordine: LIVE → trades fatti → storico</div>
|
||||||
<div class="section">PAPER — simulato (backtest + forward virtuale)</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>
|
||||||
|
<p class=warn>Solo TP01 e' armato live (cap $300/asset · disaster-SL on-book −30% · capitale reale ≈ $600). SKH01/XS01/VRP01 = paper/stat-mode. Lo "Shadow" mostra il target reale ma NON invia ordini.</p>
|
||||||
|
|
||||||
|
<div class="section">② TRADES ESEGUITI (reali) + frequenza operativa</div>
|
||||||
|
<div class=box><b>Frequenza operativa</b> (contata sui dati certificati, non stimata):<br>{freq_html}</div>
|
||||||
|
<h3 style="font-size:14px;color:#8a93a0">Trades REALI eseguiti su Deribit</h3>
|
||||||
|
<table><tr><th>data/ora UTC</th><th>strum.</th><th>dir</th><th>amount</th><th>prezzo</th><th>fee USDC</th></tr>{live_trows}</table>
|
||||||
|
<p class=warn>Oggi TP01 e' in target TSMOM risk-off → flat: nessun ordine reale. La tabella si popola quando il segnale arma una posizione e il cron giornaliero la esegue.</p>
|
||||||
|
|
||||||
|
<div class="section">③ STORICO — simulato (backtest + forward virtuale)</div>
|
||||||
<div class=cards>
|
<div class=cards>
|
||||||
<div class=card><div class=k>FULL Sharpe</div><div class="v g">{f['sharpe']:.2f}</div></div>
|
<div class=card><div class=k>FULL Sharpe</div><div class="v g">{f['sharpe']:.2f}</div></div>
|
||||||
<div class=card><div class=k>HOLD-OUT Sharpe (2025-26)</div><div class="v g">{ho['sharpe']:.2f}</div></div>
|
<div class=card><div class=k>HOLD-OUT Sharpe (2025-26)</div><div class="v g">{ho['sharpe']:.2f}</div></div>
|
||||||
<div class=card><div class=k>maxDD</div><div class=v>{f['maxdd']*100:.1f}%</div></div>
|
<div class=card><div class=k>maxDD</div><div class=v>{f['maxdd']*100:.1f}%</div></div>
|
||||||
<div class=card><div class=k>CAGR</div><div class=v>{f['cagr']*100:.0f}%</div></div>
|
<div class=card><div class=k>CAGR</div><div class=v>{f['cagr']*100:.0f}%</div></div>
|
||||||
<div class=card><div class=k>ret totale</div><div class=v>{f['ret']*100:+.0f}%</div></div>
|
<div class=card><div class=k>ret totale</div><div class=v>{f['ret']*100:+.0f}%</div></div>
|
||||||
|
<div class=card><div class=k>trade/anno (book eseguibile)</div><div class=v>{tpy_card}</div></div>
|
||||||
</div>
|
</div>
|
||||||
<div class=box><div class=k style="color:#8a93a0;font-size:12px">EQUITY backtest (2019→oggi, €2k)</div>{svg_spark(d['spark'])}</div>
|
<div class=box><div class=k style="color:#8a93a0;font-size:12px">EQUITY backtest (2019→oggi, €2k)</div>{svg_spark(d['spark'])}</div>
|
||||||
<div class=box><b>Paper forward-only:</b> {paper_html}</div>
|
<div class=box><b>Paper forward-only:</b> {paper_html}</div>
|
||||||
@@ -241,25 +275,21 @@ th{{color:#8a93a0;font-weight:500}}.y{{display:inline-block;background:#161b22;b
|
|||||||
<table><tr><th>sleeve</th><th>peso</th><th>FULL Sh</th><th>DD</th><th>HOLD Sh</th></tr>{rows}</table>
|
<table><tr><th>sleeve</th><th>peso</th><th>FULL Sh</th><th>DD</th><th>HOLD Sh</th></tr>{rows}</table>
|
||||||
<h3 style="font-size:14px;color:#8a93a0">Posizioni correnti (ultima barra chiusa)</h3>
|
<h3 style="font-size:14px;color:#8a93a0">Posizioni correnti (ultima barra chiusa)</h3>
|
||||||
<table>{pos}</table>
|
<table>{pos}</table>
|
||||||
<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 SIMULATO, 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">COMBO DEPLOYABLE — cross-venue (paper, forward-only)</div>
|
<div class="section">③·a COMBO DEPLOYABLE — cross-venue (paper, forward-only)</div>
|
||||||
<div class=box><b>TP01 (Deribit) + GTAA (IB)</b> — le DUE gambe ESEGUIBILI a basso capitale, scorrelate (corr ~0.21) → blend Sharpe ~1.5, drawdown dimezzato. <b>Nessuna esecuzione reale</b>:<br>{combo_html}<br>
|
<div class=box><b>TP01 (Deribit) + GTAA (IB)</b> — le DUE gambe ESEGUIBILI a basso capitale, scorrelate (corr ~0.21) → blend Sharpe ~1.5, drawdown dimezzato. <b>Nessuna esecuzione reale</b>:<br>{combo_html}<br>
|
||||||
<span style="color:#8a93a0;font-size:13px">posizioni azionabili IB (GTAA, peso ETF):</span> {gw_html}</div>
|
<span style="color:#8a93a0;font-size:13px">posizioni azionabili IB (GTAA, peso ETF):</span> {gw_html}</div>
|
||||||
<p class=warn>⚠️ PAPER cross-venue: valida l'operativita' su due conti (Deribit + IB) a rischio zero. Lo Sharpe ~1.5 e' ottimistico (finestra crypto corta/favorevole); il dato robusto e' la diversificazione (corr 0.21, DD dimezzato), non il livello. XS01/VRP01 esclusi (STAT-MODE): qui solo TP01+GTAA.</p>
|
<p class=warn>⚠️ PAPER cross-venue: valida l'operativita' su due conti (Deribit + IB) a rischio zero. Lo Sharpe ~1.5 e' ottimistico (finestra crypto corta/favorevole); il dato robusto e' la diversificazione (corr 0.21, DD dimezzato), non il livello. XS01/VRP01 esclusi (STAT-MODE): qui solo TP01+GTAA.</p>
|
||||||
<div class="section">BOOK DERIBIT-ONLY — eseguibile (TP01 + SKH01)</div>
|
<div class="section">③·b BOOK DERIBIT-ONLY — eseguibile (TP01 + SKH01)</div>
|
||||||
<div class=box><b>TP01 75% + SKH01 25%</b> — le due gambe direzionali BTC/ETH sullo STESSO venue (Deribit), ribilancio mensile. Sottoinsieme realmente armabile (XS01/VRP01 esclusi):<br>{deribit_html}<br>
|
<div class=box><b>TP01 75% + SKH01 25%</b> — le due gambe direzionali BTC/ETH sullo STESSO venue (Deribit), ribilancio mensile. Sottoinsieme realmente armabile (XS01/VRP01 esclusi):<br>{deribit_html}<br>
|
||||||
<span style="color:#8a93a0;font-size:13px">forecast: <code>scripts/portfolio/forecast_deribit_book.py</code> · report: <code>scripts/portfolio/run_deribit_book.py</code></span></div>
|
<span style="color:#8a93a0;font-size:13px">forecast: <code>scripts/portfolio/forecast_deribit_book.py</code> · report: <code>scripts/portfolio/run_deribit_book.py</code></span></div>
|
||||||
<p class=warn>⚠️ Accumulo = proiezione condizionata (storico bull crypto → pianifica sul conservativo); nessuna leva; SKH01 research/forward-monitor (solo TP01 armato live). A €50/g servono ~€177k @conservativo: la via è capitale+tempo, non leva.</p>
|
<p class=warn>⚠️ Accumulo = proiezione condizionata (storico bull crypto → pianifica sul conservativo); nessuna leva; SKH01 research/forward-monitor (solo TP01 armato live). A €50/g servono ~€177k @conservativo: la via è capitale+tempo, non leva.</p>
|
||||||
<div class="section">FORWARD-MONITOR — lead paper (non deploy)</div>
|
<div class="section">③·c 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>
|
<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>
|
<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>
|
<p class=warn>⚠️ Paper/monitor. XS01 e' STAT-MODE (book a 19 gambe market-neutral, non eseguibile a €2k, storia ~2.5 anni). VRP01 = lead short-vol MODELLATO (non deploy pieno). SKH01 (Skyhook dual-TF regime+breakout, BTC/ETH) = diversificatore quasi-ortogonale (corr ~0.09) aggiunto @25%: alza il FULL Sharpe del portafoglio 1.68→2.13 e dimezza il DD (14→8%) — RESEARCH/forward-monitor (book a 230m, causalita' verificata su harness ma costi reali e codice d'esecuzione da validare prima del deploy). TP01 e' l'unico deployable pieno.</p>
|
||||||
<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>
|
|
||||||
<table><tr><th>data/ora UTC</th><th>strum.</th><th>dir</th><th>amount</th><th>prezzo</th><th>fee USDC</th></tr>{live_trows}</table>
|
|
||||||
<p class=warn>⚠️ Paper/monitor. XS01 e' STAT-MODE (book a 19 gambe market-neutral, non eseguibile a €2k, storia ~2.5 anni). VRP01 = lead short-vol MODELLATO (non deploy pieno). SKH01 (Skyhook dual-TF regime+breakout, BTC/ETH) = diversificatore quasi-ortogonale (corr ~0.09) aggiunto @25%: alza il FULL Sharpe del portafoglio 1.68→2.13 e dimezza il DD (14→8%) — RESEARCH/forward-monitor (book a 230m, causalita' verificata su harness ma costi reali e codice d'esecuzione da validare prima del deploy). TP01 e' l'unico deployable pieno: lo "Shadow live" mostra cosa farebbe sul mainnet, ma NON invia ordini.</p>
|
|
||||||
</body></html>"""
|
</body></html>"""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -273,6 +273,47 @@ def skyhook_sleeve(weight: float = 0.25) -> Sleeve:
|
|||||||
return Sleeve("SKH01_skyhook", weight, _skyhook_returns, pos_fn=_skyhook_positions)
|
return Sleeve("SKH01_skyhook", weight, _skyhook_returns, pos_fn=_skyhook_positions)
|
||||||
|
|
||||||
|
|
||||||
|
_TRADE_FREQ_CACHE = None
|
||||||
|
|
||||||
|
|
||||||
|
def book_trade_frequency() -> dict:
|
||||||
|
"""Frequenza operativa del book eseguibile (TP01+SKH01), conteggiata sui dati certificati.
|
||||||
|
Strategia + storico fissi -> cache a livello di MODULO: calcolata una sola volta per processo
|
||||||
|
(le nuove barre aggiungono pochissimo). Niente magic-number: e' contata, non stimata.
|
||||||
|
- SKH01: round-trip DISCRETI (apri+chiudi) no-overlap dell'harness, per asset e per anno.
|
||||||
|
- TP01: posizione CONTINUA vol-target -> 'turnover' annuo (somma |Δpeso|), non trade discreti."""
|
||||||
|
global _TRADE_FREQ_CACHE
|
||||||
|
if _TRADE_FREQ_CACHE is not None:
|
||||||
|
return _TRADE_FREQ_CACHE
|
||||||
|
skh = {}
|
||||||
|
for a in ASSETS:
|
||||||
|
ltf, htf = build_frames(load_data(a, "5m"))
|
||||||
|
ent = skyhook_entries(ltf, htf, SKH01_V2_DD)
|
||||||
|
yr = pd.to_datetime(pd.Series(ltf["timestamp"]).astype("int64"), unit="ms", utc=True).dt.year.values
|
||||||
|
yc: dict[int, int] = {}
|
||||||
|
for i, e in enumerate(ent):
|
||||||
|
if e is not None:
|
||||||
|
y = int(yr[i]); yc[y] = yc.get(y, 0) + 1
|
||||||
|
skh[a] = dict(by_year={int(k): int(v) for k, v in sorted(yc.items())},
|
||||||
|
total=int(sum(yc.values())), first=int(min(yc)), last=int(max(yc)))
|
||||||
|
sf = min(v["first"] for v in skh.values()); sl = max(v["last"] for v in skh.values())
|
||||||
|
skh_combo = sum(v["total"] for v in skh.values()) / (sl - sf + 1)
|
||||||
|
tp = TrendPortfolio(**CANONICAL); turns = []
|
||||||
|
for a in ASSETS:
|
||||||
|
df = resample_1d(load_data(a, "1h"))
|
||||||
|
tgt = np.asarray(tp.target_series(df), dtype=float)
|
||||||
|
years = pd.DatetimeIndex(pd.to_datetime(df["datetime"])).year # ndarray-aligned key
|
||||||
|
d = np.abs(np.diff(np.nan_to_num(tgt, nan=0.0), prepend=0.0)) # turnover = somma |Δpeso|
|
||||||
|
ty = pd.Series(d).groupby(years).sum() # groupby posizionale (RangeIndex)
|
||||||
|
turns.append(float(ty.mean()))
|
||||||
|
_TRADE_FREQ_CACHE = dict(
|
||||||
|
skh=skh,
|
||||||
|
skh_per_year={a: skh[a]["total"] / (skh[a]["last"] - skh[a]["first"] + 1) for a in ASSETS},
|
||||||
|
skh_combo_per_year=skh_combo,
|
||||||
|
tp01_turnover_per_year=sum(turns) / len(turns))
|
||||||
|
return _TRADE_FREQ_CACHE
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------- REGISTRY -----------------------------
|
# ----------------------------- REGISTRY -----------------------------
|
||||||
def active_sleeves() -> list[Sleeve]:
|
def active_sleeves() -> list[Sleeve]:
|
||||||
"""Sleeve ATTIVI nel portafoglio (pesi rinormalizzati; sleeve a date diverse si attivano
|
"""Sleeve ATTIVI nel portafoglio (pesi rinormalizzati; sleeve a date diverse si attivano
|
||||||
|
|||||||
Reference in New Issue
Block a user