fix: 修复 update_stop_loss/update_take_profit 未删除旧订单的BUG

## 问题描述
更新止损止盈时,旧订单没有被删除,导致订单累积。
用户看到多个止损/止盈订单同时存在(如截图所示有4个订单)。
## 根本原因
币安Futures采用双向持仓模式(Hedge Mode),每个symbol可以同时持有LONG和SHORT两个方向的仓位。
取消订单时:
- 创建订单时指定了 PositionSide(LONG/SHORT)
- 取消订单时未遍历所有订单,导致部分订单残留
## 修复内容
### 1. binance_futures.go
- CancelStopLossOrders: 取消所有方向(LONG+SHORT)的止损订单
- CancelTakeProfitOrders: 取消所有方向(LONG+SHORT)的止盈订单
- 添加错误收集机制,记录每个失败的订单
- 增强日志输出,显示订单方向(PositionSide)
- 仅当所有取消都失败时才返回错误
### 2. aster_trader.go
- 同步应用相同的修复逻辑
- 保持多交易所一致性
## 预期效果
- 更新止损时,所有旧止损订单被删除
- 更新止盈时,所有旧止盈订单被删除
- 不会出现订单累积问题
- 更详细的日志输出,方便排查问题
## 测试建议
1. 在双向持仓模式下测试 update_stop_loss
2. 验证旧订单是否全部删除
3. 检查日志中的 positionSide 输出
Related: 用户反馈截图显示4个订单同时存在
This commit is contained in:
ZhouYongyou
2025-11-05 04:03:20 +08:00
parent 0d8b749a2c
commit 7db9e42759
2 changed files with 54 additions and 20 deletions
+28 -10
View File
@@ -1029,14 +1029,16 @@ func (t *AsterTrader) CancelStopLossOrders(symbol string) error {
return fmt.Errorf("解析订单数据失败: %w", err)
}
// 过滤出止损单并取消
// 过滤出止损单并取消(取消所有方向的止损单,包括LONG和SHORT)
canceledCount := 0
var cancelErrors []error
for _, order := range orders {
orderType, _ := order["type"].(string)
// 只取消止损订单(不取消止盈订单)
if orderType == "STOP_MARKET" || orderType == "STOP" {
orderID, _ := order["orderId"].(float64)
positionSide, _ := order["positionSide"].(string)
cancelParams := map[string]interface{}{
"symbol": symbol,
"orderId": int64(orderID),
@@ -1044,21 +1046,28 @@ func (t *AsterTrader) CancelStopLossOrders(symbol string) error {
_, err := t.request("DELETE", "/fapi/v1/order", cancelParams)
if err != nil {
log.Printf(" ⚠ 取消止损单 %d 失败: %v", int64(orderID), err)
errMsg := fmt.Sprintf("订单ID %d: %v", int64(orderID), err)
cancelErrors = append(cancelErrors, fmt.Errorf(errMsg))
log.Printf(" ⚠ 取消止损单失败: %s", errMsg)
continue
}
canceledCount++
log.Printf(" ✓ 已取消止损单 (订单ID: %d, 类型: %s)", int64(orderID), orderType)
log.Printf(" ✓ 已取消止损单 (订单ID: %d, 类型: %s, 方向: %s)", int64(orderID), orderType, positionSide)
}
}
if canceledCount == 0 {
if canceledCount == 0 && len(cancelErrors) == 0 {
log.Printf(" %s 没有止损单需要取消", symbol)
} else {
} else if canceledCount > 0 {
log.Printf(" ✓ 已取消 %s 的 %d 个止损单", symbol, canceledCount)
}
// 如果所有取消都失败了,返回错误
if len(cancelErrors) > 0 && canceledCount == 0 {
return fmt.Errorf("取消止损单失败: %v", cancelErrors)
}
return nil
}
@@ -1079,14 +1088,16 @@ func (t *AsterTrader) CancelTakeProfitOrders(symbol string) error {
return fmt.Errorf("解析订单数据失败: %w", err)
}
// 过滤出止盈单并取消
// 过滤出止盈单并取消(取消所有方向的止盈单,包括LONG和SHORT)
canceledCount := 0
var cancelErrors []error
for _, order := range orders {
orderType, _ := order["type"].(string)
// 只取消止盈订单(不取消止损订单)
if orderType == "TAKE_PROFIT_MARKET" || orderType == "TAKE_PROFIT" {
orderID, _ := order["orderId"].(float64)
positionSide, _ := order["positionSide"].(string)
cancelParams := map[string]interface{}{
"symbol": symbol,
"orderId": int64(orderID),
@@ -1094,21 +1105,28 @@ func (t *AsterTrader) CancelTakeProfitOrders(symbol string) error {
_, err := t.request("DELETE", "/fapi/v1/order", cancelParams)
if err != nil {
log.Printf(" ⚠ 取消止盈单 %d 失败: %v", int64(orderID), err)
errMsg := fmt.Sprintf("订单ID %d: %v", int64(orderID), err)
cancelErrors = append(cancelErrors, fmt.Errorf(errMsg))
log.Printf(" ⚠ 取消止盈单失败: %s", errMsg)
continue
}
canceledCount++
log.Printf(" ✓ 已取消止盈单 (订单ID: %d, 类型: %s)", int64(orderID), orderType)
log.Printf(" ✓ 已取消止盈单 (订单ID: %d, 类型: %s, 方向: %s)", int64(orderID), orderType, positionSide)
}
}
if canceledCount == 0 {
if canceledCount == 0 && len(cancelErrors) == 0 {
log.Printf(" %s 没有止盈单需要取消", symbol)
} else {
} else if canceledCount > 0 {
log.Printf(" ✓ 已取消 %s 的 %d 个止盈单", symbol, canceledCount)
}
// 如果所有取消都失败了,返回错误
if len(cancelErrors) > 0 && canceledCount == 0 {
return fmt.Errorf("取消止盈单失败: %v", cancelErrors)
}
return nil
}
+26 -10
View File
@@ -504,8 +504,9 @@ func (t *FuturesTrader) CancelStopLossOrders(symbol string) error {
return fmt.Errorf("获取未完成订单失败: %w", err)
}
// 过滤出止损单并取消
// 过滤出止损单并取消(取消所有方向的止损单,包括LONG和SHORT)
canceledCount := 0
var cancelErrors []error
for _, order := range orders {
orderType := order.Type
@@ -517,21 +518,28 @@ func (t *FuturesTrader) CancelStopLossOrders(symbol string) error {
Do(context.Background())
if err != nil {
log.Printf(" ⚠ 取消止损单 %d 失败: %v", order.OrderID, err)
errMsg := fmt.Sprintf("订单ID %d: %v", order.OrderID, err)
cancelErrors = append(cancelErrors, fmt.Errorf(errMsg))
log.Printf(" ⚠ 取消止损单失败: %s", errMsg)
continue
}
canceledCount++
log.Printf(" ✓ 已取消止损单 (订单ID: %d, 类型: %s)", order.OrderID, orderType)
log.Printf(" ✓ 已取消止损单 (订单ID: %d, 类型: %s, 方向: %s)", order.OrderID, orderType, order.PositionSide)
}
}
if canceledCount == 0 {
if canceledCount == 0 && len(cancelErrors) == 0 {
log.Printf(" %s 没有止损单需要取消", symbol)
} else {
} else if canceledCount > 0 {
log.Printf(" ✓ 已取消 %s 的 %d 个止损单", symbol, canceledCount)
}
// 如果所有取消都失败了,返回错误
if len(cancelErrors) > 0 && canceledCount == 0 {
return fmt.Errorf("取消止损单失败: %v", cancelErrors)
}
return nil
}
@@ -546,8 +554,9 @@ func (t *FuturesTrader) CancelTakeProfitOrders(symbol string) error {
return fmt.Errorf("获取未完成订单失败: %w", err)
}
// 过滤出止盈单并取消
// 过滤出止盈单并取消(取消所有方向的止盈单,包括LONG和SHORT)
canceledCount := 0
var cancelErrors []error
for _, order := range orders {
orderType := order.Type
@@ -559,21 +568,28 @@ func (t *FuturesTrader) CancelTakeProfitOrders(symbol string) error {
Do(context.Background())
if err != nil {
log.Printf(" ⚠ 取消止盈单 %d 失败: %v", order.OrderID, err)
errMsg := fmt.Sprintf("订单ID %d: %v", order.OrderID, err)
cancelErrors = append(cancelErrors, fmt.Errorf(errMsg))
log.Printf(" ⚠ 取消止盈单失败: %s", errMsg)
continue
}
canceledCount++
log.Printf(" ✓ 已取消止盈单 (订单ID: %d, 类型: %s)", order.OrderID, orderType)
log.Printf(" ✓ 已取消止盈单 (订单ID: %d, 类型: %s, 方向: %s)", order.OrderID, orderType, order.PositionSide)
}
}
if canceledCount == 0 {
if canceledCount == 0 && len(cancelErrors) == 0 {
log.Printf(" %s 没有止盈单需要取消", symbol)
} else {
} else if canceledCount > 0 {
log.Printf(" ✓ 已取消 %s 的 %d 个止盈单", symbol, canceledCount)
}
// 如果所有取消都失败了,返回错误
if len(cancelErrors) > 0 && canceledCount == 0 {
return fmt.Errorf("取消止盈单失败: %v", cancelErrors)
}
return nil
}