from datetime import datetime import json from sqlalchemy import DateTime, Float, ForeignKey, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class TradeSetup(Base): __tablename__ = "trade_setups" id: Mapped[int] = mapped_column(primary_key=True) ticker_id: Mapped[int] = mapped_column( ForeignKey("tickers.id", ondelete="CASCADE"), 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) composite_score: Mapped[float] = mapped_column(Float, nullable=False) detected_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False ) confidence_score: Mapped[float | None] = mapped_column(Float, nullable=True) targets_json: Mapped[str | None] = mapped_column(Text, nullable=True) conflict_flags_json: Mapped[str | None] = mapped_column(Text, nullable=True) recommended_action: Mapped[str | None] = mapped_column(String(20), nullable=True) reasoning: Mapped[str | None] = mapped_column(Text, nullable=True) risk_level: Mapped[str | None] = mapped_column(String(10), nullable=True) actual_outcome: Mapped[str | None] = mapped_column(String(20), nullable=True) ticker = relationship("Ticker", back_populates="trade_setups") @property def targets(self) -> list[dict]: if not self.targets_json: return [] try: parsed = json.loads(self.targets_json) except (TypeError, ValueError): return [] return parsed if isinstance(parsed, list) else [] @property def conflict_flags(self) -> list[str]: if not self.conflict_flags_json: return [] try: parsed = json.loads(self.conflict_flags_json) except (TypeError, ValueError): return [] if not isinstance(parsed, list): return [] return [str(item) for item in parsed]