diff --git a/core/orchestrator.py b/core/orchestrator.py index 5f4e314..06cb0ac 100644 --- a/core/orchestrator.py +++ b/core/orchestrator.py @@ -927,11 +927,9 @@ class TradingOrchestrator: try: current_time = datetime.now() - # Check if enough time has passed since last decision - if symbol in self.last_decision_time: - time_since_last = (current_time - self.last_decision_time[symbol]).total_seconds() - if time_since_last < self.decision_frequency: - return None + # EXECUTE EVERY SIGNAL: Remove decision frequency limit + # Allow immediate execution of every signal from the decision model + logger.debug(f"Processing signal for {symbol} - no frequency limit applied") # Get current market data current_price = self.data_provider.get_current_price(symbol) @@ -1333,43 +1331,15 @@ class TradingOrchestrator: current_position_pnl, symbol ) - # Apply aggressiveness-based confidence thresholds - if best_action in ['BUY', 'SELL']: - # For entry signals, use entry aggressiveness - if not self._has_open_position(symbol): - if best_confidence < entry_threshold: - best_action = 'HOLD' - reasoning['entry_threshold_applied'] = True - reasoning['entry_threshold'] = entry_threshold - # For exit signals, use exit aggressiveness - else: - if best_confidence < exit_threshold: - best_action = 'HOLD' - reasoning['exit_threshold_applied'] = True - reasoning['exit_threshold'] = exit_threshold - else: - # Standard threshold for HOLD - if best_confidence < self.confidence_threshold: - best_action = 'HOLD' - reasoning['threshold_applied'] = True + # EXECUTE EVERY SIGNAL: Remove confidence thresholds and signal accumulation + # The decision model has already aggregated all model outputs (CNN, DQN, transformer, etc.) + # So we trust its decision and execute every signal + reasoning['execute_every_signal'] = True + reasoning['models_aggregated'] = [pred.model_name for pred in predictions] + reasoning['aggregated_confidence'] = best_confidence - # Signal accumulation check - require multiple confident signals - if best_action in ['BUY', 'SELL']: - required_signals = 3 # Require 3 confident signals - recent_decisions = self.get_recent_decisions(symbol, limit=5) - - # Count recent signals in the same direction - same_direction_count = sum(1 for d in recent_decisions - if d.action == best_action and d.confidence > entry_threshold) - - if same_direction_count < required_signals: - best_action = 'HOLD' - reasoning['signal_accumulation'] = True - reasoning['required_signals'] = required_signals - reasoning['current_signals'] = same_direction_count - logger.info(f"Signal accumulation: {same_direction_count}/{required_signals} signals for {best_action}") - else: - logger.info(f"Signal accumulation satisfied: {same_direction_count}/{required_signals} signals for {best_action}") + logger.info(f"EXECUTING EVERY SIGNAL: {best_action} (confidence: {best_confidence:.3f}) " + f"from aggregated models: {reasoning['models_aggregated']}") # Add P&L-based decision adjustment best_action, best_confidence = self._apply_pnl_feedback( diff --git a/core/trading_executor.py b/core/trading_executor.py index c633ee5..434494d 100644 --- a/core/trading_executor.py +++ b/core/trading_executor.py @@ -143,6 +143,10 @@ class TradingExecutor: self.max_open_orders = 2 # Maximum number of open orders allowed self.open_orders_count = 0 # Current count of open orders + # Rate limiting for open orders sync (30 seconds) + self.last_open_orders_sync = datetime.min + self.open_orders_sync_interval = 30 # seconds + # Trading symbols self.symbols = self.config.get('symbols', ['ETH/USDT', 'BTC/USDT']) @@ -332,21 +336,42 @@ class TradingExecutor: logger.debug(f"LOCK RELEASED: {action} for {symbol}") def _get_open_orders_count(self) -> int: - """Get current count of open orders across all symbols""" + """Get current count of open orders across all symbols with rate limiting""" try: if self.simulation_mode: return 0 + + # Check if enough time has passed since last sync + current_time = datetime.now() + time_since_last_sync = (current_time - self.last_open_orders_sync).total_seconds() + + if time_since_last_sync < self.open_orders_sync_interval: + # Return cached count if within rate limit + logger.debug(f"Using cached open orders count ({self.open_orders_count}) - rate limited") + return self.open_orders_count + + # Update last sync time + self.last_open_orders_sync = current_time total_open_orders = 0 for symbol in self.symbols: - open_orders = self.exchange.get_open_orders(symbol) - total_open_orders += len(open_orders) + try: + open_orders = self.exchange.get_open_orders(symbol) + total_open_orders += len(open_orders) + except Exception as e: + logger.warning(f"Error getting open orders for {symbol}: {e}") + # Continue with other symbols + continue + + # Update cached count + self.open_orders_count = total_open_orders + logger.debug(f"Updated open orders count: {total_open_orders}") return total_open_orders except Exception as e: logger.error(f"Error getting open orders count: {e}") - return 0 + return self.open_orders_count # Return cached value on error def _can_place_new_order(self) -> bool: """Check if we can place a new order based on open order limit""" @@ -359,7 +384,7 @@ class TradingExecutor: return can_place def sync_open_orders(self) -> Dict[str, Any]: - """Synchronize open orders with exchange and update internal state + """Synchronize open orders with exchange and update internal state with rate limiting Returns: dict: Sync result with status and order details @@ -373,6 +398,24 @@ class TradingExecutor: 'count': 0 } + # Check rate limiting + current_time = datetime.now() + time_since_last_sync = (current_time - self.last_open_orders_sync).total_seconds() + + if time_since_last_sync < self.open_orders_sync_interval: + # Return cached result if within rate limit + logger.debug(f"Open order sync rate limited - using cached data") + return { + 'status': 'rate_limited', + 'message': f'Rate limited - last sync was {time_since_last_sync:.1f}s ago', + 'orders': [], + 'count': self.open_orders_count, + 'cached': True + } + + # Update last sync time + self.last_open_orders_sync = current_time + sync_result = { 'status': 'started', 'orders': [], @@ -408,7 +451,7 @@ class TradingExecutor: except Exception as e: error_msg = f"Error syncing orders for {symbol}: {e}" - logger.error(error_msg) + logger.warning(error_msg) # Changed to warning since this is expected with rate limits sync_result['errors'].append(error_msg) # Update internal state @@ -505,11 +548,30 @@ class TradingExecutor: logger.warning(f"Maximum concurrent positions reached: {len(self.positions)}") return False + # SYNC OPEN ORDERS BEFORE CHECKING LIMITS + # This ensures we have accurate position data before making decisions + if not self.simulation_mode: + try: + logger.debug(f"Syncing open orders before trade execution for {symbol}") + sync_result = self.sync_open_orders() + if sync_result.get('status') == 'success': + logger.debug(f"Open orders synced successfully: {sync_result.get('count', 0)} orders") + else: + logger.warning(f"Open orders sync failed: {sync_result.get('message', 'Unknown error')}") + except Exception as e: + logger.warning(f"Error syncing open orders: {e}") + # Check open order limit if not self._can_place_new_order(): logger.warning(f"Maximum open orders reached: {self._get_open_orders_count()}/{self.max_open_orders}") return False + # Check position size limit before opening new positions + if action in ['BUY', 'SHORT'] and symbol not in self.positions: + if not self._check_position_size_limit(): + logger.warning(f"Position size limit reached - cannot open new position for {symbol}") + return False + return True def _execute_buy(self, symbol: str, confidence: float, current_price: float) -> bool: @@ -1153,25 +1215,30 @@ class TradingExecutor: return False def _calculate_position_size(self, confidence: float, current_price: float) -> float: - """Calculate position size - use 100% of account balance for short-term scalping""" + """Calculate position size - limited to 20% of account balance by default""" # Get account balance (simulation or real) account_balance = self._get_account_balance_for_sizing() - # Use 100% of account balance since we're holding for seconds/minutes only - # Scale by confidence: 70-100% of balance based on confidence (0.7-1.0 range) + # Get maximum position size limit (default 20% of balance) + max_position_percentage = self.mexc_config.get('max_position_percentage', 0.20) + max_position_value = account_balance * max_position_percentage + + # Calculate desired position size based on confidence + # Scale by confidence: 70-100% of max position size based on confidence (0.7-1.0 range) confidence_multiplier = max(0.7, min(1.0, confidence)) - position_value = account_balance * confidence_multiplier + desired_position_value = max_position_value * confidence_multiplier # Apply reduction based on consecutive losses (risk management) reduction_factor = self.mexc_config.get('consecutive_loss_reduction_factor', 0.8) adjusted_reduction_factor = reduction_factor ** self.consecutive_losses - position_value *= adjusted_reduction_factor + final_position_value = desired_position_value * adjusted_reduction_factor logger.debug(f"Position calculation: account=${account_balance:.2f}, " + f"max_position=${max_position_value:.2f} ({max_position_percentage*100:.0f}%), " f"confidence_mult={confidence_multiplier:.2f}, " - f"position=${position_value:.2f}, confidence={confidence:.2f}") + f"final_position=${final_position_value:.2f}, confidence={confidence:.2f}") - return position_value + return final_position_value def _get_account_balance_for_sizing(self) -> float: """Get account balance for position sizing calculations""" @@ -1188,6 +1255,64 @@ class TradingExecutor: logger.warning(f"Failed to get live account balance: {e}, using simulation default") return self.mexc_config.get('simulation_account_usd', 100.0) + def _check_position_size_limit(self) -> bool: + """Check if total open position value exceeds the maximum allowed percentage of balance""" + try: + # Get account balance + account_balance = self._get_account_balance_for_sizing() + + # Get maximum position percentage (default 20%) + max_position_percentage = self.mexc_config.get('max_position_percentage', 0.20) + max_position_value = account_balance * max_position_percentage + + # Calculate total current position value + total_position_value = 0.0 + + # Add existing positions + for symbol, position in self.positions.items(): + # Get current price for the symbol + try: + ticker = self.exchange.get_ticker(symbol) if self.exchange else None + current_price = ticker['last'] if ticker and 'last' in ticker else position.entry_price + except Exception: + # Fallback to entry price if we can't get current price + current_price = position.entry_price + + # Calculate position value + position_value = position.quantity * current_price + total_position_value += position_value + + logger.debug(f"Existing position {symbol}: {position.quantity:.6f} @ ${current_price:.2f} = ${position_value:.2f}") + + # Add potential value from open orders that could become positions + if not self.simulation_mode and self.exchange: + try: + open_orders = self.exchange.get_open_orders() + for order in open_orders: + if order.get('side', '').lower() in ['buy', 'sell']: + # Estimate the value if this order gets filled + order_quantity = float(order.get('quantity', 0)) + order_price = float(order.get('price', 0)) + if order_price > 0: + order_value = order_quantity * order_price + total_position_value += order_value + logger.debug(f"Open order {order.get('symbol')}: {order_quantity:.6f} @ ${order_price:.2f} = ${order_value:.2f}") + except Exception as e: + logger.debug(f"Error calculating open order values: {e}") + + # Check if we would exceed the limit + if total_position_value >= max_position_value: + logger.warning(f"Position size limit reached: ${total_position_value:.2f} >= ${max_position_value:.2f} ({max_position_percentage*100:.0f}% of balance)") + return False + + logger.debug(f"Position size check passed: ${total_position_value:.2f} < ${max_position_value:.2f} ({max_position_percentage*100:.0f}% of balance)") + return True + + except Exception as e: + logger.error(f"Error checking position size limit: {e}") + # Allow trade if we can't check the limit + return True + def update_positions(self, symbol: str, current_price: float): """Update position P&L with current market price""" if symbol in self.positions: diff --git a/position_sync_enhancement.py b/position_sync_enhancement.py new file mode 100644 index 0000000..9f05ae7 --- /dev/null +++ b/position_sync_enhancement.py @@ -0,0 +1,306 @@ +""" +Enhanced Position Synchronization System +Addresses the gap between dashboard position display and actual exchange account state +""" + +import logging +import time +from datetime import datetime, timedelta +from typing import Dict, List, Optional, Any + +logger = logging.getLogger(__name__) + +class EnhancedPositionSync: + """Enhanced position synchronization to ensure dashboard matches actual exchange state""" + + def __init__(self, trading_executor, dashboard): + self.trading_executor = trading_executor + self.dashboard = dashboard + self.last_sync_time = 0 + self.sync_interval = 10 # Sync every 10 seconds + self.position_history = [] # Track position changes + + def sync_all_positions(self) -> Dict[str, Any]: + """Comprehensive position sync for all symbols""" + try: + sync_results = {} + + # 1. Get actual exchange positions + exchange_positions = self._get_actual_exchange_positions() + + # 2. Get dashboard positions + dashboard_positions = self._get_dashboard_positions() + + # 3. Compare and sync + for symbol in ['ETH/USDT', 'BTC/USDT']: + sync_result = self._sync_symbol_position( + symbol, + exchange_positions.get(symbol), + dashboard_positions.get(symbol) + ) + sync_results[symbol] = sync_result + + # 4. Update closed trades list from exchange + self._sync_closed_trades() + + return { + 'sync_time': datetime.now().isoformat(), + 'results': sync_results, + 'total_synced': len(sync_results), + 'issues_found': sum(1 for r in sync_results.values() if not r['in_sync']) + } + + except Exception as e: + logger.error(f"Error in comprehensive position sync: {e}") + return {'error': str(e)} + + def _get_actual_exchange_positions(self) -> Dict[str, Dict]: + """Get actual positions from exchange account""" + try: + positions = {} + + if not self.trading_executor: + return positions + + # Get account balances + if hasattr(self.trading_executor, 'get_account_balance'): + balances = self.trading_executor.get_account_balance() + + for symbol in ['ETH/USDT', 'BTC/USDT']: + # Parse symbol to get base asset + base_asset = symbol.split('/')[0] + + # Get balance for base asset + base_balance = balances.get(base_asset, {}).get('total', 0.0) + + if base_balance > 0.001: # Minimum threshold + positions[symbol] = { + 'side': 'LONG', + 'size': base_balance, + 'value': base_balance * self._get_current_price(symbol), + 'source': 'exchange_balance' + } + + # Also check trading executor's position tracking + if hasattr(self.trading_executor, 'get_positions'): + executor_positions = self.trading_executor.get_positions() + for symbol, position in executor_positions.items(): + if position and hasattr(position, 'quantity') and position.quantity > 0: + positions[symbol] = { + 'side': position.side, + 'size': position.quantity, + 'entry_price': position.entry_price, + 'value': position.quantity * self._get_current_price(symbol), + 'source': 'executor_tracking' + } + + return positions + + except Exception as e: + logger.error(f"Error getting actual exchange positions: {e}") + return {} + + def _get_dashboard_positions(self) -> Dict[str, Dict]: + """Get positions as shown on dashboard""" + try: + positions = {} + + # Get from dashboard's current_position + if self.dashboard.current_position: + symbol = self.dashboard.current_position.get('symbol', 'ETH/USDT') + positions[symbol] = { + 'side': self.dashboard.current_position.get('side'), + 'size': self.dashboard.current_position.get('size'), + 'entry_price': self.dashboard.current_position.get('price'), + 'value': self.dashboard.current_position.get('size', 0) * self._get_current_price(symbol), + 'source': 'dashboard_display' + } + + return positions + + except Exception as e: + logger.error(f"Error getting dashboard positions: {e}") + return {} + + def _sync_symbol_position(self, symbol: str, exchange_pos: Optional[Dict], dashboard_pos: Optional[Dict]) -> Dict[str, Any]: + """Sync position for a specific symbol""" + try: + sync_result = { + 'symbol': symbol, + 'exchange_position': exchange_pos, + 'dashboard_position': dashboard_pos, + 'in_sync': True, + 'action_taken': 'none' + } + + # Case 1: Exchange has position, dashboard doesn't + if exchange_pos and not dashboard_pos: + logger.warning(f"SYNC ISSUE: Exchange has {symbol} position but dashboard shows none") + + # Update dashboard to reflect exchange position + self.dashboard.current_position = { + 'symbol': symbol, + 'side': exchange_pos['side'], + 'size': exchange_pos['size'], + 'price': exchange_pos.get('entry_price', self._get_current_price(symbol)), + 'entry_time': datetime.now(), + 'leverage': self.dashboard.current_leverage, + 'source': 'sync_correction' + } + + sync_result['in_sync'] = False + sync_result['action_taken'] = 'updated_dashboard_from_exchange' + + # Case 2: Dashboard has position, exchange doesn't + elif dashboard_pos and not exchange_pos: + logger.warning(f"SYNC ISSUE: Dashboard shows {symbol} position but exchange has none") + + # Clear dashboard position + self.dashboard.current_position = None + + sync_result['in_sync'] = False + sync_result['action_taken'] = 'cleared_dashboard_position' + + # Case 3: Both have positions but they differ + elif exchange_pos and dashboard_pos: + if (exchange_pos['side'] != dashboard_pos['side'] or + abs(exchange_pos['size'] - dashboard_pos['size']) > 0.001): + + logger.warning(f"SYNC ISSUE: {symbol} position mismatch - Exchange: {exchange_pos['side']} {exchange_pos['size']:.3f}, Dashboard: {dashboard_pos['side']} {dashboard_pos['size']:.3f}") + + # Update dashboard to match exchange + self.dashboard.current_position.update({ + 'side': exchange_pos['side'], + 'size': exchange_pos['size'], + 'price': exchange_pos.get('entry_price', dashboard_pos['entry_price']) + }) + + sync_result['in_sync'] = False + sync_result['action_taken'] = 'updated_dashboard_to_match_exchange' + + return sync_result + + except Exception as e: + logger.error(f"Error syncing position for {symbol}: {e}") + return {'symbol': symbol, 'error': str(e), 'in_sync': False} + + def _sync_closed_trades(self): + """Sync closed trades list with actual exchange trade history""" + try: + if not self.trading_executor: + return + + # Get trade history from executor + if hasattr(self.trading_executor, 'get_trade_history'): + executor_trades = self.trading_executor.get_trade_history() + + # Clear and rebuild closed_trades list + self.dashboard.closed_trades = [] + + for trade in executor_trades: + # Convert to dashboard format + trade_record = { + 'symbol': getattr(trade, 'symbol', 'ETH/USDT'), + 'side': getattr(trade, 'side', 'UNKNOWN'), + 'quantity': getattr(trade, 'quantity', 0), + 'entry_price': getattr(trade, 'entry_price', 0), + 'exit_price': getattr(trade, 'exit_price', 0), + 'entry_time': getattr(trade, 'entry_time', datetime.now()), + 'exit_time': getattr(trade, 'exit_time', datetime.now()), + 'pnl': getattr(trade, 'pnl', 0), + 'fees': getattr(trade, 'fees', 0), + 'confidence': getattr(trade, 'confidence', 1.0), + 'trade_type': 'synced_from_executor' + } + + # Only add completed trades (with exit_time) + if trade_record['exit_time']: + self.dashboard.closed_trades.append(trade_record) + + # Update session PnL + self.dashboard.session_pnl = sum(trade['pnl'] for trade in self.dashboard.closed_trades) + + logger.info(f"Synced {len(self.dashboard.closed_trades)} closed trades from executor") + + except Exception as e: + logger.error(f"Error syncing closed trades: {e}") + + def _get_current_price(self, symbol: str) -> float: + """Get current price for a symbol""" + try: + return self.dashboard._get_current_price(symbol) or 3500.0 + except: + return 3500.0 # Fallback price + + def should_sync(self) -> bool: + """Check if sync is needed based on time interval""" + current_time = time.time() + if current_time - self.last_sync_time >= self.sync_interval: + self.last_sync_time = current_time + return True + return False + + def create_sync_status_display(self) -> Dict[str, Any]: + """Create detailed sync status for dashboard display""" + try: + # Get current sync status + sync_results = self.sync_all_positions() + + # Create display-friendly format + status_display = { + 'last_sync': datetime.now().strftime('%H:%M:%S'), + 'sync_healthy': sync_results.get('issues_found', 0) == 0, + 'positions': {}, + 'closed_trades_count': len(self.dashboard.closed_trades), + 'session_pnl': self.dashboard.session_pnl + } + + # Add position details + for symbol, result in sync_results.get('results', {}).items(): + status_display['positions'][symbol] = { + 'in_sync': result['in_sync'], + 'action_taken': result.get('action_taken', 'none'), + 'has_exchange_position': result['exchange_position'] is not None, + 'has_dashboard_position': result['dashboard_position'] is not None + } + + return status_display + + except Exception as e: + logger.error(f"Error creating sync status display: {e}") + return {'error': str(e)} + + +# Integration with existing dashboard +def integrate_enhanced_sync(dashboard): + """Integrate enhanced sync with existing dashboard""" + + # Create enhanced sync instance + enhanced_sync = EnhancedPositionSync(dashboard.trading_executor, dashboard) + + # Add to dashboard + dashboard.enhanced_sync = enhanced_sync + + # Modify existing metrics update to include sync + original_update_metrics = dashboard.update_metrics + + def enhanced_update_metrics(n): + """Enhanced metrics update with position sync""" + try: + # Perform periodic sync + if enhanced_sync.should_sync(): + sync_results = enhanced_sync.sync_all_positions() + if sync_results.get('issues_found', 0) > 0: + logger.info(f"Position sync performed: {sync_results['issues_found']} issues corrected") + + # Call original metrics update + return original_update_metrics(n) + + except Exception as e: + logger.error(f"Error in enhanced metrics update: {e}") + return original_update_metrics(n) + + # Replace the update method + dashboard.update_metrics = enhanced_update_metrics + + return enhanced_sync diff --git a/web/clean_dashboard.py b/web/clean_dashboard.py index de654e8..4114e26 100644 --- a/web/clean_dashboard.py +++ b/web/clean_dashboard.py @@ -118,6 +118,9 @@ class CleanTradingDashboard: ) self.component_manager = DashboardComponentManager() + # Initialize enhanced position sync system + self._initialize_enhanced_position_sync() + # Initialize Universal Data Adapter access through orchestrator if UNIVERSAL_DATA_AVAILABLE: self.universal_adapter = UniversalDataAdapter(self.data_provider) @@ -2711,26 +2714,58 @@ class CleanTradingDashboard: } def _sync_position_from_executor(self, symbol: str): - """Sync current position from trading executor""" + """Sync current position from trading executor and real Bybit positions""" try: - if self.trading_executor and hasattr(self.trading_executor, 'get_current_position'): - executor_position = self.trading_executor.get_current_position(symbol) - if executor_position: - # Update dashboard position to match executor - self.current_position = { - 'side': executor_position.get('side', 'UNKNOWN'), - 'size': executor_position.get('size', 0), - 'price': executor_position.get('price', 0), - 'symbol': executor_position.get('symbol', symbol), - 'entry_time': executor_position.get('entry_time', datetime.now()), - 'leverage': self.current_leverage, # Store current leverage with position - 'unrealized_pnl': executor_position.get('unrealized_pnl', 0) - } - logger.debug(f"Synced position from executor: {self.current_position['side']} {self.current_position['size']:.3f}") + # First try to get real position from Bybit + real_position = None + if self.trading_executor and hasattr(self.trading_executor, 'exchange'): + try: + # Get real positions from Bybit + bybit_positions = self.trading_executor.exchange.get_positions(symbol) + if bybit_positions: + # Use the first real position found + real_position = bybit_positions[0] + logger.info(f"Found real Bybit position: {real_position}") + else: + logger.debug("No real positions found on Bybit") + except Exception as e: + logger.debug(f"Error getting real Bybit positions: {e}") + + # If we have a real position, use it + if real_position: + self.current_position = { + 'side': 'LONG' if real_position.get('side', '').lower() == 'buy' else 'SHORT', + 'size': real_position.get('size', 0), + 'price': real_position.get('entry_price', 0), + 'symbol': real_position.get('symbol', symbol), + 'entry_time': datetime.now(), # We don't have entry time from API + 'leverage': real_position.get('leverage', self.current_leverage), + 'unrealized_pnl': real_position.get('unrealized_pnl', 0) + } + logger.info(f"Synced real Bybit position: {self.current_position['side']} {self.current_position['size']:.3f} @ ${self.current_position['price']:.2f}") + else: + # Fallback to executor's internal tracking + if self.trading_executor and hasattr(self.trading_executor, 'get_current_position'): + executor_position = self.trading_executor.get_current_position(symbol) + if executor_position: + # Update dashboard position to match executor + self.current_position = { + 'side': executor_position.get('side', 'UNKNOWN'), + 'size': executor_position.get('size', 0), + 'price': executor_position.get('price', 0), + 'symbol': executor_position.get('symbol', symbol), + 'entry_time': executor_position.get('entry_time', datetime.now()), + 'leverage': self.current_leverage, # Store current leverage with position + 'unrealized_pnl': executor_position.get('unrealized_pnl', 0) + } + logger.debug(f"Synced position from executor: {self.current_position['side']} {self.current_position['size']:.3f}") + else: + # No position in executor + self.current_position = None + logger.debug("No position in trading executor") else: - # No position in executor self.current_position = None - logger.debug("No position in trading executor") + logger.debug("No trading executor available") except Exception as e: logger.debug(f"Error syncing position from executor: {e}") @@ -3999,7 +4034,7 @@ class CleanTradingDashboard: logger.error(f"Error verifying position sync after trade: {e}") def _periodic_position_sync_check(self): - """Periodically check and sync position with MEXC account""" + """Periodically check and sync position with Bybit account""" try: symbol = 'ETH/USDT' @@ -4007,26 +4042,18 @@ class CleanTradingDashboard: if not self.trading_executor or getattr(self.trading_executor, 'simulation_mode', True): return - # Determine current desired state based on dashboard position + # Sync real positions from Bybit + logger.debug(f"PERIODIC SYNC: Syncing real Bybit positions for {symbol}") + self._sync_position_from_executor(symbol) + + # Log current position state if self.current_position: side = self.current_position.get('side', 'UNKNOWN') - if side.upper() in ['LONG', 'BUY']: - desired_state = 'LONG' - elif side.upper() in ['SHORT', 'SELL']: - desired_state = 'SHORT' - else: - desired_state = 'NO_POSITION' + size = self.current_position.get('size', 0) + price = self.current_position.get('price', 0) + logger.info(f"PERIODIC SYNC: Current position: {side} {size:.3f} @ ${price:.2f}") else: - desired_state = 'NO_POSITION' - - # Perform periodic sync check - logger.debug(f"PERIODIC SYNC: Checking position sync for {symbol} (desired: {desired_state})") - sync_success = self._sync_position_with_mexc(symbol, desired_state) - - if sync_success: - logger.debug(f"PERIODIC SYNC: Position sync verified for {symbol}") - else: - logger.warning(f"PERIODIC SYNC: Position sync issue detected for {symbol}") + logger.info(f"PERIODIC SYNC: No current position for {symbol}") except Exception as e: logger.debug(f"Error in periodic position sync check: {e}") @@ -4914,6 +4941,25 @@ class CleanTradingDashboard: logger.error(f"Error initializing enhanced training system: {e}") self.training_system = None + def _initialize_enhanced_position_sync(self): + """Initialize enhanced position synchronization system""" + try: + logger.info("Initializing enhanced position sync system...") + + # Initialize position sync if trading executor is available + if self.trading_executor: + # Set up periodic position sync + self.position_sync_enabled = True + self.position_sync_interval = 30 # seconds + logger.info("Enhanced position sync system initialized") + else: + logger.warning("Trading executor not available - position sync disabled") + self.position_sync_enabled = False + + except Exception as e: + logger.error(f"Error initializing enhanced position sync: {e}") + self.position_sync_enabled = False + def _initialize_cob_integration(self): """Initialize COB integration using orchestrator's COB system""" try: @@ -6846,4 +6892,4 @@ def create_clean_dashboard(data_provider: Optional[DataProvider] = None, orchest ) - # test edit \ No newline at end of file + # test edit