import logging from typing import Dict, Optional from decimal import Decimal logger = logging.getLogger(__name__) class EntryStrategy: """Handles entry strategy with slippage optimization""" def __init__(self): self.max_slippage = 0.001 # 0.1% max slippage self.min_order_book_depth = 10000 # Minimum order book depth in USD async def calculate_entry_prices(self, exchange_manager, long_exchange: str, short_exchange: str, symbol: str, position_size: float) -> Optional[Dict]: """Calculate optimal entry prices considering slippage and order book depth""" try: # Get order books for both exchanges long_orderbook = await exchange_manager.get_orderbook(long_exchange, symbol, limit=20) short_orderbook = await exchange_manager.get_orderbook(short_exchange, symbol, limit=20) if not long_orderbook or not short_orderbook: logger.warning("Could not fetch order books, using ticker prices") return await self._calculate_from_ticker(exchange_manager, long_exchange, short_exchange, symbol) # Calculate prices based on order book depth long_price = self._calculate_price_from_orderbook( long_orderbook['asks'], position_size, 'buy' ) short_price = self._calculate_price_from_orderbook( short_orderbook['bids'], position_size, 'sell' ) # Check if slippage is acceptable long_ticker = await exchange_manager.get_ticker(long_exchange, symbol) short_ticker = await exchange_manager.get_ticker(short_exchange, symbol) if long_ticker and short_ticker: long_bid = long_ticker.get('bid') long_ask = long_ticker.get('ask') short_bid = short_ticker.get('bid') short_ask = short_ticker.get('ask') if all([long_bid, long_ask, short_bid, short_ask]): long_mid = (long_bid + long_ask) / 2 short_mid = (short_bid + short_ask) / 2 long_slippage = abs(long_price - long_mid) / long_mid if long_mid > 0 else 0 short_slippage = abs(short_price - short_mid) / short_mid if short_mid > 0 else 0 if long_slippage > self.max_slippage or short_slippage > self.max_slippage: logger.warning(f"Slippage too high: Long {long_slippage:.4%}, Short {short_slippage:.4%}") return None return { 'long_price': long_price, 'short_price': short_price, 'long_exchange': long_exchange, 'short_exchange': short_exchange, } except Exception as e: logger.error(f"Error calculating entry prices: {str(e)}") return await self._calculate_from_ticker(exchange_manager, long_exchange, short_exchange, symbol) def _calculate_price_from_orderbook(self, order_book_side: list, size: float, side: str) -> float: """Calculate average price from order book for given size""" total_cost = 0.0 remaining_size = size for order in order_book_side: price = order[0] available = order[1] if remaining_size <= 0: break if remaining_size <= available: total_cost += price * remaining_size remaining_size = 0 else: total_cost += price * available remaining_size -= available if remaining_size > 0: # Not enough liquidity, use last price if order_book_side: return order_book_side[-1][0] return 0 return total_cost / size if size > 0 else 0 async def _calculate_from_ticker(self, exchange_manager, long_exchange: str, short_exchange: str, symbol: str) -> Optional[Dict]: """Fallback to ticker prices if order book unavailable""" long_ticker = await exchange_manager.get_ticker(long_exchange, symbol) short_ticker = await exchange_manager.get_ticker(short_exchange, symbol) if not long_ticker or not short_ticker: return None # Use mid price, handle None values long_bid = long_ticker.get('bid') long_ask = long_ticker.get('ask') short_bid = short_ticker.get('bid') short_ask = short_ticker.get('ask') if not all([long_bid, long_ask, short_bid, short_ask]): return None long_price = (long_bid + long_ask) / 2 short_price = (short_bid + short_ask) / 2 return { 'long_price': long_price, 'short_price': short_price, 'long_exchange': long_exchange, 'short_exchange': short_exchange, }