1
This commit is contained in:
parent
ef0d40dbd1
commit
8cb4ad9762
@ -696,12 +696,15 @@ class BaseExecutor(ABC):
|
||||
success = result.get('success', False)
|
||||
order_id = result.get('order_id', '')
|
||||
error_msg = result.get('error', result.get('message', ''))
|
||||
already_closed = result.get('already_closed', False)
|
||||
|
||||
if success:
|
||||
title = f"✅ [{target_key or self.platform_name}] 撤单成功 - {symbol}"
|
||||
title = f"✅ [{target_key or self.platform_name}] 撤单已完成 - {symbol}" if already_closed else f"✅ [{target_key or self.platform_name}] 撤单成功 - {symbol}"
|
||||
|
||||
content_parts = self._build_notification_header(symbol, account_id, target_key)
|
||||
self._append_notification_detail(content_parts, "订单ID", order_id)
|
||||
if already_closed:
|
||||
self._append_notification_detail(content_parts, "状态", "订单已不在挂单列表,按已完成处理")
|
||||
|
||||
if details and 'reason' in details:
|
||||
self._append_notification_detail(content_parts, "撤单原因", details['reason'])
|
||||
|
||||
@ -581,7 +581,22 @@ class BitgetLiveTradingService:
|
||||
if success:
|
||||
logger.info(f"✅ Bitget 单笔撤单成功: {symbol} #{order_id}")
|
||||
return {"success": True, "order_id": normalized_order_id, "symbol": symbol}
|
||||
return {"success": False, "order_id": str(order_id), "error": "cancel_order 返回 False"}
|
||||
|
||||
# 某些场景下,交易所会返回 False,但订单实际上已经成交/撤销/失效。
|
||||
# 为了避免误报,再做一次挂单列表确认;若已不在 open orders 中,视为幂等成功。
|
||||
refreshed_open_orders = self.get_open_orders(symbol)
|
||||
still_exists = any(str(o.get('order_id', '')) == normalized_order_id for o in refreshed_open_orders)
|
||||
if not still_exists:
|
||||
logger.info(f"ℹ️ Bitget 撤单返回 False,但订单已不在挂单列表,视为完成: {symbol} #{order_id}")
|
||||
return {
|
||||
"success": True,
|
||||
"order_id": normalized_order_id,
|
||||
"symbol": symbol,
|
||||
"already_closed": True,
|
||||
"message": "订单撤单请求后已不在挂单列表,可能已成交、已撤销或已失效",
|
||||
}
|
||||
|
||||
return {"success": False, "order_id": str(order_id), "error": "cancel_order 返回 False,且订单仍存在于挂单列表"}
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Bitget 单笔撤单失败: {symbol} #{order_id} {e}")
|
||||
return {"success": False, "order_id": str(order_id), "error": str(e)}
|
||||
|
||||
@ -250,6 +250,46 @@ class TestGetPositionForSymbol:
|
||||
assert pos['coin'] == 'ETH'
|
||||
|
||||
|
||||
class TestCancelOrder:
|
||||
|
||||
def test_cancel_order_returns_success_when_order_missing_before_cancel(self):
|
||||
service, mock_api = make_service()
|
||||
service.get_open_orders = MagicMock(return_value=[])
|
||||
|
||||
result = service.cancel_order('SOL', '123')
|
||||
|
||||
assert result['success'] is True
|
||||
assert result['already_closed'] is True
|
||||
mock_api.cancel_order.assert_not_called()
|
||||
|
||||
def test_cancel_order_returns_success_when_exchange_false_but_order_disappears(self):
|
||||
service, mock_api = make_service()
|
||||
service.get_open_orders = MagicMock(side_effect=[
|
||||
[{'order_id': '123', 'symbol': 'SOL'}],
|
||||
[],
|
||||
])
|
||||
mock_api.cancel_order.return_value = False
|
||||
|
||||
result = service.cancel_order('SOL', '123')
|
||||
|
||||
assert result['success'] is True
|
||||
assert result['already_closed'] is True
|
||||
assert '可能已成交' in result['message']
|
||||
|
||||
def test_cancel_order_returns_failure_when_exchange_false_and_order_still_exists(self):
|
||||
service, mock_api = make_service()
|
||||
service.get_open_orders = MagicMock(side_effect=[
|
||||
[{'order_id': '123', 'symbol': 'SOL'}],
|
||||
[{'order_id': '123', 'symbol': 'SOL'}],
|
||||
])
|
||||
mock_api.cancel_order.return_value = False
|
||||
|
||||
result = service.cancel_order('SOL', '123')
|
||||
|
||||
assert result['success'] is False
|
||||
assert '仍存在于挂单列表' in result['error']
|
||||
|
||||
|
||||
# ==================== TestPlaceMarketOrder ====================
|
||||
|
||||
class TestPlaceMarketOrder:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user