From ffb12cdb64cff1291db7fdc5389d8476aa2081c3 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 11 Mar 2025 15:51:40 +0800 Subject: [PATCH] 1 --- app/admin/service/dto/line_pre_order.go | 11 ++-- app/admin/service/line_pre_order.go | 73 ++++++++++++++++--------- pkg/utility/decimalhelper.go | 13 +++++ services/binanceservice/futuresrest.go | 6 +- services/scriptservice/order.go | 15 +++-- 5 files changed, 80 insertions(+), 38 deletions(-) diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 4166c36..fbcbf25 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -437,13 +437,15 @@ func (req LineBatchAddPreOrderReq) CheckParams() error { return errors.New("止盈价格不正确") } - if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 && + v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && + (v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) { return errors.New("止盈后止盈价格不正确") } - if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { - return errors.New("止盈后止损价格不正确") - } + // if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 { + // return errors.New("止盈后止损价格不正确") + // } } return nil @@ -582,6 +584,7 @@ type ClosePosition struct { Rate string `json:"rate"` //限价平仓百分比 CloseType int `json:"close_type"` //现货平仓=1 合约平仓=2 ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type + Side string `json:"side"` //SELL=卖出(空) BUY=买入(多) } func (m *ClosePosition) CheckParams() error { diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 96ebcc9..c771b50 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -443,8 +443,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP if req.PricePattern == "percentage" { AddOrder.Rate = req.Price - orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10% - priceRate := orderPrice.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 + orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10% + priceRate := utility.SafeDiv(orderPrice, (decimal.NewFromInt(100))) //下单价除100 =0.1 if strings.ToUpper(req.Site) == "BUY" { //购买方向 //实际下单价格 @@ -486,12 +486,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365 //在换算成对应交易对对应的价值 //LTCBTC --> LTCUSDT == LTCUSDT -- 100.502 - div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) + div := utility.SafeDiv(utility.SafeDiv(tickerPrice, decimal.NewFromInt(1)), uTickerPrice) //计算下单数量 - AddOrder.Num = buyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String() + AddOrder.Num = utility.SafeDiv(buyPrice, div).Truncate(int32(tradeSet.AmountDigit)).String() } else { fromString, _ := decimal.NewFromString(AddOrder.Price) - AddOrder.Num = buyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String() + AddOrder.Num = utility.SafeDiv(buyPrice, fromString).Truncate(int32(tradeSet.AmountDigit)).String() } if utility.StringToFloat64(AddOrder.Num) < tradeSet.MinQty { *errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 小于最小下单数量", id, req.Symbol)) @@ -534,11 +534,14 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP StopLossRatio: req.ReduceStopLossRatio, } mainPrice := utility.StringToDecimal(AddOrder.Price) - mainAmount := buyPrice.Div(mainPrice) + mainAmount := utility.SafeDiv(buyPrice, mainPrice) defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit)) defultExt2.TotalBefore = defultExt.TotalAfter - defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) - defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2) + + // if decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Cmp(decimal.Zero) > 0 { + defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(utility.SafeDiv(req.ReduceNumRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit)) + defultExt2.ReTakeRatio = utility.SafeDiv(req.ReducePriceRatio, utility.SafeDiv(decimal.NewFromInt(100).Sub(req.ReduceNumRatio), decimal.NewFromInt(100))).Truncate(2) + preOrderExts = append(preOrderExts, defultExt) preOrderExts = append(preOrderExts, defultExt2) @@ -644,9 +647,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP profitOrder.MainId = AddOrder.Id if req.ProfitNumRatio.Cmp(decimal.Zero) > 0 { - numPercent := req.ProfitNumRatio.Div(decimal.NewFromInt(100)) + numPercent := utility.SafeDiv(req.ProfitNumRatio, decimal.NewFromInt(100)) profitOrder.Num = utility.StrToDecimal(profitOrder.Num).Mul(numPercent).Truncate(int32(tradeSet.AmountDigit)).String() - } tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&profitOrder) @@ -668,10 +670,10 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP if req.ReducePriceRatio.Cmp(decimal.Zero) > 0 { if strings.ToUpper(req.Site) == "BUY" { stopOrder.Site = "SELL" - stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() } else { stopOrder.Site = "BUY" - stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() + stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(req.ReducePriceRatio, decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() } stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) stopOrder.Pid = AddOrder.Id @@ -807,12 +809,12 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx data.Price = dataPrice.String() if v.AddPositionType == 1 { - buyPrice := utility.StrToDecimal(preOrder.BuyPrice).Mul(v.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(2) - data.Num = buyPrice.Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() + buyPrice := utility.StrToDecimal(preOrder.BuyPrice).Mul(utility.SafeDiv(v.AddPositionVal, decimal.NewFromInt(100))).Truncate(2) + data.Num = utility.SafeDiv(buyPrice, dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() data.BuyPrice = buyPrice.String() } else { data.BuyPrice = v.AddPositionVal.Truncate(2).String() - data.Num = v.AddPositionVal.Truncate(2).Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() + data.Num = utility.SafeDiv(v.AddPositionVal.Truncate(2), dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() } return data @@ -886,7 +888,7 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO if ext.TakeProfitNumRatio.Cmp(decimal.Zero) <= 0 || ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) >= 0 { profitOrder.Num = ext.TotalAfter.Truncate(int32(tradeSet.AmountDigit)).String() } else { - profitOrder.Num = ext.TotalAfter.Mul(ext.TakeProfitNumRatio).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)).String() + profitOrder.Num = ext.TotalAfter.Mul(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() } // 止盈需要累加之前的亏损 @@ -969,7 +971,7 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line } if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { - percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100))) + percent := decimal.NewFromInt(1).Sub(utility.SafeDiv(ext.TakeProfitNumRatio, decimal.NewFromInt(100))) num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit)) } @@ -1473,7 +1475,7 @@ func (e *LinePreOrder) GetFutSpotOrderInfo(req dto.ManuallyCover, symbol, orderT func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPrice decimal.Decimal, amountDigit int, notUsdt bool, symbolInfo models.LineSymbol) (decimal.Decimal, error) { var amt decimal.Decimal if req.CoverType == 1 { - decimalValue := utility.StringToDecimal(req.Value).Div(decimal.NewFromInt(100)) + decimalValue := utility.SafeDiv(utility.StringToDecimal(req.Value), decimal.NewFromInt(100)) amt = totalNum.Mul(decimalValue) } else { decimalValue := utility.StringToDecimal(req.Value) @@ -1495,8 +1497,8 @@ func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPric for _, symbolMap := range tickerSymbolMaps { if symbolMap.Symbol == strings.ToUpper(symbolInfo.QuoteAsset+"USDT") { uTickerPrice, _ := decimal.NewFromString(symbolMap.Price) - div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice)) - amt = decimalValue.Div(div) + div := utility.SafeDiv(tickerPrice, decimal.NewFromInt(1).Div(uTickerPrice)) + amt = utility.SafeDiv(decimalValue, div) break } } @@ -1571,7 +1573,7 @@ func (e *LinePreOrder) SpotClosePosition(position *dto.ClosePosition, errs *[]er "newClientOrderId": utility.Int64ToString(snowflakehelper.GetOrderId()), "timeInForce": "GTC", } - price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) paramsMaps["price"] = price.String() } else { *errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 下单数量小于最小下单数量", position.ApiId))) @@ -1630,9 +1632,27 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err //查询已经开仓的合约交易对 var futList []models.LinePreOrder if position.Symbol == "" { - e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId).Find(&futList) + query := e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId) + + switch strings.ToUpper(position.Side) { + case "BUY": + query = query.Where("site = 'BUY'") + case "SELL": + query = query.Where("site = 'SELL'") + } + + query.Find(&futList) } else { - e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol).Find(&futList) + query := e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol) + + switch strings.ToUpper(position.Side) { + case "BUY": + query = query.Where("side = 'BUY'") + case "SELL": + query = query.Where("side = 'SELL'") + } + + query.Find(&futList) } if len(futList) <= 0 { *errs = append(*errs, fmt.Errorf("api_id:%d 没有可平仓的交易对", position.ApiId)) @@ -1677,10 +1697,10 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err if list.Site == "BUY" && risk.PositionSide == "LONG" { //做多 //根据仓位数量去平多 orderSide = "SELL" - price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) } else if list.Site == "SELL" && risk.PositionSide == "SHORT" { orderSide = "BUY" - price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) + price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.SafeDiv(utility.StringToDecimal(position.Rate), decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) } if price.LessThanOrEqual(decimal.Zero) { @@ -1726,6 +1746,9 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err //撤销合约的委托(根据方向撤) orderSns, _ := binanceservice.GetOpenOrderSns(e.Orm, []int{list.Id}) + if len(orderSns) > 0 { + logger.Infof("平仓 取消 订单id :%v 订单号: %v", orderSns) + } api.CancelBatchFutOrder(apiUserInfo, list.Symbol, orderSns) // api.CancelAllFutOrder(apiUserInfo, list.Symbol) //side=BUY&positionSide=LONG是开多, diff --git a/pkg/utility/decimalhelper.go b/pkg/utility/decimalhelper.go index 93f75d8..0fb9831 100644 --- a/pkg/utility/decimalhelper.go +++ b/pkg/utility/decimalhelper.go @@ -87,3 +87,16 @@ func DiscardDecimal(value decimal.Decimal, discardDigits int32) decimal.Decimal return value } + +// SafeDiv 安全除法 +// dividend: 被除数 +// divisor: 除数 +func SafeDiv(dividend, divisor decimal.Decimal) decimal.Decimal { + var result decimal.Decimal + + if dividend.Cmp(decimal.Zero) != 0 && divisor.Cmp(decimal.Zero) != 0 { + result = dividend.Div(divisor) + } + + return result +} diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 0d2ce5b..6855131 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -303,12 +303,12 @@ func handleClosePosition(db *gorm.DB, preOrder *DbModels.LinePreOrder) { removeFutLossAndAddPosition(preOrder.MainId, preOrder.OrderSn) removePosition(db, preOrder) - futApi := FutRestApi{} apiUserInfo, _ := GetApiInfo(preOrder.ApiId) if apiUserInfo.Id > 0 { - if err := futApi.CancelAllFutOrder(apiUserInfo, preOrder.Symbol); err != nil { - logger.Errorf("止盈单成功 取消其它订单失败 订单号:%s:", err) + mainIds := []int{preOrder.MainId} + if err := cancelMainOrders(mainIds, db, apiUserInfo, preOrder.Symbol, false); err != nil { + logger.Errorf("平仓单成功 取消其它订单失败 订单号:%s:", err) } } diff --git a/services/scriptservice/order.go b/services/scriptservice/order.go index 0b8edd5..4a78831 100644 --- a/services/scriptservice/order.go +++ b/services/scriptservice/order.go @@ -1,17 +1,19 @@ package scriptservice import ( - "github.com/bytedance/sonic" - log "github.com/go-admin-team/go-admin-core/logger" - sysservice "github.com/go-admin-team/go-admin-core/sdk/service" "go-admin/app/admin/models" "go-admin/app/admin/service" "go-admin/app/admin/service/dto" "go-admin/common/const/rediskey" "go-admin/common/helper" - "gorm.io/gorm" + "go-admin/pkg/utility" "strings" "sync" + + "github.com/bytedance/sonic" + log "github.com/go-admin-team/go-admin-core/logger" + sysservice "github.com/go-admin-team/go-admin-core/sdk/service" + "gorm.io/gorm" ) type PreOrder struct { @@ -23,8 +25,9 @@ func (receiver *PreOrder) AddOrder(orm *gorm.DB) { var wg sync.WaitGroup for i := 1; i <= GoroutineNum; i++ { wg.Add(1) - go workerWithLock(orm, &wg) - + utility.SafeGo(func() { + workerWithLock(orm, &wg) + }) } wg.Wait() }