118 lines
3.0 KiB
Python
118 lines
3.0 KiB
Python
|
|
#!/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
|