An escalated gap stops pausing setups while the issuer's own fundamentals are still recent. That reprieve ends on its own — the stored filings age past GAP_GATE_RECENT_FILING_DAYS, or a newer gap arrives and the all-escalated condition fails — and nothing reported either, because filing_gap_aged only escalates gaps whose escalated_at is NULL and so never fires twice for the same gap. For the 43 issuers behind the previous commit that lands around 2026-10-26, when their late-April filings age out together. sec_filing_gaps.exempted_at (migration 034) makes the transition observable: stamped quietly while the issuer is exempt, cleared when the exemption lapses, and the clear is what raises filing_gap_repaused — once per lapse, re-arming if the issuer's data recovers and ages out again. A gap that was never exempt has no transition and stays silent; it is simply still paused, which filing_gap_aged already said. gap_exempt_ciks is public so the importer alerts on membership changes in exactly the set the gate reads, rather than restating the rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Lo99z3jWqu9X9ueBU3Z3D
38 lines
1.8 KiB
Python
38 lines
1.8 KiB
Python
from datetime import date, datetime
|
|
|
|
from sqlalchemy import Date, DateTime, Index, String, Text, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.database import Base
|
|
|
|
|
|
class SecFilingGap(Base):
|
|
"""Active SEC filing that could not yet be reconstructed.
|
|
|
|
Rows form a small retry queue. Successful snapshot ingestion deletes the
|
|
matching row; a later valid filing supersedes it. While a current row remains,
|
|
tickers mapped to its CIK are not eligible for actionable trade setups.
|
|
"""
|
|
|
|
__tablename__ = "sec_filing_gaps"
|
|
__table_args__ = (
|
|
UniqueConstraint("accession", name="uq_sec_filing_gaps_accession"),
|
|
Index("ix_sec_filing_gaps_cik", "cik"),
|
|
)
|
|
|
|
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 | None] = mapped_column(String(12), nullable=True)
|
|
index_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
|
reason: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
coregistrant_ciks_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
first_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
last_attempted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
escalated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
# Set while this gap's issuer is exempt from the setup pause (escalated, and
|
|
# its own fundamentals still recent — see fundamentals_quality_service).
|
|
# Cleared when the exemption lapses, which is the moment the pause silently
|
|
# comes back and the only moment worth alerting on.
|
|
exempted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|