diff --git a/CHANGELOG.md b/CHANGELOG.md index a066bcf..92a1504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to Stock Oracle API will be documented in this file. +## [3.0.1] - 2026-03-14 + +### Added +- **Filing Search `accepted_at` field**: `GET /filings/search/{ticker}` response now includes `accepted_at` (ISO 8601 timestamp) in each `FilingSummary` + - Sourced from `acceptanceDateTime` in SEC EDGAR submissions JSON — no additional HTTP requests needed + - `SECFiling` DB model gains a nullable `accepted_at TIMESTAMP` column + - SQLite auto-migration: `ALTER TABLE sec_filings ADD COLUMN accepted_at TIMESTAMP` runs at startup (idempotent) + - Existing rows have `accepted_at = NULL`; re-index with `force_refresh=true` to backfill + - Enables downstream consumers (e.g. `filing_poller`) to populate `Document.accepted_at_utc` and `Event.filed_at_utc` + ## [3.0.0] - 2026-03-12 ### Added diff --git a/app/api/v1/endpoints/filings.py b/app/api/v1/endpoints/filings.py index 73a68d8..3de3100 100644 --- a/app/api/v1/endpoints/filings.py +++ b/app/api/v1/endpoints/filings.py @@ -98,6 +98,7 @@ async def search_filings( accession_number=f.accession_number, form_type=f.form_type, filing_date=f.filing_date.strftime("%Y-%m-%d"), + accepted_at=f.accepted_at.isoformat() if f.accepted_at else None, primary_document=f.primary_document, filing_description=f.filing_description, documents_count=docs_count, diff --git a/app/main.py b/app/main.py index 265b942..5117734 100644 --- a/app/main.py +++ b/app/main.py @@ -21,6 +21,13 @@ async def lifespan(app: FastAPI): # Startup - ensure tables exist async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) + # SQLite migration: add accepted_at column if missing + try: + await conn.exec_driver_sql( + "ALTER TABLE sec_filings ADD COLUMN accepted_at TIMESTAMP" + ) + except Exception: + pass # Column already exists # Start overlay batch scheduler (graceful no-op if apscheduler not installed) try: from app.services.overlay.scheduler import start_scheduler diff --git a/app/models/filing.py b/app/models/filing.py index 1e0179f..b682938 100644 --- a/app/models/filing.py +++ b/app/models/filing.py @@ -18,6 +18,7 @@ class SECFiling(Base): accession_number = Column(String(30), nullable=False, unique=True, index=True) form_type = Column(String(20), nullable=False, index=True) filing_date = Column(TIMESTAMP(timezone=True), nullable=False) + accepted_at = Column(TIMESTAMP(timezone=True)) primary_document = Column(String(256)) primary_document_url = Column(String(512)) filing_description = Column(String(512)) diff --git a/app/schemas/filing.py b/app/schemas/filing.py index b545876..b71e10e 100644 --- a/app/schemas/filing.py +++ b/app/schemas/filing.py @@ -19,6 +19,7 @@ class FilingSummary(BaseModel): accession_number: str form_type: str filing_date: str + accepted_at: Optional[str] = None primary_document: Optional[str] = None filing_description: Optional[str] = None documents_count: Optional[int] = None diff --git a/app/services/sec_filings_service.py b/app/services/sec_filings_service.py index a2a7844..4ff1998 100644 --- a/app/services/sec_filings_service.py +++ b/app/services/sec_filings_service.py @@ -65,6 +65,7 @@ class SECFilingsService: accessions = block.get("accessionNumber", []) primary_docs = block.get("primaryDocument", []) descriptions = block.get("primaryDocDescription", []) + accepted_dates = block.get("acceptanceDateTime", []) for i, (form, dt_str, acc) in enumerate(zip(forms, dates, accessions)): if form not in target_forms: continue @@ -76,6 +77,14 @@ class SECFilingsService: ) except Exception: continue + accepted_at = None + if i < len(accepted_dates) and accepted_dates[i]: + try: + accepted_at = datetime.fromisoformat( + accepted_dates[i].replace("Z", "+00:00") + ) + except Exception: + pass acc_clean = acc.replace("-", "") pri_doc_url = None if pri_doc: @@ -88,6 +97,7 @@ class SECFilingsService: "accession_number": acc, "form_type": form, "filing_date": filing_date, + "accepted_at": accepted_at, "primary_document": pri_doc, "primary_document_url": pri_doc_url, "filing_description": desc, @@ -127,6 +137,7 @@ class SECFilingsService: existing.cik = rf["cik"] existing.form_type = rf["form_type"] existing.filing_date = rf["filing_date"] + existing.accepted_at = rf["accepted_at"] existing.primary_document = rf["primary_document"] existing.primary_document_url = rf["primary_document_url"] existing.filing_description = rf["filing_description"] @@ -140,6 +151,7 @@ class SECFilingsService: accession_number=rf["accession_number"], form_type=rf["form_type"], filing_date=rf["filing_date"], + accepted_at=rf["accepted_at"], primary_document=rf["primary_document"], primary_document_url=rf["primary_document_url"], filing_description=rf["filing_description"],