""" Quantitative Models: Features, Factors, and Signals. Stores computed quantitative data — technical features, factor exposures, and generated trading signals. """ from __future__ import annotations from datetime import date, datetime from sqlalchemy import Date, DateTime, Float, ForeignKey, Integer, String, Text, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class FeatureData(Base): """Computed technical features for an asset (SMA, RSI, MACD, etc.).""" __tablename__ = "feature_data" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) asset_id: Mapped[int] = mapped_column( Integer, ForeignKey("assets.id"), nullable=False, index=True ) date: Mapped[date] = mapped_column(Date, nullable=False, index=True) feature_name: Mapped[str] = mapped_column(String(100), nullable=False, index=True) feature_value: Mapped[float] = mapped_column(Float, nullable=True) parameters_json: Mapped[str] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now() ) asset = relationship("Asset", back_populates="features") def __repr__(self) -> str: return f"" class Factor(Base): """Quantitative factor definition (momentum, value, size, etc.).""" __tablename__ = "factors" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True) display_name: Mapped[str] = mapped_column(String(200), nullable=True) description: Mapped[str] = mapped_column(Text, nullable=True) category: Mapped[str] = mapped_column( String(50), nullable=False ) # style, macro, sector, custom calculation_method: Mapped[str] = mapped_column(Text, nullable=True) is_active: Mapped[bool] = mapped_column(default=True, nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now() ) exposures = relationship("FactorExposure", back_populates="factor", lazy="noload") def __repr__(self) -> str: return f"" class FactorExposure(Base): """Factor exposure (loading) for an asset on a given date.""" __tablename__ = "factor_exposures" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) asset_id: Mapped[int] = mapped_column( Integer, ForeignKey("assets.id"), nullable=False, index=True ) factor_id: Mapped[int] = mapped_column( Integer, ForeignKey("factors.id"), nullable=False, index=True ) date: Mapped[date] = mapped_column(Date, nullable=False, index=True) exposure_value: Mapped[float] = mapped_column(Float, nullable=False) z_score: Mapped[float] = mapped_column(Float, nullable=True) percentile_rank: Mapped[float] = mapped_column(Float, nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now() ) asset = relationship("Asset", back_populates="factor_exposures") factor = relationship("Factor", back_populates="exposures") def __repr__(self) -> str: return f"" class Signal(Base): """Generated quantitative trading signal.""" __tablename__ = "signals" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(200), nullable=False, index=True) signal_type: Mapped[str] = mapped_column( String(50), nullable=False ) # momentum, mean_reversion, sentiment, volatility, factor asset_id: Mapped[int] = mapped_column( Integer, ForeignKey("assets.id"), nullable=True, index=True ) date: Mapped[date] = mapped_column(Date, nullable=False, index=True) value: Mapped[float] = mapped_column(Float, nullable=False) strength: Mapped[float] = mapped_column(Float, nullable=True) # 0-1 confidence direction: Mapped[str] = mapped_column( String(10), nullable=True ) # long, short, neutral metadata_json: Mapped[str] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now() ) asset = relationship("Asset", back_populates="signals") def __repr__(self) -> str: return f""