""" Abstract DataSource interface. Inspired by TradingView's Datafeed API with extensions for flexible column schemas and AI-native metadata. """ from abc import ABC, abstractmethod from typing import Callable, List, Optional from .schema import DatafeedConfig, HistoryResult, SearchResult, SymbolInfo class DataSource(ABC): """ Abstract base class for time-series data sources. Provides a standardized interface for: - Symbol search and metadata retrieval - Historical data queries (time-based, paginated) - Real-time data subscriptions All data rows must have a timestamp. Additional columns are flexible and described via ColumnInfo metadata. """ @abstractmethod async def get_config(self) -> DatafeedConfig: """ Get datafeed configuration and capabilities. Called once during initialization to understand what this data source supports (resolutions, exchanges, search, etc.). Returns: DatafeedConfig describing this datafeed's capabilities """ pass @abstractmethod async def search_symbols( self, query: str, type: Optional[str] = None, exchange: Optional[str] = None, limit: int = 30, ) -> List[SearchResult]: """ Search for symbols matching a text query. Args: query: Free-text search string type: Optional filter by instrument type exchange: Optional filter by exchange limit: Maximum number of results Returns: List of matching symbols with basic metadata """ pass @abstractmethod async def resolve_symbol(self, symbol: str) -> SymbolInfo: """ Get complete metadata for a symbol. Called after a symbol is selected to retrieve full information including supported resolutions, column schema, trading session, etc. Args: symbol: Symbol identifier Returns: Complete SymbolInfo including column definitions Raises: ValueError: If symbol is not found """ pass @abstractmethod async def get_bars( self, symbol: str, resolution: str, from_time: int, to_time: int, countback: Optional[int] = None, ) -> HistoryResult: """ Get historical bars for a symbol and resolution. Time range is specified in Unix timestamps (seconds). If more data is available beyond the requested range, the result should include a nextTime value for pagination. Args: symbol: Symbol identifier resolution: Time resolution (e.g., "1", "5", "60", "1D") from_time: Start time (Unix timestamp in seconds) to_time: End time (Unix timestamp in seconds) countback: Optional limit on number of bars to return Returns: HistoryResult with bars, column schema, and pagination info Raises: ValueError: If symbol or resolution is not supported """ pass @abstractmethod async def subscribe_bars( self, symbol: str, resolution: str, on_tick: Callable[[dict], None], ) -> str: """ Subscribe to real-time bar updates. The callback will be invoked with new bar data as it becomes available. The data dict will match the column schema from resolve_symbol(). Args: symbol: Symbol identifier resolution: Time resolution on_tick: Callback function receiving bar data dict Returns: Subscription ID for later unsubscribe Raises: ValueError: If symbol or resolution is not supported """ pass @abstractmethod async def unsubscribe_bars(self, subscription_id: str) -> None: """ Unsubscribe from real-time updates. Args: subscription_id: ID returned from subscribe_bars() """ pass