diff --git a/NN/utils/data_interface.py b/NN/utils/data_interface.py index 7fd59c7..cc5f296 100644 --- a/NN/utils/data_interface.py +++ b/NN/utils/data_interface.py @@ -209,8 +209,8 @@ class DataInterface: curr_close = data[window_size-1:-1, 3] price_changes = (next_close - curr_close) / curr_close - # Define thresholds for price movement classification - threshold = 0.0005 # 0.05% threshold - smaller to encourage more signals + # Define thresholds for price movement classification + threshold = 0.001 # 0.10% threshold - prefer bigger moves to beat fees y = np.zeros(len(price_changes), dtype=int) y[price_changes > threshold] = 2 # Up y[price_changes < -threshold] = 0 # Down diff --git a/NN/utils/signal_interpreter.py b/NN/utils/signal_interpreter.py index ffa087a..49ab0f2 100644 --- a/NN/utils/signal_interpreter.py +++ b/NN/utils/signal_interpreter.py @@ -50,7 +50,7 @@ class SignalInterpreter: self.oscillation_filter_enabled = self.config.get('oscillation_filter_enabled', False) # Disable oscillation filter by default # Sensitivity parameters - self.min_price_movement = self.config.get('min_price_movement', 0.0001) # Lower price movement threshold + self.min_price_movement = self.config.get('min_price_movement', 0.001) # Align with deadzone; prefer bigger moves self.hold_cooldown = self.config.get('hold_cooldown', 1) # Shorter hold cooldown self.consecutive_signals_required = self.config.get('consecutive_signals_required', 1) # Require only one signal diff --git a/core/orchestrator.py b/core/orchestrator.py index f21fd45..65906b0 100644 --- a/core/orchestrator.py +++ b/core/orchestrator.py @@ -5880,8 +5880,9 @@ class TradingOrchestrator: logger.info(f"Decision fusion training completed: loss={current_loss:.4f}, samples={len(inputs)}") - # Save checkpoint periodically - if self.decision_fusion_decisions_count % (self.decision_fusion_training_interval * 5) == 0: + # Save checkpoint: ensure first save after minimum samples, then periodic saves + if (len(self.decision_fusion_training_data) == self.decision_fusion_min_samples) or \ + (self.decision_fusion_decisions_count % (self.decision_fusion_training_interval * 5) == 0): self._save_decision_fusion_checkpoint() except Exception as e: diff --git a/web/clean_dashboard.py b/web/clean_dashboard.py index 2cce514..74cb010 100644 --- a/web/clean_dashboard.py +++ b/web/clean_dashboard.py @@ -1536,6 +1536,37 @@ class CleanTradingDashboard: self._execute_manual_trade('SELL') return [html.I(className="fas fa-arrow-down me-1"), "SELL"] + @self.app.callback( + Output('manual-close-btn', 'children'), + [Input('manual-close-btn', 'n_clicks')], + prevent_initial_call=True + ) + def handle_manual_close(n_clicks): + """Handle manual close/exit button: closes any active position for the primary symbol""" + if not n_clicks: + raise PreventUpdate + symbol = getattr(self, 'primary_symbol', 'ETH/USDT') + try: + # If executor tracks positions, close whichever side is active + if hasattr(self.trading_executor, 'positions') and symbol in self.trading_executor.positions: + position = self.trading_executor.positions[symbol] + if getattr(position, 'side', 'LONG') == 'LONG': + # Close long by selling + if hasattr(self.trading_executor, 'close_long_position'): + self.trading_executor.close_long_position(symbol, confidence=1.0) + elif hasattr(self.trading_executor, 'sell'): + self.trading_executor.sell(symbol, quantity=position.quantity, confidence=1.0) + else: + # Close short by buying + if hasattr(self.trading_executor, 'close_short_position'): + self.trading_executor.close_short_position(symbol, confidence=1.0) + elif hasattr(self.trading_executor, 'buy'): + self.trading_executor.buy(symbol, quantity=position.quantity, confidence=1.0) + return [html.I(className="fas fa-times me-1"), "CLOSE"] + except Exception as e: + logger.error(f"Error closing position manually: {e}") + return [html.I(className="fas fa-times me-1"), "CLOSE"] + # Leverage slider callback @self.app.callback( Output('leverage-display', 'children'), @@ -7261,28 +7292,14 @@ class CleanTradingDashboard: def _initialize_enhanced_training_system(self): """Initialize enhanced training system for model predictions""" try: - # Try to import and initialize enhanced training system - from enhanced_realtime_training import EnhancedRealtimeTrainingSystem # Optional - - self.training_system = EnhancedRealtimeTrainingSystem( - orchestrator=self.orchestrator, - data_provider=self.data_provider, - dashboard=self - ) - - # Initialize prediction storage - if not hasattr(self.orchestrator, 'recent_dqn_predictions'): - self.orchestrator.recent_dqn_predictions = {} - if not hasattr(self.orchestrator, 'recent_cnn_predictions'): - self.orchestrator.recent_cnn_predictions = {} - - logger.debug("Enhanced training system initialized for model predictions") - + # Optional module is not required; skip and rely on orchestrator built-in training + self.training_system = None + return except ImportError: - logger.warning("Enhanced training system not available - predictions disabled for this module") + logger.info("Enhanced training system not available - using built-in training only") self.training_system = None except Exception as e: - logger.error(f"Error initializing enhanced training system: {e}") + logger.info(f"Enhanced training system skipped: {e}") self.training_system = None def _initialize_standardized_cnn(self): diff --git a/web/layout_manager.py b/web/layout_manager.py index 8409af2..f2d53e3 100644 --- a/web/layout_manager.py +++ b/web/layout_manager.py @@ -344,7 +344,12 @@ class DashboardLayoutManager: html.Button([ html.I(className="fas fa-arrow-down me-1"), "SELL" - ], id="manual-sell-btn", className="btn btn-danger btn-sm", + ], id="manual-sell-btn", className="btn btn-danger btn-sm me-2", + style={"fontSize": "10px", "padding": "2px 8px"}), + html.Button([ + html.I(className="fas fa-times me-1"), + "CLOSE" + ], id="manual-close-btn", className="btn btn-secondary btn-sm", style={"fontSize": "10px", "padding": "2px 8px"}) ], className="d-flex") ], className="d-flex justify-content-between align-items-center mb-2"),