From ebf65494a875b6e848650ed8ef8bcb00c2296476 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Sun, 13 Jul 2025 23:41:47 +0300 Subject: [PATCH] try to fix input dimentions --- NN/models/cob_rl_model.py | 6 + NN/models/dqn_agent.py | 7 ++ NN/models/enhanced_cnn.py | 16 ++- core/orchestrator.py | 225 +++++++++++++++++---------------- enhanced_realtime_training.py | 226 +++++++++++++++++++++++++++++----- web/clean_dashboard.py | 19 ++- web/component_manager.py | 4 + 7 files changed, 358 insertions(+), 145 deletions(-) diff --git a/NN/models/cob_rl_model.py b/NN/models/cob_rl_model.py index df9cc91..23391e0 100644 --- a/NN/models/cob_rl_model.py +++ b/NN/models/cob_rl_model.py @@ -250,6 +250,12 @@ class COBRLModelInterface(ModelInterface): logger.info(f"COB RL Model Interface initialized on {self.device}") + def to(self, device): + """PyTorch-style device movement method""" + self.device = device + self.model = self.model.to(device) + return self + def predict(self, cob_features: np.ndarray) -> Dict[str, Any]: """Make prediction using the model""" self.model.eval() diff --git a/NN/models/dqn_agent.py b/NN/models/dqn_agent.py index 3c93716..b5e2d10 100644 --- a/NN/models/dqn_agent.py +++ b/NN/models/dqn_agent.py @@ -454,6 +454,13 @@ class DQNAgent: logger.error(f"Failed to move models to {self.device}: {str(e)}") return False + def to(self, device): + """PyTorch-style device movement method""" + self.device = device + self.policy_net = self.policy_net.to(device) + self.target_net = self.target_net.to(device) + return self + def remember(self, state: np.ndarray, action: int, reward: float, next_state: np.ndarray, done: bool, is_extrema: bool = False): """ diff --git a/NN/models/enhanced_cnn.py b/NN/models/enhanced_cnn.py index 722aec8..2ed55b4 100644 --- a/NN/models/enhanced_cnn.py +++ b/NN/models/enhanced_cnn.py @@ -498,10 +498,20 @@ class EnhancedCNN(nn.Module): """Enhanced action selection with ultra massive model predictions""" if explore and np.random.random() < 0.1: # 10% random exploration return np.random.choice(self.n_actions) - + self.eval() - state_tensor = torch.FloatTensor(state).unsqueeze(0).to(self.device) - + + # Accept both NumPy arrays and already-built torch tensors + if isinstance(state, torch.Tensor): + state_tensor = state.detach().to(self.device) + if state_tensor.dim() == 1: + state_tensor = state_tensor.unsqueeze(0) + else: + # Convert to tensor **directly on the target device** to avoid intermediate CPU copies + state_tensor = torch.as_tensor(state, dtype=torch.float32, device=self.device) + if state_tensor.dim() == 1: + state_tensor = state_tensor.unsqueeze(0) + with torch.no_grad(): q_values, extrema_pred, price_predictions, features, advanced_predictions = self(state_tensor) diff --git a/core/orchestrator.py b/core/orchestrator.py index f1bd678..76188a7 100644 --- a/core/orchestrator.py +++ b/core/orchestrator.py @@ -99,6 +99,10 @@ class TradingOrchestrator: self.model_registry = model_registry or get_model_registry() self.enhanced_rl_training = enhanced_rl_training + # Determine the device to use (GPU if available, else CPU) + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + logger.info(f"Using device: {self.device}") + # Configuration - AGGRESSIVE for more training data self.confidence_threshold = self.config.orchestrator.get('confidence_threshold', 0.15) # Lowered from 0.20 self.confidence_threshold_close = self.config.orchestrator.get('confidence_threshold_close', 0.08) # Lowered from 0.10 @@ -226,6 +230,7 @@ class TradingOrchestrator: state_size = self.config.rl.get('state_size', 13800) # Enhanced with COB features action_size = self.config.rl.get('action_space', 3) self.rl_agent = DQNAgent(state_shape=state_size, n_actions=action_size) + self.rl_agent.to(self.device) # Move DQN agent to the determined device # Load best checkpoint and capture initial state checkpoint_loaded = False @@ -268,6 +273,7 @@ class TradingOrchestrator: cnn_input_shape = self.config.cnn.get('input_shape', 100) cnn_n_actions = self.config.cnn.get('n_actions', 3) self.cnn_model = EnhancedCNN(input_shape=cnn_input_shape, n_actions=cnn_n_actions) + self.cnn_model.to(self.device) # Move CNN model to the determined device self.cnn_optimizer = optim.Adam(self.cnn_model.parameters(), lr=0.001) # Initialize optimizer for CNN # Load best checkpoint and capture initial state @@ -300,6 +306,7 @@ class TradingOrchestrator: try: from NN.models.cnn_model import CNNModel self.cnn_model = CNNModel() + self.cnn_model.to(self.device) # Move basic CNN model to the determined device self.cnn_optimizer = optim.Adam(self.cnn_model.parameters(), lr=0.001) # Initialize optimizer for basic CNN # Load checkpoint for basic CNN as well @@ -355,6 +362,9 @@ class TradingOrchestrator: try: from NN.models.cob_rl_model import COBRLModelInterface self.cob_rl_agent = COBRLModelInterface() + # Move COB RL agent to the determined device if it supports it + if hasattr(self.cob_rl_agent, 'to'): + self.cob_rl_agent.to(self.device) # Load best checkpoint and capture initial state checkpoint_loaded = False @@ -458,7 +468,19 @@ class TradingOrchestrator: def predict(self, data): try: if hasattr(self.model, 'predict'): - return self.model.predict(data) + # Ensure data has correct dimensions for COB RL model (2000 features) + if isinstance(data, np.ndarray): + features = data.flatten() + # COB RL expects 2000 features + if len(features) < 2000: + padded_features = np.zeros(2000) + padded_features[:len(features)] = features + features = padded_features + elif len(features) > 2000: + features = features[:2000] + return self.model.predict(features) + else: + return self.model.predict(data) return None except Exception as e: logger.error(f"Error in COB RL prediction: {e}") @@ -929,7 +951,7 @@ class TradingOrchestrator: logger.error(f"Error in decision callback: {e}") # Clean up memory periodically - if len(self.recent_decisions[symbol]) % 50 == 0: + if len(self.recent_decisions[symbol]) % 200 == 0: # Reduced from 50 to 200 self.model_registry.cleanup_all_models() return decision @@ -970,119 +992,93 @@ class TradingOrchestrator: async def _get_cnn_predictions(self, model: CNNModelInterface, symbol: str) -> List[Prediction]: """Get predictions from CNN model for all timeframes with enhanced COB features""" predictions = [] - try: - # Safely get timeframes from config - timeframes = getattr(self.config, 'timeframes', None) - if timeframes is None: - timeframes = ['1m', '5m', '15m', '1h'] # Default timeframes - + timeframes = getattr(self.config, 'timeframes', ['1m','5m','15m','1h']) for timeframe in timeframes: - # Get standard feature matrix for this timeframe + # 1) build or fetch your feature matrix (and optionally augment with COB)… feature_matrix = self.data_provider.get_feature_matrix( symbol=symbol, timeframes=[timeframe], window_size=getattr(model, 'window_size', 20) ) - - # Enhance with COB feature matrix if available - enhanced_features = feature_matrix - if feature_matrix is not None and self.cob_integration: - try: - # Get COB feature matrix (5-minute history) - cob_feature_matrix = self.get_cob_feature_matrix(symbol, sequence_length=60) - - if cob_feature_matrix is not None: - # Take the latest COB features to augment the standard features - latest_cob_features = cob_feature_matrix[-1:, :] # Shape: (1, 400) - - # Resize to match the feature matrix timeframe dimension - timeframe_count = feature_matrix.shape[0] - cob_features_expanded = np.repeat(latest_cob_features, timeframe_count, axis=0) - - # Concatenate COB features with standard features - # Standard features shape: (timeframes, window_size, features) - # COB features shape: (timeframes, 400) - # We'll add COB as additional features to each timeframe - window_size = feature_matrix.shape[1] - cob_features_reshaped = cob_features_expanded.reshape(timeframe_count, 1, 400) - cob_features_tiled = np.tile(cob_features_reshaped, (1, window_size, 1)) - - # Concatenate along feature dimension - enhanced_features = np.concatenate([feature_matrix, cob_features_tiled], axis=2) - - logger.debug(f"Enhanced CNN features with COB data for {symbol}: " - f"{feature_matrix.shape} + COB -> {enhanced_features.shape}") - - except Exception as cob_error: - logger.debug(f"Could not enhance CNN features with COB data: {cob_error}") - enhanced_features = feature_matrix - - if enhanced_features is not None: - # Get CNN prediction - use the actual underlying model - try: - if hasattr(model.model, 'act'): - # Use the CNN's act method - action_result = model.model.act(enhanced_features, explore=False) - if isinstance(action_result, tuple): - action_idx, confidence = action_result - else: - action_idx = action_result - confidence = 0.7 # Default confidence - - # Convert to action probabilities - action_probs = [0.1, 0.1, 0.8] # Default distribution - action_probs[action_idx] = confidence + if feature_matrix is None: + continue + + # …apply COB‐augmentation here (omitted for brevity)— + enhanced_features = self._augment_with_cob(feature_matrix, symbol) + + # 2) Initialize these before we call the model + action_probs, confidence = None, None + + # 3) Try the actual model inference + try: + # if your model has an .act() that returns (probs, conf) + if hasattr(model.model, 'act'): + # Flatten / reshape enhanced_features as needed… + x = self._prepare_cnn_input(enhanced_features) + action_probs, confidence = model.model.act(x, explore=False) + else: + # fallback to generic predict + result = model.predict(enhanced_features) + if isinstance(result, tuple) and len(result)==2: + action_probs, confidence = result else: - # Fallback to generic predict method - prediction_result = model.predict(enhanced_features) - if prediction_result is not None: - if isinstance(prediction_result, tuple) and len(prediction_result) == 2: - action_probs, confidence = prediction_result - else: - action_probs = prediction_result - confidence = 0.7 - else: - action_probs, confidence = None, None - except Exception as e: - logger.warning(f"CNN prediction failed: {e}") - action_probs, confidence = None, None - - if action_probs is not None: - # Convert to prediction object - action_names = ['SELL', 'HOLD', 'BUY'] - best_action_idx = np.argmax(action_probs) - best_action = action_names[best_action_idx] - - prediction = Prediction( - action=best_action, - confidence=float(confidence) if confidence is not None else float(action_probs[best_action_idx]), - probabilities={name: float(prob) for name, prob in zip(action_names, action_probs)}, - timeframe=timeframe, - timestamp=datetime.now(), - model_name=model.name, - metadata={ - 'timeframe_specific': True, - 'cob_enhanced': enhanced_features is not feature_matrix, - 'feature_shape': str(enhanced_features.shape) - } - ) - - predictions.append(prediction) - - # Capture CNN prediction for dashboard visualization - current_price = self._get_current_price(symbol) - if current_price: - direction = best_action_idx # 0=SELL, 1=HOLD, 2=BUY - pred_confidence = float(confidence) if confidence is not None else float(action_probs[best_action_idx]) - predicted_price = current_price * (1 + (pred_confidence * 0.01 if best_action == 'BUY' else -pred_confidence * 0.01 if best_action == 'SELL' else 0)) - self.capture_cnn_prediction(symbol, int(direction), pred_confidence, current_price, predicted_price) - + action_probs = result + confidence = 0.7 + except Exception as e: + logger.warning(f"CNN inference failed for {symbol}@{timeframe}: {e}") + continue # skip this timeframe entirely + + # 4) If we still don’t have valid probs, skip + if action_probs is None: + continue + + # 5) Build your Prediction + action_names = ['SELL','HOLD','BUY'] + best_idx = int(np.argmax(action_probs)) + best_action = action_names[best_idx] + pred = Prediction( + action=best_action, + confidence=float(confidence), + probabilities={n: float(p) for n,p in zip(action_names, action_probs)}, + timeframe=timeframe, + timestamp=datetime.now(), + model_name=model.name, + metadata={ + 'feature_shape': str(enhanced_features.shape), + 'cob_enhanced': enhanced_features is not feature_matrix + } + ) + predictions.append(pred) + + # …and capture for the dashboard if you like… + current_price = self._get_current_price(symbol) + if current_price is not None: + predicted_price = current_price * (1 + (0.01 * (confidence if best_action=='BUY' else -confidence if best_action=='SELL' else 0))) + self.capture_cnn_prediction( + symbol, + direction=best_idx, + confidence=confidence, + current_price=current_price, + predicted_price=predicted_price + ) except Exception as e: - logger.error(f"Error getting CNN predictions: {e}") - + logger.error(f"Orch: Error getting CNN predictions: {e}") return predictions - + + # helper stubs for clarity + def _augment_with_cob(self, feature_matrix, symbol): + # your existing cob‐augmentation logic… + return feature_matrix + + def _prepare_cnn_input(self, features): + arr = features.flatten() + # pad/truncate to 300, reshape to (1,300) + if len(arr) < 300: + arr = np.pad(arr, (0,300-len(arr)), 'constant') + else: + arr = arr[:300] + return arr.reshape(1,-1) async def _get_rl_prediction(self, model: RLAgentInterface, symbol: str) -> Optional[Prediction]: """Get prediction from RL agent""" try: @@ -1230,7 +1226,20 @@ class TradingOrchestrator: # This would come from a portfolio manager in a real implementation additional_state = np.array([0.0, 1.0, 0.0]) # [position, balance, unrealized_pnl] - return np.concatenate([state, additional_state]) + combined_state = np.concatenate([state, additional_state]) + + # Ensure DQN gets exactly 403 features (expected by the model) + target_size = 403 + if len(combined_state) < target_size: + # Pad with zeros + padded_state = np.zeros(target_size) + padded_state[:len(combined_state)] = combined_state + combined_state = padded_state + elif len(combined_state) > target_size: + # Truncate to target size + combined_state = combined_state[:target_size] + + return combined_state return None @@ -1547,7 +1556,9 @@ class TradingOrchestrator: return torch.softmax(self.fc3(x), dim=1) self.decision_fusion_network = DecisionFusionNet() - logger.info("Decision fusion network initialized") + # Move decision fusion network to the device + self.decision_fusion_network.to(self.device) + logger.info(f"Decision fusion network initialized on device: {self.device}") except Exception as e: logger.warning(f"Decision fusion initialization failed: {e}") diff --git a/enhanced_realtime_training.py b/enhanced_realtime_training.py index a7fa424..2000699 100644 --- a/enhanced_realtime_training.py +++ b/enhanced_realtime_training.py @@ -56,6 +56,7 @@ class EnhancedRealtimeTrainingSystem: self.performance_history = { 'dqn_losses': deque(maxlen=1000), 'cnn_losses': deque(maxlen=1000), + 'cob_rl_losses': deque(maxlen=1000), # Added COB RL loss tracking 'prediction_accuracy': deque(maxlen=500), 'trading_performance': deque(maxlen=200), 'validation_scores': deque(maxlen=100) @@ -553,18 +554,33 @@ class EnhancedRealtimeTrainingSystem: # Statistical features across time for each aggregated dimension for feature_idx in range(agg_matrix.shape[1]): feature_series = agg_matrix[:, feature_idx] - combined_features.extend([ - np.mean(feature_series), - np.std(feature_series), - np.min(feature_series), - np.max(feature_series), - feature_series[-1] - feature_series[0] if len(feature_series) > 1 else 0, # Total change - np.mean(np.diff(feature_series)) if len(feature_series) > 1 else 0, # Average momentum - np.std(np.diff(feature_series)) if len(feature_series) > 2 else 0, # Momentum volatility - np.percentile(feature_series, 25), # 25th percentile - np.percentile(feature_series, 75), # 75th percentile - len([x for x in np.diff(feature_series) if x > 0]) / max(len(feature_series) - 1, 1) if len(feature_series) > 1 else 0.5 # Positive change ratio - ]) + # Clean feature series to prevent division warnings + feature_series_clean = feature_series[np.isfinite(feature_series)] + + if len(feature_series_clean) > 0: + # Safe percentile calculation + try: + percentile_25 = np.percentile(feature_series_clean, 25) + percentile_75 = np.percentile(feature_series_clean, 75) + except (ValueError, RuntimeWarning): + percentile_25 = np.median(feature_series_clean) if len(feature_series_clean) > 0 else 0 + percentile_75 = np.median(feature_series_clean) if len(feature_series_clean) > 0 else 0 + + combined_features.extend([ + np.mean(feature_series_clean), + np.std(feature_series_clean), + np.min(feature_series_clean), + np.max(feature_series_clean), + feature_series_clean[-1] - feature_series_clean[0] if len(feature_series_clean) > 1 else 0, # Total change + np.mean(np.diff(feature_series_clean)) if len(feature_series_clean) > 1 else 0, # Average momentum + np.std(np.diff(feature_series_clean)) if len(feature_series_clean) > 2 else 0, # Momentum volatility + percentile_25, # 25th percentile + percentile_75, # 75th percentile + len([x for x in np.diff(feature_series_clean) if x > 0]) / max(len(feature_series_clean) - 1, 1) if len(feature_series_clean) > 1 else 0.5 # Positive change ratio + ]) + else: + # All values are NaN or inf, use zeros + combined_features.extend([0.0] * 10) else: combined_features.extend([0.0] * (15 * 10)) # 15 features * 10 statistics @@ -702,13 +718,14 @@ class EnhancedRealtimeTrainingSystem: lows = np.array([bar['low'] for bar in self.real_time_data['ohlcv_1m']]) # Update indicators + price_mean = np.mean(prices[-20:]) self.technical_indicators = { 'sma_10': np.mean(prices[-10:]), 'sma_20': np.mean(prices[-20:]), 'rsi': self._calculate_rsi(prices, 14), - 'volatility': np.std(prices[-20:]) / np.mean(prices[-20:]), + 'volatility': np.std(prices[-20:]) / price_mean if price_mean > 0 else 0, 'volume_sma': np.mean(volumes[-10:]), - 'price_momentum': (prices[-1] - prices[-5]) / prices[-5] if len(prices) >= 5 else 0, + 'price_momentum': (prices[-1] - prices[-5]) / prices[-5] if len(prices) >= 5 and prices[-5] > 0 else 0, 'atr': np.mean(highs[-14:] - lows[-14:]) if len(prices) >= 14 else 0 } @@ -724,8 +741,8 @@ class EnhancedRealtimeTrainingSystem: current_time = time.time() current_bar = self.real_time_data['ohlcv_1m'][-1] - # Create comprehensive state features - state_features = self._build_comprehensive_state() + # Create comprehensive state features with default dimensions + state_features = self._build_comprehensive_state(100) # Use default 100 for general experiences # Create experience with proper reward calculation experience = { @@ -748,8 +765,8 @@ class EnhancedRealtimeTrainingSystem: except Exception as e: logger.debug(f"Error creating training experiences: {e}") - def _build_comprehensive_state(self) -> np.ndarray: - """Build comprehensive state vector for RL training""" + def _build_comprehensive_state(self, target_dimensions: int = 100) -> np.ndarray: + """Build comprehensive state vector for RL training with adaptive dimensions""" try: state_features = [] @@ -792,15 +809,138 @@ class EnhancedRealtimeTrainingSystem: state_features.append(np.cos(2 * np.pi * now.hour / 24)) state_features.append(now.weekday() / 6.0) # Day of week - # Pad to fixed size (100 features) - while len(state_features) < 100: + # Current count: 10 (prices) + 7 (indicators) + 1 (volume) + 5 (COB) + 3 (time) = 26 base features + + # 6. Enhanced features for larger dimensions + if target_dimensions > 50: + # Add more price history + if len(self.real_time_data['ohlcv_1m']) >= 20: + extended_prices = [bar['close'] for bar in list(self.real_time_data['ohlcv_1m'])[-20:]] + base_price = extended_prices[0] + extended_normalized = [(p - base_price) / base_price for p in extended_prices[10:]] # Additional 10 + state_features.extend(extended_normalized) + else: + state_features.extend([0.0] * 10) + + # Add volume history + if len(self.real_time_data['ohlcv_1m']) >= 10: + volume_history = [bar['volume'] for bar in list(self.real_time_data['ohlcv_1m'])[-10:]] + avg_vol = np.mean(volume_history) if volume_history else 1.0 + # Prevent division by zero + if avg_vol == 0: + avg_vol = 1.0 + normalized_volumes = [v / avg_vol for v in volume_history] + state_features.extend(normalized_volumes) + else: + state_features.extend([0.0] * 10) + + # Add extended COB features + extended_cob = self._extract_cob_features() + state_features.extend(extended_cob[5:]) # Remaining COB features + + # Add 5m timeframe data if available + if len(self.real_time_data['ohlcv_5m']) >= 5: + tf_5m_prices = [bar['close'] for bar in list(self.real_time_data['ohlcv_5m'])[-5:]] + if tf_5m_prices: + base_5m = tf_5m_prices[0] + # Prevent division by zero + if base_5m == 0: + base_5m = 1.0 + normalized_5m = [(p - base_5m) / base_5m for p in tf_5m_prices] + state_features.extend(normalized_5m) + else: + state_features.extend([0.0] * 5) + else: + state_features.extend([0.0] * 5) + + # 7. Adaptive padding/truncation based on target dimensions + current_length = len(state_features) + + if target_dimensions > current_length: + # Pad with additional engineered features + remaining = target_dimensions - current_length + + # Add statistical features if we have data + if len(self.real_time_data['ohlcv_1m']) >= 20: + all_prices = [bar['close'] for bar in list(self.real_time_data['ohlcv_1m'])[-20:]] + all_volumes = [bar['volume'] for bar in list(self.real_time_data['ohlcv_1m'])[-20:]] + + # Statistical features + additional_features = [ + np.std(all_prices) / np.mean(all_prices) if np.mean(all_prices) > 0 else 0, # Price CV + np.std(all_volumes) / np.mean(all_volumes) if np.mean(all_volumes) > 0 else 0, # Volume CV + (max(all_prices) - min(all_prices)) / np.mean(all_prices) if np.mean(all_prices) > 0 else 0, # Price range + # Safe correlation calculation + np.corrcoef(all_prices, all_volumes)[0, 1] if (len(all_prices) == len(all_volumes) and len(all_prices) > 1 and + np.std(all_prices) > 0 and np.std(all_volumes) > 0) else 0, # Price-volume correlation + ] + + # Add momentum features + for window in [3, 5, 10]: + if len(all_prices) >= window: + momentum = (all_prices[-1] - all_prices[-window]) / all_prices[-window] if all_prices[-window] > 0 else 0 + additional_features.append(momentum) + else: + additional_features.append(0.0) + + # Extend to fill remaining space + while len(additional_features) < remaining and len(additional_features) < 50: + additional_features.extend([ + np.sin(len(additional_features) * 0.1), # Sine waves for variety + np.cos(len(additional_features) * 0.1), + np.tanh(len(additional_features) * 0.01) + ]) + + state_features.extend(additional_features[:remaining]) + else: + # Fill with structured zeros/patterns if no data + pattern_features = [] + for i in range(remaining): + pattern_features.append(np.sin(i * 0.01)) # Small oscillating pattern + state_features.extend(pattern_features) + + # Ensure exact target dimension + state_features = state_features[:target_dimensions] + while len(state_features) < target_dimensions: state_features.append(0.0) - return np.array(state_features[:100]) + return np.array(state_features) except Exception as e: logger.error(f"Error building state: {e}") - return np.zeros(100) + return np.zeros(target_dimensions) + + def _get_model_expected_dimensions(self, model_type: str) -> int: + """Get expected input dimensions for different model types""" + try: + if model_type == 'dqn': + # Try to get DQN expected dimensions from model + if (self.orchestrator and hasattr(self.orchestrator, 'rl_agent') + and self.orchestrator.rl_agent and hasattr(self.orchestrator.rl_agent, 'policy_net')): + # Get first layer input size + first_layer = list(self.orchestrator.rl_agent.policy_net.children())[0] + if hasattr(first_layer, 'in_features'): + return first_layer.in_features + return 403 # Default for DQN based on error logs + + elif model_type == 'cnn': + # CNN might have different input expectations + if (self.orchestrator and hasattr(self.orchestrator, 'cnn_model') + and self.orchestrator.cnn_model): + # Try to get CNN input size + if hasattr(self.orchestrator.cnn_model, 'input_shape'): + return self.orchestrator.cnn_model.input_shape + return 300 # Default for CNN based on error logs + + elif model_type == 'cob_rl': + return 2000 # COB RL expects 2000 features + + else: + return 100 # Default + + except Exception as e: + logger.debug(f"Error getting model dimensions for {model_type}: {e}") + return 100 # Fallback def _extract_cob_features(self) -> List[float]: """Extract features from COB data""" @@ -964,6 +1104,18 @@ class EnhancedRealtimeTrainingSystem: aggregated_matrix = self.get_cob_training_matrix(symbol, '1s_aggregated') if combined_features is not None: + # Ensure features are exactly 2000 dimensions + if len(combined_features) != 2000: + logger.warning(f"COB features wrong size: {len(combined_features)}, padding/truncating to 2000") + if len(combined_features) < 2000: + # Pad with zeros + padded_features = np.zeros(2000, dtype=np.float32) + padded_features[:len(combined_features)] = combined_features + combined_features = padded_features + else: + # Truncate to 2000 + combined_features = combined_features[:2000] + # Create enhanced COB training experience current_price = self._get_current_price_from_data(symbol) if current_price: @@ -973,7 +1125,7 @@ class EnhancedRealtimeTrainingSystem: # Calculate reward based on COB prediction accuracy reward = self._calculate_cob_reward(symbol, action, combined_features) - # Create comprehensive state vector for COB RL + # Create comprehensive state vector for COB RL (exactly 2000 dimensions) state = combined_features # 2000-dimensional state # Store experience in COB RL agent @@ -1268,16 +1420,29 @@ class EnhancedRealtimeTrainingSystem: # Moving averages if len(prev_prices) >= 5: ma5 = sum(prev_prices[-5:]) / 5 - tech_features.append((current_price - ma5) / ma5) + # Prevent division by zero + if ma5 != 0: + tech_features.append((current_price - ma5) / ma5) + else: + tech_features.append(0.0) if len(prev_prices) >= 10: ma10 = sum(prev_prices[-10:]) / 10 - tech_features.append((current_price - ma10) / ma10) + # Prevent division by zero + if ma10 != 0: + tech_features.append((current_price - ma10) / ma10) + else: + tech_features.append(0.0) # Volatility measure if len(prev_prices) >= 5: - volatility = np.std(prev_prices[-5:]) / np.mean(prev_prices[-5:]) - tech_features.append(volatility) + price_mean = np.mean(prev_prices[-5:]) + # Prevent division by zero + if price_mean != 0: + volatility = np.std(prev_prices[-5:]) / price_mean + tech_features.append(volatility) + else: + tech_features.append(0.0) # Pad technical features to 200 while len(tech_features) < 200: @@ -1978,8 +2143,9 @@ class EnhancedRealtimeTrainingSystem: def _generate_forward_dqn_prediction(self, symbol: str, current_time: float): """Generate a DQN prediction for future price movement""" try: - # Get current market state (only historical data) - current_state = self._build_comprehensive_state() + # Get current market state with DQN-specific dimensions + target_dims = self._get_model_expected_dimensions('dqn') + current_state = self._build_comprehensive_state(target_dims) current_price = self._get_current_price_from_data(symbol) # SKIP prediction if price is invalid @@ -2051,7 +2217,7 @@ class EnhancedRealtimeTrainingSystem: self.last_prediction_time[symbol] = int(current_time) - logger.info(f"Forward DQN prediction: {symbol} action={['BUY','SELL','HOLD'][action]} confidence={confidence:.2f} price=${current_price:.2f} target={target_time.strftime('%H:%M:%S')}") + logger.info(f"Forward DQN prediction: {symbol} action={['BUY','SELL','HOLD'][action]} confidence={confidence:.2f} price=${current_price:.2f} target={target_time.strftime('%H:%M:%S')} dims={len(current_state)}") except Exception as e: logger.error(f"Error generating forward DQN prediction: {e}") diff --git a/web/clean_dashboard.py b/web/clean_dashboard.py index d916a45..37edf32 100644 --- a/web/clean_dashboard.py +++ b/web/clean_dashboard.py @@ -984,6 +984,10 @@ class CleanTradingDashboard: timestamp = pred.get('timestamp', datetime.now()) price = pred.get('price', 0) + # FILTER OUT INVALID PRICES - Skip predictions with price 0 or None + if price is None or price <= 0: + continue + if confidence > 0.3: # Only show predictions with reasonable confidence pred_data = { 'x': timestamp, @@ -1096,7 +1100,12 @@ class CleanTradingDashboard: current_price = pred.get('current_price', 0) predicted_price = pred.get('predicted_price', current_price) - if confidence > 0.4 and current_price > 0: # Only show confident predictions + # FILTER OUT INVALID PRICES - Skip predictions with price 0 or None + if (current_price is None or current_price <= 0 or + predicted_price is None or predicted_price <= 0): + continue + + if confidence > 0.4: # Only show confident predictions # Calculate prediction end point (5 minutes ahead) end_time = timestamp + timedelta(minutes=5) @@ -1171,10 +1180,10 @@ class CleanTradingDashboard: if not cob_predictions: return # No real predictions to display - # Separate predictions by direction - up_predictions = [p for p in cob_predictions if p['direction'] == 2] - down_predictions = [p for p in cob_predictions if p['direction'] == 0] - sideways_predictions = [p for p in cob_predictions if p['direction'] == 1] + # Separate predictions by direction and filter out invalid prices + up_predictions = [p for p in cob_predictions if p['direction'] == 2 and p.get('price', 0) > 0] + down_predictions = [p for p in cob_predictions if p['direction'] == 0 and p.get('price', 0) > 0] + sideways_predictions = [p for p in cob_predictions if p['direction'] == 1 and p.get('price', 0) > 0] # Add COB_RL UP predictions (blue diamonds) if up_predictions: diff --git a/web/component_manager.py b/web/component_manager.py index 4d4ae6a..c2f6e2c 100644 --- a/web/component_manager.py +++ b/web/component_manager.py @@ -45,6 +45,10 @@ class DashboardComponentManager: blocked = decision.get('blocked', False) manual = decision.get('manual', False) + # FILTER OUT INVALID PRICES - Skip signals with price 0 or None + if price is None or price <= 0: + continue + # Determine signal style if executed: badge_class = "bg-success"