A1 carry-forward. The SEC cover-page share count (dei:EntityCommonStockSharesOutstanding) is reported "as of" its own date, which can differ from the fiscal period_end — store that date so market cap uses the right point-in-time count. Migration 026 edited in place (never run with data). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
146 lines
5.9 KiB
Python
146 lines
5.9 KiB
Python
"""Dolt/SEC fundamentals schema — workstream A
|
|
|
|
Revision ID: 026
|
|
Revises: 025
|
|
Create Date: 2026-07-21 00:00:00.000000
|
|
|
|
Foundational schema for the Dolt bulk-data integration (workstream A): the
|
|
batch import-run audit table, the SEC-sourced immutable fundamental snapshots
|
|
(CIK-keyed, one row per accession), the Dolt earnings calendar/history, and the
|
|
SEC issuer identity columns on ``tickers``. No data is populated here — the
|
|
importers land in a later phase. ``fundamental_data`` is left untouched; its
|
|
cutover is gated separately (phase A5). ``data_import_runs`` is created first
|
|
because the other two tables carry an ``import_run_id`` FK to it.
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision: str = "026"
|
|
down_revision: Union[str, None] = "025"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"data_import_runs",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("source", sa.String(length=32), nullable=False),
|
|
sa.Column("revision", sa.String(length=64), nullable=True),
|
|
sa.Column("status", sa.String(length=16), nullable=False),
|
|
sa.Column("source_max_date", sa.Date(), nullable=True),
|
|
sa.Column("row_counts_json", sa.Text(), nullable=True),
|
|
sa.Column("validation_json", sa.Text(), nullable=True),
|
|
sa.Column("started_at", sa.DateTime(timezone=True), nullable=False),
|
|
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("error_details", sa.Text(), nullable=True),
|
|
)
|
|
op.create_index(
|
|
"ix_data_import_runs_source_started", "data_import_runs", ["source", "started_at"]
|
|
)
|
|
|
|
op.create_table(
|
|
"fundamental_snapshots",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("cik", sa.String(length=10), nullable=False),
|
|
sa.Column("accession", sa.String(length=25), nullable=False),
|
|
sa.Column("form", sa.String(length=12), nullable=False),
|
|
sa.Column("filed_date", sa.Date(), nullable=False),
|
|
sa.Column("accepted_at", sa.DateTime(timezone=True), nullable=False),
|
|
sa.Column("period_start", sa.Date(), nullable=True),
|
|
sa.Column("period_end", sa.Date(), nullable=False),
|
|
sa.Column("fiscal_year", sa.Integer(), nullable=False),
|
|
sa.Column("fiscal_period", sa.String(length=4), nullable=False),
|
|
# duration facts — cumulative YTD/FY
|
|
sa.Column("revenue", sa.Float(), nullable=True),
|
|
sa.Column("net_income", sa.Float(), nullable=True),
|
|
sa.Column("operating_income", sa.Float(), nullable=True),
|
|
sa.Column("diluted_eps", sa.Float(), nullable=True),
|
|
sa.Column("cfo", sa.Float(), nullable=True),
|
|
sa.Column("capex", sa.Float(), nullable=True),
|
|
sa.Column("depreciation_amortization", sa.Float(), nullable=True),
|
|
# balance-sheet facts — period-end
|
|
sa.Column("cash_and_st_investments", sa.Float(), nullable=True),
|
|
sa.Column("total_debt", sa.Float(), nullable=True),
|
|
sa.Column("shares_outstanding", sa.Float(), nullable=True),
|
|
sa.Column("shares_outstanding_date", sa.Date(), nullable=True),
|
|
sa.Column(
|
|
"import_run_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("data_import_runs.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
|
sa.UniqueConstraint("accession", name="uq_fundamental_snapshots_accession"),
|
|
)
|
|
op.create_index(
|
|
"ix_fundamental_snapshots_cik_period",
|
|
"fundamental_snapshots",
|
|
["cik", "fiscal_year", "fiscal_period"],
|
|
)
|
|
op.create_index(
|
|
"ix_fundamental_snapshots_cik_period_end",
|
|
"fundamental_snapshots",
|
|
["cik", "period_end"],
|
|
)
|
|
|
|
op.create_table(
|
|
"earnings_events",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column(
|
|
"ticker_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("tickers.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column("announce_date", sa.Date(), nullable=False),
|
|
sa.Column("session", sa.String(length=10), nullable=False),
|
|
sa.Column("period_end", sa.Date(), nullable=True),
|
|
sa.Column("eps_estimate", sa.Float(), nullable=True),
|
|
sa.Column("eps_actual", sa.Float(), nullable=True),
|
|
sa.Column("source", sa.String(length=32), nullable=False),
|
|
sa.Column(
|
|
"import_run_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("data_import_runs.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
|
|
sa.UniqueConstraint("ticker_id", "announce_date", name="uq_earnings_ticker_announce"),
|
|
)
|
|
op.create_index(
|
|
"ix_earnings_events_announce_date", "earnings_events", ["announce_date"]
|
|
)
|
|
|
|
# SEC issuer identity on tickers (nullable; the only ticker<->issuer join point).
|
|
op.add_column("tickers", sa.Column("cik", sa.String(length=10), nullable=True))
|
|
op.add_column("tickers", sa.Column("sic", sa.String(length=4), nullable=True))
|
|
op.add_column(
|
|
"tickers", sa.Column("sic_description", sa.String(length=160), nullable=True)
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("tickers", "sic_description")
|
|
op.drop_column("tickers", "sic")
|
|
op.drop_column("tickers", "cik")
|
|
|
|
op.drop_index("ix_earnings_events_announce_date", table_name="earnings_events")
|
|
op.drop_table("earnings_events")
|
|
|
|
op.drop_index(
|
|
"ix_fundamental_snapshots_cik_period_end", table_name="fundamental_snapshots"
|
|
)
|
|
op.drop_index(
|
|
"ix_fundamental_snapshots_cik_period", table_name="fundamental_snapshots"
|
|
)
|
|
op.drop_table("fundamental_snapshots")
|
|
|
|
op.drop_index(
|
|
"ix_data_import_runs_source_started", table_name="data_import_runs"
|
|
)
|
|
op.drop_table("data_import_runs")
|