From 7db9e42759b89b133b8889d54150edb744f7f31b Mon Sep 17 00:00:00 2001 From: ZhouYongyou <128128010+zhouyongyou@users.noreply.github.com> Date: Wed, 5 Nov 2025 04:03:20 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20update=5Fstop=5Flos?= =?UTF-8?q?s/update=5Ftake=5Fprofit=20=E6=9C=AA=E5=88=A0=E9=99=A4=E6=97=A7?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=9A=84BUG=20##=20=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=20=E6=9B=B4=E6=96=B0=E6=AD=A2=E6=8D=9F?= =?UTF-8?q?=E6=AD=A2=E7=9B=88=E6=97=B6=EF=BC=8C=E6=97=A7=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=A2=AB=E5=88=A0=E9=99=A4=EF=BC=8C=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E8=AE=A2=E5=8D=95=E7=B4=AF=E7=A7=AF=E3=80=82=20?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=9C=8B=E5=88=B0=E5=A4=9A=E4=B8=AA=E6=AD=A2?= =?UTF-8?q?=E6=8D=9F/=E6=AD=A2=E7=9B=88=E8=AE=A2=E5=8D=95=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E5=AD=98=E5=9C=A8=EF=BC=88=E5=A6=82=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E6=89=80=E7=A4=BA=E6=9C=894=E4=B8=AA=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=EF=BC=89=E3=80=82=20##=20=E6=A0=B9=E6=9C=AC=E5=8E=9F=E5=9B=A0?= =?UTF-8?q?=20=E5=B8=81=E5=AE=89Futures=E9=87=87=E7=94=A8=E5=8F=8C?= =?UTF-8?q?=E5=90=91=E6=8C=81=E4=BB=93=E6=A8=A1=E5=BC=8F(Hedge=20Mode)?= =?UTF-8?q?=EF=BC=8C=E6=AF=8F=E4=B8=AAsymbol=E5=8F=AF=E4=BB=A5=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E6=8C=81=E6=9C=89LONG=E5=92=8CSHORT=E4=B8=A4=E4=B8=AA?= =?UTF-8?q?=E6=96=B9=E5=90=91=E7=9A=84=E4=BB=93=E4=BD=8D=E3=80=82=20?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E8=AE=A2=E5=8D=95=E6=97=B6=EF=BC=9A=20-=20?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E8=AE=A2=E5=8D=95=E6=97=B6=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E4=BA=86=20PositionSide=EF=BC=88LONG/SHORT=EF=BC=89=20-=20?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E8=AE=A2=E5=8D=95=E6=97=B6=E6=9C=AA=E9=81=8D?= =?UTF-8?q?=E5=8E=86=E6=89=80=E6=9C=89=E8=AE=A2=E5=8D=95=EF=BC=8C=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E9=83=A8=E5=88=86=E8=AE=A2=E5=8D=95=E6=AE=8B=E7=95=99?= =?UTF-8?q?=20##=20=E4=BF=AE=E5=A4=8D=E5=86=85=E5=AE=B9=20###=201.=20binan?= =?UTF-8?q?ce=5Ffutures.go=20-=20CancelStopLossOrders:=20=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E6=89=80=E6=9C=89=E6=96=B9=E5=90=91=EF=BC=88LONG+SHOR?= =?UTF-8?q?T=EF=BC=89=E7=9A=84=E6=AD=A2=E6=8D=9F=E8=AE=A2=E5=8D=95=20-=20C?= =?UTF-8?q?ancelTakeProfitOrders:=20=E5=8F=96=E6=B6=88=E6=89=80=E6=9C=89?= =?UTF-8?q?=E6=96=B9=E5=90=91=EF=BC=88LONG+SHORT=EF=BC=89=E7=9A=84?= =?UTF-8?q?=E6=AD=A2=E7=9B=88=E8=AE=A2=E5=8D=95=20-=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=94=B6=E9=9B=86=E6=9C=BA=E5=88=B6=EF=BC=8C?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=AF=8F=E4=B8=AA=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=20-=20=E5=A2=9E=E5=BC=BA=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=BE=93=E5=87=BA=EF=BC=8C=E6=98=BE=E7=A4=BA=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=96=B9=E5=90=91(PositionSide)=20-=20=E4=BB=85=E5=BD=93?= =?UTF-8?q?=E6=89=80=E6=9C=89=E5=8F=96=E6=B6=88=E9=83=BD=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E6=97=B6=E6=89=8D=E8=BF=94=E5=9B=9E=E9=94=99=E8=AF=AF=20###=20?= =?UTF-8?q?2.=20aster=5Ftrader.go=20-=20=E5=90=8C=E6=AD=A5=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E7=9B=B8=E5=90=8C=E7=9A=84=E4=BF=AE=E5=A4=8D=E9=80=BB?= =?UTF-8?q?=E8=BE=91=20-=20=E4=BF=9D=E6=8C=81=E5=A4=9A=E4=BA=A4=E6=98=93?= =?UTF-8?q?=E6=89=80=E4=B8=80=E8=87=B4=E6=80=A7=20##=20=E9=A2=84=E6=9C=9F?= =?UTF-8?q?=E6=95=88=E6=9E=9C=20-=20=E6=9B=B4=E6=96=B0=E6=AD=A2=E6=8D=9F?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E6=89=80=E6=9C=89=E6=97=A7=E6=AD=A2=E6=8D=9F?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E8=A2=AB=E5=88=A0=E9=99=A4=20-=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=AD=A2=E7=9B=88=E6=97=B6=EF=BC=8C=E6=89=80=E6=9C=89?= =?UTF-8?q?=E6=97=A7=E6=AD=A2=E7=9B=88=E8=AE=A2=E5=8D=95=E8=A2=AB=E5=88=A0?= =?UTF-8?q?=E9=99=A4=20-=20=E4=B8=8D=E4=BC=9A=E5=87=BA=E7=8E=B0=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E7=B4=AF=E7=A7=AF=E9=97=AE=E9=A2=98=20-=20=E6=9B=B4?= =?UTF-8?q?=E8=AF=A6=E7=BB=86=E7=9A=84=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= =?UTF-8?q?=EF=BC=8C=E6=96=B9=E4=BE=BF=E6=8E=92=E6=9F=A5=E9=97=AE=E9=A2=98?= =?UTF-8?q?=20##=20=E6=B5=8B=E8=AF=95=E5=BB=BA=E8=AE=AE=201.=20=E5=9C=A8?= =?UTF-8?q?=E5=8F=8C=E5=90=91=E6=8C=81=E4=BB=93=E6=A8=A1=E5=BC=8F=E4=B8=8B?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=20update=5Fstop=5Floss=202.=20=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E6=97=A7=E8=AE=A2=E5=8D=95=E6=98=AF=E5=90=A6=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E5=88=A0=E9=99=A4=203.=20=E6=A3=80=E6=9F=A5=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E4=B8=AD=E7=9A=84=20positionSide=20=E8=BE=93=E5=87=BA?= =?UTF-8?q?=20Related:=20=E7=94=A8=E6=88=B7=E5=8F=8D=E9=A6=88=E6=88=AA?= =?UTF-8?q?=E5=9B=BE=E6=98=BE=E7=A4=BA4=E4=B8=AA=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=AD=98=E5=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- trader/aster_trader.go | 38 ++++++++++++++++++++++++++++---------- trader/binance_futures.go | 36 ++++++++++++++++++++++++++---------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/trader/aster_trader.go b/trader/aster_trader.go index a4a5a41f..bd3483c7 100644 --- a/trader/aster_trader.go +++ b/trader/aster_trader.go @@ -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 } diff --git a/trader/binance_futures.go b/trader/binance_futures.go index e8e8f083..243bada5 100644 --- a/trader/binance_futures.go +++ b/trader/binance_futures.go @@ -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 }