jashdoshi77's picture
QuantHedge: Full deployment with Docker + nginx + uvicorn
9d29748
"""
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"<FeatureData({self.feature_name}={self.feature_value}, date={self.date})>"
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"<Factor(name='{self.name}', category='{self.category}')>"
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"<FactorExposure(asset={self.asset_id}, factor={self.factor_id}, value={self.exposure_value})>"
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"<Signal(name='{self.name}', type='{self.signal_type}', direction='{self.direction}')>"