"""Pydantic schemas for the Kevin strategy. Used by KevinStrategy.evaluate_mention as input/output contracts and by the live signal bridge + backtest engine to talk to the strategy. """ from __future__ import annotations from decimal import Decimal from enum import Enum from pydantic import BaseModel, ConfigDict, Field, model_validator class KevinDecisionType(str, Enum): OPEN_LONG = "open_long" # new position or top-up CLOSE_LONG = "close_long" # exit existing long NO_OP = "no_op" # filter says skip class KevinDecision(BaseModel): """A single trade decision emitted by KevinStrategy.evaluate_mention.""" model_config = ConfigDict(frozen=True) decision: KevinDecisionType symbol: str = Field(min_length=1, max_length=16) target_dollars: Decimal | None = None # required for OPEN_LONG holding_days: int | None = None # required for OPEN_LONG effective_conviction: Decimal | None = None # post-aggregation, 0-1 rationale: str # one-line audit string @model_validator(mode="after") def _open_long_requires_target_dollars(self) -> "KevinDecision": if self.decision == KevinDecisionType.OPEN_LONG: if self.target_dollars is None: raise ValueError("OPEN_LONG requires target_dollars") if self.holding_days is None: raise ValueError("OPEN_LONG requires holding_days") if self.target_dollars <= 0: raise ValueError("target_dollars must be positive") return self class KevinAccountState(BaseModel): """Snapshot of the account passed to KevinStrategy.evaluate_mention. The bridge populates this from live Alpaca account + Redis counters; the backtest populates it from the simulated portfolio state. Same shape. """ model_config = ConfigDict(frozen=True) equity_usd: Decimal cash_usd: Decimal held_positions: dict[str, Decimal] # symbol -> cost-basis $ blocklisted_symbols: frozenset[str] | set[str] daily_trade_count: int daily_alloc_usd: Decimal paused: bool def is_held(self, symbol: str) -> bool: return symbol in self.held_positions and self.held_positions[symbol] > 0 def is_blocklisted(self, symbol: str) -> bool: return symbol in self.blocklisted_symbols