Enhance SL/TP calculation and order handling in LiveRegimeStrategy and OKXClient

- Updated `calculate_sl_tp` method to handle invalid entry prices and sides, returning (None, None) when necessary.
- Improved logging for SL/TP values in `LiveTradingBot` to display "N/A" for invalid values.
- Refined order placement in `OKXClient` to ensure guaranteed fill price retrieval, with fallback mechanisms for fetching order details and ticker prices if needed.
- Added error handling for scenarios where fill prices cannot be determined.
This commit is contained in:
2026-01-16 13:54:26 +08:00
parent 62c470b3de
commit 35992ee374
3 changed files with 75 additions and 22 deletions

View File

@@ -153,7 +153,7 @@ class OKXClient:
reduce_only: bool = False
) -> dict:
"""
Place a market order.
Place a market order and fetch the fill price.
Args:
symbol: Trading pair symbol
@@ -162,7 +162,10 @@ class OKXClient:
reduce_only: If True, only reduce existing position
Returns:
Order result dictionary
Order result dictionary with guaranteed 'average' fill price
Raises:
RuntimeError: If order placement fails or fill price unavailable
"""
params = {
'tdMode': self.trading_config.margin_mode,
@@ -173,10 +176,48 @@ class OKXClient:
order = self.exchange.create_market_order(
symbol, side, amount, params=params
)
order_id = order.get('id')
if not order_id:
raise RuntimeError(f"Order placement failed: no order ID returned")
logger.info(
f"Market {side.upper()} order placed: {amount} {symbol} "
f"@ market price, order_id={order['id']}"
f"@ market price, order_id={order_id}"
)
# Fetch order to get actual fill price if not in initial response
fill_price = order.get('average')
if fill_price is None or fill_price == 0:
logger.info(f"Fetching order {order_id} for fill price...")
try:
fetched_order = self.exchange.fetch_order(order_id, symbol)
fill_price = fetched_order.get('average')
order['average'] = fill_price
order['filled'] = fetched_order.get('filled', order.get('filled'))
order['status'] = fetched_order.get('status', order.get('status'))
except Exception as e:
logger.warning(f"Could not fetch order details: {e}")
# Final fallback: use current ticker price
if fill_price is None or fill_price == 0:
logger.warning(
f"No fill price from order response, fetching ticker..."
)
try:
ticker = self.get_ticker(symbol)
fill_price = ticker.get('last')
order['average'] = fill_price
except Exception as e:
logger.error(f"Could not fetch ticker: {e}")
if fill_price is None or fill_price <= 0:
raise RuntimeError(
f"Could not determine fill price for order {order_id}. "
f"Order response: {order}"
)
logger.info(f"Order {order_id} filled at {fill_price}")
return order
def place_limit_order(