3 Commits

Author SHA1 Message Date
root 76d1a4a32d chore(gitignore): ignore .omc/ (oh-my-claudecode session/memory dir)
Directory creata localmente dall'infrastruttura OMC per stato sessione
(project-memory.json, research/, sessions/, state/). Non è artefatto
del progetto cerbero-bite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:08:00 +00:00
root e978a44bff feat(gui): Strategia pannello P/L con slider sizing + fix max_loss
Pannello "P/L atteso — Conservativa vs Aggressiva":
* Sostituiti slider Capitale/Spot con slider parametrici Cap/trade
  (EUR) + posizioni concorrenti. Il capitale richiesto viene calcolato
  in automatico via Kelly-binding aggregato:
  capital = cap_pertrade_usd × concorrenza / max(kelly, 1e-3).
* Profili Conservativa/Aggressiva ora ereditano dai yaml SOLO le leve
  qualitative (width_pct, credit_ratio, kelly_fraction, feature
  attive); le leve di sizing (cap, concorrenza) sono comandate dagli
  slider per confronti omogenei.
* Tre metriche header: capitale richiesto, cap aggregato notional,
  cap per trade USD.

Fix in `_compute_pl`:
* Max loss per contratto era `width` (errato per credit spread).
  Corretto a `width − credit` allineato a core/sizing_engine.py.
  Effetto: n_kelly aumenta proporzionalmente al credit incassato →
  P/L stimato più realistico per spread con credit_to_width_ratio
  alto (es. 0.30+ in profilo Aggressiva).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:00:42 +00:00
root efa829f7aa feat(runtime): orchestrator option-chain snapshot multi-asset (ETH+BTC)
Sostituisce `option_chain_asset: str = "ETH"` con
`option_chain_assets: tuple[str, ...] = ("ETH", "BTC")` e itera nel
job schedulato. Coerente con `market_snapshot_assets` già multi-asset
e con i 64 strikes BTC + 51 strikes ETH già visibili in
option_chain_snapshots.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:00:24 +00:00
3 changed files with 66 additions and 17 deletions
+3
View File
@@ -43,3 +43,6 @@ data/
.env
.env.*
!.env.example
# Tooling state (oh-my-claudecode session/memory dir)
.omc/
+60 -13
View File
@@ -476,7 +476,11 @@ def _compute_pl(
cap_pertrade_usd = caps["cap_pertrade_eur"] * eur_to_usd
risk_target = min(caps["kelly"] * capital, cap_pertrade_usd)
n_kelly = int(risk_target // width) if width > 0 else 0
# Max loss per contratto = width credit (NON width). Su un put
# spread incassi `credit` upfront, quindi la perdita massima è la
# larghezza meno il credito (vedi core/sizing_engine.py).
max_loss_per_contract = max(width - credit, 1e-6)
n_kelly = int(risk_target // max_loss_per_contract)
n_per_trade = max(0, min(n_kelly, int(caps["max_n"])))
prob_time_stop = 0.07
@@ -670,27 +674,33 @@ def _render_pl_panel(
"""Pannello P/L: confronto Conservativa vs Aggressiva sugli stessi slider."""
st.subheader("💰 P/L atteso — Conservativa vs Aggressiva")
st.caption(
"Stessi slider, due profili di sizing. **Conservativa** = la "
"golden config attuale (`strategy.yaml`). **Aggressiva** = "
"`strategy.aggressiva.yaml` con cap_per_trade 4×, max contratti "
"4×, 2 posizioni concorrenti. Le regole §2-§9 sono identiche; "
"cambiano SOLO le leve di sizing — quello che il P/L "
"conservativo lascia sul tavolo."
"Slider parametrici: scegli **cap per trade** e **posizioni "
"concorrenti**, il capitale richiesto viene calcolato in "
"automatico (Kelly-binding × concurrency / kelly_fraction). "
"Conservativa e Aggressiva ereditano dai rispettivi yaml SOLO "
"le leve qualitative (width_pct, credit_ratio, kelly_fraction, "
"feature attive); le leve di sizing (cap, concorrenza) le "
"controlli qui sotto."
)
col_a, col_b, col_c, col_d = st.columns(4)
capital = col_a.slider(
"Capitale (USD)", 720, 50_000, value=10_000, step=100
col_a, col_b, col_c, col_d, col_e = st.columns(5)
cap_per_trade_eur = col_a.slider(
"Cap/trade (EUR)", 50, 2000, value=200, step=10,
help="Massima perdita per singolo trade. Bound al rischio.",
)
spot = col_b.slider("Spot ETH (USD)", 1500, 6000, value=3000, step=100)
win_rate = col_c.slider(
concurrency_override = col_b.slider(
"Pos. concorrenti", 1, 10, value=3, step=1,
help="Quanti trade simultanei. Cap aggregato = cap/trade × N.",
)
spot = col_c.slider("Spot ETH (USD)", 1500, 6000, value=3000, step=100)
win_rate = col_d.slider(
"Win rate atteso", 0.50, 0.90, value=0.75, step=0.01,
help=(
"Senza filtri quant ≈ 0.650.70. CON filtri (dealer gamma>0, "
"no macro, IVRV>0, liquidation_*_risk≠high) sale a 0.750.80."
),
)
trades_per_year = col_d.slider(
trades_per_year = col_e.slider(
"Trade / anno (post-filtri)", 20, 200, value=110, step=5,
help=(
"Crypto è 24/7: l'entry cycle gira ogni giorno alle 14:00 UTC "
@@ -702,6 +712,43 @@ def _render_pl_panel(
cons_caps = _profile_caps(strategy_conservativa or strategy_main)
aggr_caps = _profile_caps(strategy_aggressiva)
# Override sizing dai slider (sostituisce le leve cap/trade,
# cap_aggregate, max_concurrent dei yaml).
eur_to_usd = 1.075
cap_pertrade_usd = cap_per_trade_eur * eur_to_usd
cap_aggregate_override = float(cap_per_trade_eur * concurrency_override)
cons_caps = {
**cons_caps,
"cap_pertrade_eur": float(cap_per_trade_eur),
"cap_aggregate_eur": cap_aggregate_override,
"max_concurrent": float(concurrency_override),
}
aggr_caps = {
**aggr_caps,
"cap_pertrade_eur": float(cap_per_trade_eur),
"cap_aggregate_eur": cap_aggregate_override,
"max_concurrent": float(concurrency_override),
}
# Capitale richiesto: Kelly-binding aggregato.
# Per ogni trade slot, kelly × capital ≥ cap_pertrade_usd → capital
# ≥ cap_pertrade_usd / kelly. Per N concorrenti, scala linearmente
# come limite conservativo del notional cumulato.
kelly_cons = cons_caps.get("kelly", 0.13)
kelly_aggr = aggr_caps.get("kelly", 0.13)
capital_cons = int(
cap_pertrade_usd * concurrency_override / max(kelly_cons, 1e-3)
)
capital_aggr = int(
cap_pertrade_usd * concurrency_override / max(kelly_aggr, 1e-3)
)
capital = max(capital_cons, capital_aggr)
cap_col1, cap_col2, cap_col3 = st.columns(3)
cap_col1.metric("📊 Capitale richiesto", f"${capital:,}")
cap_col2.metric(
"💸 Cap aggregato (notional)",
f"${int(cap_pertrade_usd * concurrency_override):,}",
)
cap_col3.metric("🎯 Cap per trade (USD)", f"${int(cap_pertrade_usd):,}")
cons_feats = _detect_features(strategy_conservativa or strategy_main)
aggr_feats = _detect_features(strategy_aggressiva)
+3 -4
View File
@@ -222,7 +222,7 @@ class Orchestrator:
market_snapshot_cron: str = _CRON_MARKET_SNAPSHOT,
market_snapshot_assets: tuple[str, ...] = DEFAULT_ASSETS,
option_chain_cron: str = _CRON_OPTION_CHAIN_SNAPSHOT,
option_chain_asset: str = "ETH",
option_chain_assets: tuple[str, ...] = ("ETH", "BTC"),
backup_dir: Path | None = None,
backup_retention_days: int = _BACKUP_RETENTION_DAYS,
) -> AsyncIOScheduler:
@@ -290,9 +290,8 @@ class Orchestrator:
async def _option_chain_snapshot() -> None:
async def _do() -> None:
await collect_option_chain_snapshot(
self._ctx, asset=option_chain_asset
)
for asset in option_chain_assets:
await collect_option_chain_snapshot(self._ctx, asset=asset)
await _safe("option_chain_snapshot", _do)