Espone la GUI Streamlit su https://cerbero-bite.tielogic.xyz tramite il
Traefik già attivo sull'host (label allineate al pattern di cerbero-mcp,
TLS via Let's Encrypt, websocket pass-through). Aggiunge:
- nuova tab `📚 Strategia` con stato live dei gate §2 confrontati con
l'ultimo tick di market_snapshots, pannello P/L parametrico
affiancato Conservativa vs Aggressiva, tabella di sensibilità
win-rate → APR e rendering del documento canonico esteso.
- doc `13-strategia-spiegata.md` che lega ogni regola §2-§9 al campo di
market_snapshots che la alimenta, con sezioni §4-bis (P/L atteso
realistico, win-rate empirico, drawdown, Sharpe) e §4-ter (confronto
fra i due profili e quando passare dall'uno all'altro).
- `strategy.conservativa.yaml` (golden config v1.0.0 esplicita) e
`strategy.aggressiva.yaml` (cap_per_trade 4×, max_concurrent 2×,
max_contracts 4×, deroga §11 documentata) con config_hash validi.
- nel compose: servizio dedicato `cerbero-bite-gui` (Streamlit su
0.0.0.0:8765, healthcheck su /_stcore/health, label Traefik), env
condivisi via anchor YAML `x-bite-env`, `--environment mainnet`
passato a `start` per allineare il boot check al token del .env (era
testnet vs mainnet → kill switch armato all'avvio).
- Dockerfile installa anche l'extra `gui` (streamlit) e copia
`docs/` + i due nuovi profili nell'immagine; `.dockerignore` non
esclude più `docs/` (causa del primo build silenzioso).
Fix bonus: `_try_load` nella pagina ritornava `LoadedConfig` ma la GUI
leggeva `.sizing.*` direttamente — l'`except: pass` mascherava
l'AttributeError facendo cadere sui default conservativi sia nel
pannello P/L sia nello stato gate (stesso pattern presente nella
Calibrazione). Ora ritorna `.config`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adegua Cerbero Bite alla nuova versione 2.0.0 del server MCP unificato
(testnet/mainnet routing per token, header X-Bot-Tag obbligatorio) e
introduce due interruttori operativi indipendenti per separare la
raccolta dati dall'esecuzione di strategia.
Auth e collegamento MCP
- Token bearer letto dalla nuova variabile CERBERO_BITE_MCP_TOKEN; il
valore sceglie l'ambiente upstream (testnet vs mainnet) sul server.
Rimosso il caricamento da file (`secrets/core.token`,
CERBERO_BITE_CORE_TOKEN_FILE, Docker secret /run/secrets/core_token).
- Aggiunto header X-Bot-Tag (default `BOT__CERBERO_BITE`, override via
CERBERO_BITE_MCP_BOT_TAG) su ogni call MCP, con validazione lato client
(non vuoto, ≤ 64 caratteri).
- Cartella `secrets/` rimossa, `.gitignore` ripulito, Dockerfile e
docker-compose.yml aggiornati con env passthrough e fail-fast quando
manca il token.
Modalità operativa (RuntimeFlags)
- Nuovo modulo `config/runtime_flags.py` con `RuntimeFlags(
data_analysis_enabled, strategy_enabled)` e loader che parserizza
CERBERO_BITE_ENABLE_DATA_ANALYSIS e CERBERO_BITE_ENABLE_STRATEGY
(true/false/yes/no/on/off/enabled/disabled, case-insensitive).
- L'orchestratore espone i flag, audita e logga la modalità al boot
(`engine started: env=… data_analysis=… strategy=…`), e in
`install_scheduler` esclude i job `entry`/`monitor` quando strategy è
off e il job `market_snapshot` quando data analysis è off. I job di
infrastruttura (health, backup, manual_actions) restano sempre attivi.
- Default profile = "solo analisi dati" (data_analysis=true,
strategy=false), pensato per la finestra di soak post-deploy.
GUI saldi
- `gui/live_data.py::_fetch_deribit_currency` riconosce il campo soft
`error` nel payload V2 (HTTP 200 con `error` valorizzato dal server
quando l'auth Deribit fallisce) e lo propaga come `BalanceRow.error`,
evitando di mostrare un fuorviante equity = 0,00.
CLI
- Sostituita l'opzione `--token-file` con `--token` (stringa) sui comandi
start/dry-run/ping; il default proviene dall'env. Le chiamate al
builder dell'orchestrator passano anche `bot_tag` e `flags`.
Documentazione
- `docs/04-mcp-integration.md`: descrizione del nuovo flusso di auth V2
(token = ambiente, X-Bot-Tag nell'audit) e router unificati.
- `docs/06-operational-flow.md`: nuova sezione "Modalità operativa" con
i tre profili canonici e tabella di gating per ogni job; aggiunto
`market_snapshot` al cron summary.
- `docs/10-config-spec.md`: nuova sezione "Variabili d'ambiente"
tabellare con tutti gli env, comprese le bool dei flag operativi.
- `docs/02-architecture.md`: layout del repo aggiornato (`secrets/`
rimosso, `runtime_flags.py` aggiunto), descrizione di `config/`
estesa.
Test
- 5 nuovi test su `_fetch_deribit_currency` (soft-error, payload pulito,
eccezione, error blank, signature parity).
- 7 nuovi test su `load_runtime_flags` (default, override, parsing
truthy/falsy, blank fallback, valore invalido).
- 4 nuovi test su `HttpToolClient` (X-Bot-Tag default e custom, blank e
troppo lungo rifiutati).
- 3 nuovi test integration sull'orchestratore (gating dei job in base
ai flag).
- Test esistenti su token/CLI ping/orchestrator aggiornati al nuovo
schema. Suite intera: 404 passed, 1 skipped (sqlite3 CLI assente
sull'host di sviluppo).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sistema dedicato di raccolta dati per scegliere le soglie dei filtri
sui percentili reali invece di valori a istinto.
Nuovi componenti:
* state/migrations/0003_market_snapshots.sql — tabella + index, PK
composta (timestamp, asset). Ogni colonna numerica è NULL-able per
preservare la continuità della serie quando un singolo MCP fallisce.
* state/models.py — MarketSnapshotRecord Pydantic.
* state/repository.py — record_market_snapshot, list_market_snapshots,
_row_to_market_snapshot.
* runtime/market_snapshot_cycle.py — collettore best-effort che chiama
spot/dvol/realized_vol/dealer_gamma/funding_perp/funding_cross/
liquidation_heatmap/macro per ogni asset; raccoglie gli errori in
fetch_errors_json e segna fetch_ok=false ma persiste comunque la
riga.
* clients/deribit.py — generalizzati dealer_gamma_profile(currency),
realized_vol(currency), spot_perp_price(asset). dealer_gamma_profile_eth
resta come alias per la chiamata dell'entry cycle.
* runtime/orchestrator.py — nuovo job APScheduler `market_snapshot`
cron */15 con assets configurabili (default ETH+BTC); il consumer
manual_actions ora dispatcha anche kind=run_cycle cycle=market_snapshot
per la GUI.
* gui/data_layer.py — load_market_snapshots, enqueue_run_cycle accetta
market_snapshot; tipo MarketSnapshotRecord esposto.
* gui/pages/6_📐_Calibrazione.py — selezione asset+finestra, conteggio
fetch_ok, per ogni metrica: istogramma, soglia da strategy.yaml come
vline rossa, percentili P5/P10/P25/P50/P75/P90/P95, % di tick che la
soglia avrebbe filtrato.
* gui/pages/1_📊_Status.py — bottone "📐 Forza snapshot" (4° del pannello
Forza ciclo) per popolare la tabella senza aspettare il cron.
5 nuovi test sul collector (happy, fault tolerance, asset switch,
macro fail, empty assets); test_orchestrator job set aggiornato.
368/368 tests pass; ruff clean; mypy strict src clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Localizzazione italiana di tutte le pagine (Stato, Audit, Equity,
Storico, Posizione) e della home; date relative ("5s fa", "12m fa").
* Logo Cerbero (cane a tre teste) in src/cerbero_bite/gui/assets/
cerbero_logo.png — sostituisce l'emoji 🐺 (lupo, semanticamente
errata) sia come favicon (`page_icon`) sia in sidebar e header.
* Caricamento automatico di `.env` dal CWD all'avvio della CLI (skip
sotto pytest tramite PYTEST_CURRENT_TEST), evitando di doversi
esportare manualmente le 4 URL MCP. Aggiunto python-dotenv come
dipendenza, `.env.example` committato come template, `.env` resta
ignorato da git.
* Pagina Stato: nuovo pannello "Saldi exchange" che fa fetch live
via gateway MCP (Deribit USDC + USDT, Hyperliquid USDC + opzionale
USDT spot) con cache TTL 60s e bottone refresh; tile riassuntivi
totale USD / EUR / cambio.
* Pagina Stato: nuovo pannello "Forza ciclo" con tre bottoni
(entry/monitor/health) che accodano azioni `run_cycle` nella tabella
manual_actions; il consumer dell'engine — quando in esecuzione —
dispatcha al `Orchestrator.run_*` corrispondente.
* manual_actions: nuovo `kind="run_cycle"` nello schema
ManualAction; consumer accetta dict di cycle_runners che
l'orchestrator popola in install_scheduler. 3 nuovi test (dispatch
entry, ciclo sconosciuto, fallback senza runner).
* gui/live_data.py — modulo dedicato al fetch MCP dalla GUI
(relax controllato della regola "no MCP from GUI" solo per i saldi,
non per i dati di trading).
363/363 tests pass; ruff clean; mypy strict src clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the GUI's first write path through the manual_actions queue:
* runtime/manual_actions_consumer.py — drains the queue and
dispatches arm_kill / disarm_kill via KillSwitch (preserving the
audit chain). Unsupported kinds (force_close, approve/reject_proposal)
are marked result="not_supported" so they don't sit forever.
* runtime/orchestrator.py — adds a `manual_actions` job at */1 cron
to the canonical scheduler manifest.
* gui/data_layer.py — write helpers enqueue_arm_kill /
enqueue_disarm_kill (the only write path the GUI uses) plus
load_pending_manual_actions for the pending strip.
* gui/pages/1_📊_Status.py — kill-switch arm/disarm panel with typed
confirmation ("yes I am sure") + reason field; pending-actions table
rendered when the queue is non-empty.
End-to-end smoke against the testnet state.sqlite:
GUI enqueue → consumer dispatch → KillSwitch transition → audit
chain hash linkage holds, "source":"manual_gui" recorded.
7 new unit tests for the consumer (arm, disarm, drain, unsupported,
default-reason, KillSwitchError handling, empty queue); 360/360 pass.
ruff clean; mypy strict src clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* gui/data_layer.py — adds load_position_by_id, load_decisions_for_position,
compute_payoff_curve (pure math: bull_put / bear_call piecewise linear
P&L at expiry, with breakeven), compute_distance_metrics (OTM%,
days-to-expiry, days-held, width%).
* gui/pages/5_💼_Position.py — selector across open + 10 most-recent
closed positions (with deep-link support via ?proposal_id=…), header
metrics, distance summary, leg snapshot table (entry-time only —
the GUI never calls MCP), plotly payoff diagram with strike/breakeven/
entry-spot annotations and max profit/max loss tiles, decision
history table from the decisions table.
Live greeks/mid are deliberately not pulled: per docs/11-gui-streamlit.md
the GUI reads SQLite + audit log only and lets the engine refresh data.
Validated math against a synthetic bull_put 2475/2350 × 2 contracts:
breakeven 2452.50, max profit $45, max loss $-160 — all matching the
expected formulas (credit, width × n − credit).
353/353 tests still pass; ruff clean; mypy strict src clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the analytics surface of the dashboard:
* gui/data_layer.py — extended with load_closed_positions (windowed
filter on closed_at) and three pure-function aggregators:
compute_equity_curve, compute_kpis, compute_monthly_stats. Drawdown
is measured against the running peak of cumulative realised P&L.
* gui/pages/3_📈_Equity.py — KPI strip, plotly cumulative-PnL line,
drawdown area below, P&L histogram by close_reason, per-month table
with win-rate.
* gui/pages/4_📜_History.py — windowed table of closed trades with
multiselect close-reason and winners/losers radio filters, six-tile
KPI strip, CSV export button.
* pyproject.toml — relax mypy on plotly + pandas (no shipped stubs).
Validated with synthetic data: 3 trades, 67% win rate, $50 total,
max drawdown $30 — all matching expected math. GUI launches, HTTP 200
on / and /_stcore/health.
353/353 tests still pass; ruff clean; mypy strict src clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the foundation of the local observation dashboard described
in docs/11-gui-streamlit.md:
* gui/data_layer.py — read-only wrappers over Repository (system_state,
open positions) and audit_log (tail iteration, chain verify). The GUI
never imports runtime/ nor calls MCP services.
* gui/main.py — Streamlit entry point with sidebar (engine health
badge, kill switch banner, last health check age), home overview.
* gui/pages/1_📊_Status.py — engine status with colored health banner,
kill switch detail, audit anchor, open positions table.
* gui/pages/2_🔍_Audit.py — live audit log stream (newest-first),
event filters, hash-chain integrity verify button.
* cli.py gui — replaces the placeholder with os.execvpe to
`python -m streamlit run` bound to 127.0.0.1, --browser.gatherUsageStats
false; --db / --audit paths exported via env to the GUI process.
* pyproject.toml — N999 ignore for src/cerbero_bite/gui/pages/* (Streamlit
auto-discovers pages whose filename contains numbers and emoji icons).
Smoke test: GUI launches, HTTP 200 on / and /_stcore/health, data layer
correctly reflects current testnet state (engine=running, kill_switch
disarmed, 0 open positions, audit chain integra 7 entries).
353/353 tests still pass; ruff clean; mypy strict src clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>