executor now can do shorty and long
This commit is contained in:
@ -158,7 +158,7 @@ class TradingExecutor:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def execute_signal(self, symbol: str, action: str, confidence: float,
|
def execute_signal(self, symbol: str, action: str, confidence: float,
|
||||||
current_price: float = None) -> bool:
|
current_price: Optional[float] = None) -> bool:
|
||||||
"""Execute a trading signal
|
"""Execute a trading signal
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -189,6 +189,9 @@ class TradingExecutor:
|
|||||||
return False
|
return False
|
||||||
current_price = ticker['last']
|
current_price = ticker['last']
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
try:
|
try:
|
||||||
if action == 'BUY':
|
if action == 'BUY':
|
||||||
@ -244,9 +247,14 @@ class TradingExecutor:
|
|||||||
|
|
||||||
def _execute_buy(self, symbol: str, confidence: float, current_price: float) -> bool:
|
def _execute_buy(self, symbol: str, confidence: float, current_price: float) -> bool:
|
||||||
"""Execute a buy order"""
|
"""Execute a buy order"""
|
||||||
# Check if we already have a position
|
# Check if we have a short position to close
|
||||||
if symbol in self.positions:
|
if symbol in self.positions:
|
||||||
logger.info(f"Already have position in {symbol}")
|
position = self.positions[symbol]
|
||||||
|
if position.side == 'SHORT':
|
||||||
|
logger.info(f"Closing SHORT position in {symbol}")
|
||||||
|
return self._close_short_position(symbol, confidence, current_price)
|
||||||
|
else:
|
||||||
|
logger.info(f"Already have LONG position in {symbol}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Calculate position size
|
# Calculate position size
|
||||||
@ -282,6 +290,16 @@ class TradingExecutor:
|
|||||||
limit_price = current_price * 1.001 # 0.1% above market
|
limit_price = current_price * 1.001 # 0.1% above market
|
||||||
|
|
||||||
# Place buy order
|
# 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(
|
order = self.exchange.place_order(
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
side='buy',
|
side='buy',
|
||||||
@ -319,8 +337,7 @@ class TradingExecutor:
|
|||||||
# Check if we have a position to sell
|
# Check if we have a position to sell
|
||||||
if symbol not in self.positions:
|
if symbol not in self.positions:
|
||||||
logger.info(f"No position to sell in {symbol}. Opening short position")
|
logger.info(f"No position to sell in {symbol}. Opening short position")
|
||||||
# TODO: Open short position
|
return self._execute_short(symbol, confidence, current_price)
|
||||||
|
|
||||||
|
|
||||||
position = self.positions[symbol]
|
position = self.positions[symbol]
|
||||||
|
|
||||||
@ -368,6 +385,16 @@ class TradingExecutor:
|
|||||||
limit_price = current_price * 0.999 # 0.1% below market
|
limit_price = current_price * 0.999 # 0.1% below market
|
||||||
|
|
||||||
# Place sell order
|
# 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(
|
order = self.exchange.place_order(
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
side='sell',
|
side='sell',
|
||||||
@ -414,6 +441,199 @@ class TradingExecutor:
|
|||||||
logger.error(f"Error executing SELL order: {e}")
|
logger.error(f"Error executing SELL order: {e}")
|
||||||
return False
|
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
|
||||||
|
|
||||||
|
logger.info(f"Executing SHORT: {quantity:.6f} {symbol} at ${current_price:.2f} "
|
||||||
|
f"(value: ${position_value:.2f}, confidence: {confidence:.2f})")
|
||||||
|
|
||||||
|
if self.simulation_mode:
|
||||||
|
logger.info(f"SIMULATION MODE ({self.trading_mode.upper()}) - Short position logged but not executed")
|
||||||
|
# Create mock short position for tracking
|
||||||
|
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())}"
|
||||||
|
)
|
||||||
|
self.last_trade_time[symbol] = datetime.now()
|
||||||
|
self.daily_trades += 1
|
||||||
|
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:
|
||||||
|
# Create short position record
|
||||||
|
self.positions[symbol] = Position(
|
||||||
|
symbol=symbol,
|
||||||
|
side='SHORT',
|
||||||
|
quantity=quantity,
|
||||||
|
entry_price=current_price,
|
||||||
|
entry_time=datetime.now(),
|
||||||
|
order_id=order.get('orderId', 'unknown')
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error executing SHORT order: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _close_short_position(self, symbol: str, confidence: float, current_price: float) -> bool:
|
||||||
|
"""Close a short position by buying back"""
|
||||||
|
if symbol not in self.positions:
|
||||||
|
logger.warning(f"No position to close in {symbol}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
position = self.positions[symbol]
|
||||||
|
if position.side != 'SHORT':
|
||||||
|
logger.warning(f"Position in {symbol} is not SHORT, cannot close with BUY")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"Closing SHORT 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()}) - Short close logged but not executed")
|
||||||
|
# Calculate P&L for short position
|
||||||
|
pnl = position.calculate_pnl(current_price)
|
||||||
|
|
||||||
|
# Create trade record
|
||||||
|
trade_record = TradeRecord(
|
||||||
|
symbol=symbol,
|
||||||
|
side='SHORT',
|
||||||
|
quantity=position.quantity,
|
||||||
|
entry_price=position.entry_price,
|
||||||
|
exit_price=current_price,
|
||||||
|
entry_time=position.entry_time,
|
||||||
|
exit_time=datetime.now(),
|
||||||
|
pnl=pnl,
|
||||||
|
fees=0.0,
|
||||||
|
confidence=confidence
|
||||||
|
)
|
||||||
|
|
||||||
|
self.trade_history.append(trade_record)
|
||||||
|
self.daily_loss += max(0, -pnl) # Add to daily loss if negative
|
||||||
|
|
||||||
|
# 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}")
|
||||||
|
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 to close short
|
||||||
|
if order_type == 'market':
|
||||||
|
order = self.exchange.place_order(
|
||||||
|
symbol=symbol,
|
||||||
|
side='buy', # Buy to close short 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='buy', # Buy to close short position
|
||||||
|
order_type=order_type,
|
||||||
|
quantity=position.quantity,
|
||||||
|
price=limit_price
|
||||||
|
)
|
||||||
|
|
||||||
|
if order:
|
||||||
|
# Calculate P&L
|
||||||
|
pnl = position.calculate_pnl(current_price)
|
||||||
|
fees = self._calculate_trading_fee(order, symbol, position.quantity, current_price)
|
||||||
|
|
||||||
|
# Create trade record
|
||||||
|
trade_record = TradeRecord(
|
||||||
|
symbol=symbol,
|
||||||
|
side='SHORT',
|
||||||
|
quantity=position.quantity,
|
||||||
|
entry_price=position.entry_price,
|
||||||
|
exit_price=current_price,
|
||||||
|
entry_time=position.entry_time,
|
||||||
|
exit_time=datetime.now(),
|
||||||
|
pnl=pnl - fees,
|
||||||
|
fees=fees,
|
||||||
|
confidence=confidence
|
||||||
|
)
|
||||||
|
|
||||||
|
self.trade_history.append(trade_record)
|
||||||
|
self.daily_loss += max(0, -(pnl - fees)) # Add to daily loss if negative
|
||||||
|
|
||||||
|
# Remove position
|
||||||
|
del self.positions[symbol]
|
||||||
|
self.last_trade_time[symbol] = datetime.now()
|
||||||
|
self.daily_trades += 1
|
||||||
|
|
||||||
|
logger.info(f"SHORT close order executed: {order}")
|
||||||
|
logger.info(f"SHORT position closed - P&L: ${pnl - fees:.2f}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.error("Failed to place SHORT close order")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error closing SHORT position: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def _calculate_position_size(self, confidence: float, current_price: float) -> float:
|
def _calculate_position_size(self, confidence: float, current_price: float) -> float:
|
||||||
"""Calculate position size based on configuration and confidence"""
|
"""Calculate position size based on configuration and confidence"""
|
||||||
max_value = self.mexc_config.get('max_position_value_usd', 1.0)
|
max_value = self.mexc_config.get('max_position_value_usd', 1.0)
|
||||||
@ -859,7 +1079,7 @@ class TradingExecutor:
|
|||||||
logger.error(f"Error getting closed trades: {e}")
|
logger.error(f"Error getting closed trades: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_current_position(self, symbol: str = None) -> Optional[Dict[str, Any]]:
|
def get_current_position(self, symbol: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
||||||
"""Get current position for a symbol or all positions
|
"""Get current position for a symbol or all positions
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
Reference in New Issue
Block a user