stability
This commit is contained in:
@ -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")
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user