From 47173a8554300af1e943b8c0da0a124d9d1991da Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Wed, 25 Jun 2025 02:59:16 +0300 Subject: [PATCH] adding model predictions to dash (wip) --- core/data_provider.py | 15 ++- web/clean_dashboard.py | 260 ++++++++++++++++++++++++++++++++++++++- web/component_manager.py | 2 +- 3 files changed, 268 insertions(+), 9 deletions(-) diff --git a/core/data_provider.py b/core/data_provider.py index 7e2ca59..59d2c35 100644 --- a/core/data_provider.py +++ b/core/data_provider.py @@ -1295,12 +1295,19 @@ class DataProvider: try: cache_file = self.cache_dir / f"{symbol.replace('/', '')}_{timeframe}.parquet" if cache_file.exists(): - # Check if cache is recent (less than 1 hour old) + # Check if cache is recent - stricter rules for startup cache_age = time.time() - cache_file.stat().st_mtime - if cache_age < 3600: # 1 hour + + # For 1m data, use cache only if less than 5 minutes old to avoid gaps + if timeframe == '1m': + max_age = 300 # 5 minutes + else: + max_age = 3600 # 1 hour for other timeframes + + if cache_age < max_age: try: df = pd.read_parquet(cache_file) - logger.debug(f"Loaded {len(df)} rows from cache for {symbol} {timeframe}") + logger.debug(f"Loaded {len(df)} rows from cache for {symbol} {timeframe} (age: {cache_age/60:.1f}min)") return df except Exception as parquet_e: # Handle corrupted Parquet file @@ -1314,7 +1321,7 @@ class DataProvider: else: raise parquet_e else: - logger.debug(f"Cache for {symbol} {timeframe} is too old ({cache_age/3600:.1f}h)") + logger.debug(f"Cache for {symbol} {timeframe} is too old ({cache_age/60:.1f}min > {max_age/60:.1f}min)") return None except Exception as e: logger.warning(f"Error loading cache for {symbol} {timeframe}: {e}") diff --git a/web/clean_dashboard.py b/web/clean_dashboard.py index 455a578..fc1187f 100644 --- a/web/clean_dashboard.py +++ b/web/clean_dashboard.py @@ -339,9 +339,15 @@ class CleanTradingDashboard: def _create_price_chart(self, symbol: str) -> go.Figure: """Create 1-minute main chart with 1-second mini chart - Updated every second""" try: - # FIXED: Merge historical + live data instead of replacing - # 1. Get historical 1-minute data as base (180 candles = 3 hours) - df_historical = self.data_provider.get_historical_data(symbol, '1m', limit=180) + # FIXED: Always get fresh data on startup to avoid gaps + # 1. Get historical 1-minute data as base (180 candles = 3 hours) - FORCE REFRESH on first load + is_startup = not hasattr(self, '_chart_initialized') or not self._chart_initialized + df_historical = self.data_provider.get_historical_data(symbol, '1m', limit=180, refresh=is_startup) + + # Mark chart as initialized to use cache on subsequent loads + if is_startup: + self._chart_initialized = True + logger.info(f"[STARTUP] Fetched fresh {symbol} 1m data to avoid gaps") # 2. Get WebSocket 1s data and convert to 1m bars ws_data_raw = self._get_websocket_chart_data(symbol, 'raw') @@ -433,6 +439,12 @@ class CleanTradingDashboard: row=1, col=1 ) + # ADD MODEL PREDICTIONS TO MAIN CHART + self._add_model_predictions_to_chart(fig, symbol, df_main, row=1) + + # ADD TRADES TO MAIN CHART + self._add_trades_to_chart(fig, symbol, df_main, row=1) + # Mini 1-second chart (if available) if has_mini_chart: fig.add_trace( @@ -465,7 +477,7 @@ class CleanTradingDashboard: fig.update_layout( title=f'{symbol} Live Chart - {main_source} (Updated Every Second)', template='plotly_dark', - showlegend=False, + showlegend=True, # Show legend for model predictions height=chart_height, margin=dict(l=50, r=50, t=60, b=50), xaxis_rangeslider_visible=False @@ -502,6 +514,246 @@ class CleanTradingDashboard: xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False) + def _add_model_predictions_to_chart(self, fig: go.Figure, symbol: str, df_main: pd.DataFrame, row: int = 1): + """Add model predictions to the chart""" + try: + # Get CNN predictions from orchestrator + if self.orchestrator and hasattr(self.orchestrator, 'get_recent_predictions'): + try: + cnn_predictions = self.orchestrator.get_recent_predictions(symbol) + if cnn_predictions: + # Separate by prediction type + buy_predictions = [] + sell_predictions = [] + + for pred in cnn_predictions[-20:]: # Last 20 predictions + pred_time = pred.get('timestamp') + pred_price = pred.get('price', 0) + pred_action = pred.get('action', 'HOLD') + pred_confidence = pred.get('confidence', 0) + + if pred_time and pred_price and pred_confidence > 0.5: # Only confident predictions + if pred_action == 'BUY': + buy_predictions.append({'x': pred_time, 'y': pred_price, 'confidence': pred_confidence}) + elif pred_action == 'SELL': + sell_predictions.append({'x': pred_time, 'y': pred_price, 'confidence': pred_confidence}) + + # Add BUY predictions (green triangles) + if buy_predictions: + fig.add_trace( + go.Scatter( + x=[p['x'] for p in buy_predictions], + y=[p['y'] for p in buy_predictions], + mode='markers', + marker=dict( + symbol='triangle-up', + size=12, + color='rgba(0, 255, 100, 0.8)', + line=dict(width=2, color='green') + ), + name='CNN BUY Predictions', + showlegend=True, + hovertemplate="CNN BUY Prediction
" + + "Price: $%{y:.2f}
" + + "Time: %{x}
" + + "Confidence: %{customdata:.1%}", + customdata=[p['confidence'] for p in buy_predictions] + ), + row=row, col=1 + ) + + # Add SELL predictions (red triangles) + if sell_predictions: + fig.add_trace( + go.Scatter( + x=[p['x'] for p in sell_predictions], + y=[p['y'] for p in sell_predictions], + mode='markers', + marker=dict( + symbol='triangle-down', + size=12, + color='rgba(255, 100, 100, 0.8)', + line=dict(width=2, color='red') + ), + name='CNN SELL Predictions', + showlegend=True, + hovertemplate="CNN SELL Prediction
" + + "Price: $%{y:.2f}
" + + "Time: %{x}
" + + "Confidence: %{customdata:.1%}", + customdata=[p['confidence'] for p in sell_predictions] + ), + row=row, col=1 + ) + except Exception as e: + logger.debug(f"Could not get CNN predictions: {e}") + + # Get COB RL predictions + if hasattr(self, 'cob_predictions') and symbol in self.cob_predictions: + try: + cob_preds = self.cob_predictions[symbol][-10:] # Last 10 COB predictions + + up_predictions = [] + down_predictions = [] + + for pred in cob_preds: + pred_time = pred.get('timestamp') + pred_direction = pred.get('direction', 1) # 0=DOWN, 1=SIDEWAYS, 2=UP + pred_confidence = pred.get('confidence', 0) + + if pred_time and pred_confidence > 0.7: # Only high confidence COB predictions + # Get price from main chart at that time + pred_price = self._get_price_at_time(df_main, pred_time) + if pred_price: + if pred_direction == 2: # UP + up_predictions.append({'x': pred_time, 'y': pred_price, 'confidence': pred_confidence}) + elif pred_direction == 0: # DOWN + down_predictions.append({'x': pred_time, 'y': pred_price, 'confidence': pred_confidence}) + + # Add COB UP predictions (cyan diamonds) + if up_predictions: + fig.add_trace( + go.Scatter( + x=[p['x'] for p in up_predictions], + y=[p['y'] for p in up_predictions], + mode='markers', + marker=dict( + symbol='diamond', + size=10, + color='rgba(0, 255, 255, 0.9)', + line=dict(width=2, color='cyan') + ), + name='COB RL UP (1B)', + showlegend=True, + hovertemplate="COB RL UP Prediction
" + + "Price: $%{y:.2f}
" + + "Time: %{x}
" + + "Confidence: %{customdata:.1%}
" + + "Model: 1B Parameters", + customdata=[p['confidence'] for p in up_predictions] + ), + row=row, col=1 + ) + + # Add COB DOWN predictions (magenta diamonds) + if down_predictions: + fig.add_trace( + go.Scatter( + x=[p['x'] for p in down_predictions], + y=[p['y'] for p in down_predictions], + mode='markers', + marker=dict( + symbol='diamond', + size=10, + color='rgba(255, 0, 255, 0.9)', + line=dict(width=2, color='magenta') + ), + name='COB RL DOWN (1B)', + showlegend=True, + hovertemplate="COB RL DOWN Prediction
" + + "Price: $%{y:.2f}
" + + "Time: %{x}
" + + "Confidence: %{customdata:.1%}
" + + "Model: 1B Parameters", + customdata=[p['confidence'] for p in down_predictions] + ), + row=row, col=1 + ) + except Exception as e: + logger.debug(f"Could not get COB predictions: {e}") + + except Exception as e: + logger.warning(f"Error adding model predictions to chart: {e}") + + def _add_trades_to_chart(self, fig: go.Figure, symbol: str, df_main: pd.DataFrame, row: int = 1): + """Add executed trades to the chart""" + try: + if not self.closed_trades: + return + + buy_trades = [] + sell_trades = [] + + for trade in self.closed_trades[-20:]: # Last 20 trades + entry_time = trade.get('entry_time') + side = trade.get('side', 'UNKNOWN') + entry_price = trade.get('entry_price', 0) + pnl = trade.get('pnl', 0) + + if entry_time and entry_price: + trade_data = {'x': entry_time, 'y': entry_price, 'pnl': pnl} + + if side == 'BUY': + buy_trades.append(trade_data) + elif side == 'SELL': + sell_trades.append(trade_data) + + # Add BUY trades (green circles) + if buy_trades: + fig.add_trace( + go.Scatter( + x=[t['x'] for t in buy_trades], + y=[t['y'] for t in buy_trades], + mode='markers', + marker=dict( + symbol='circle', + size=8, + color='rgba(0, 255, 0, 0.7)', + line=dict(width=2, color='green') + ), + name='BUY Trades', + showlegend=True, + hovertemplate="BUY Trade Executed
" + + "Price: $%{y:.2f}
" + + "Time: %{x}
" + + "P&L: $%{customdata:.2f}", + customdata=[t['pnl'] for t in buy_trades] + ), + row=row, col=1 + ) + + # Add SELL trades (red circles) + if sell_trades: + fig.add_trace( + go.Scatter( + x=[t['x'] for t in sell_trades], + y=[t['y'] for t in sell_trades], + mode='markers', + marker=dict( + symbol='circle', + size=8, + color='rgba(255, 0, 0, 0.7)', + line=dict(width=2, color='red') + ), + name='SELL Trades', + showlegend=True, + hovertemplate="SELL Trade Executed
" + + "Price: $%{y:.2f}
" + + "Time: %{x}
" + + "P&L: $%{customdata:.2f}", + customdata=[t['pnl'] for t in sell_trades] + ), + row=row, col=1 + ) + + except Exception as e: + logger.warning(f"Error adding trades to chart: {e}") + + def _get_price_at_time(self, df: pd.DataFrame, timestamp) -> Optional[float]: + """Get price from dataframe at specific timestamp""" + try: + if isinstance(timestamp, str): + timestamp = pd.to_datetime(timestamp) + + # Find closest timestamp in dataframe + closest_idx = df.index.get_indexer([timestamp], method='nearest')[0] + if closest_idx >= 0 and closest_idx < len(df): + return float(df.iloc[closest_idx]['close']) + + return None + except Exception: + return None + def _get_websocket_chart_data(self, symbol: str, timeframe: str = '1m') -> Optional[pd.DataFrame]: """Get WebSocket chart data - supports both 1m and 1s timeframes""" try: diff --git a/web/component_manager.py b/web/component_manager.py index 521436a..d79aaa4 100644 --- a/web/component_manager.py +++ b/web/component_manager.py @@ -22,7 +22,7 @@ class DashboardComponentManager: return [html.P("No recent signals", className="text-muted small")] signals = [] - for decision in recent_decisions[-10:]: # Last 10 signals + for decision in reversed(recent_decisions[-10:]): # Last 10 signals, reversed # Handle both TradingDecision objects and dictionary formats if hasattr(decision, 'timestamp'): # This is a TradingDecision object (dataclass)