Spaces:
Sleeping
Sleeping
| """ | |
| 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]}...')>" | |