safety measures - 5 consequtive losses

This commit is contained in:
Dobromir Popov
2025-07-17 21:06:49 +03:00
parent 26d440f772
commit 23f0caea74

View File

@ -156,6 +156,13 @@ class TradingExecutor:
# Store trading mode for compatibility
self.trading_mode = self.primary_config.get('trading_mode', 'simulation')
# Safety feature: Auto-disable live trading after consecutive losses
self.max_consecutive_losses = 5 # Disable live trading after 5 consecutive losses
self.min_success_rate_to_reenable = 0.55 # Require 55% success rate to re-enable
self.trades_to_evaluate = 20 # Evaluate last 20 trades for success rate
self.original_trading_mode = self.trading_mode # Store original mode
self.safety_triggered = False # Track if safety feature was triggered
# Initialize session stats
self.session_start_time = datetime.now()
self.session_trades = 0
@ -615,12 +622,96 @@ class TradingExecutor:
logger.error(f"Error cancelling open orders for {symbol}: {e}")
return 0
def _can_reenable_live_trading(self) -> bool:
"""Check if trading performance has improved enough to re-enable live trading
Returns:
bool: True if performance meets criteria to re-enable live trading
"""
try:
# Need enough trades to evaluate
if len(self.trade_history) < self.trades_to_evaluate:
logger.debug(f"Not enough trades to evaluate for re-enabling live trading: {len(self.trade_history)}/{self.trades_to_evaluate}")
return False
# Get the most recent trades for evaluation
recent_trades = self.trade_history[-self.trades_to_evaluate:]
# Calculate success rate
winning_trades = sum(1 for trade in recent_trades if trade.pnl > 0.001)
success_rate = winning_trades / len(recent_trades)
# Calculate average PnL
avg_pnl = sum(trade.pnl for trade in recent_trades) / len(recent_trades)
# Calculate win/loss ratio
losing_trades = sum(1 for trade in recent_trades if trade.pnl < -0.001)
win_loss_ratio = winning_trades / max(1, losing_trades) # Avoid division by zero
logger.info(f"SAFETY FEATURE: Performance evaluation - Success rate: {success_rate:.2%}, Avg PnL: ${avg_pnl:.2f}, Win/Loss ratio: {win_loss_ratio:.2f}")
# Criteria to re-enable live trading:
# 1. Success rate must exceed minimum threshold
# 2. Average PnL must be positive
# 3. Win/loss ratio must be at least 1.0 (equal wins and losses)
if (success_rate >= self.min_success_rate_to_reenable and
avg_pnl > 0 and
win_loss_ratio >= 1.0):
logger.info(f"SAFETY FEATURE: Performance criteria met for re-enabling live trading")
return True
else:
logger.debug(f"SAFETY FEATURE: Performance criteria not yet met for re-enabling live trading")
return False
except Exception as e:
logger.error(f"Error evaluating trading performance: {e}")
return False
except Exception as e:
logger.error(f"Error evaluating trading performance: {e}")
return False
def _check_safety_conditions(self, symbol: str, action: str) -> bool:
"""Check if it's safe to execute a trade"""
# Check if trading is stopped
if self.exchange_config.get('emergency_stop', False):
logger.warning("Emergency stop is active - no trades allowed")
return False
# Safety feature: Check consecutive losses and switch to simulation mode if needed
if not self.simulation_mode and self.consecutive_losses >= self.max_consecutive_losses:
logger.warning(f"SAFETY FEATURE ACTIVATED: {self.consecutive_losses} consecutive losses detected")
logger.warning(f"Switching from live trading to simulation mode for safety")
# Store original mode and switch to simulation
self.original_trading_mode = self.trading_mode
self.trading_mode = 'simulation'
self.simulation_mode = True
self.safety_triggered = True
# Log the event
logger.info(f"Trading mode changed to SIMULATION due to safety feature")
logger.info(f"Will continue to monitor performance and re-enable live trading when success rate improves")
# Continue allowing trades in simulation mode
return True
# Check if we should try to re-enable live trading after safety feature was triggered
if self.simulation_mode and self.safety_triggered and self.original_trading_mode != 'simulation':
# Check if performance has improved enough to re-enable live trading
if self._can_reenable_live_trading():
logger.info(f"SAFETY FEATURE: Performance has improved, re-enabling live trading")
# Switch back to original mode
self.trading_mode = self.original_trading_mode
self.simulation_mode = (self.trading_mode == 'simulation')
self.safety_triggered = False
self.consecutive_losses = 0 # Reset consecutive losses counter
logger.info(f"Trading mode restored to {self.trading_mode}")
# Continue with the trade
return True
# Check symbol allowlist
allowed_symbols = self.exchange_config.get('allowed_symbols', [])
@ -2209,6 +2300,88 @@ class TradingExecutor:
logger.info("TRADING EXECUTOR: Test mode enabled - bypassing safety checks")
else:
logger.info("TRADING EXECUTOR: Test mode disabled - normal safety checks active")
def get_status(self) -> Dict[str, Any]:
"""Get trading executor status with safety feature information"""
try:
# Get account balance
if self.simulation_mode:
balance = self.simulation_balance
else:
balance = self.exchange.get_balance('USDT') if self.exchange else 0.0
# Get open positions
positions = self.get_positions()
# Calculate total fees paid
total_fees = sum(trade.fees for trade in self.trade_history)
total_volume = sum(trade.quantity * trade.exit_price for trade in self.trade_history)
# Estimate fee breakdown (since we don't track maker vs taker separately)
maker_fee_rate = self.exchange_config.get('maker_fee', 0.0002)
taker_fee_rate = self.exchange_config.get('taker_fee', 0.0006)
avg_fee_rate = (maker_fee_rate + taker_fee_rate) / 2
# Fee impact analysis
total_pnl = sum(trade.pnl for trade in self.trade_history)
gross_pnl = total_pnl + total_fees
fee_impact_percent = (total_fees / max(1, abs(gross_pnl))) * 100 if gross_pnl != 0 else 0
# Calculate success rate for recent trades
recent_trades = self.trade_history[-self.trades_to_evaluate:] if len(self.trade_history) >= self.trades_to_evaluate else self.trade_history
winning_trades = sum(1 for trade in recent_trades if trade.pnl > 0.001) if recent_trades else 0
success_rate = (winning_trades / len(recent_trades)) if recent_trades else 0
# Safety feature status
safety_status = {
'active': self.safety_triggered,
'consecutive_losses': self.consecutive_losses,
'max_consecutive_losses': self.max_consecutive_losses,
'original_mode': self.original_trading_mode if self.safety_triggered else self.trading_mode,
'success_rate': success_rate,
'min_success_rate_to_reenable': self.min_success_rate_to_reenable,
'trades_evaluated': len(recent_trades),
'trades_needed': self.trades_to_evaluate,
'can_reenable': self._can_reenable_live_trading() if self.safety_triggered else False
}
return {
'trading_enabled': self.trading_enabled,
'simulation_mode': self.simulation_mode,
'trading_mode': self.trading_mode,
'balance': balance,
'positions': len(positions),
'daily_trades': self.daily_trades,
'daily_pnl': self.daily_pnl,
'daily_loss': self.daily_loss,
'consecutive_losses': self.consecutive_losses,
'total_trades': len(self.trade_history),
'safety_feature': safety_status,
'pnl': {
'total': total_pnl,
'gross': gross_pnl,
'fees': total_fees,
'fee_impact_percent': fee_impact_percent,
'pnl_after_fees': total_pnl,
'pnl_before_fees': gross_pnl,
'avg_fee_per_trade': total_fees / max(1, len(self.trade_history))
},
'fee_efficiency': {
'total_volume': total_volume,
'total_fees': total_fees,
'effective_fee_rate': (total_fees / max(0.01, total_volume)) if total_volume > 0 else 0,
'expected_fee_rate': avg_fee_rate,
'fee_efficiency': (avg_fee_rate / ((total_fees / max(0.01, total_volume)) if total_volume > 0 else 1)) if avg_fee_rate > 0 else 0
}
}
except Exception as e:
logger.error(f"Error getting trading executor status: {e}")
return {
'trading_enabled': self.trading_enabled,
'simulation_mode': self.simulation_mode,
'trading_mode': self.trading_mode,
'error': str(e)
}
def sync_position_with_mexc(self, symbol: str, desired_state: str) -> bool:
"""Synchronize dashboard position state with actual MEXC account positions