Files
root 6ff021fbf4 feat(strategy): abbandono gating settimanale — entry daily 24/7
Crypto opera 24/7: la cadenza settimanale lunedì-only era un retaggio
TradFi senza giustificazione. La nuova cadenza è giornaliera (cron
0 14 * * *), con i gate quantitativi a decidere se entrare o saltare.

Cambiamenti principali:

* runtime/orchestrator.py — _CRON_ENTRY 0 14 * * * (era MON)
* runtime/auto_pause.py — pause_until(days=) (era weeks=); minimo
  clamp 1 giorno (era 1 settimana)
* core/backtest.py — MondayPick→DailyPick, monday_picks→daily_picks
  (1 pick per calendar-day all'ora target); Sharpe annualization su
  ~120 trade/anno (era 52)
* config/schema.py — default cron daily; max_concurrent_positions 1→5;
  AutoPauseConfig.pause_weeks→pause_days, default 14
* runtime/option_chain_snapshot_cycle.py + orchestrator — cron */15
  per accumulo continuo dataset di backtest empirico

Strategy yamls (config_version 1.3.0 → 1.4.0, hash rigenerati):

* strategy.yaml — max_concurrent 1→5, cap_aggregate coerente
* strategy.aggressiva.yaml — max_concurrent 2→8, cap_aggregate
  3200→6400, max_contracts_per_trade invariato a 16
* strategy.conservativa.yaml — max_concurrent 1→3
* tutti — pause_weeks→pause_days: 14

GUI (pages/7_📚_Strategia.py):

* slider Trade/anno: range 20-200 (era 8-30), default 110, help
  riallineato sulla math 365 candidature × pass-rate 30-40%
* card profili: versione letta dinamicamente da config_version invece
  che hard-coded "v1.2.0"
* warning "entrambi perdono soldi" ora valuta i P/L effettivi
  (cons['annual_pl'], aggr['annual_pl']) invece del win_rate grezzo;
  aggiunto stato intermedio quando solo conservativo è in perdita

Tests (450/450 passati):

* test_auto_pause: pause_days, clamp ≥1 giorno
* test_backtest: rinomina + ridisegno daily picks (assert su
  calendar-day dedupe e hour filter)
* test_sizing_engine: other_open_positions=5 per cap default
* test_config_loader: version 1.4.0

Docs (README + 9 file in docs/) — tutti i riferimenti weekly/lunedì
allineati a daily/24-7, volume option_chain ricalcolato per cron
*/15 (~1.1 MB/giorno, ~400 MB/anno).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 16:21:16 +00:00

10 KiB
Raw Permalink Blame History

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)

# 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)

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

# 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

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

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

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.