"""Provider contracts for external market data sources. The first implementation wraps TickFlow. Other providers (Tushare/AkShare/etc.) should return the same normalized Polars schemas so storage, indicators and backtests stay data-source agnostic. """ from __future__ import annotations from dataclasses import dataclass from datetime import datetime from typing import Literal, Protocol import polars as pl AssetType = Literal["stock", "index", "etf"] @dataclass(frozen=True) class ProviderCapabilities: instruments: bool = False daily: bool = False adj_factor: bool = False minute: bool = False financial: bool = False class MarketDataProvider(Protocol): name: str capabilities: ProviderCapabilities def get_instruments(self, asset_type: AssetType) -> pl.DataFrame: """Return normalized instruments: symbol/name/code/exchange/asset_type/source.""" def get_daily( self, symbols: list[str], start_time: datetime | None, end_time: datetime | None, asset_type: AssetType, ) -> pl.DataFrame: """Return normalized daily K rows.""" def get_adj_factors( self, symbols: list[str], start_time: datetime | None, end_time: datetime | None, asset_type: AssetType, ) -> pl.DataFrame: """Return normalized adjustment factors: symbol/trade_date/ex_factor.""" def get_minute( self, symbols: list[str], start_time: datetime | None, end_time: datetime | None, asset_type: AssetType, freq: str = "1m", ) -> pl.DataFrame: """Return normalized minute K rows. Implementations may return empty."""