""" Real-time Strategy Callbacks This module provides callbacks for integrating real-time strategy execution with the dashboard chart refresh cycle and user interactions. """ import json from dash import Output, Input, State, Patch, ctx, html, no_update, dcc, callback import dash_bootstrap_components as dbc from datetime import datetime, timedelta from typing import Dict, Any, List, Optional from utils.logger import get_logger from strategies.realtime_execution import ( get_realtime_strategy_processor, initialize_realtime_strategy_system, RealTimeConfig, RealTimeSignal ) from strategies.manager import StrategyManager from config.strategies.config_utils import StrategyConfigurationManager logger = get_logger() # Global processor instance _processor = None def get_processor(): """Get or initialize the real-time strategy processor.""" global _processor if _processor is None: config = RealTimeConfig( refresh_interval_seconds=30, max_strategies_concurrent=3, incremental_calculation=True, signal_batch_size=50, enable_signal_broadcasting=True ) _processor = initialize_realtime_strategy_system(config) return _processor def register_realtime_strategy_callbacks(app): """Register real-time strategy callbacks.""" @app.callback( Output('realtime-strategies-store', 'data'), [Input('realtime-strategy-toggle', 'value'), Input('symbol-dropdown', 'value'), Input('timeframe-dropdown', 'value'), Input('strategy-dropdown', 'value')], [State('realtime-strategies-store', 'data')], prevent_initial_call=True ) def manage_realtime_strategies(enable_realtime, symbol, timeframe, strategy_name, current_data): """ Manage real-time strategy registration based on user selections. This callback handles enabling/disabling real-time strategy execution and registers strategies based on current chart selections. """ try: current_data = current_data or {'active_strategies': [], 'enabled': False} processor = get_processor() if not enable_realtime: # Disable all strategies for context_id in current_data.get('active_strategies', []): processor.unregister_strategy(context_id) logger.info(f"Unregistered real-time strategy: {context_id}") return {'active_strategies': [], 'enabled': False} # Enable real-time strategies if symbol and timeframe and strategy_name and strategy_name != 'basic': # Load strategy configuration try: config_manager = StrategyConfigurationManager() strategy_config = config_manager.load_user_strategy_config(strategy_name) if not strategy_config: # Load from templates if user config doesn't exist strategy_config = config_manager.load_strategy_template(strategy_name) if strategy_config: # Register strategy for real-time execution context_id = processor.register_strategy( strategy_name=strategy_name, strategy_config=strategy_config, symbol=symbol, timeframe=timeframe ) active_strategies = [context_id] logger.info(f"Registered real-time strategy: {context_id}") return { 'active_strategies': active_strategies, 'enabled': True, 'current_symbol': symbol, 'current_timeframe': timeframe, 'current_strategy': strategy_name } except Exception as e: logger.error(f"Error loading strategy configuration for {strategy_name}: {e}") return current_data return current_data except Exception as e: logger.error(f"Error managing real-time strategies: {e}") return current_data or {'active_strategies': [], 'enabled': False} @app.callback( Output('realtime-strategy-status', 'children'), [Input('realtime-strategies-store', 'data'), Input('interval-component', 'n_intervals')], prevent_initial_call=True ) def update_realtime_status(strategy_data, n_intervals): """ Update real-time strategy status display. Shows current status of real-time strategy execution including active strategies and performance metrics. """ try: if not strategy_data or not strategy_data.get('enabled'): return dbc.Alert("Real-time strategy execution is disabled", color="secondary", className="mb-2") processor = get_processor() active_strategies = processor.get_active_strategies() perf_stats = processor.get_performance_stats() if not active_strategies: return dbc.Alert("No active real-time strategies", color="warning", className="mb-2") # Build status display status_items = [] # Active strategies for context_id, context in active_strategies.items(): status_items.append( html.Li([ html.Strong(f"{context.strategy_name}: "), f"{context.symbol} {context.timeframe}", html.Span( " ✓" if context.is_active else " ⚠️", style={'color': 'green' if context.is_active else 'orange'} ) ]) ) # Performance metrics success_rate = 0 if perf_stats['total_calculations'] > 0: success_rate = (perf_stats['successful_calculations'] / perf_stats['total_calculations']) * 100 metrics_text = f"Calculations: {perf_stats['total_calculations']} | " \ f"Success Rate: {success_rate:.1f}% | " \ f"Signals Generated: {perf_stats['signals_generated']}" return dbc.Card([ dbc.CardHeader("Real-time Strategy Status"), dbc.CardBody([ html.H6("Active Strategies:", className="mb-2"), html.Ul(status_items, className="mb-3"), html.P(metrics_text, className="small mb-0") ]) ], className="mb-2") except Exception as e: logger.error(f"Error updating real-time status: {e}") return dbc.Alert(f"Error updating status: {str(e)}", color="danger", className="mb-2") # Integration with chart refresh cycle @app.callback( Output('realtime-execution-trigger', 'data'), [Input('interval-component', 'n_intervals')], [State('symbol-dropdown', 'value'), State('timeframe-dropdown', 'value'), State('realtime-strategies-store', 'data'), State('analysis-mode-toggle', 'value')], prevent_initial_call=True ) def trigger_realtime_execution(n_intervals, symbol, timeframe, strategy_data, analysis_mode): """ Trigger real-time strategy execution when new data is available. This callback integrates with the existing chart refresh cycle to execute real-time strategies when new candle data arrives. """ try: # Only execute in live mode if analysis_mode == 'locked': return no_update # Only execute if real-time strategies are enabled if not strategy_data or not strategy_data.get('enabled'): return no_update # Only execute if we have symbol and timeframe if not symbol or not timeframe: return no_update processor = get_processor() # Execute real-time strategy update signals = processor.execute_realtime_update( symbol=symbol, timeframe=timeframe, exchange="okx" ) if signals: logger.info(f"Real-time execution generated {len(signals)} signals for {symbol} {timeframe}") return { 'timestamp': datetime.now().isoformat(), 'signals_generated': len(signals), 'symbol': symbol, 'timeframe': timeframe } return no_update except Exception as e: logger.error(f"Error in real-time strategy execution: {e}") return no_update def add_realtime_strategy_components(): """ Add real-time strategy components to the dashboard layout. Returns: List of Dash components for real-time strategy controls """ return [ # Real-time strategy toggle dbc.Row([ dbc.Col([ dbc.Label("Real-time Strategy Execution", className="fw-bold"), dbc.Switch( id="realtime-strategy-toggle", label="Enable Real-time Execution", value=False, className="mb-2" ), ], width=12) ], className="mb-3"), # Status display html.Div(id="realtime-strategy-status"), # Hidden stores for state management dcc.Store(id="realtime-strategies-store", data={'active_strategies': [], 'enabled': False}), dcc.Store(id="realtime-execution-trigger", data={}), ] def setup_chart_update_callback(): """ Setup chart update callback for real-time signals. This function configures the real-time processor to trigger chart updates when new signals are generated. """ def chart_update_callback(signal: RealTimeSignal): """Handle chart updates for real-time signals.""" try: # This would trigger chart refresh for the specific symbol/timeframe # For now, we'll log the signal and let the regular refresh cycle handle it logger.debug( f"Chart update requested for signal: {signal.context.strategy_name} " f"on {signal.context.symbol} {signal.context.timeframe}" ) # Future enhancement: Could trigger specific chart layer updates here except Exception as e: logger.error(f"Error in chart update callback: {e}") processor = get_processor() processor.set_chart_update_callback(chart_update_callback) # Initialize the chart update callback when module is imported setup_chart_update_callback()