feat(runtime): audit log include threshold rolling e window usata
Risponde al final review (spec §6.4): il decisions log ora contiene iv_rv_threshold_used (la soglia P_q effettivamente applicata) e iv_rv_window_used_days (giorni di history nella finestra). Permette ricostruire ex-post perché un trade è stato saltato e con quali numeri. Helper privi di I/O — la soglia viene ricomputata in base alla history già caricata, costo trascurabile. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ from cerbero_bite.clients.macro import MacroClient
|
||||
from cerbero_bite.clients.portfolio import PortfolioClient
|
||||
from cerbero_bite.clients.sentiment import SentimentClient
|
||||
from cerbero_bite.config.schema import StrategyConfig
|
||||
from cerbero_bite.core.adaptive_threshold import compute_adaptive_threshold
|
||||
from cerbero_bite.core.combo_builder import ComboProposal, build, select_strikes
|
||||
from cerbero_bite.core.entry_validator import (
|
||||
EntryContext,
|
||||
@@ -315,6 +316,44 @@ async def _build_quotes(
|
||||
return out
|
||||
|
||||
|
||||
def _audit_threshold(
|
||||
entry_cfg: object,
|
||||
iv_rv_history: tuple[Decimal, ...],
|
||||
) -> str | None:
|
||||
"""Soglia P_q rolling effettivamente usata dal gate, per il decisions log."""
|
||||
if not getattr(entry_cfg, "iv_minus_rv_filter_enabled", False):
|
||||
return None
|
||||
if not getattr(entry_cfg, "iv_minus_rv_adaptive_enabled", False):
|
||||
return str(getattr(entry_cfg, "iv_minus_rv_min", Decimal("0")))
|
||||
threshold = compute_adaptive_threshold(
|
||||
history=iv_rv_history,
|
||||
percentile=entry_cfg.iv_minus_rv_percentile, # type: ignore[attr-defined]
|
||||
absolute_floor=entry_cfg.iv_minus_rv_min, # type: ignore[attr-defined]
|
||||
min_days=entry_cfg.iv_minus_rv_window_min_days, # type: ignore[attr-defined]
|
||||
target_days=entry_cfg.iv_minus_rv_window_target_days, # type: ignore[attr-defined]
|
||||
)
|
||||
return None if threshold is None else str(threshold)
|
||||
|
||||
|
||||
def _audit_window_days(
|
||||
entry_cfg: object,
|
||||
iv_rv_history: tuple[Decimal, ...],
|
||||
) -> int | None:
|
||||
"""Numero di giorni effettivamente usati dalla finestra rolling."""
|
||||
if not getattr(entry_cfg, "iv_minus_rv_adaptive_enabled", False):
|
||||
return None
|
||||
n_days = len(iv_rv_history) // 96
|
||||
target = int(getattr(entry_cfg, "iv_minus_rv_window_target_days", 60))
|
||||
min_days = int(getattr(entry_cfg, "iv_minus_rv_window_min_days", 30))
|
||||
if n_days < 1:
|
||||
return 0
|
||||
if n_days >= target:
|
||||
return target
|
||||
if n_days >= min_days:
|
||||
return min_days
|
||||
return n_days
|
||||
|
||||
|
||||
def _max_loss_per_contract_usd(short_strike: Decimal, long_strike: Decimal) -> Decimal:
|
||||
return (short_strike - long_strike).copy_abs()
|
||||
|
||||
@@ -490,6 +529,10 @@ async def run_entry_cycle(
|
||||
str(snap.iv_minus_rv) if snap.iv_minus_rv is not None else None
|
||||
),
|
||||
"iv_rv_history_n": len(iv_rv_history),
|
||||
"iv_rv_threshold_used": _audit_threshold(entry_cfg, iv_rv_history),
|
||||
"iv_rv_window_used_days": _audit_window_days(
|
||||
entry_cfg, iv_rv_history
|
||||
),
|
||||
"dvol_24h_ago": (
|
||||
str(dvol_24h_ago) if dvol_24h_ago is not None else None
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user