""" Database models for SEC financial data """ from sqlalchemy import Column, String, Float, DateTime, Integer, JSON, Boolean, UniqueConstraint, Index from sqlalchemy.dialects.postgresql import UUID, TIMESTAMP from datetime import datetime, timezone import uuid from app.core.database import Base class Company(Base): __tablename__ = "companies" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) ticker = Column(String(10), unique=True, nullable=False, index=True) name = Column(String(255), nullable=False) cik = Column(String(20), unique=True, nullable=True) sector = Column(String(100), nullable=True) industry = Column(String(100), nullable=True) business_description = Column(String, nullable=True) created_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)) updated_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) __table_args__ = ( Index('idx_company_ticker', 'ticker'), Index('idx_company_cik', 'cik'), ) class FinancialData(Base): __tablename__ = "financial_data" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) ticker = Column(String(10), nullable=False, index=True) period_date = Column(TIMESTAMP(timezone=True), nullable=False) period_type = Column(String(10), nullable=False) # 'quarterly' or 'annual' filing_type = Column(String(10), nullable=True) # '10-K', '10-Q', etc. # Income Statement revenue = Column(Float, nullable=True) gross_profit = Column(Float, nullable=True) operating_income = Column(Float, nullable=True) net_income = Column(Float, nullable=True) eps = Column(Float, nullable=True) # Balance Sheet total_assets = Column(Float, nullable=True) total_equity = Column(Float, nullable=True) total_debt = Column(Float, nullable=True) cash = Column(Float, nullable=True) shares_outstanding = Column(Float, nullable=True) # Cash Flow operating_cash_flow = Column(Float, nullable=True) free_cash_flow = Column(Float, nullable=True) capex = Column(Float, nullable=True) # Raw data storage (for additional fields) raw_data = Column(JSON, nullable=True) # Metadata data_source = Column(String(50), default='SEC_EDGAR') is_estimated = Column(Boolean, default=False) created_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)) updated_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) __table_args__ = ( UniqueConstraint('ticker', 'period_date', 'period_type', name='uq_financial_data'), Index('idx_financial_ticker_date', 'ticker', 'period_date'), Index('idx_financial_period', 'period_date', 'period_type'), Index('idx_financial_data_source', 'data_source'), ) class CalculatedMetrics(Base): __tablename__ = "calculated_metrics" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) ticker = Column(String(10), nullable=False, index=True) calculation_date = Column(TIMESTAMP(timezone=True), nullable=False) period_date = Column(TIMESTAMP(timezone=True), nullable=False) # The financial data period this is based on # Valuation Ratios (require price data) pe_ratio = Column(Float, nullable=True) pb_ratio = Column(Float, nullable=True) ps_ratio = Column(Float, nullable=True) ev_ebitda = Column(Float, nullable=True) # Profitability Metrics roe = Column(Float, nullable=True) # Return on Equity % roa = Column(Float, nullable=True) # Return on Assets % gross_margin = Column(Float, nullable=True) # % operating_margin = Column(Float, nullable=True) # % net_margin = Column(Float, nullable=True) # % # Growth Metrics revenue_growth_yoy = Column(Float, nullable=True) # % revenue_growth_qoq = Column(Float, nullable=True) # % eps_growth_yoy = Column(Float, nullable=True) # % eps_growth_qoq = Column(Float, nullable=True) # % # Liquidity & Solvency debt_to_equity = Column(Float, nullable=True) debt_to_assets = Column(Float, nullable=True) current_ratio = Column(Float, nullable=True) quick_ratio = Column(Float, nullable=True) # Efficiency asset_turnover = Column(Float, nullable=True) inventory_turnover = Column(Float, nullable=True) # Cash Flow Metrics ocf_margin = Column(Float, nullable=True) # Operating Cash Flow Margin % fcf_margin = Column(Float, nullable=True) # Free Cash Flow Margin % fcf_yield = Column(Float, nullable=True) # % # Market Metrics (when price data available) market_cap = Column(Float, nullable=True) enterprise_value = Column(Float, nullable=True) # Additional metrics stored as JSON additional_metrics = Column(JSON, nullable=True) # Metadata created_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)) updated_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) __table_args__ = ( UniqueConstraint('ticker', 'calculation_date', 'period_date', name='uq_calculated_metrics'), Index('idx_metrics_ticker_date', 'ticker', 'calculation_date'), ) class PriceData(Base): __tablename__ = "price_data" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) ticker = Column(String(10), nullable=False, index=True) date = Column(TIMESTAMP(timezone=True), nullable=False) open = Column(Float, nullable=True) high = Column(Float, nullable=True) low = Column(Float, nullable=True) close = Column(Float, nullable=False) volume = Column(Float, nullable=True) adjusted_close = Column(Float, nullable=True) # Additional price metrics market_cap = Column(Float, nullable=True) pe_ratio = Column(Float, nullable=True) dividend_yield = Column(Float, nullable=True) # Metadata data_source = Column(String(50), default='MOCK') # Will be updated when real data source is added created_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)) updated_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) __table_args__ = ( UniqueConstraint('ticker', 'date', name='uq_price_data'), Index('idx_price_ticker_date', 'ticker', 'date'), Index('idx_price_data_source', 'data_source'), ) class DataUpdateLog(Base): __tablename__ = "data_update_logs" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) ticker = Column(String(10), nullable=False, index=True) update_type = Column(String(50), nullable=False) # 'financial', 'metrics', 'price' start_date = Column(TIMESTAMP(timezone=True), nullable=True) end_date = Column(TIMESTAMP(timezone=True), nullable=True) status = Column(String(20), nullable=False) # 'pending', 'processing', 'completed', 'failed' error_message = Column(String, nullable=True) records_processed = Column(Integer, default=0) started_at = Column(TIMESTAMP(timezone=True), default=lambda: datetime.now(timezone.utc)) completed_at = Column(TIMESTAMP(timezone=True), nullable=True) __table_args__ = ( Index('idx_update_log_ticker', 'ticker'), Index('idx_update_log_status', 'status'), )