in the bussiness -but wip
This commit is contained in:
@ -365,66 +365,27 @@ class TradingExecutor:
|
||||
self._cancel_open_orders(symbol)
|
||||
|
||||
# Calculate position size
|
||||
position_value = self._calculate_position_size(confidence, current_price)
|
||||
quantity = position_value / current_price
|
||||
position_size = self._calculate_position_size(confidence, current_price)
|
||||
quantity = position_size / current_price
|
||||
|
||||
logger.info(f"Executing BUY: {quantity:.6f} {symbol} at ${current_price:.2f} "
|
||||
f"(value: ${position_value:.2f}, confidence: {confidence:.2f}) "
|
||||
f"[{'SIMULATION' if self.simulation_mode else 'LIVE'}]")
|
||||
logger.info(f"Executing BUY: {quantity:.6f} {symbol} at ${current_price:.2f} (value: ${position_size:.2f}, confidence: {confidence:.2f}) [{'SIM' 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
|
||||
# Create simulated position
|
||||
self.positions[symbol] = Position(
|
||||
symbol=symbol,
|
||||
side='LONG',
|
||||
quantity=quantity,
|
||||
entry_price=current_price,
|
||||
entry_time=datetime.now(),
|
||||
order_id=f"sim_{int(time.time())}"
|
||||
order_id=f"sim_{int(datetime.now().timestamp())}"
|
||||
)
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
logger.info(f"Simulated BUY order: {quantity:.6f} {symbol} at ${current_price:.2f}")
|
||||
return True
|
||||
|
||||
try:
|
||||
# Get order type from config
|
||||
order_type = self.mexc_config.get('order_type', 'market').lower()
|
||||
|
||||
# For limit orders, set price slightly above market for immediate execution
|
||||
limit_price = None
|
||||
if order_type == 'limit':
|
||||
# Set buy price slightly above market to ensure immediate execution
|
||||
limit_price = current_price * 1.001 # 0.1% above market
|
||||
|
||||
# Place buy order
|
||||
if order_type == 'market':
|
||||
order = self.exchange.place_order(
|
||||
symbol=symbol,
|
||||
side='buy',
|
||||
order_type=order_type,
|
||||
quantity=quantity
|
||||
)
|
||||
else:
|
||||
# For limit orders, price is required
|
||||
assert limit_price is not None, "limit_price required for limit orders"
|
||||
order = self.exchange.place_order(
|
||||
symbol=symbol,
|
||||
side='buy',
|
||||
order_type=order_type,
|
||||
quantity=quantity,
|
||||
price=limit_price
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
else:
|
||||
# Place real order with enhanced error handling
|
||||
result = self._place_order_with_retry(symbol, 'BUY', 'MARKET', quantity, current_price)
|
||||
if result and 'orderId' in result:
|
||||
# Create position record
|
||||
self.positions[symbol] = Position(
|
||||
symbol=symbol,
|
||||
@ -432,233 +393,58 @@ class TradingExecutor:
|
||||
quantity=quantity,
|
||||
entry_price=current_price,
|
||||
entry_time=datetime.now(),
|
||||
order_id=order.get('orderId', 'unknown')
|
||||
order_id=result['orderId']
|
||||
)
|
||||
|
||||
logger.info(f"BUY order executed: {result}")
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
|
||||
logger.info(f"BUY order executed: {order}")
|
||||
return True
|
||||
else:
|
||||
logger.error("Failed to place BUY order")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing BUY order: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def _execute_sell(self, symbol: str, confidence: float, current_price: float) -> bool:
|
||||
"""Execute a sell order"""
|
||||
# Check if we have a position to sell
|
||||
if symbol not in self.positions:
|
||||
"""Execute a sell order (close long position or open short position)"""
|
||||
if symbol in self.positions:
|
||||
position = self.positions[symbol]
|
||||
if position.side == 'LONG':
|
||||
logger.info(f"Closing LONG position in {symbol}")
|
||||
return self._close_long_position(symbol, confidence, current_price)
|
||||
else:
|
||||
logger.info(f"Already have SHORT position in {symbol}")
|
||||
return False
|
||||
else:
|
||||
# No position to sell, open short position
|
||||
logger.info(f"No position to sell in {symbol}. Opening short position")
|
||||
return self._execute_short(symbol, confidence, current_price)
|
||||
|
||||
position = self.positions[symbol]
|
||||
|
||||
|
||||
def _execute_short(self, symbol: str, confidence: float, current_price: float) -> bool:
|
||||
"""Execute a short order (sell without holding the asset)"""
|
||||
# Cancel any existing open orders before placing new order
|
||||
if not self.simulation_mode:
|
||||
self._cancel_open_orders(symbol)
|
||||
|
||||
logger.info(f"Executing SELL: {position.quantity:.6f} {symbol} at ${current_price:.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 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,
|
||||
side='LONG',
|
||||
quantity=position.quantity,
|
||||
entry_price=position.entry_price,
|
||||
exit_price=current_price,
|
||||
entry_time=position.entry_time,
|
||||
exit_time=exit_time,
|
||||
pnl=pnl,
|
||||
fees=simulated_fees,
|
||||
confidence=confidence,
|
||||
hold_time_seconds=hold_time_seconds
|
||||
)
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
self.daily_loss += max(0, -pnl) # Add to daily loss if negative
|
||||
|
||||
# Update consecutive losses
|
||||
if pnl < -0.001: # A losing trade
|
||||
self.consecutive_losses += 1
|
||||
elif pnl > 0.001: # A winning trade
|
||||
self.consecutive_losses = 0
|
||||
else: # Breakeven trade
|
||||
self.consecutive_losses = 0
|
||||
|
||||
# Remove position
|
||||
del self.positions[symbol]
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
|
||||
logger.info(f"Position closed - P&L: ${pnl:.2f}")
|
||||
return True
|
||||
|
||||
try:
|
||||
# Get order type from config
|
||||
order_type = self.mexc_config.get('order_type', 'market').lower()
|
||||
|
||||
# For limit orders, set price slightly below market for immediate execution
|
||||
limit_price = None
|
||||
if order_type == 'limit':
|
||||
# Set sell price slightly below market to ensure immediate execution
|
||||
limit_price = current_price * 0.999 # 0.1% below market
|
||||
|
||||
# Place sell order
|
||||
if order_type == 'market':
|
||||
order = self.exchange.place_order(
|
||||
symbol=symbol,
|
||||
side='sell',
|
||||
order_type=order_type,
|
||||
quantity=position.quantity
|
||||
)
|
||||
else:
|
||||
# For limit orders, price is required
|
||||
assert limit_price is not None, "limit_price required for limit orders"
|
||||
order = self.exchange.place_order(
|
||||
symbol=symbol,
|
||||
side='sell',
|
||||
order_type=order_type,
|
||||
quantity=position.quantity,
|
||||
price=limit_price
|
||||
)
|
||||
|
||||
if order:
|
||||
# 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 = simulated_fees
|
||||
exit_time = datetime.now()
|
||||
hold_time_seconds = (exit_time - position.entry_time).total_seconds()
|
||||
|
||||
# Create trade record
|
||||
trade_record = TradeRecord(
|
||||
symbol=symbol,
|
||||
side='LONG',
|
||||
quantity=position.quantity,
|
||||
entry_price=position.entry_price,
|
||||
exit_price=current_price,
|
||||
entry_time=position.entry_time,
|
||||
exit_time=exit_time,
|
||||
pnl=pnl - fees,
|
||||
fees=fees,
|
||||
confidence=confidence,
|
||||
hold_time_seconds=hold_time_seconds
|
||||
)
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
self.daily_loss += max(0, -(pnl - fees)) # Add to daily loss if negative
|
||||
|
||||
# Update consecutive losses
|
||||
if pnl < -0.001: # A losing trade
|
||||
self.consecutive_losses += 1
|
||||
elif pnl > 0.001: # A winning trade
|
||||
self.consecutive_losses = 0
|
||||
else: # Breakeven trade
|
||||
self.consecutive_losses = 0
|
||||
|
||||
# Remove position
|
||||
del self.positions[symbol]
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
|
||||
logger.info(f"SELL order executed: {order}")
|
||||
logger.info(f"Position closed - P&L: ${pnl - fees:.2f}")
|
||||
return True
|
||||
else:
|
||||
logger.error("Failed to place SELL order")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing SELL order: {e}")
|
||||
return False
|
||||
|
||||
def _execute_short(self, symbol: str, confidence: float, current_price: float) -> bool:
|
||||
"""Execute a short position opening"""
|
||||
# Check if we already have a position
|
||||
if symbol in self.positions:
|
||||
logger.info(f"Already have position in {symbol}")
|
||||
return False
|
||||
|
||||
# Calculate position size
|
||||
position_value = self._calculate_position_size(confidence, current_price)
|
||||
quantity = position_value / current_price
|
||||
position_size = self._calculate_position_size(confidence, current_price)
|
||||
quantity = position_size / current_price
|
||||
|
||||
logger.info(f"Executing SHORT: {quantity:.6f} {symbol} at ${current_price:.2f} "
|
||||
f"(value: ${position_value:.2f}, confidence: {confidence:.2f}) "
|
||||
f"[{'SIMULATION' if self.simulation_mode else 'LIVE'}]")
|
||||
logger.info(f"Executing SHORT: {quantity:.6f} {symbol} at ${current_price:.2f} (value: ${position_size:.2f}, confidence: {confidence:.2f}) [{'SIM' 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
|
||||
# Create simulated short position
|
||||
self.positions[symbol] = Position(
|
||||
symbol=symbol,
|
||||
side='SHORT',
|
||||
quantity=quantity,
|
||||
entry_price=current_price,
|
||||
entry_time=datetime.now(),
|
||||
order_id=f"sim_short_{int(time.time())}"
|
||||
order_id=f"sim_{int(datetime.now().timestamp())}"
|
||||
)
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
logger.info(f"Simulated SHORT order: {quantity:.6f} {symbol} at ${current_price:.2f}")
|
||||
return True
|
||||
|
||||
try:
|
||||
# Get order type from config
|
||||
order_type = self.mexc_config.get('order_type', 'market').lower()
|
||||
|
||||
# For limit orders, set price slightly below market for immediate execution
|
||||
limit_price = None
|
||||
if order_type == 'limit':
|
||||
# Set short price slightly below market to ensure immediate execution
|
||||
limit_price = current_price * 0.999 # 0.1% below market
|
||||
|
||||
# Place short sell order
|
||||
if order_type == 'market':
|
||||
order = self.exchange.place_order(
|
||||
symbol=symbol,
|
||||
side='sell', # Short selling starts with a sell order
|
||||
order_type=order_type,
|
||||
quantity=quantity
|
||||
)
|
||||
else:
|
||||
# For limit orders, price is required
|
||||
assert limit_price is not None, "limit_price required for limit orders"
|
||||
order = self.exchange.place_order(
|
||||
symbol=symbol,
|
||||
side='sell', # Short selling starts with a sell order
|
||||
order_type=order_type,
|
||||
quantity=quantity,
|
||||
price=limit_price
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
else:
|
||||
# Place real short order with enhanced error handling
|
||||
result = self._place_order_with_retry(symbol, 'SELL', 'MARKET', quantity, current_price)
|
||||
if result and 'orderId' in result:
|
||||
# Create short position record
|
||||
self.positions[symbol] = Position(
|
||||
symbol=symbol,
|
||||
@ -666,24 +452,88 @@ class TradingExecutor:
|
||||
quantity=quantity,
|
||||
entry_price=current_price,
|
||||
entry_time=datetime.now(),
|
||||
order_id=order.get('orderId', 'unknown')
|
||||
order_id=result['orderId']
|
||||
)
|
||||
|
||||
logger.info(f"SHORT order executed: {result}")
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
|
||||
logger.info(f"SHORT order executed: {order}")
|
||||
return True
|
||||
else:
|
||||
logger.error("Failed to place SHORT order")
|
||||
return False
|
||||
|
||||
def _place_order_with_retry(self, symbol: str, side: str, order_type: str, quantity: float, current_price: float, max_retries: int = 3) -> Dict[str, Any]:
|
||||
"""Place order with retry logic for MEXC error handling"""
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
result = self.exchange.place_order(symbol, side, order_type, quantity, current_price)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing SHORT order: {e}")
|
||||
return False
|
||||
# Check if result contains error information
|
||||
if isinstance(result, dict) and 'error' in result:
|
||||
error_type = result.get('error')
|
||||
error_code = result.get('code')
|
||||
error_msg = result.get('message', 'Unknown error')
|
||||
|
||||
if error_type == 'oversold' and error_code == 30005:
|
||||
logger.warning(f"MEXC Oversold error on attempt {attempt + 1}/{max_retries}")
|
||||
logger.warning(f"Error: {error_msg}")
|
||||
|
||||
if attempt < max_retries - 1:
|
||||
# Wait with exponential backoff
|
||||
wait_time = result.get('retry_after', 60) * (2 ** attempt)
|
||||
logger.info(f"Waiting {wait_time} seconds before retry due to oversold condition...")
|
||||
time.sleep(wait_time)
|
||||
|
||||
# Reduce quantity for next attempt to avoid oversold
|
||||
quantity = quantity * 0.8 # Reduce by 20%
|
||||
logger.info(f"Reducing quantity to {quantity:.6f} for retry")
|
||||
continue
|
||||
else:
|
||||
logger.error(f"Max retries reached for oversold condition")
|
||||
return {}
|
||||
|
||||
elif error_type == 'direction_not_allowed':
|
||||
logger.error(f"Trading direction not allowed for {symbol} {side}")
|
||||
return {}
|
||||
|
||||
elif error_type == 'insufficient_position':
|
||||
logger.error(f"Insufficient position for {symbol} {side}")
|
||||
return {}
|
||||
|
||||
else:
|
||||
logger.error(f"MEXC API error: {error_code} - {error_msg}")
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(5 * (attempt + 1)) # Wait 5, 10, 15 seconds
|
||||
continue
|
||||
else:
|
||||
return {}
|
||||
|
||||
# Success case
|
||||
elif isinstance(result, dict) and ('orderId' in result or 'symbol' in result):
|
||||
logger.info(f"Order placed successfully on attempt {attempt + 1}")
|
||||
return result
|
||||
|
||||
# Empty result - treat as failure
|
||||
else:
|
||||
logger.warning(f"Empty result on attempt {attempt + 1}/{max_retries}")
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(2 * (attempt + 1)) # Wait 2, 4, 6 seconds
|
||||
continue
|
||||
else:
|
||||
return {}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Exception on order attempt {attempt + 1}/{max_retries}: {e}")
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(3 * (attempt + 1)) # Wait 3, 6, 9 seconds
|
||||
continue
|
||||
else:
|
||||
return {}
|
||||
|
||||
logger.error(f"Failed to place order after {max_retries} attempts")
|
||||
return {}
|
||||
|
||||
def _close_short_position(self, symbol: str, confidence: float, current_price: float) -> bool:
|
||||
"""Close a short position by buying back"""
|
||||
"""Close a short position by buying"""
|
||||
if symbol not in self.positions:
|
||||
logger.warning(f"No position to close in {symbol}")
|
||||
return False
|
||||
@ -724,13 +574,21 @@ class TradingExecutor:
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
self.daily_loss += max(0, -pnl) # Add to daily loss if negative
|
||||
|
||||
# Update consecutive losses
|
||||
if pnl < -0.001: # A losing trade
|
||||
self.consecutive_losses += 1
|
||||
elif pnl > 0.001: # A winning trade
|
||||
self.consecutive_losses = 0
|
||||
else: # Breakeven trade
|
||||
self.consecutive_losses = 0
|
||||
|
||||
# Remove position
|
||||
del self.positions[symbol]
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
|
||||
logger.info(f"SHORT position closed - P&L: ${pnl:.2f}")
|
||||
logger.info(f"Position closed - P&L: ${pnl:.2f}")
|
||||
return True
|
||||
|
||||
try:
|
||||
@ -814,6 +672,147 @@ class TradingExecutor:
|
||||
except Exception as e:
|
||||
logger.error(f"Error closing SHORT position: {e}")
|
||||
return False
|
||||
|
||||
def _close_long_position(self, symbol: str, confidence: float, current_price: float) -> bool:
|
||||
"""Close a long position by selling"""
|
||||
if symbol not in self.positions:
|
||||
logger.warning(f"No position to close in {symbol}")
|
||||
return False
|
||||
|
||||
position = self.positions[symbol]
|
||||
if position.side != 'LONG':
|
||||
logger.warning(f"Position in {symbol} is not LONG, cannot close with SELL")
|
||||
return False
|
||||
|
||||
logger.info(f"Closing LONG position: {position.quantity:.6f} {symbol} at ${current_price:.2f} "
|
||||
f"(confidence: {confidence:.2f})")
|
||||
|
||||
if self.simulation_mode:
|
||||
logger.info(f"SIMULATION MODE ({self.trading_mode.upper()}) - Long close 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 = position.quantity * current_price * taker_fee_rate
|
||||
|
||||
# Calculate P&L for long 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(
|
||||
symbol=symbol,
|
||||
side='LONG',
|
||||
quantity=position.quantity,
|
||||
entry_price=position.entry_price,
|
||||
exit_price=current_price,
|
||||
entry_time=position.entry_time,
|
||||
exit_time=exit_time,
|
||||
pnl=pnl,
|
||||
fees=simulated_fees,
|
||||
confidence=confidence,
|
||||
hold_time_seconds=hold_time_seconds
|
||||
)
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
self.daily_loss += max(0, -pnl) # Add to daily loss if negative
|
||||
|
||||
# Update consecutive losses
|
||||
if pnl < -0.001: # A losing trade
|
||||
self.consecutive_losses += 1
|
||||
elif pnl > 0.001: # A winning trade
|
||||
self.consecutive_losses = 0
|
||||
else: # Breakeven trade
|
||||
self.consecutive_losses = 0
|
||||
|
||||
# Remove position
|
||||
del self.positions[symbol]
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
|
||||
logger.info(f"Position closed - P&L: ${pnl:.2f}")
|
||||
return True
|
||||
|
||||
try:
|
||||
# Get order type from config
|
||||
order_type = self.mexc_config.get('order_type', 'market').lower()
|
||||
|
||||
# For limit orders, set price slightly below market for immediate execution
|
||||
limit_price = None
|
||||
if order_type == 'limit':
|
||||
# Set sell price slightly below market to ensure immediate execution
|
||||
limit_price = current_price * 0.999 # 0.1% below market
|
||||
|
||||
# Place sell order to close long
|
||||
if order_type == 'market':
|
||||
order = self.exchange.place_order(
|
||||
symbol=symbol,
|
||||
side='sell', # Sell to close long position
|
||||
order_type=order_type,
|
||||
quantity=position.quantity
|
||||
)
|
||||
else:
|
||||
# For limit orders, price is required
|
||||
assert limit_price is not None, "limit_price required for limit orders"
|
||||
order = self.exchange.place_order(
|
||||
symbol=symbol,
|
||||
side='sell', # Sell to close long position
|
||||
order_type=order_type,
|
||||
quantity=position.quantity,
|
||||
price=limit_price
|
||||
)
|
||||
|
||||
if order:
|
||||
# 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 = simulated_fees
|
||||
exit_time = datetime.now()
|
||||
hold_time_seconds = (exit_time - position.entry_time).total_seconds()
|
||||
|
||||
# Create trade record
|
||||
trade_record = TradeRecord(
|
||||
symbol=symbol,
|
||||
side='LONG',
|
||||
quantity=position.quantity,
|
||||
entry_price=position.entry_price,
|
||||
exit_price=current_price,
|
||||
entry_time=position.entry_time,
|
||||
exit_time=exit_time,
|
||||
pnl=pnl - fees,
|
||||
fees=fees,
|
||||
confidence=confidence,
|
||||
hold_time_seconds=hold_time_seconds
|
||||
)
|
||||
|
||||
self.trade_history.append(trade_record)
|
||||
self.daily_loss += max(0, -(pnl - fees)) # Add to daily loss if negative
|
||||
|
||||
# Update consecutive losses
|
||||
if pnl < -0.001: # A losing trade
|
||||
self.consecutive_losses += 1
|
||||
elif pnl > 0.001: # A winning trade
|
||||
self.consecutive_losses = 0
|
||||
else: # Breakeven trade
|
||||
self.consecutive_losses = 0
|
||||
|
||||
# Remove position
|
||||
del self.positions[symbol]
|
||||
self.last_trade_time[symbol] = datetime.now()
|
||||
self.daily_trades += 1
|
||||
|
||||
logger.info(f"LONG close order executed: {order}")
|
||||
logger.info(f"LONG position closed - P&L: ${pnl - fees:.2f}")
|
||||
return True
|
||||
else:
|
||||
logger.error("Failed to place LONG close order")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error closing LONG position: {e}")
|
||||
return False
|
||||
|
||||
def _calculate_position_size(self, confidence: float, current_price: float) -> float:
|
||||
"""Calculate position size - use 100% of account balance for short-term scalping"""
|
||||
|
Reference in New Issue
Block a user