refactoring. inference real data triggers
This commit is contained in:
@@ -236,6 +236,11 @@ class DataProvider:
|
||||
self.raw_tick_callbacks = []
|
||||
self.ohlcv_bar_callbacks = []
|
||||
|
||||
# Event subscriptions for inference training system
|
||||
self.candle_completion_callbacks: Dict[Tuple[str, str], List[Callable]] = {} # {(symbol, timeframe): [callbacks]}
|
||||
self.pivot_event_callbacks: Dict[Tuple[str, str, str], List[Callable]] = {} # {(symbol, timeframe, pivot_type): [callbacks]}
|
||||
self.last_completed_candles: Dict[Tuple[str, str], datetime] = {} # Track last completed candle per symbol/timeframe
|
||||
|
||||
# Performance tracking for subscribers
|
||||
self.distribution_stats = {
|
||||
'total_ticks_received': 0,
|
||||
@@ -267,6 +272,7 @@ class DataProvider:
|
||||
self.pivot_points_cache: Dict[str, Dict[int, TrendLevel]] = {} # {symbol: {level: TrendLevel}}
|
||||
self.last_pivot_calculation: Dict[str, datetime] = {}
|
||||
self.pivot_calculation_interval = timedelta(minutes=5) # Recalculate every 5 minutes
|
||||
self.last_emitted_pivots: Dict[Tuple[str, str], List[Tuple[str, datetime]]] = {} # Track emitted pivots to avoid duplicates
|
||||
|
||||
# Unified storage system (optional, initialized on demand)
|
||||
self.unified_storage: Optional['UnifiedDataProviderExtension'] = None
|
||||
@@ -2833,6 +2839,9 @@ class DataProvider:
|
||||
williams = self.williams_structure[symbol]
|
||||
pivot_levels = williams.calculate_recursive_pivot_points(ohlcv_array)
|
||||
|
||||
# Emit pivot events for new pivots (L2L, L2H, L3L, L3H, etc.)
|
||||
self._check_and_emit_pivot_events(symbol, tf, pivot_levels)
|
||||
|
||||
logger.debug(f"Retrieved Williams pivot levels for {symbol}: {len(pivot_levels)} levels")
|
||||
return pivot_levels
|
||||
|
||||
@@ -3580,6 +3589,11 @@ class DataProvider:
|
||||
|
||||
# Check if we need a new candle
|
||||
if not candle_queue or candle_queue[-1]['timestamp'] != candle_start:
|
||||
# Emit candle completion event for previous candle (if exists)
|
||||
if candle_queue:
|
||||
completed_candle = candle_queue[-1]
|
||||
self._emit_candle_completion(symbol, timeframe, completed_candle)
|
||||
|
||||
# Create new candle
|
||||
new_candle = {
|
||||
'timestamp': candle_start,
|
||||
@@ -3601,6 +3615,165 @@ class DataProvider:
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating candle for {symbol} {timeframe}: {e}")
|
||||
|
||||
def subscribe_candle_completion(self, callback: Callable, symbol: str, timeframe: str) -> None:
|
||||
"""
|
||||
Subscribe to candle completion events.
|
||||
|
||||
Args:
|
||||
callback: Function to call when candle completes: callback(event: CandleCompletionEvent)
|
||||
symbol: Trading symbol
|
||||
timeframe: Timeframe (1m, 5m, etc.)
|
||||
"""
|
||||
key = (symbol, timeframe)
|
||||
if key not in self.candle_completion_callbacks:
|
||||
self.candle_completion_callbacks[key] = []
|
||||
self.candle_completion_callbacks[key].append(callback)
|
||||
logger.debug(f"Subscribed to candle completion: {symbol} {timeframe}")
|
||||
|
||||
def subscribe_pivot_events(self, callback: Callable, symbol: str, timeframe: str, pivot_types: List[str]) -> None:
|
||||
"""
|
||||
Subscribe to pivot point events (L2L, L2H, etc.).
|
||||
|
||||
Args:
|
||||
callback: Function to call when pivot detected: callback(event: PivotEvent)
|
||||
symbol: Trading symbol
|
||||
timeframe: Timeframe
|
||||
pivot_types: List of pivot types to subscribe to (e.g., ['L2L', 'L2H', 'L3L'])
|
||||
"""
|
||||
for pivot_type in pivot_types:
|
||||
key = (symbol, timeframe, pivot_type)
|
||||
if key not in self.pivot_event_callbacks:
|
||||
self.pivot_event_callbacks[key] = []
|
||||
self.pivot_event_callbacks[key].append(callback)
|
||||
logger.debug(f"Subscribed to pivot events: {symbol} {timeframe} {pivot_types}")
|
||||
|
||||
def _emit_candle_completion(self, symbol: str, timeframe: str, candle: Dict) -> None:
|
||||
"""Emit candle completion event to subscribers"""
|
||||
try:
|
||||
from ANNOTATE.core.inference_training_system import CandleCompletionEvent
|
||||
|
||||
key = (symbol, timeframe)
|
||||
if key not in self.candle_completion_callbacks:
|
||||
return
|
||||
|
||||
# Check if we already emitted for this candle
|
||||
last_emitted = self.last_completed_candles.get(key)
|
||||
if last_emitted == candle['timestamp']:
|
||||
return # Already emitted
|
||||
|
||||
# Create event
|
||||
event = CandleCompletionEvent(
|
||||
symbol=symbol,
|
||||
timeframe=timeframe,
|
||||
timestamp=candle['timestamp'],
|
||||
ohlcv={
|
||||
'open': float(candle['open']),
|
||||
'high': float(candle['high']),
|
||||
'low': float(candle['low']),
|
||||
'close': float(candle['close']),
|
||||
'volume': float(candle.get('volume', 0))
|
||||
}
|
||||
)
|
||||
|
||||
# Notify subscribers
|
||||
for callback in self.candle_completion_callbacks[key]:
|
||||
try:
|
||||
callback(event)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in candle completion callback: {e}", exc_info=True)
|
||||
|
||||
# Mark as emitted
|
||||
self.last_completed_candles[key] = candle['timestamp']
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error emitting candle completion event: {e}", exc_info=True)
|
||||
|
||||
def _check_and_emit_pivot_events(self, symbol: str, timeframe: str, pivot_levels: Dict[int, Any]) -> None:
|
||||
"""
|
||||
Check for new pivots and emit events to subscribers.
|
||||
|
||||
Args:
|
||||
symbol: Trading symbol
|
||||
timeframe: Timeframe
|
||||
pivot_levels: Dict of pivot levels from Williams Market Structure
|
||||
"""
|
||||
try:
|
||||
key = (symbol, timeframe)
|
||||
last_emitted = self.last_emitted_pivots.get(key, [])
|
||||
|
||||
# Check levels 2, 3, 4, 5 for new pivots
|
||||
for level in [2, 3, 4, 5]:
|
||||
if level not in pivot_levels:
|
||||
continue
|
||||
|
||||
trend_level = pivot_levels[level]
|
||||
if not hasattr(trend_level, 'pivot_points') or not trend_level.pivot_points:
|
||||
continue
|
||||
|
||||
# Get latest pivot for this level
|
||||
latest_pivot = trend_level.pivot_points[-1] if trend_level.pivot_points else None
|
||||
if not latest_pivot:
|
||||
continue
|
||||
|
||||
# Check if we've already emitted this pivot
|
||||
pivot_id = (f"L{level}{latest_pivot.pivot_type[0].upper()}", latest_pivot.timestamp)
|
||||
if pivot_id in last_emitted:
|
||||
continue
|
||||
|
||||
# Emit event
|
||||
pivot_type = f"L{level}{latest_pivot.pivot_type[0].upper()}" # L2L, L2H, L3L, etc.
|
||||
self._emit_pivot_event(
|
||||
symbol=symbol,
|
||||
timeframe=timeframe,
|
||||
pivot_type=pivot_type,
|
||||
timestamp=latest_pivot.timestamp,
|
||||
price=latest_pivot.price,
|
||||
level=level,
|
||||
strength=latest_pivot.strength
|
||||
)
|
||||
|
||||
# Mark as emitted
|
||||
last_emitted.append(pivot_id)
|
||||
# Keep only last 100 emitted pivots to prevent memory bloat
|
||||
if len(last_emitted) > 100:
|
||||
last_emitted = last_emitted[-100:]
|
||||
|
||||
self.last_emitted_pivots[key] = last_emitted
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking and emitting pivot events: {e}", exc_info=True)
|
||||
|
||||
def _emit_pivot_event(self, symbol: str, timeframe: str, pivot_type: str,
|
||||
timestamp: datetime, price: float, level: int, strength: float) -> None:
|
||||
"""Emit pivot event to subscribers"""
|
||||
try:
|
||||
from ANNOTATE.core.inference_training_system import PivotEvent
|
||||
|
||||
key = (symbol, timeframe, pivot_type)
|
||||
if key not in self.pivot_event_callbacks:
|
||||
return
|
||||
|
||||
# Create event
|
||||
event = PivotEvent(
|
||||
symbol=symbol,
|
||||
timeframe=timeframe,
|
||||
timestamp=timestamp,
|
||||
pivot_type=pivot_type,
|
||||
price=price,
|
||||
level=level,
|
||||
strength=strength
|
||||
)
|
||||
|
||||
# Notify subscribers
|
||||
for callback in self.pivot_event_callbacks[key]:
|
||||
try:
|
||||
callback(event)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in pivot event callback: {e}", exc_info=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error emitting pivot event: {e}", exc_info=True)
|
||||
|
||||
def get_latest_candles(self, symbol: str, timeframe: str, limit: int = 100) -> pd.DataFrame:
|
||||
"""Get the latest candles from cached data only"""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user