from datetime import datetime from sqlalchemy import DateTime, Float, ForeignKey, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class SignalContextSnapshot(Base): """Point-in-time context captured when a trade setup is generated. This stores the discretionary overlay inputs (scores, sentiment, fundamentals) as they looked at detection time, so future analysis can test whether human filtering improved or hurt the qualified-list strategy. """ __tablename__ = "signal_context_snapshots" id: Mapped[int] = mapped_column(primary_key=True) trade_setup_id: Mapped[int] = mapped_column( ForeignKey("trade_setups.id", ondelete="CASCADE"), nullable=False, unique=True ) ticker_id: Mapped[int] = mapped_column( ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False ) detected_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) strategy_version: Mapped[str] = mapped_column(String(80), nullable=False) direction: Mapped[str] = mapped_column(String(10), nullable=False) entry_price: Mapped[float] = mapped_column(Float, nullable=False) stop_loss: Mapped[float] = mapped_column(Float, nullable=False) target: Mapped[float] = mapped_column(Float, nullable=False) rr_ratio: Mapped[float] = mapped_column(Float, nullable=False) confidence_score: Mapped[float | None] = mapped_column(Float, nullable=True) recommended_action: Mapped[str | None] = mapped_column(String(20), nullable=True) risk_level: Mapped[str | None] = mapped_column(String(10), nullable=True) momentum_percentile: Mapped[float | None] = mapped_column(Float, nullable=True) score_context_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") sentiment_context_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") fundamental_context_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") trade_setup = relationship("TradeSetup") ticker = relationship("Ticker")