"""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": ...}] @dataclass(frozen=True, slots=True) class FundamentalData: """Fundamental metrics returned by fundamental providers.""" ticker: str pe_ratio: float | None revenue_growth: float | None earnings_surprise: float | None market_cap: float | None fetched_at: datetime unavailable_fields: dict[str, str] = field(default_factory=dict) # --------------------------------------------------------------------------- # 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.""" ... class FundamentalProvider(Protocol): """Protocol for fundamental data providers.""" async def fetch_fundamentals(self, ticker: str) -> FundamentalData: """Fetch fundamental data for a ticker.""" ...