# 10 — Config Spec (`strategy.yaml`) Tutti i parametri della strategia sono esposti in un file YAML versionato. Modificare il file = decisione esplicita di Adriano. Modifiche richiedono nuovo `config_version`, ricalcolo `config_hash` e commit con motivazione. ## Schema completo (golden default) ```yaml # strategy.yaml — Cerberus Bite golden config v1.0.0 # Riferimento: docs/01-strategy-rules.md # NB: hash auto-calcolato dal CLI `cerbero-bite config hash` config_version: "1.0.0" config_hash: "0000000000000000000000000000000000000000000000000000000000000000" # placeholder last_review: "2026-04-26" last_reviewer: "Adriano" asset: symbol: "ETH" exchange: "deribit" # === ENTRY === entry: # finestra di valutazione giornaliera (crypto 24/7) cron: "0 14 * * *" # ogni giorno 14:00 UTC skip_holidays_country: "IT" # filtri di accesso (vedi 01-strategy-rules.md §2) capital_min_usd: 720 dvol_min: 35 dvol_max: 90 funding_perp_abs_max_annualized: 0.80 eth_holdings_pct_max: 0.30 no_position_concurrent: true # 1 sola posizione alla volta exclude_macro_severity: ["high"] exclude_macro_countries: ["US", "EU"] # bias direzionale (§3.1) trend_window_days: 30 trend_bull_threshold_pct: 0.05 trend_bear_threshold_pct: -0.05 funding_bull_threshold_annualized: 0.20 funding_bear_threshold_annualized: -0.20 iron_condor_dvol_min: 55 iron_condor_adx_max: 20 iron_condor_trend_neutral_band_pct: 0.05 # === STRUCTURE === structure: dte_target: 18 dte_min: 14 dte_max: 21 short_strike: delta_target: 0.12 delta_min: 0.10 delta_max: 0.15 distance_otm_pct_min: 0.15 distance_otm_pct_max: 0.25 spread_width: target_pct_of_spot: 0.04 min_pct_of_spot: 0.03 max_pct_of_spot: 0.05 credit_to_width_ratio_min: 0.30 # === LIQUIDITY GATE === liquidity: open_interest_min: 100 volume_24h_min: 20 bid_ask_spread_pct_max: 0.15 book_depth_top3_min: 5 slippage_pct_of_credit_max: 0.08 # === SIZING === sizing: kelly_fraction: 0.13 # Quarter Kelly basato su MC # cap nominali (Cerbero v4 hard prohibitions) cap_per_trade_eur: 200 cap_aggregate_open_eur: 1000 max_concurrent_positions: 1 # da 2+ solo via override esplicito # soft cap difensivi (interni a Cerbero Bite) max_contracts_per_trade: 4 # aggiustamento volatilità dvol_adjustment: - {dvol_under: 45, multiplier: 1.00} - {dvol_under: 60, multiplier: 0.85} - {dvol_under: 80, multiplier: 0.65} # >= 80 → no entry (gestito da entry filters) # === EXIT === exit: # ordine di valutazione vincolante profit_take_pct_of_credit: 0.50 stop_loss_mark_x_credit: 2.50 # mark = 250% credito → perdita 1.5× vol_stop_dvol_increase: 10 time_stop_dte_remaining: 7 time_stop_skip_if_close_to_profit_pct: 0.70 # skip se mark ≤ 70% di profit_take delta_breach_threshold: 0.30 adverse_move_4h_pct: 0.05 monitor_cron: "0 2,14 * * *" # 02 + 14 UTC user_confirmation_timeout_min: 30 # escalation per urgenza alta escalate_on_timeout: - "CLOSE_STOP" - "CLOSE_VOL" - "CLOSE_DELTA" # === EXECUTION === execution: combo_only: true # mai due ordini separati initial_limit: "mid" reprice_step_ticks: 1 reprice_max_steps: 3 reprice_max_steps_urgent: 5 # per chiusure urgenti order_tif: "GTC" order_expiry_min: 30 ack_timeout_s: 300 # ack Cerbero core # === MONITORING === monitoring: health_check_interval_s: 300 health_failures_before_kill: 3 health_failures_before_restart: 5 daily_digest_cron: "0 8 * * *" monthly_report_cron: "0 12 1 * *" # === STORAGE === storage: sqlite_path: "data/state.sqlite" log_path: "data/log/" log_retention_days: 365 backup_path: "data/backups/" backup_retention_days: 30 # === MCP === mcp: config_file: "~/.config/cerbero-suite/mcp.json" call_timeout_s: 8 retry_max: 3 retry_base_delay_s: 1 required_versions: cerbero-deribit: "^2.0.0" cerbero-hyperliquid: "^1.5.0" cerbero-memory: "^4.0.0" cerbero-portfolio: "^1.2.0" cerbero-macro: "^1.0.0" cerbero-sentiment: "^1.0.0" cerbero-telegram: "^1.0.0" cerbero-brain-bridge: "^1.0.0" # === TELEGRAM === telegram: parse_mode: "MarkdownV2" confirmation_timeout_min: 60 exit_confirmation_timeout_min: 30 backup_channel_on_critical: true # canale BotPapà su severity critical # === KELLY RECALIBRATION === kelly_recalibration: lookback_days: 365 min_sample_low_confidence: 30 min_sample_high_confidence: 100 weight_when_medium_confidence: 0.50 ``` ## Validazione (`config/schema.py`) ```python from pydantic import BaseModel, Field, model_validator from decimal import Decimal class StrategyConfig(BaseModel): config_version: str config_hash: str last_review: str last_reviewer: str asset: AssetConfig entry: EntryConfig structure: StructureConfig liquidity: LiquidityConfig sizing: SizingConfig exit: ExitConfig execution: ExecutionConfig monitoring: MonitoringConfig storage: StorageConfig mcp: McpConfig telegram: TelegramConfig kelly_recalibration: KellyConfig @model_validator(mode="after") def validate_consistency(self) -> "StrategyConfig": # short delta range coerente s = self.structure.short_strike if not (s.delta_min <= s.delta_target <= s.delta_max): raise ValueError("delta_target outside [delta_min, delta_max]") # OTM range coerente if s.distance_otm_pct_min >= s.distance_otm_pct_max: raise ValueError("OTM range invalid") # Kelly range if not (0 < self.sizing.kelly_fraction < 0.5): raise ValueError("kelly_fraction out of safe range (0, 0.5)") # spread width range sw = self.structure.spread_width if not (sw.min_pct_of_spot <= sw.target_pct_of_spot <= sw.max_pct_of_spot): raise ValueError("spread_width target outside min/max") # exit thresholds sensati if self.exit.profit_take_pct_of_credit >= 1.0: raise ValueError("profit_take >= 100% impossible") if self.exit.stop_loss_mark_x_credit <= 1.0: raise ValueError("stop_loss multiple <= 1× makes no sense") return self ``` ## Override locale Se esiste `strategy.local.yaml` (gitignored), i suoi campi sovrascrivono `strategy.yaml`. Usato per: - Test in dry-run con cap dimezzati - Personalizzazioni dello sviluppatore senza inquinare il main - Override emergenza ("max_concurrent_positions: 0" per congelare entry senza disabilitare l'engine) L'override viene loggato all'avvio: ``` 2026-04-27T10:00:00Z | OVERRIDE_APPLIED | sizing.cap_per_trade_eur: 200 → 100 ``` ## Hash integrity ```bash # Calcolo manuale cerbero-bite config hash # Aggiornamento automatico (richiede review prompt) cerbero-bite config update --reason "Reduce kelly to 0.10 after Apr drawdown" ``` Il comando: 1. Apre il file in editor. 2. All'esci, ricalcola SHA-256 escludendo la riga `config_hash`. 3. Aggiorna `config_hash`, `last_review`, `last_reviewer`. 4. Mostra diff ad Adriano. 5. Firma in audit chain. Mismatch hash a runtime = kill switch immediato (vedi `07-risk-controls.md`). ## Esempi di modifiche valide ### Riduzione difensiva post-drawdown ```diff sizing: - kelly_fraction: 0.13 + kelly_fraction: 0.10 ``` Commit: `chore(config): reduce kelly to 0.10 after April -20% DD`. ### Aggiunta nuovo evento macro escluso ```diff entry: exclude_macro_severity: ["high"] + exclude_macro_keywords: ["Powell", "Lagarde", "ETF approval"] ``` Commit: `chore(config): exclude ETF-related events from entry window`. ### Disabilitazione temporanea iron condor ```diff entry: - iron_condor_dvol_min: 55 + iron_condor_dvol_min: 999 # de facto disabled ``` Commit: `chore(config): disable IC pending review of vol regime`. ## Cosa NON è in config Non è permesso parametrizzare: - L'**ordine** dei trigger di uscita (è una scelta strategica codificata). - Le **strutture** ammesse (vietato vendita nuda, ecc., scolpito nel codice). - Le **hard prohibitions** Cerbero v4 (cap 200/1000 EUR sono limiti superiori, non ulteriormente liberalizzabili). - Lo **scheduler** per intervalli più stretti (un'ottimizzazione che non si fa via config). ## Variabili d'ambiente `strategy.yaml` definisce **cosa** fa il bot quando è acceso. Le variabili d'ambiente in `.env` definiscono **come** si collega al mondo esterno e **quali interruttori operativi** sono attivi. Queste vivono fuori da `strategy.yaml` perché cambiano per ambiente (testnet vs mainnet, soak vs trading) ma non per regola di strategia. | Variabile | Tipo | Default | Uso | |---|---|---|---| | `CERBERO_BITE_MCP_TOKEN` | string (obbligatoria) | — | Bearer token presentato a Cerbero MCP V2. Il valore decide l'ambiente upstream (testnet o mainnet). Cambia il valore = cambia l'ambiente. | | `CERBERO_BITE_MCP_BOT_TAG` | string ≤ 64 char | `BOT__CERBERO_BITE` | Header `X-Bot-Tag` registrato nell'audit log del server MCP per ogni write. | | `CERBERO_BITE_MCP_DERIBIT_URL` | URL | gateway pubblico | Override URL router Deribit. | | `CERBERO_BITE_MCP_HYPERLIQUID_URL` | URL | gateway pubblico | Override URL router Hyperliquid. | | `CERBERO_BITE_MCP_MACRO_URL` | URL | gateway pubblico | Override URL router Macro. | | `CERBERO_BITE_MCP_SENTIMENT_URL` | URL | gateway pubblico | Override URL router Sentiment. | | `CERBERO_BITE_ENABLE_DATA_ANALYSIS` | bool (`true`/`false`) | `true` | Abilita il job `market_snapshot` (raccolta dati MCP ogni 15 min). | | `CERBERO_BITE_ENABLE_STRATEGY` | bool (`true`/`false`) | `false` | Abilita i job `entry` e `monitor` (esecuzione regole §2-§9). | | `CERBERO_BITE_TELEGRAM_BOT_TOKEN` | string | — | Token bot Telegram (notify-only). Senza, il client è in modalità disabled. | | `CERBERO_BITE_TELEGRAM_CHAT_ID` | string | — | Chat ID destinatario notifiche Telegram. | I valori bool accettano in input `1`/`0`, `true`/`false`, `yes`/`no`, `on`/`off`, `enabled`/`disabled` (case-insensitive). Qualunque altro valore fa fallire il boot con `ValueError`. Vedi `06-operational-flow.md` §"Modalità operativa" per i profili canonici di `ENABLE_DATA_ANALYSIS` e `ENABLE_STRATEGY`.