From fa1f869eea32899758fc3456529872012fba8f7a Mon Sep 17 00:00:00 2001 From: ZhouYongyou <128128010+zhouyongyou@users.noreply.github.com> Date: Thu, 6 Nov 2025 00:45:02 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E5=8F=8C=E5=90=91?= =?UTF-8?q?=E6=8C=81=E4=BB=93=E9=98=B2=E5=BE=A1=E6=80=A7=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E8=AF=AF=E5=88=A0=E9=99=A4=E5=AF=B9?= =?UTF-8?q?=E5=90=91=E8=AE=A2=E5=8D=95=20=E5=9C=A8=E4=B8=8A=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E4=BF=AE=E5=A4=8D=EF=BC=88113a30f=EF=BC=89=E4=B8=AD?= =?UTF-8?q?=EF=BC=8C=E8=99=BD=E7=84=B6=E8=A7=A3=E5=86=B3=E4=BA=86=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E7=B4=AF=E7=A7=AF=E9=97=AE=E9=A2=98=EF=BC=8C=E4=BD=86?= =?UTF-8?q?=E5=BC=95=E5=85=A5=E4=BA=86=E6=96=B0=E7=9A=84=E9=A3=8E=E9=99=A9?= =?UTF-8?q?=EF=BC=9A=20=E5=A6=82=E6=9E=9C=E7=94=A8=E6=88=B7=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E6=8C=81=E6=9C=89=E5=90=8C=E4=B8=80symbol=E7=9A=84?= =?UTF-8?q?=E5=A4=9A=E7=A9=BA=E5=8F=8C=E5=90=91=E6=8C=81=E4=BB=93=EF=BC=8C?= =?UTF-8?q?update=5Fstop=5Floss/update=5Ftake=5Fprofit=20=E4=BC=9A?= =?UTF-8?q?=E8=AF=AF=E5=88=A0=E9=99=A4=E5=8F=A6=E4=B8=80=E6=96=B9=E5=90=91?= =?UTF-8?q?=E7=9A=84=E4=BF=9D=E6=8A=A4=E8=AE=A2=E5=8D=95=E3=80=82=20```=20?= =?UTF-8?q?=E5=81=87=E8=AE=BE=EF=BC=9A=20-=20BTCUSDT=20LONG=20=E6=8C=81?= =?UTF-8?q?=E4=BB=93=EF=BC=88=E6=AD=A2=E6=8D=9F=2095000=EF=BC=89=20-=20BTC?= =?UTF-8?q?USDT=20SHORT=20=E6=8C=81=E4=BB=93=EF=BC=88=E6=AD=A2=E6=8D=9F=20?= =?UTF-8?q?105000=EF=BC=89=20AI=20=E6=89=A7=E8=A1=8C=EF=BC=9Aupdate=5Fstop?= =?UTF-8?q?=5Floss=20for=20SHORT=20=E2=86=92=20CancelStopLossOrders("BTCUS?= =?UTF-8?q?DT")=20=E5=88=A0=E9=99=A4=E6=89=80=E6=9C=89=E6=AD=A2=E6=8D=9F?= =?UTF-8?q?=20=E2=86=92=20SetStopLoss("BTCUSDT",=20"SHORT",=20...)=20?= =?UTF-8?q?=E5=8F=AA=E8=AE=BE=E7=BD=AE=20SHORT=20=E6=AD=A2=E6=8D=9F=20?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=EF=BC=9A=20-=20SHORT=20=E6=AD=A2=E6=8D=9F?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E6=9B=B4=E6=96=B0=20=E2=9C=85=20-=20LONG=20?= =?UTF-8?q?=E6=AD=A2=E6=8D=9F=E8=A2=AB=E8=AF=AF=E5=88=A0=20=E2=9D=8C=20?= =?UTF-8?q?=E5=A4=B1=E5=8E=BB=E4=BF=9D=E6=8A=A4=EF=BC=81=20```=201.=20?= =?UTF-8?q?=E2=9C=85=20=E6=8A=80=E6=9C=AF=E6=94=AF=E6=8C=81=EF=BC=9ABinanc?= =?UTF-8?q?e=20=E8=AE=BE=E7=BD=AE=E4=B8=BA=E5=8F=8C=E5=90=91=E6=8C=81?= =?UTF-8?q?=E4=BB=93=E6=A8=A1=E5=BC=8F(Hedge=20Mode)=202.=20=E2=9D=8C=20?= =?UTF-8?q?=E7=AD=96=E7=95=A5=E7=A6=81=E6=AD=A2=EF=BC=9APrompt=20=E6=98=8E?= =?UTF-8?q?=E7=A1=AE=E7=A6=81=E6=AD=A2"=E5=AF=B9=E5=90=8C=E4=B8=80?= =?UTF-8?q?=E6=A0=87=E7=9A=84=E5=90=8C=E6=97=B6=E6=8C=81=E6=9C=89=E5=A4=9A?= =?UTF-8?q?=E7=A9=BA"=203.=20=E2=9D=8C=20=E4=BB=A3=E7=A0=81=E4=BF=9D?= =?UTF-8?q?=E6=8A=A4=EF=BC=9A=E5=BC=80=E4=BB=93=E6=97=B6=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E5=B7=B2=E6=9C=89=E5=90=8C=E5=90=91=E6=8C=81=E4=BB=93=E5=B9=B6?= =?UTF-8?q?=E6=8B=92=E7=BB=9D=20=E7=90=86=E8=AE=BA=E4=B8=8A=E4=B8=8D?= =?UTF-8?q?=E5=BA=94=E8=AF=A5=E5=87=BA=E7=8E=B0=E5=8F=8C=E5=90=91=E6=8C=81?= =?UTF-8?q?=E4=BB=93=EF=BC=8C=E4=BD=86=E4=BB=8D=E9=9C=80=E9=98=B2=E5=BE=A1?= =?UTF-8?q?=EF=BC=9A=20-=20=E7=94=A8=E6=88=B7=E6=89=8B=E5=8A=A8=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=20-=20=E5=B9=B6=E5=8F=91bug=20-=20=E9=81=97=E7=95=99?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=20=E5=9C=A8=20auto=5Ftrader.go=20=E7=9A=84?= =?UTF-8?q?=20update=5Fstop=5Floss/update=5Ftake=5Fprofit=20=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E4=B8=AD=EF=BC=9A=201.=20=E6=89=A7=E8=A1=8C=E5=89=8D?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E6=98=AF=E5=90=A6=E5=AD=98=E5=9C=A8=E5=AF=B9?= =?UTF-8?q?=E5=90=91=E6=8C=81=E4=BB=93=202.=20=E5=A6=82=E6=9E=9C=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E5=88=B0=E5=8F=8C=E5=90=91=E6=8C=81=E4=BB=93=EF=BC=9A?= =?UTF-8?q?=20=20=20=20-=20=E8=AE=B0=E5=BD=95=20=F0=9F=9A=A8=20=E4=B8=A5?= =?UTF-8?q?=E9=87=8D=E8=AD=A6=E5=91=8A=E6=97=A5=E5=BF=97=20=20=20=20-=20?= =?UTF-8?q?=E8=AF=B4=E6=98=8E=E8=BF=99=E8=BF=9D=E5=8F=8D=E7=AD=96=E7=95=A5?= =?UTF-8?q?=E8=A7=84=E5=88=99=20=20=20=20-=20=E6=8F=90=E7=A4=BA=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E7=9A=84=E5=8E=9F=E5=9B=A0=E5=92=8C=E5=BB=BA=E8=AE=AE?= =?UTF-8?q?=203.=20=E7=BB=A7=E7=BB=AD=E6=89=A7=E8=A1=8C=E5=BD=93=E5=89=8D?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=88=E5=9B=A0=E4=B8=BA=E7=AD=96=E7=95=A5?= =?UTF-8?q?=E6=9C=AC=E8=BA=AB=E7=A6=81=E6=AD=A2=E5=8F=8C=E5=90=91=E6=8C=81?= =?UTF-8?q?=E4=BB=93=EF=BC=89=20-=20executeUpdateStopLossWithRecord:=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=8C=E5=90=91=E6=8C=81=E4=BB=93=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=EF=BC=88=E7=AC=AC1175-1194=E8=A1=8C=EF=BC=89=20-=20ex?= =?UTF-8?q?ecuteUpdateTakeProfitWithRecord:=20=E6=B7=BB=E5=8A=A0=E5=8F=8C?= =?UTF-8?q?=E5=90=91=E6=8C=81=E4=BB=93=E6=A3=80=E6=B5=8B=EF=BC=88=E7=AC=AC?= =?UTF-8?q?1259-1278=E8=A1=8C=EF=BC=89=20```=20=F0=9F=9A=A8=20=E8=AD=A6?= =?UTF-8?q?=E5=91=8A=EF=BC=9A=E6=A3=80=E6=B5=8B=E5=88=B0=20BTCUSDT=20?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E5=8F=8C=E5=90=91=E6=8C=81=E4=BB=93=EF=BC=88?= =?UTF-8?q?SHORT=20+=20LONG=EF=BC=89=EF=BC=8C=E8=BF=99=E8=BF=9D=E5=8F=8D?= =?UTF-8?q?=E4=BA=86=E7=AD=96=E7=95=A5=E8=A7=84=E5=88=99=20=F0=9F=9A=A8=20?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=AD=A2=E6=8D=9F=E5=8D=95=E5=B0=86=E5=BD=B1?= =?UTF-8?q?=E5=93=8D=E4=B8=A4=E4=B8=AA=E6=96=B9=E5=90=91=E7=9A=84=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=EF=BC=8C=E8=AF=B7=E6=A3=80=E6=9F=A5=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E4=B8=BA=E7=94=A8=E6=88=B7=E6=89=8B=E5=8A=A8=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=20=F0=9F=9A=A8=20=E5=BB=BA=E8=AE=AE=EF=BC=9A?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=E5=B9=B3=E6=8E=89=E5=85=B6=E4=B8=AD=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E6=96=B9=E5=90=91=E7=9A=84=E6=8C=81=E4=BB=93=EF=BC=8C?= =?UTF-8?q?=E6=88=96=E6=A3=80=E6=9F=A5=E7=B3=BB=E7=BB=9F=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E6=9C=89BUG=20```=20-=20=E4=BC=9A=E5=BD=B1=E5=93=8D=E6=89=80?= =?UTF-8?q?=E6=9C=89=E5=AE=9E=E7=8E=B0=E7=B1=BB=EF=BC=88binance/aster/hype?= =?UTF-8?q?rliquid=EF=BC=89=20-=20=E5=A2=9E=E5=8A=A0=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E5=BA=A6=20-=20=E7=AD=96=E7=95=A5=E5=B7=B2=E7=A6=81=E6=AD=A2?= =?UTF-8?q?=E5=8F=8C=E5=90=91=E6=8C=81=E4=BB=93=EF=BC=8C=E5=B1=9E=E4=BA=8E?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=9C=BA=E6=99=AF=20-=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=BF=87=E4=BA=8E=E5=A4=8D=E6=9D=82=20-=20=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=AE=9E=E7=8E=B0=E8=AE=A2=E5=8D=95=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=20-=20=E7=AD=96=E7=95=A5=E7=A6=81?= =?UTF-8?q?=E6=AD=A2=E5=9C=BA=E6=99=AF=E4=B8=8D=E5=BA=94=E8=AF=A5=E5=87=BA?= =?UTF-8?q?=E7=8E=B0=20-=20=E2=9C=85=20=E6=9C=80=E5=B0=8F=E4=BE=B5?= =?UTF-8?q?=E5=85=A5=E6=80=A7=E4=BF=AE=E6=94=B9=20-=20=E2=9C=85=20?= =?UTF-8?q?=E5=8F=8A=E6=97=B6=E8=AD=A6=E5=91=8A=E5=BC=82=E5=B8=B8=E6=83=85?= =?UTF-8?q?=E5=86=B5=20-=20=E2=9C=85=20=E4=B8=8D=E5=BD=B1=E5=93=8D?= =?UTF-8?q?=E6=AD=A3=E5=B8=B8=E6=B5=81=E7=A8=8B=20-=20=E2=9C=85=20?= =?UTF-8?q?=E4=B8=BA=E8=B0=83=E8=AF=95=E6=8F=90=E4=BE=9B=E7=BA=BF=E7=B4=A2?= =?UTF-8?q?=20-=20=E6=AD=A3=E5=B8=B8=E4=BD=BF=E7=94=A8=EF=BC=88=E5=8D=95?= =?UTF-8?q?=E5=90=91=E6=8C=81=E4=BB=93=EF=BC=89=EF=BC=9A=E6=97=A0=E5=BD=B1?= =?UTF-8?q?=E5=93=8D=EF=BC=8C=E6=AD=A3=E5=B8=B8=E5=B7=A5=E4=BD=9C=20?= =?UTF-8?q?=E2=9C=85=20-=20=E5=BC=82=E5=B8=B8=E5=9C=BA=E6=99=AF=EF=BC=88?= =?UTF-8?q?=E5=8F=8C=E5=90=91=E6=8C=81=E4=BB=93=EF=BC=89=EF=BC=9A=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=AD=A6=E5=91=8A=EF=BC=8C=E6=8F=90=E7=A4=BA=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=A3=80=E6=9F=A5=20=E2=9A=A0=EF=B8=8F=20Related:=201?= =?UTF-8?q?13a30f=20(=E5=8E=9F=E5=A7=8B=E4=BF=AE=E5=A4=8D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- trader/auto_trader.go | 52 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/trader/auto_trader.go b/trader/auto_trader.go index 6e269a5b..54c1b1b7 100644 --- a/trader/auto_trader.go +++ b/trader/auto_trader.go @@ -991,8 +991,30 @@ func (at *AutoTrader) executeUpdateStopLossWithRecord(decision *decision.Decisio return fmt.Errorf("空单止损必须高于当前价格 (当前: %.2f, 新止损: %.2f)", marketData.CurrentPrice, decision.NewStopLoss) } - // 取消旧的止损单(避免多个止损单共存) - if err := at.trader.CancelStopOrders(decision.Symbol); err != nil { + // ⚠️ 防御性检查:检测是否存在双向持仓(不应该出现,但提供保护) + var hasOppositePosition bool + oppositeSide := "" + for _, pos := range positions { + symbol, _ := pos["symbol"].(string) + posSide, _ := pos["side"].(string) + posAmt, _ := pos["positionAmt"].(float64) + if symbol == decision.Symbol && posAmt != 0 && strings.ToUpper(posSide) != positionSide { + hasOppositePosition = true + oppositeSide = strings.ToUpper(posSide) + break + } + } + + if hasOppositePosition { + log.Printf(" 🚨 警告:检测到 %s 存在双向持仓(%s + %s),这违反了策略规则", + decision.Symbol, positionSide, oppositeSide) + log.Printf(" 🚨 取消止损单将影响两个方向的订单,请检查是否为用户手动操作导致") + log.Printf(" 🚨 建议:手动平掉其中一个方向的持仓,或检查系统是否有BUG") + } + + // 取消旧的止损单(只删除止损单,不影响止盈单) + // 注意:如果存在双向持仓,这会删除两个方向的止损单 + if err := at.trader.CancelStopLossOrders(decision.Symbol); err != nil { log.Printf(" ⚠ 取消旧止损单失败: %v", err) // 不中断执行,继续设置新止损 } @@ -1053,8 +1075,30 @@ func (at *AutoTrader) executeUpdateTakeProfitWithRecord(decision *decision.Decis return fmt.Errorf("空单止盈必须低于当前价格 (当前: %.2f, 新止盈: %.2f)", marketData.CurrentPrice, decision.NewTakeProfit) } - // 取消旧的止盈单(避免多个止盈单共存) - if err := at.trader.CancelStopOrders(decision.Symbol); err != nil { + // ⚠️ 防御性检查:检测是否存在双向持仓(不应该出现,但提供保护) + var hasOppositePosition bool + oppositeSide := "" + for _, pos := range positions { + symbol, _ := pos["symbol"].(string) + posSide, _ := pos["side"].(string) + posAmt, _ := pos["positionAmt"].(float64) + if symbol == decision.Symbol && posAmt != 0 && strings.ToUpper(posSide) != positionSide { + hasOppositePosition = true + oppositeSide = strings.ToUpper(posSide) + break + } + } + + if hasOppositePosition { + log.Printf(" 🚨 警告:检测到 %s 存在双向持仓(%s + %s),这违反了策略规则", + decision.Symbol, positionSide, oppositeSide) + log.Printf(" 🚨 取消止盈单将影响两个方向的订单,请检查是否为用户手动操作导致") + log.Printf(" 🚨 建议:手动平掉其中一个方向的持仓,或检查系统是否有BUG") + } + + // 取消旧的止盈单(只删除止盈单,不影响止损单) + // 注意:如果存在双向持仓,这会删除两个方向的止盈单 + if err := at.trader.CancelTakeProfitOrders(decision.Symbol); err != nil { log.Printf(" ⚠ 取消旧止盈单失败: %v", err) // 不中断执行,继续设置新止盈 }