execution and training fixes
This commit is contained in:
@ -58,6 +58,7 @@ class TradeRecord:
|
||||
pnl: float
|
||||
fees: float
|
||||
confidence: float
|
||||
hold_time_seconds: float = 0.0 # Hold time in seconds
|
||||
|
||||
class TradingExecutor:
|
||||
"""Handles trade execution through MEXC API with risk management"""
|
||||
@ -206,9 +207,9 @@ class TradingExecutor:
|
||||
# Assert that current_price is not None for type checking
|
||||
assert current_price is not None, "current_price should not be None at this point"
|
||||
|
||||
# --- Balance check before executing trade ---
|
||||
# Only perform balance check for BUY actions or SHORT (initial sell) actions
|
||||
if action == 'BUY' or (action == 'SELL' and symbol not in self.positions) or (action == 'SHORT'):
|
||||
# --- Balance check before executing trade (skip in simulation mode) ---
|
||||
# Only perform balance check for live trading, not simulation
|
||||
if not self.simulation_mode and (action == 'BUY' or (action == 'SELL' and symbol not in self.positions) or (action == 'SHORT')):
|
||||
# Determine the quote asset (e.g., USDT, USDC) from the symbol
|
||||
if '/' in symbol:
|
||||
quote_asset = symbol.split('/')[1].upper() # Assuming symbol is like ETH/USDT
|
||||
@ -244,6 +245,8 @@ class TradingExecutor:
|
||||
logger.warning(f"Trade blocked for {symbol} {action}: Insufficient {quote_asset} balance. "
|
||||
f"Required: ${required_capital:.2f}, Available: ${available_balance:.2f}")
|
||||
return False
|
||||
elif self.simulation_mode:
|
||||
logger.debug(f"SIMULATION MODE: Skipping balance check for {symbol} {action} - allowing trade for model training")
|
||||
# --- End Balance check ---
|
||||
|
||||
with self.lock:
|
||||
@ -318,10 +321,15 @@ class TradingExecutor:
|
||||
quantity = position_value / current_price
|
||||
|
||||
logger.info(f"Executing BUY: {quantity:.6f} {symbol} at ${current_price:.2f} "
|
||||
f"(value: ${position_value:.2f}, confidence: {confidence:.2f})")
|
||||
f"(value: ${position_value:.2f}, confidence: {confidence:.2f}) "
|
||||
f"[{'SIMULATION' if self.simulation_mode else 'LIVE'}]")
|
||||
|
||||
if self.simulation_mode:
|
||||
logger.info(f"SIMULATION MODE ({self.trading_mode.upper()}) - Trade logged but not executed")
|
||||
# Calculate simulated fees in simulation mode
|
||||
taker_fee_rate = self.mexc_config.get('trading_fees', {}).get('taker_fee', 0.0006)
|
||||
simulated_fees = quantity * current_price * taker_fee_rate
|
||||
|
||||
# Create mock position for tracking
|
||||
self.positions[symbol] = Position(
|
||||
symbol=symbol,
|
||||
@ -365,6 +373,10 @@ class TradingExecutor:
|
||||
)
|
||||
|
||||
if order:
|
||||
# Calculate simulated fees in simulation mode
|
||||
taker_fee_rate = self.mexc_config.get('trading_fees', {}).get('taker_fee', 0.0006)
|
||||
simulated_fees = quantity * current_price * taker_fee_rate
|
||||
|
||||
# Create position record
|
||||
self.positions[symbol] = Position(
|
||||
symbol=symbol,
|
||||
@ -398,13 +410,19 @@ class TradingExecutor:
|
||||
position = self.positions[symbol]
|
||||
|
||||
logger.info(f"Executing SELL: {position.quantity:.6f} {symbol} at ${current_price:.2f} "
|
||||
f"(confidence: {confidence:.2f})")
|
||||
f"(confidence: {confidence:.2f}) [{'SIMULATION' if self.simulation_mode else 'LIVE'}]")
|
||||
|
||||
if self.simulation_mode:
|
||||
logger.info(f"SIMULATION MODE ({self.trading_mode.upper()}) - Trade logged but not executed")
|
||||
# Calculate P&L
|
||||
# Calculate P&L and hold time
|
||||
pnl = position.calculate_pnl(current_price)
|
||||
exit_time = datetime.now()
|
||||
hold_time_seconds = (exit_time - position.entry_time).total_seconds()
|
||||
|
||||
# Calculate simulated fees in simulation mode
|
||||
taker_fee_rate = self.mexc_config.get('trading_fees', {}).get('taker_fee', 0.0006)
|
||||
simulated_fees = position.quantity * current_price * taker_fee_rate
|
||||
|
||||
# Create trade record
|
||||
trade_record = TradeRecord(
|
||||
symbol=symbol,
|
||||
@ -413,10 +431,11 @@ class TradingExecutor:
|
||||
entry_price=position.entry_price,
|
||||
exit_price=current_price,
|
||||
entry_time=position.entry_time,
|
||||
exit_time=datetime.now(),
|
||||
exit_time=exit_time,
|
||||
pnl=pnl,
|
||||
fees=0.0,
|
||||
confidence=confidence
|
||||
fees=simulated_fees,
|
||||
confidence=confidence,
|
||||
hold_time_seconds=hold_time_seconds
|
||||
)
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
@ -460,9 +479,15 @@ class TradingExecutor:
|
||||
)
|
||||
|
||||
if order:
|
||||
# Calculate P&L
|
||||
# Calculate simulated fees in simulation mode
|
||||
taker_fee_rate = self.mexc_config.get('trading_fees', {}).get('taker_fee', 0.0006)
|
||||
simulated_fees = position.quantity * current_price * taker_fee_rate
|
||||
|
||||
# Calculate P&L, fees, and hold time
|
||||
pnl = position.calculate_pnl(current_price)
|
||||
fees = self._calculate_trading_fee(order, symbol, position.quantity, current_price)
|
||||
fees = simulated_fees
|
||||
exit_time = datetime.now()
|
||||
hold_time_seconds = (exit_time - position.entry_time).total_seconds()
|
||||
|
||||
# Create trade record
|
||||
trade_record = TradeRecord(
|
||||
@ -472,10 +497,11 @@ class TradingExecutor:
|
||||
entry_price=position.entry_price,
|
||||
exit_price=current_price,
|
||||
entry_time=position.entry_time,
|
||||
exit_time=datetime.now(),
|
||||
exit_time=exit_time,
|
||||
pnl=pnl - fees,
|
||||
fees=fees,
|
||||
confidence=confidence
|
||||
confidence=confidence,
|
||||
hold_time_seconds=hold_time_seconds
|
||||
)
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
@ -509,10 +535,15 @@ class TradingExecutor:
|
||||
quantity = position_value / current_price
|
||||
|
||||
logger.info(f"Executing SHORT: {quantity:.6f} {symbol} at ${current_price:.2f} "
|
||||
f"(value: ${position_value:.2f}, confidence: {confidence:.2f})")
|
||||
f"(value: ${position_value:.2f}, confidence: {confidence:.2f}) "
|
||||
f"[{'SIMULATION' if self.simulation_mode else 'LIVE'}]")
|
||||
|
||||
if self.simulation_mode:
|
||||
logger.info(f"SIMULATION MODE ({self.trading_mode.upper()}) - Short position logged but not executed")
|
||||
# Calculate simulated fees in simulation mode
|
||||
taker_fee_rate = self.mexc_config.get('trading_fees', {}).get('taker_fee', 0.0006)
|
||||
simulated_fees = quantity * current_price * taker_fee_rate
|
||||
|
||||
# Create mock short position for tracking
|
||||
self.positions[symbol] = Position(
|
||||
symbol=symbol,
|
||||
@ -556,6 +587,10 @@ class TradingExecutor:
|
||||
)
|
||||
|
||||
if order:
|
||||
# Calculate simulated fees in simulation mode
|
||||
taker_fee_rate = self.mexc_config.get('trading_fees', {}).get('taker_fee', 0.0006)
|
||||
simulated_fees = quantity * current_price * taker_fee_rate
|
||||
|
||||
# Create short position record
|
||||
self.positions[symbol] = Position(
|
||||
symbol=symbol,
|
||||
@ -595,8 +630,14 @@ class TradingExecutor:
|
||||
|
||||
if self.simulation_mode:
|
||||
logger.info(f"SIMULATION MODE ({self.trading_mode.upper()}) - Short close logged but not executed")
|
||||
# Calculate P&L for short position
|
||||
# Calculate simulated fees in simulation mode
|
||||
taker_fee_rate = self.mexc_config.get('trading_fees', {}).get('taker_fee', 0.0006)
|
||||
simulated_fees = position.quantity * current_price * taker_fee_rate
|
||||
|
||||
# Calculate P&L for short position and hold time
|
||||
pnl = position.calculate_pnl(current_price)
|
||||
exit_time = datetime.now()
|
||||
hold_time_seconds = (exit_time - position.entry_time).total_seconds()
|
||||
|
||||
# Create trade record
|
||||
trade_record = TradeRecord(
|
||||
@ -606,10 +647,11 @@ class TradingExecutor:
|
||||
entry_price=position.entry_price,
|
||||
exit_price=current_price,
|
||||
entry_time=position.entry_time,
|
||||
exit_time=datetime.now(),
|
||||
exit_time=exit_time,
|
||||
pnl=pnl,
|
||||
fees=0.0,
|
||||
confidence=confidence
|
||||
fees=simulated_fees,
|
||||
confidence=confidence,
|
||||
hold_time_seconds=hold_time_seconds
|
||||
)
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
@ -653,9 +695,15 @@ class TradingExecutor:
|
||||
)
|
||||
|
||||
if order:
|
||||
# Calculate P&L
|
||||
# Calculate simulated fees in simulation mode
|
||||
taker_fee_rate = self.mexc_config.get('trading_fees', {}).get('taker_fee', 0.0006)
|
||||
simulated_fees = position.quantity * current_price * taker_fee_rate
|
||||
|
||||
# Calculate P&L, fees, and hold time
|
||||
pnl = position.calculate_pnl(current_price)
|
||||
fees = self._calculate_trading_fee(order, symbol, position.quantity, current_price)
|
||||
fees = simulated_fees
|
||||
exit_time = datetime.now()
|
||||
hold_time_seconds = (exit_time - position.entry_time).total_seconds()
|
||||
|
||||
# Create trade record
|
||||
trade_record = TradeRecord(
|
||||
@ -665,10 +713,11 @@ class TradingExecutor:
|
||||
entry_price=position.entry_price,
|
||||
exit_price=current_price,
|
||||
entry_time=position.entry_time,
|
||||
exit_time=datetime.now(),
|
||||
exit_time=exit_time,
|
||||
pnl=pnl - fees,
|
||||
fees=fees,
|
||||
confidence=confidence
|
||||
confidence=confidence,
|
||||
hold_time_seconds=hold_time_seconds
|
||||
)
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
@ -691,15 +740,44 @@ class TradingExecutor:
|
||||
return False
|
||||
|
||||
def _calculate_position_size(self, confidence: float, current_price: float) -> float:
|
||||
"""Calculate position size based on configuration and confidence"""
|
||||
max_value = self.mexc_config.get('max_position_value_usd', 1.0)
|
||||
min_value = self.mexc_config.get('min_position_value_usd', 0.1)
|
||||
"""Calculate position size based on percentage of account balance, confidence, and leverage"""
|
||||
# Get account balance (simulation or real)
|
||||
account_balance = self._get_account_balance_for_sizing()
|
||||
|
||||
# Get position sizing percentages
|
||||
max_percent = self.mexc_config.get('max_position_percent', 20.0) / 100.0
|
||||
min_percent = self.mexc_config.get('min_position_percent', 2.0) / 100.0
|
||||
base_percent = self.mexc_config.get('base_position_percent', 5.0) / 100.0
|
||||
leverage = self.mexc_config.get('leverage', 50.0)
|
||||
|
||||
# Scale position size by confidence
|
||||
base_value = max_value * confidence
|
||||
position_value = max(min_value, min(base_value, max_value))
|
||||
position_percent = min(max_percent, max(min_percent, base_percent * confidence))
|
||||
position_value = account_balance * position_percent
|
||||
|
||||
return position_value
|
||||
# Apply leverage to get effective position size
|
||||
leveraged_position_value = position_value * leverage
|
||||
|
||||
logger.debug(f"Position calculation: account=${account_balance:.2f}, "
|
||||
f"percent={position_percent*100:.1f}%, base=${position_value:.2f}, "
|
||||
f"leverage={leverage}x, effective=${leveraged_position_value:.2f}, "
|
||||
f"confidence={confidence:.2f}")
|
||||
|
||||
return leveraged_position_value
|
||||
|
||||
def _get_account_balance_for_sizing(self) -> float:
|
||||
"""Get account balance for position sizing calculations"""
|
||||
if self.simulation_mode:
|
||||
return self.mexc_config.get('simulation_account_usd', 100.0)
|
||||
else:
|
||||
# For live trading, get actual USDT/USDC balance
|
||||
try:
|
||||
balances = self.get_account_balance()
|
||||
usdt_balance = balances.get('USDT', {}).get('total', 0)
|
||||
usdc_balance = balances.get('USDC', {}).get('total', 0)
|
||||
return max(usdt_balance, usdc_balance)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to get live account balance: {e}, using simulation default")
|
||||
return self.mexc_config.get('simulation_account_usd', 100.0)
|
||||
|
||||
def update_positions(self, symbol: str, current_price: float):
|
||||
"""Update position P&L with current market price"""
|
||||
@ -720,15 +798,16 @@ class TradingExecutor:
|
||||
total_pnl = sum(trade.pnl for trade in self.trade_history)
|
||||
total_fees = sum(trade.fees for trade in self.trade_history)
|
||||
gross_pnl = total_pnl + total_fees # P&L before fees
|
||||
winning_trades = len([t for t in self.trade_history if t.pnl > 0])
|
||||
losing_trades = len([t for t in self.trade_history if t.pnl < 0])
|
||||
winning_trades = len([t for t in self.trade_history if t.pnl > 0.001]) # Avoid rounding issues
|
||||
losing_trades = len([t for t in self.trade_history if t.pnl < -0.001]) # Avoid rounding issues
|
||||
total_trades = len(self.trade_history)
|
||||
breakeven_trades = total_trades - winning_trades - losing_trades
|
||||
|
||||
# Calculate average trade values
|
||||
avg_trade_pnl = total_pnl / max(1, total_trades)
|
||||
avg_trade_fee = total_fees / max(1, total_trades)
|
||||
avg_winning_trade = sum(t.pnl for t in self.trade_history if t.pnl > 0) / max(1, winning_trades)
|
||||
avg_losing_trade = sum(t.pnl for t in self.trade_history if t.pnl < 0) / max(1, losing_trades)
|
||||
avg_winning_trade = sum(t.pnl for t in self.trade_history if t.pnl > 0.001) / max(1, winning_trades)
|
||||
avg_losing_trade = sum(t.pnl for t in self.trade_history if t.pnl < -0.001) / max(1, losing_trades)
|
||||
|
||||
# Enhanced fee analysis from config
|
||||
fee_structure = self.mexc_config.get('trading_fees', {})
|
||||
@ -749,6 +828,7 @@ class TradingExecutor:
|
||||
'total_fees': total_fees,
|
||||
'winning_trades': winning_trades,
|
||||
'losing_trades': losing_trades,
|
||||
'breakeven_trades': breakeven_trades,
|
||||
'total_trades': total_trades,
|
||||
'win_rate': winning_trades / max(1, total_trades),
|
||||
'avg_trade_pnl': avg_trade_pnl,
|
||||
@ -1147,7 +1227,8 @@ class TradingExecutor:
|
||||
'exit_time': trade.exit_time,
|
||||
'pnl': trade.pnl,
|
||||
'fees': trade.fees,
|
||||
'confidence': trade.confidence
|
||||
'confidence': trade.confidence,
|
||||
'hold_time_seconds': trade.hold_time_seconds
|
||||
}
|
||||
trades.append(trade_dict)
|
||||
return trades
|
||||
@ -1185,4 +1266,59 @@ class TradingExecutor:
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting current position: {e}")
|
||||
return None
|
||||
return None
|
||||
|
||||
def get_leverage(self) -> float:
|
||||
"""Get current leverage setting"""
|
||||
return self.mexc_config.get('leverage', 50.0)
|
||||
|
||||
def set_leverage(self, leverage: float) -> bool:
|
||||
"""Set leverage (for UI control)
|
||||
|
||||
Args:
|
||||
leverage: New leverage value
|
||||
|
||||
Returns:
|
||||
bool: True if successful
|
||||
"""
|
||||
try:
|
||||
# Update in-memory config
|
||||
self.mexc_config['leverage'] = leverage
|
||||
logger.info(f"TRADING EXECUTOR: Leverage updated to {leverage}x")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting leverage: {e}")
|
||||
return False
|
||||
|
||||
def get_account_info(self) -> Dict[str, Any]:
|
||||
"""Get account information for UI display"""
|
||||
try:
|
||||
account_balance = self._get_account_balance_for_sizing()
|
||||
leverage = self.get_leverage()
|
||||
|
||||
return {
|
||||
'account_balance': account_balance,
|
||||
'leverage': leverage,
|
||||
'trading_mode': self.trading_mode,
|
||||
'simulation_mode': self.simulation_mode,
|
||||
'trading_enabled': self.trading_enabled,
|
||||
'position_sizing': {
|
||||
'base_percent': self.mexc_config.get('base_position_percent', 5.0),
|
||||
'max_percent': self.mexc_config.get('max_position_percent', 20.0),
|
||||
'min_percent': self.mexc_config.get('min_position_percent', 2.0)
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting account info: {e}")
|
||||
return {
|
||||
'account_balance': 100.0,
|
||||
'leverage': 50.0,
|
||||
'trading_mode': 'simulation',
|
||||
'simulation_mode': True,
|
||||
'trading_enabled': False,
|
||||
'position_sizing': {
|
||||
'base_percent': 5.0,
|
||||
'max_percent': 20.0,
|
||||
'min_percent': 2.0
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user