1
This commit is contained in:
parent
ef0d40dbd1
commit
8cb4ad9762
@ -696,12 +696,15 @@ class BaseExecutor(ABC):
|
|||||||
success = result.get('success', False)
|
success = result.get('success', False)
|
||||||
order_id = result.get('order_id', '')
|
order_id = result.get('order_id', '')
|
||||||
error_msg = result.get('error', result.get('message', ''))
|
error_msg = result.get('error', result.get('message', ''))
|
||||||
|
already_closed = result.get('already_closed', False)
|
||||||
|
|
||||||
if success:
|
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)
|
content_parts = self._build_notification_header(symbol, account_id, target_key)
|
||||||
self._append_notification_detail(content_parts, "订单ID", order_id)
|
self._append_notification_detail(content_parts, "订单ID", order_id)
|
||||||
|
if already_closed:
|
||||||
|
self._append_notification_detail(content_parts, "状态", "订单已不在挂单列表,按已完成处理")
|
||||||
|
|
||||||
if details and 'reason' in details:
|
if details and 'reason' in details:
|
||||||
self._append_notification_detail(content_parts, "撤单原因", details['reason'])
|
self._append_notification_detail(content_parts, "撤单原因", details['reason'])
|
||||||
|
|||||||
@ -581,7 +581,22 @@ class BitgetLiveTradingService:
|
|||||||
if success:
|
if success:
|
||||||
logger.info(f"✅ Bitget 单笔撤单成功: {symbol} #{order_id}")
|
logger.info(f"✅ Bitget 单笔撤单成功: {symbol} #{order_id}")
|
||||||
return {"success": True, "order_id": normalized_order_id, "symbol": symbol}
|
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:
|
except Exception as e:
|
||||||
logger.error(f"❌ Bitget 单笔撤单失败: {symbol} #{order_id} {e}")
|
logger.error(f"❌ Bitget 单笔撤单失败: {symbol} #{order_id} {e}")
|
||||||
return {"success": False, "order_id": str(order_id), "error": str(e)}
|
return {"success": False, "order_id": str(order_id), "error": str(e)}
|
||||||
|
|||||||
@ -250,6 +250,46 @@ class TestGetPositionForSymbol:
|
|||||||
assert pos['coin'] == 'ETH'
|
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 ====================
|
# ==================== TestPlaceMarketOrder ====================
|
||||||
|
|
||||||
class TestPlaceMarketOrder:
|
class TestPlaceMarketOrder:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user