safety measures - 5 consequtive losses
This commit is contained in:
@ -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
|
||||
|
Reference in New Issue
Block a user