#!/usr/bin/env python3 """ PORT: DataFeed ============== Abstract interface for market data sources. Clean Architecture Principle: - Core business logic depends on this PORT (interface) - Adapters implement this port - Easy to swap: Hazelcast → Binance → In-Kernel Rust Future Evolution: - Current: HazelcastAdapter (DolphinNG6 feed) - Next: BinanceWebsocketAdapter (direct) - Future: RustKernelAdapter (in-kernel, zero-copy) """ from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Dict, List, Optional, Callable, Any from datetime import datetime @dataclass(frozen=True) class MarketSnapshot: """ Immutable market snapshot - single source of truth. Contains BOTH price and computed features (eigenvalues, etc.) Guaranteed to be synchronized - same timestamp for all fields. """ timestamp: datetime symbol: str # Price data price: float bid: Optional[float] = None ask: Optional[float] = None # Computed features (from DolphinNG6) eigenvalues: Optional[List[float]] = None eigenvectors: Optional[Any] = None # Matrix velocity_divergence: Optional[float] = None irp_alignment: Optional[float] = None # Metadata scan_number: Optional[int] = None source: str = "unknown" # "hazelcast", "binance", "kernel" def is_valid(self) -> bool: """Check if snapshot has required fields.""" return self.price > 0 and self.eigenvalues is not None @dataclass class ACBUpdate: """Adaptive Circuit Breaker update.""" timestamp: datetime boost: float beta: float cut: float posture: str class DataFeedPort(ABC): """ PORT: Abstract data feed interface. Implementations: - HazelcastDataFeed: Current (DolphinNG6 integration) - BinanceDataFeed: Direct WebSocket - RustKernelDataFeed: Future in-kernel implementation """ @abstractmethod async def connect(self) -> bool: """Connect to data source.""" pass @abstractmethod async def disconnect(self): """Clean disconnect.""" pass @abstractmethod async def get_latest_snapshot(self, symbol: str) -> Optional[MarketSnapshot]: """ Get latest synchronized snapshot (price + features). This is the KEY method - returns ATOMIC data. No sync issues possible. """ pass @abstractmethod async def subscribe_snapshots(self, callback: Callable[[MarketSnapshot], None]): """ Subscribe to snapshot stream. callback receives MarketSnapshot whenever new data arrives. """ pass @abstractmethod async def get_acb_update(self) -> Optional[ACBUpdate]: """Get latest ACB (Adaptive Circuit Breaker) update.""" pass @abstractmethod def get_latency_ms(self) -> float: """Report current data latency (for monitoring).""" pass @abstractmethod def health_check(self) -> bool: """Check if feed is healthy.""" pass