stability

This commit is contained in:
Dobromir Popov
2025-08-08 12:09:15 +03:00
parent c58ec789f2
commit e39e9ee95a
2 changed files with 74 additions and 16 deletions

View File

@ -317,4 +317,32 @@ def setup_logging(config: Optional[Config] = None):
except Exception as e: except Exception as e:
logger.warning(f"Failed to initialize error log handler: {e}") logger.warning(f"Failed to initialize error log handler: {e}")
# Add dedicated trade log to capture all NN signals (executed and ignored)
try:
from logging.handlers import RotatingFileHandler
trade_log_dir = Path('logs')
trade_log_dir.mkdir(parents=True, exist_ok=True)
trade_log_path = trade_log_dir / 'trades.log'
class FlushingTradeHandler(RotatingFileHandler):
def emit(self, record):
super().emit(record)
self.flush()
trade_handler = FlushingTradeHandler(
str(trade_log_path), maxBytes=20*1024*1024, backupCount=10, encoding='utf-8'
)
trade_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
trade_handler.setFormatter(trade_formatter)
trade_handler.setLevel(logging.INFO)
trade_logger = logging.getLogger('trade_log')
trade_logger.setLevel(logging.INFO)
trade_logger.addHandler(trade_handler)
# Avoid double propagation to root to keep trade log clean
trade_logger.propagate = False
logger.info("Trade log handler initialized at logs/trades.log")
except Exception as e:
logger.warning(f"Failed to initialize trade log handler: {e}")
logger.info("Logging configured successfully with SafeFormatter") logger.info("Logging configured successfully with SafeFormatter")

View File

@ -608,21 +608,18 @@ class CleanTradingDashboard:
def _monitor_order_execution(self): def _monitor_order_execution(self):
"""Monitor order execution status in live mode and update dashboard signals""" """Monitor order execution status in live mode and update dashboard signals"""
try: logger.info("Starting order execution monitoring for live mode")
logger.info("Starting order execution monitoring for live mode") while True:
while True: try:
time.sleep(5) # Check every 5 seconds time.sleep(5) # Check every 5 seconds
# Check for signals that were attempted but not yet executed # Check for signals that were attempted but not yet executed
for decision in self.recent_decisions: for decision in self.recent_decisions:
if (decision.get('execution_attempted', False) and if (decision.get('execution_attempted', False) and
not decision.get('executed', False) and not decision.get('executed', False) and
not decision.get('execution_failure', False)): not decision.get('execution_failure', False)):
# Check if the order was actually filled # Check if the order was actually filled
symbol = decision.get('symbol', 'ETH/USDT') symbol = decision.get('symbol', 'ETH/USDT')
action = decision.get('action', 'HOLD') action = decision.get('action', 'HOLD')
# Check if position was actually opened/closed # Check if position was actually opened/closed
if self.trading_executor and hasattr(self.trading_executor, 'positions'): if self.trading_executor and hasattr(self.trading_executor, 'positions'):
if symbol in self.trading_executor.positions: if symbol in self.trading_executor.positions:
@ -639,9 +636,8 @@ class CleanTradingDashboard:
else: else:
# No position exists, order might still be pending # No position exists, order might still be pending
logger.debug(f"No position found for {symbol}, order may still be pending") logger.debug(f"No position found for {symbol}, order may still be pending")
except Exception as e:
except Exception as e: logger.warning(f"Order execution monitoring iteration error: {e}")
logger.error(f"Error in order execution monitoring: {e}")
def _delayed_training_check(self): def _delayed_training_check(self):
"""Check and start training after a delay to allow initialization""" """Check and start training after a delay to allow initialization"""
@ -8898,6 +8894,7 @@ class CleanTradingDashboard:
async def _on_trading_decision(self, decision): async def _on_trading_decision(self, decision):
"""Handle trading decision from orchestrator and execute through trading executor.""" """Handle trading decision from orchestrator and execute through trading executor."""
try: try:
trade_logger = logging.getLogger('trade_log')
# Handle both object and dict formats # Handle both object and dict formats
if hasattr(decision, 'action'): if hasattr(decision, 'action'):
action = getattr(decision, 'action', 'HOLD') action = getattr(decision, 'action', 'HOLD')
@ -8940,6 +8937,10 @@ class CleanTradingDashboard:
dashboard_decision['source'] = 'Unknown' dashboard_decision['source'] = 'Unknown'
logger.info(f"[ORCHESTRATOR SIGNAL] Received: {action} for {symbol} (confidence: {confidence:.3f})") logger.info(f"[ORCHESTRATOR SIGNAL] Received: {action} for {symbol} (confidence: {confidence:.3f})")
try:
trade_logger.info(f"SIGNAL action={action} symbol={symbol} confidence={confidence:.4f} price={price if price is not None else 'NA'} source={dashboard_decision.get('source','Unknown')}")
except Exception:
pass
# EXECUTE THE DECISION THROUGH TRADING EXECUTOR # EXECUTE THE DECISION THROUGH TRADING EXECUTOR
# check if we are entering or exiting a position with this signal # check if we are entering or exiting a position with this signal
@ -9003,18 +9004,37 @@ class CleanTradingDashboard:
else: else:
logger.warning(f"[ORCHESTRATOR EXECUTION] FAILED: {action} execution blocked for {symbol}") logger.warning(f"[ORCHESTRATOR EXECUTION] FAILED: {action} execution blocked for {symbol}")
dashboard_decision['execution_failure'] = True dashboard_decision['execution_failure'] = True
try:
trade_logger.info(f"IGNORED action={action} symbol={symbol} confidence={confidence:.4f} reason=blocked")
except Exception:
pass
except Exception as e: except Exception as e:
logger.error(f"[ORCHESTRATOR EXECUTION] ERROR: Failed to execute {action} for {symbol}: {e}") logger.error(f"[ORCHESTRATOR EXECUTION] ERROR: Failed to execute {action} for {symbol}: {e}")
dashboard_decision['execution_error'] = str(e) dashboard_decision['execution_error'] = str(e)
else: else:
if not self.trading_executor: if not self.trading_executor:
logger.warning("[ORCHESTRATOR EXECUTION] No trading executor available") logger.warning("[ORCHESTRATOR EXECUTION] No trading executor available")
elif confidence <= 0.5: try:
logger.info(f"[ORCHESTRATOR EXECUTION] Low confidence signal ignored: {action} for {symbol} (confidence: {confidence:.3f})") trade_logger.info(f"IGNORED action={action} symbol={symbol} confidence={confidence:.4f} reason=no_executor")
except Exception:
pass
elif confidence <= 0.5:
logger.info(f"[ORCHESTRATOR EXECUTION] Low confidence signal ignored: {action} for {symbol} (confidence: {confidence:.3f})")
try:
trade_logger.info(f"IGNORED action={action} symbol={symbol} confidence={confidence:.4f} reason=low_conf_threshold")
except Exception:
pass
# Store decision in dashboard # Store decision in dashboard
self.recent_decisions.append(dashboard_decision) self.recent_decisions.append(dashboard_decision)
try:
if dashboard_decision.get('executed'):
trade_logger.info(f"EXECUTED action={action} symbol={symbol} confidence={confidence:.4f} executed={dashboard_decision.get('executed')} sim={self.trading_executor.simulation_mode if self.trading_executor else 'NA'}")
elif dashboard_decision.get('execution_attempted'):
trade_logger.info(f"ATTEMPTED action={action} symbol={symbol} confidence={confidence:.4f} live=True")
except Exception:
pass
if len(self.recent_decisions) > 200: if len(self.recent_decisions) > 200:
self.recent_decisions.pop(0) self.recent_decisions.pop(0)
@ -9102,8 +9122,18 @@ class CleanTradingDashboard:
logger.info("WebSocket connected") logger.info("WebSocket connected")
self.is_streaming = True self.is_streaming = True
ws_url = "wss://stream.binance.com:9443/ws/ethusdt@kline_1s" ws_url = "wss://stream.binance.com:9443/ws/ethusdt@kline_1s"
ws = websocket.WebSocketApp(ws_url, on_message=on_message, on_error=on_error, on_close=on_close, on_open=on_open) backoff = 1
ws.run_forever() while True:
try:
ws = websocket.WebSocketApp(ws_url, on_message=on_message, on_error=on_error, on_close=on_close, on_open=on_open)
ws.run_forever(ping_interval=20, ping_timeout=10)
logger.info(f"WebSocket stopped, restarting in {backoff}s")
time.sleep(backoff)
backoff = min(backoff * 2, 60)
except Exception as _ws_e:
logger.error(f"WebSocket loop error: {_ws_e}")
time.sleep(backoff)
backoff = min(backoff * 2, 60)
except Exception as e: except Exception as e:
logger.error(f"WebSocket worker error: {e}") logger.error(f"WebSocket worker error: {e}")
self.is_streaming = False self.is_streaming = False