quanthedge / backend /app /models /market.py
jashdoshi77's picture
QuantHedge: Full deployment with Docker + nginx + uvicorn
9d29748
"""
Market Data ORM Models.
Models for exchanges, assets, historical price data, and news articles.
Supports multi-exchange, multi-currency global market data.
"""
from __future__ import annotations
from datetime import date, datetime
from sqlalchemy import (
Boolean,
Date,
DateTime,
Float,
ForeignKey,
Integer,
String,
Text,
BigInteger,
func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class Exchange(Base):
"""Global exchange metadata (NYSE, NSE, LSE, etc.)."""
__tablename__ = "exchanges"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
code: Mapped[str] = mapped_column(String(20), unique=True, nullable=False, index=True)
name: Mapped[str] = mapped_column(String(255), nullable=False)
country: Mapped[str] = mapped_column(String(100), nullable=False)
currency: Mapped[str] = mapped_column(String(10), nullable=False)
timezone: Mapped[str] = mapped_column(String(50), nullable=False)
mic_code: Mapped[str] = mapped_column(String(10), nullable=True)
suffix: Mapped[str] = mapped_column(String(10), nullable=True, default="")
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now()
)
assets = relationship("Asset", back_populates="exchange", lazy="selectin")
def __repr__(self) -> str:
return f"<Exchange(code='{self.code}', country='{self.country}')>"
class Asset(Base):
"""Financial instrument (stock, ETF, index, etc.)."""
__tablename__ = "assets"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
ticker: Mapped[str] = mapped_column(String(20), nullable=False, index=True)
name: Mapped[str] = mapped_column(String(500), nullable=True)
exchange_id: Mapped[int] = mapped_column(
Integer, ForeignKey("exchanges.id"), nullable=True, index=True
)
asset_type: Mapped[str] = mapped_column(
String(50), nullable=False, default="equity"
) # equity, etf, index, bond, commodity
sector: Mapped[str] = mapped_column(String(100), nullable=True)
industry: Mapped[str] = mapped_column(String(200), nullable=True)
market_cap: Mapped[float] = mapped_column(Float, nullable=True)
currency: Mapped[str] = mapped_column(String(10), nullable=True)
country: Mapped[str] = mapped_column(String(100), nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
metadata_json: Mapped[str] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
exchange = relationship("Exchange", back_populates="assets", lazy="selectin")
price_data = relationship("PriceData", back_populates="asset", lazy="noload")
features = relationship("FeatureData", back_populates="asset", lazy="noload")
factor_exposures = relationship("FactorExposure", back_populates="asset", lazy="noload")
signals = relationship("Signal", back_populates="asset", lazy="noload")
def __repr__(self) -> str:
return f"<Asset(ticker='{self.ticker}', type='{self.asset_type}')>"
class PriceData(Base):
"""OHLCV price data for an asset on a specific date."""
__tablename__ = "price_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)
open: Mapped[float] = mapped_column(Float, nullable=True)
high: Mapped[float] = mapped_column(Float, nullable=True)
low: Mapped[float] = mapped_column(Float, nullable=True)
close: Mapped[float] = mapped_column(Float, nullable=False)
adj_close: Mapped[float] = mapped_column(Float, nullable=True)
volume: Mapped[int] = mapped_column(BigInteger, nullable=True)
source: Mapped[str] = mapped_column(String(50), default="yahoo", nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now()
)
asset = relationship("Asset", back_populates="price_data")
def __repr__(self) -> str:
return f"<PriceData(asset_id={self.asset_id}, date={self.date}, close={self.close})>"
class NewsArticle(Base):
"""News article with optional sentiment scoring."""
__tablename__ = "news_articles"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
title: Mapped[str] = mapped_column(String(1000), nullable=False)
source: Mapped[str] = mapped_column(String(200), nullable=True)
author: Mapped[str] = mapped_column(String(300), nullable=True)
url: Mapped[str] = mapped_column(String(2000), nullable=True)
published_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
content: Mapped[str] = mapped_column(Text, nullable=True)
description: Mapped[str] = mapped_column(Text, nullable=True)
sentiment_score: Mapped[float] = mapped_column(Float, nullable=True)
tickers_json: Mapped[str] = mapped_column(Text, nullable=True)
category: Mapped[str] = mapped_column(String(100), nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now()
)
def __repr__(self) -> str:
return f"<NewsArticle(title='{self.title[:50]}...')>"