Files
signal-platform/app/providers/protocol.py
T
dennisthiessenandClaude Opus 5 3e83d63b05 chore: decommission FMP, Finnhub and Alpha Vantage (A6)
The A5 cutover has been on and observed in production, so SEC Company Facts +
DoltHub earnings are already the live source for `fundamental_data`. This
removes everything the legacy path still occupied.

Gone: the three providers and their config/env keys; the weekly
`fundamental_collector` job; the cutover toggle (SEC + Dolt is now the
unconditional path, so `off` can no longer silently freeze scoring inputs); the
A5 parity report, whose deltas became structurally zero once the candidate
builder started writing the table it compared against; and the FMP tier of
universe bootstrap.

Two behavioral notes:

- Disabling **SEC Fundamentals Import** now stops the SEC network fetch only.
  The local cache refresh moved outside the job-enable check, because candidates
  also derive from daily closes and earnings events — freezing those on an
  ingestion pause would stale scoring with no fallback left to recover from.
- `/ingestion/fetch?sources=fundamentals` still accepts the key and reports
  `skipped`; there is no per-ticker fetch any more.

Migration 029 does not blanket-delete the leftover settings rows. Migrations run
before the service restart, and pre-A6 code reads an absent `job_*_enabled` row
as *enabled* — so the two behavior-bearing keys become tombstones pinned to safe
values (hidden in Admin) and only the inert three are deleted. Removing the
provider keys from the production `.env` is the matching rollout step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 11:19:28 +02:00

72 lines
2.1 KiB
Python

"""Provider protocols and lightweight data transfer objects.
Protocols define the interface for external data providers.
DTOs are simple dataclasses — NOT SQLAlchemy models — used to
transfer data between providers and the service layer.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import date, datetime
from typing import Protocol
# ---------------------------------------------------------------------------
# Data Transfer Objects
# ---------------------------------------------------------------------------
@dataclass(frozen=True, slots=True)
class OHLCVData:
"""Lightweight OHLCV record returned by market data providers."""
ticker: str
date: date
open: float
high: float
low: float
close: float
volume: int
@dataclass(frozen=True, slots=True)
class SentimentData:
"""Sentiment analysis result returned by sentiment providers."""
ticker: str
classification: str # "bullish" | "bearish" | "neutral"
confidence: int # 0-100
source: str
timestamp: datetime
reasoning: str = ""
citations: list[dict[str, str]] = field(default_factory=list) # [{"url": ..., "title": ...}]
recommendation: str | None = None # "buy" | "hold" | "avoid" — actionable LLM view
# ---------------------------------------------------------------------------
# Provider Protocols
# ---------------------------------------------------------------------------
class MarketDataProvider(Protocol):
"""Protocol for OHLCV market data providers."""
async def fetch_ohlcv(
self, ticker: str, start_date: date, end_date: date
) -> list[OHLCVData]:
"""Fetch OHLCV data for a ticker in a date range."""
...
class SentimentProvider(Protocol):
"""Protocol for sentiment analysis providers."""
async def fetch_sentiment(self, ticker: str) -> SentimentData:
"""Fetch current sentiment analysis for a ticker."""
...
# No fundamentals provider protocol: since A6 fundamentals come only from the
# batch SEC/Dolt imports, never from a request-time provider call.