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 e674eeb19e
commit 113a30f007
2 changed files with 54 additions and 20 deletions

View File

@@ -1068,14 +1068,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),
@@ -1083,21 +1085,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
}
@@ -1118,14 +1127,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),
@@ -1133,21 +1144,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
}

View File

@@ -792,8 +792,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
@@ -805,21 +806,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
}
@@ -834,8 +842,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
@@ -847,21 +856,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
}