You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
7.7 KiB
Python

"""
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)
exchange = Column(String(20), nullable=True)
sector = Column(String(100), nullable=True)
industry = Column(String(100), nullable=True)
country = Column(String(100), nullable=True)
market_cap = Column(Float, 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'),
Index('idx_metrics_calculation_date', 'calculation_date'),
Index('idx_metrics_period_date', 'period_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'),
)