from datetime import date, datetime from sqlalchemy import Date, DateTime, Float, ForeignKey, Index, String, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column from app.database import Base class FundamentalSnapshot(Base): """CIK-keyed, one immutable row per SEC accession. Keyed by issuer (CIK), not ticker — multi-class issuers (GOOG/GOOGL) share one CIK and one set of fundamentals; the ``tickers.cik`` column is the only join point. Amendments are retained: every accession is a distinct immutable row, and readers pick the newest valid ``accepted_at`` per (cik, fiscal_year, fiscal_period) at read time — no flags, no mutation. **Facts are stored as the filing reports them, never as derived quarters.** Duration facts (revenue, net_income, operating_income, diluted_eps, cfo, capex, depreciation_amortization) hold the filing's normalized **cumulative YTD/FY** value over (period_start -> period_end). Balance-sheet facts (cash_and_st_investments, total_debt, shares_outstanding) are **period-end** values. ``shares_outstanding`` is a point-in-time count (``dei:EntityCommonStockSharesOutstanding``, summed across share classes for a multi-class issuer) — deliberately not the weighted-average diluted share count, since both consumers (estimated market cap, YoY dilution read) want a point-in-time value. Discrete quarters (10-Q YTD deltas, Q4 = FY - Q1..Q3), TTM, YoY and the quarter tape are all derived at read time — so non-calendar fiscal years resolve correctly and a later amendment never leaves a stale frozen quarter. """ __tablename__ = "fundamental_snapshots" __table_args__ = ( UniqueConstraint("accession", name="uq_fundamental_snapshots_accession"), Index("ix_fundamental_snapshots_cik_period", "cik", "fiscal_year", "fiscal_period"), Index("ix_fundamental_snapshots_cik_period_end", "cik", "period_end"), ) id: Mapped[int] = mapped_column(primary_key=True) cik: Mapped[str] = mapped_column(String(10), nullable=False) accession: Mapped[str] = mapped_column(String(25), nullable=False) form: Mapped[str] = mapped_column(String(12), nullable=False) # 10-Q, 10-K, 10-K/A ... filed_date: Mapped[date] = mapped_column(Date, nullable=False) # Kept although PIT enforcement is deferred (one timestamp now vs painful retrofit). accepted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) # Period identity — required to align non-calendar fiscal years and to derive # discrete quarters from cumulative facts. period_start: Mapped[date | None] = mapped_column(Date, nullable=True) period_end: Mapped[date] = mapped_column(Date, nullable=False) fiscal_year: Mapped[int] = mapped_column(nullable=False) fiscal_period: Mapped[str] = mapped_column(String(4), nullable=False) # Q1|Q2|Q3|Q4|FY # Duration facts — cumulative YTD/FY over (period_start -> period_end). revenue: Mapped[float | None] = mapped_column(Float, nullable=True) net_income: Mapped[float | None] = mapped_column(Float, nullable=True) operating_income: Mapped[float | None] = mapped_column(Float, nullable=True) diluted_eps: Mapped[float | None] = mapped_column(Float, nullable=True) cfo: Mapped[float | None] = mapped_column(Float, nullable=True) # cash flow from operations capex: Mapped[float | None] = mapped_column(Float, nullable=True) depreciation_amortization: Mapped[float | None] = mapped_column(Float, nullable=True) # Balance-sheet facts — period-end values. cash_and_st_investments: Mapped[float | None] = mapped_column(Float, nullable=True) total_debt: Mapped[float | None] = mapped_column(Float, nullable=True) shares_outstanding: Mapped[float | None] = mapped_column(Float, nullable=True) import_run_id: Mapped[int | None] = mapped_column( ForeignKey("data_import_runs.id", ondelete="SET NULL"), nullable=True ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=datetime.utcnow, nullable=False )