""" Base interface for exchange WebSocket connectors. """ from abc import ABC, abstractmethod from typing import Callable, List, Optional from ..models.core import ConnectionStatus, OrderBookSnapshot, TradeEvent class ExchangeConnector(ABC): """Base interface for exchange WebSocket connectors""" def __init__(self, exchange_name: str): self.exchange_name = exchange_name self._data_callbacks: List[Callable] = [] self._status_callbacks: List[Callable] = [] self._connection_status = ConnectionStatus.DISCONNECTED @abstractmethod async def connect(self) -> bool: """ Establish connection to the exchange WebSocket. Returns: bool: True if connection successful, False otherwise """ pass @abstractmethod async def disconnect(self) -> None: """Disconnect from the exchange WebSocket.""" pass @abstractmethod async def subscribe_orderbook(self, symbol: str) -> None: """ Subscribe to order book updates for a symbol. Args: symbol: Trading symbol (e.g., 'BTCUSDT') """ pass @abstractmethod async def subscribe_trades(self, symbol: str) -> None: """ Subscribe to trade updates for a symbol. Args: symbol: Trading symbol (e.g., 'BTCUSDT') """ pass @abstractmethod async def unsubscribe_orderbook(self, symbol: str) -> None: """ Unsubscribe from order book updates for a symbol. Args: symbol: Trading symbol (e.g., 'BTCUSDT') """ pass @abstractmethod async def unsubscribe_trades(self, symbol: str) -> None: """ Unsubscribe from trade updates for a symbol. Args: symbol: Trading symbol (e.g., 'BTCUSDT') """ pass def get_connection_status(self) -> ConnectionStatus: """ Get current connection status. Returns: ConnectionStatus: Current connection status """ return self._connection_status def add_data_callback(self, callback: Callable) -> None: """ Add callback for data updates. Args: callback: Function to call when data is received Signature: callback(data: Union[OrderBookSnapshot, TradeEvent]) """ if callback not in self._data_callbacks: self._data_callbacks.append(callback) def remove_data_callback(self, callback: Callable) -> None: """ Remove data callback. Args: callback: Callback function to remove """ if callback in self._data_callbacks: self._data_callbacks.remove(callback) def add_status_callback(self, callback: Callable) -> None: """ Add callback for status updates. Args: callback: Function to call when status changes Signature: callback(exchange: str, status: ConnectionStatus) """ if callback not in self._status_callbacks: self._status_callbacks.append(callback) def remove_status_callback(self, callback: Callable) -> None: """ Remove status callback. Args: callback: Callback function to remove """ if callback in self._status_callbacks: self._status_callbacks.remove(callback) def _notify_data_callbacks(self, data): """Notify all data callbacks of new data.""" for callback in self._data_callbacks: try: callback(data) except Exception as e: # Log error but don't stop other callbacks print(f"Error in data callback: {e}") def _notify_status_callbacks(self, status: ConnectionStatus): """Notify all status callbacks of status change.""" self._connection_status = status for callback in self._status_callbacks: try: callback(self.exchange_name, status) except Exception as e: # Log error but don't stop other callbacks print(f"Error in status callback: {e}") @abstractmethod async def get_symbols(self) -> List[str]: """ Get list of available trading symbols. Returns: List[str]: List of available symbols """ pass @abstractmethod def normalize_symbol(self, symbol: str) -> str: """ Normalize symbol to exchange format. Args: symbol: Standard symbol format (e.g., 'BTCUSDT') Returns: str: Exchange-specific symbol format """ pass @abstractmethod async def get_orderbook_snapshot(self, symbol: str, depth: int = 20) -> Optional[OrderBookSnapshot]: """ Get current order book snapshot. Args: symbol: Trading symbol depth: Number of price levels to retrieve Returns: OrderBookSnapshot: Current order book or None if unavailable """ pass @property def name(self) -> str: """Get exchange name.""" return self.exchange_name @property def is_connected(self) -> bool: """Check if connector is connected.""" return self._connection_status == ConnectionStatus.CONNECTED