"""Pydantic response models for Stock Oracle API.""" from __future__ import annotations from typing import Any from pydantic import BaseModel, Field # --------------------------------------------------------------------------- # Company Info # --------------------------------------------------------------------------- class CompanyInfo(BaseModel): ticker: str name: str | None = None cik: str | None = None exchange: str | None = None sector: str | None = None industry: str | None = None country: str | None = None market_cap: float | None = None extra: dict[str, Any] = Field(default_factory=dict) # --------------------------------------------------------------------------- # SEC Filings # --------------------------------------------------------------------------- class FilingEntry(BaseModel): accession_no: str form_type: str filing_date: str # ISO date string accepted_at: str | None = None primary_document: str | None = None description: str | None = None items: list[str] = Field(default_factory=list) class FilingSearchResponse(BaseModel): ticker: str filings: list[FilingEntry] = Field(default_factory=list) total: int = 0 class ExhibitDocument(BaseModel): exhibit_type: str filename: str | None = None url: str | None = None class FilingDocumentsResponse(BaseModel): accession_no: str exhibits: list[ExhibitDocument] = Field(default_factory=list) class ExhibitResponse(BaseModel): accession_no: str exhibit_type: str content: str content_type: str = "text/plain" # --------------------------------------------------------------------------- # Price / Market Data # --------------------------------------------------------------------------- class PriceBar(BaseModel): date: str # ISO date open: float high: float low: float close: float volume: int vwap: float | None = None trade_count: int | None = None class PriceDataResponse(BaseModel): ticker: str bars: list[PriceBar] = Field(default_factory=list) source: str = "yfinance" class PriceQuote(BaseModel): ticker: str price: float bid: float | None = None ask: float | None = None volume: int | None = None timestamp: str | None = None class IntradayBar(BaseModel): timestamp: str open: float high: float low: float close: float volume: int class IntradayResponse(BaseModel): ticker: str bars: list[IntradayBar] = Field(default_factory=list) timeframe: str = "1m" # --------------------------------------------------------------------------- # Financial / XBRL # --------------------------------------------------------------------------- class FinancialPeriod(BaseModel): period: str # e.g. "2025-Q4" period_end: str # ISO date revenue: float | None = None net_income: float | None = None eps: float | None = None gross_margin: float | None = None operating_margin: float | None = None extra: dict[str, Any] = Field(default_factory=dict) class FinancialDataResponse(BaseModel): ticker: str periods: list[FinancialPeriod] = Field(default_factory=list) # --------------------------------------------------------------------------- # FRED # --------------------------------------------------------------------------- class FredObservation(BaseModel): date: str # ISO date value: float | None = None class FredProxyResponse(BaseModel): series_id: str observations: list[FredObservation] = Field(default_factory=list) realtime_start: str | None = None realtime_end: str | None = None class FredSeriesInfo(BaseModel): id: str title: str | None = None frequency: str | None = None units: str | None = None notes: str | None = None # --------------------------------------------------------------------------- # FINRA Short Volume # --------------------------------------------------------------------------- class ShortVolumeEntry(BaseModel): date: str # ISO date short_volume: int short_exempt_volume: int | None = None total_volume: int | None = None class ShortVolumeResponse(BaseModel): symbol: str data: list[ShortVolumeEntry] = Field(default_factory=list) class ShortRatioPoint(BaseModel): date: str short_ratio: float | None = None short_percent: float | None = None class ShortRatioResponse(BaseModel): symbol: str data: list[ShortRatioPoint] = Field(default_factory=list) # --------------------------------------------------------------------------- # Screener # --------------------------------------------------------------------------- class ScreenerStock(BaseModel): symbol: str name: str | None = None exchange: str | None = None market_cap: float | None = None avg_volume: float | None = None class ScreenerResponse(BaseModel): stocks: list[ScreenerStock] = Field(default_factory=list) total: int = 0 page: int = 1 page_size: int = 250 # --------------------------------------------------------------------------- # Attention # --------------------------------------------------------------------------- class EntityInfo(BaseModel): ticker: str canonical_name: str wiki_title: str | None = None gdelt_query: str | None = None aliases: list[str] = Field(default_factory=list) resolver_confidence: float = 0.0 is_manual_override: bool = False class EntityResolveResponse(BaseModel): ticker: str entity: EntityInfo status: str message: str class WikiFeatures(BaseModel): views: int | None = None baseline_10d: float | None = None spike_10d: float | None = None zscore_20d: float | None = None class NewsFeatures(BaseModel): article_count_1d: int = 0 article_count_3d: int = 0 unique_domains_3d: int = 0 us_article_count_3d: int = 0 gdelt_status: str = "not_collected" class EventAttentionResponse(BaseModel): ticker: str event_date: str entity: EntityInfo wiki: WikiFeatures news: NewsFeatures metadata: dict[str, Any] = Field(default_factory=dict) class CollectionStatusResponse(BaseModel): ticker: str source: str records_collected: int date_range: dict[str, Any] = Field(default_factory=dict) status: str