From 0aa2ab7355dce9908c112c8b6c63ff45f5cafc32 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Fri, 7 Mar 2025 16:48:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86=E5=8A=A0=E4=BB=93=E3=80=81?= =?UTF-8?q?=E5=87=8F=E4=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/service/dto/line_pre_order.go | 16 ++- app/admin/service/line_pre_order.go | 128 ++++++++++++++++----- services/binanceservice/futuresrest.go | 104 +++++++++++++---- services/binanceservice/spotreset.go | 144 ++++++++++++++++++------ 4 files changed, 303 insertions(+), 89 deletions(-) diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 2199b32..56a8d76 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -277,6 +277,10 @@ func (req LineAddPreOrderReq) Valid() error { return errors.New("主单减仓数量百分比不能为空") } + if req.ReducePriceRatio.IsZero() || req.ReducePriceRatio.Cmp(decimal.NewFromInt(100)) >= 0 { + return errors.New("主单减仓价格百分比错误") + } + for _, v := range req.Ext { name := "加仓" @@ -299,13 +303,17 @@ func (req LineAddPreOrderReq) Valid() 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.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 && + // v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && + // (v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) { + // return errors.New("止盈后止损价格不正确") + // } } return nil diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 78af479..5e36941 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -481,10 +481,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP 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) preOrderExts = append(preOrderExts, defultExt) calculateResp := dto.CalculateBreakEvenRatioResp{} mainParam := dto.CalculateBreakEevenRatioReq{ + AddType: 2, Price: mainPrice, ExchangeType: req.ExchangeType, Symbol: req.Symbol, @@ -492,8 +494,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP BuyPrice: buyPrice, LossBeginPercent: decimal.Zero, LossEndPercent: req.ReducePriceRatio, - AddPositionType: 2, - AddPositionVal: decimal.Zero, + AddPositionType: 1, + AddPositionVal: req.ReduceNumRatio, } //计算减仓后 @@ -510,14 +512,17 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP ext := models.LinePreOrderExt{ AddType: addPosition.AddType, OrderType: addPosition.OrderType, + PriceRatio: addPosition.PriceRatio, TakeProfitRatio: addPosition.TakeProfitRatio, TakeProfitNumRatio: addPosition.TakeProfitNumRatio, + StopLossRatio: addPosition.StopLossRatio, TpTpPriceRatio: addPosition.TpTpPriceRatio, TpSlPriceRatio: addPosition.TpSlPriceRatio, AddPositionType: addPosition.AddPositionType, AddPositionVal: addPosition.AddPositionVal, } + mainParam.AddType = addPosition.AddType mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType mainParam.AddPositionVal = req.Ext[index].AddPositionVal @@ -623,7 +628,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&stopOrder) if req.ReduceNumRatio.Cmp(decimal.Zero) > 0 && req.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { - if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt, tradeSet); err != nil { + if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt2, tradeSet, false); err != nil { logger.Errorf("主单减仓生成止盈、减仓失败 err:%v", err) return err } else if len(newOrders) > 0 { @@ -638,30 +643,30 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP //添加止盈单 for index, v := range preOrderExts { preOrderExts[index].MainOrderId = AddOrder.Id - if index == 0 { - preOrderExts[index].OrderId = AddOrder.Id - continue - } - var AddOrder models.LinePreOrder + // if index == 0 { + // preOrderExts[index].OrderId = AddOrder.Id + // continue + // } + var newOrder models.LinePreOrder if v.AddType == 1 { - AddOrder = createPreAddPosition(&AddOrder, v, tradeSet) + newOrder = createPreAddPosition(&AddOrder, v, tradeSet) } else if v.AddType == 2 { - AddOrder = createPreReduceOrder(&AddOrder, v, tradeSet) + newOrder = createPreReduceOrder(&AddOrder, v, tradeSet) } - if AddOrder.OrderSn == "" { + if newOrder.OrderSn == "" { continue } - if err := e.Orm.Create(&AddOrder).Error; err != nil { + if err := e.Orm.Create(&newOrder).Error; err != nil { logger.Error("保存加仓单失败") return err } - preOrderExts[index].OrderId = AddOrder.Id + preOrderExts[index].OrderId = newOrder.Id //止盈、减仓 - orders, err := makeFuturesTakeAndReduce(&AddOrder, v, tradeSet) + orders, err := makeFuturesTakeAndReduce(&newOrder, v, tradeSet) if err != nil { logger.Error("构造止盈、止损失败") @@ -676,7 +681,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP for index := range orders { //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 if orders[index].OrderType == 1 && v.TakeProfitRatio.Cmp(decimal.Zero) > 0 && v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) < 0 { - reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet) + reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet, true) if err != nil { logger.Error("生产止盈后止盈、减仓失败") @@ -722,6 +727,11 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx data.Pid = preOrder.Id data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) data.MainId = preOrder.Id + + if preOrder.MainId > 0 { + data.MainId = preOrder.MainId + } + data.CreatedAt = time.Now() data.MainOrderType = v.OrderType data.Status = 0 @@ -761,12 +771,18 @@ func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrder stopOrder.Id = 0 stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) stopOrder.Pid = preOrder.Id - stopOrder.MainId = preOrder.MainId + stopOrder.MainId = preOrder.Id + + if preOrder.MainId > 0 { + stopOrder.MainId = preOrder.MainId + } + stopOrder.OrderType = 4 stopOrder.Status = 0 stopOrder.Rate = ext.PriceRatio.String() stopOrder.Num = ext.TotalAfter.Sub(ext.TotalBefore).Abs().Truncate(int32(tradeSet.AmountDigit)).String() stopOrder.BuyPrice = "0" + stopOrder.Rate = ext.PriceRatio.String() if strings.ToUpper(preOrder.Site) == "BUY" { stopOrder.Site = "SELL" @@ -801,7 +817,11 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO profitOrder.Pid = preOrder.Id profitOrder.OrderType = 1 profitOrder.Status = 0 - profitOrder.MainId = preOrder.MainId + profitOrder.MainId = preOrder.Id + + if preOrder.MainId > 0 { + profitOrder.MainId = preOrder.MainId + } profitOrder.BuyPrice = "0" profitOrder.Site = side @@ -877,16 +897,25 @@ func makeTpOrder(parentOrder *models.LinePreOrder, reminQuantity decimal.Decimal } // 构建减仓后止盈止损 -func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { +// isTpTp 是否止盈后止盈止损 +func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet, isTpTp bool) ([]models.LinePreOrder, error) { orders := make([]models.LinePreOrder, 0) - var num decimal.Decimal + num := ext.TotalAfter + var takeProfitRatio, slPriceRatio decimal.Decimal + if isTpTp { + takeProfitRatio = ext.TpTpPriceRatio + slPriceRatio = ext.TpSlPriceRatio + } else { + takeProfitRatio = ext.TakeProfitRatio + slPriceRatio = ext.StopLossRatio + } 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))) num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit)) } - if ext.TpTpPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { + if takeProfitRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { takeProfitOrder := models.LinePreOrder{} copier.Copy(&takeProfitOrder, parentOrder) takeProfitOrder.Id = 0 @@ -894,21 +923,25 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) takeProfitOrder.Status = 0 takeProfitOrder.OrderType = 1 - takeProfitOrder.Rate = ext.TpTpPriceRatio.String() takeProfitOrder.SignPrice = parentOrder.Price takeProfitOrder.CreatedAt = time.Now() takeProfitOrder.BuyPrice = "0" takeProfitOrder.MainOrderType = "LIMIT" takeProfitOrder.Num = num.String() //止盈需要累加之前的亏损 - takeProfitOrder.Rate = ext.TpTpPriceRatio.Truncate(2).String() + if isTpTp { + takeProfitOrder.Rate = takeProfitRatio.Truncate(2).String() + } else { + takeProfitOrder.Rate = takeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String() + } + takeProfitOrder.BuyPrice = "0" binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet) orders = append(orders, takeProfitOrder) } //有止损单 - if ext.TpSlPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { + if slPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 { var stoploss models.LinePreOrder copier.Copy(&stoploss, parentOrder) @@ -920,7 +953,7 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line stoploss.OrderType = 2 stoploss.SignPrice = parentOrder.Price stoploss.BuyPrice = "0" - stoploss.Rate = ext.TpSlPriceRatio.String() + stoploss.Rate = slPriceRatio.String() stoploss.MainOrderType = "LIMIT" stoploss.Num = num.String() stoploss.BuyPrice = "0" @@ -1658,6 +1691,7 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err // ClearUnTriggered 清除待触发的交易对 func (e *LinePreOrder) ClearUnTriggered() error { var orderLists []models.LinePreOrder + positions := map[string]positiondto.LinePreOrderPositioinDelReq{} e.Orm.Model(&models.LinePreOrder{}).Where("main_id = 0 AND pid = 0 AND status = '0'").Find(&orderLists).Unscoped().Delete(&models.LinePreOrder{}) for _, order := range orderLists { @@ -1680,8 +1714,43 @@ func (e *LinePreOrder) ClearUnTriggered() error { key := fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType) helper.DefaultRedis.LRem(key, string(marshal)) } + + //会影响持仓的 + removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType) + + if _, ok := positions[removeSymbolKey]; !ok { + positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{ + ApiId: order.ApiId, + Symbol: order.Symbol, + ExchangeType: order.ExchangeType, + Side: order.Site, + SymbolType: order.SymbolType, + } + } + e.Orm.Model(&models.LinePreOrder{}).Where("main_id = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{}) } + + //清理仓位缓存 + for _, v := range positions { + var count int64 + e.Orm.Model(&models.LinePreOrder{}). + Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", + v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count) + + //没有已开仓的订单 直接清理仓位 + if count == 0 { + var key string + + if v.SymbolType == 1 { + key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } else { + key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side) + } + + helper.DefaultRedis.DeleteString(key) + } + } return nil } @@ -1774,8 +1843,8 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error { BuyPrice: buyPrice, LossBeginPercent: lossBeginPercent, LossEndPercent: req.ReducePriceRatio, - AddPositionType: 2, - AddPositionVal: decimal.Zero, + AddPositionType: 1, + AddPositionVal: req.ReduceNumRatio, } //计算减仓后 @@ -1794,6 +1863,7 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error { }) for index := range req.Ext { + mainParam.AddType = req.Ext[index].AddType mainParam.LossBeginPercent = lossBeginPercent mainParam.LossEndPercent = req.Ext[index].PriceRatio mainParam.AddPositionType = req.Ext[index].AddPositionType @@ -1819,9 +1889,9 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio var addPositionBuyPrice decimal.Decimal - if req.AddPositionType == 1 { + if req.AddType == 1 && req.AddPositionType == 1 { addPositionBuyPrice = req.BuyPrice.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(2) - } else { + } else if req.AddType == 1 { addPositionBuyPrice = req.AddPositionVal.Truncate(2) } @@ -1839,7 +1909,7 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.PriceDigit)) //计算减仓数量 - if req.AddPositionType == 2 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 { + if req.AddType == 2 && req.AddPositionType == 1 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 { reduceAmount = totalAmount.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit)) } diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 6f075ac..5cd72a9 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -100,6 +100,9 @@ func handleFutOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderSta //减仓回调 case preOrder.OrderType == 4 && orderStatus == 6: handleReduceFilled(db, preOrder) + //主单取消 + case preOrder.OrderType == 0 && preOrder.Pid == 0 && orderStatus == 4: + handleMainOrderCancel(db, preOrder, 2) //止损成交 case preOrder.OrderType == 2 && orderStatus == 6: handleStopLoss(db, preOrder) @@ -154,6 +157,12 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) price := utility.StrToDecimal(preOrder.Price).Truncate(int32(tradeSet.PriceDigit)) futApi := FutRestApi{} + mainId := preOrder.Id + + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } + db.Model(&orderExt).Where("order_id =?", preOrder.Pid).First(&orderExt) for _, v := range orders { @@ -177,39 +186,46 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { processFutStopLossOrder(db, v, utility.StrToDecimal(v.Price), totalNum) } } + nextFuturesReduceTrigger(db, mainId, totalNum, tradeSet) +} - //加仓待触发 - addPositionOrder := DbModels.LinePreOrder{} +// 下一个合约待触发 +func nextFuturesReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tradeSet models2.TradeSet) { + nextOrder := DbModels.LinePreOrder{} + nextExt := DbModels.LinePreOrderExt{} - if err := db.Model(&addPositionOrder).Where("main_id =? AND order_category=3 AND status=0", preOrder.MainId).First(&addPositionOrder).Error; err != nil { - logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + if err := db.Model(&models.LinePreOrder{}).Where("main_id =? AND order_type =4 AND status=0", mainId).Order("rate asc").First(&nextOrder).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) return } - keyFutAddpositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) - - addPositionData := AddPositionList{ - Id: addPositionOrder.Id, - OrderSn: addPositionOrder.OrderSn, - MainId: addPositionOrder.MainId, - Pid: addPositionOrder.Pid, - Price: utility.StrToDecimal(addPositionOrder.Price), - ApiId: addPositionOrder.ApiId, - Symbol: addPositionOrder.Symbol, - Side: addPositionOrder.Site, - SymbolType: addPositionOrder.SymbolType, + if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) } - addVal, err := sonic.MarshalString(addPositionData) + num := totalNum + //移除缓存 + key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) + vals, _ := helper.DefaultRedis.GetAllList(key) + item := ReduceListItem{} - if err != nil { - logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + for _, val := range vals { + sonic.Unmarshal([]byte(val), &item) + if item.MainId == mainId { + if _, err := helper.DefaultRedis.LRem(key, val); err != nil { + logger.Errorf("减仓单 redis删除失败 main_id:%v err:%v", mainId, err) + } + } } - if err := helper.DefaultRedis.RPushList(keyFutAddpositionKey, addVal); err != nil { - logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + // + if nextExt.AddPositionVal.Cmp(decimal.Zero) > 0 && nextExt.AddPositionVal.Cmp(decimal.Zero) < 100 { + // 计算减仓数量 + num = totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + nextOrder.Num = num.String() } + + processFutReduceOrder(nextOrder, utility.StrToDecimal(nextOrder.Price).Truncate(int32(tradeSet.PriceDigit)), num) } // 获取合约可用数量 @@ -407,6 +423,7 @@ func removeFutLossAndAddPosition(mainId int, orderSn string) { func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrderId int, first bool) { // 获取交易对配置和API信息 tradeSet, err := GetTradeSet(preOrder.Symbol, 1) + mainId := preOrder.Id if err != nil || tradeSet.Coin == "" { logger.Errorf("获取交易对配置失败, 回调订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return @@ -418,6 +435,10 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd return } + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } + // 处理主单加仓 if preOrder.OrderCategory == 3 { if err := handleMainOrderAddPosition(db, preOrder); err != nil { @@ -430,6 +451,39 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd logger.Errorf("取消主单相关订单失败, 订单号:%s, 错误信息: %v", preOrder.OrderSn, err) return } + + //加仓待触发 + addPositionOrders := make([]DbModels.LinePreOrder, 0) + + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.Id).Order("rate asc").Find(&addPositionOrders).Error; err != nil { + logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + keyFutAddpositionKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE) + + for _, addPositionOrder := range addPositionOrders { + addPositionData := AddPositionList{ + Id: addPositionOrder.Id, + OrderSn: addPositionOrder.OrderSn, + MainId: addPositionOrder.MainId, + Pid: addPositionOrder.Pid, + Price: utility.StrToDecimal(addPositionOrder.Price), + ApiId: addPositionOrder.ApiId, + Symbol: addPositionOrder.Symbol, + Side: addPositionOrder.Site, + SymbolType: addPositionOrder.SymbolType, + } + + addVal, err := sonic.MarshalString(addPositionData) + + if err != nil { + logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + return + } + + if err := helper.DefaultRedis.RPushList(keyFutAddpositionKey, addVal); err != nil { + logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + } } // 获取止盈止损订单 @@ -493,10 +547,12 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder, extOrd } processFutStopLossOrder(db, order, price, num) - case 4: // 减仓 - processFutReduceOrder(order, price, num) + // case 4: // 减仓 + // processFutReduceOrder(order, price, num) } } + + nextFuturesReduceTrigger(db, mainId, num, tradeSet) } // 处理主单加仓 diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 3a9dbb6..6718086 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -119,7 +119,7 @@ func handleOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderStatus handleMainReduceFilled(db, preOrder) //主单取消 case preOrder.OrderType == 0 && preOrder.Pid == 0 && orderStatus == 4: - handleMainOrderCancel(preOrder) + handleMainOrderCancel(db, preOrder, 1) // 止盈成交 case preOrder.OrderType == 1 && orderStatus == 6: handleSpotTakeProfitFilled(db, preOrder) @@ -224,38 +224,57 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } - //加仓待触发 - addPositionOrder := DbModels.LinePreOrder{} + mainId := preOrder.Id - if err := db.Model(&addPositionOrder).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.MainId).First(&addPositionOrder).Error; err != nil { - logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + if preOrder.MainId > 0 { + mainId = preOrder.MainId } - keySpotAddPosition := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) + nextSpotReduceTrigger(db, mainId, totalNum, tradeSet) +} - addPositionData := AddPositionList{ - Id: addPositionOrder.Id, - OrderSn: addPositionOrder.OrderSn, - MainId: addPositionOrder.MainId, - Pid: addPositionOrder.Pid, - Price: utility.StrToDecimal(addPositionOrder.Price), - ApiId: addPositionOrder.ApiId, - Symbol: addPositionOrder.Symbol, - Side: addPositionOrder.Site, - SymbolType: addPositionOrder.SymbolType, +// 缓存下一个减仓单 +func nextSpotReduceTrigger(db *gorm.DB, mainId int, totalNum decimal.Decimal, tradeSet models2.TradeSet) bool { + nextOrder := DbModels.LinePreOrder{} + nextExt := DbModels.LinePreOrderExt{} + // var percentag decimal.Decimal + + if err := db.Model(&models.LinePreOrder{}).Where("main_id =? AND order_type =4 AND status=0", mainId).Order("rate asc").First(&nextOrder).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) + return true } - addVal, err := sonic.MarshalString(addPositionData) - - if err != nil { - logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) - return + if err := db.Model(&models.LinePreOrderExt{}).Where("id =?", nextOrder.Id).First(&nextExt).Error; err != nil { + logger.Errorf("获取下一个单失败 err:%v", err) } - if err := helper.DefaultRedis.RPushList(keySpotAddPosition, addVal); err != nil { - logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + //移除缓存 + key := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE) + vals, _ := helper.DefaultRedis.GetAllList(key) + item := ReduceListItem{} + + for _, val := range vals { + sonic.Unmarshal([]byte(val), &item) + if item.MainId == mainId { + if _, err := helper.DefaultRedis.LRem(key, val); err != nil { + logger.Errorf("减仓单 redis删除失败 main_id:%v err:%v", mainId, err) + } + } } + + //减仓配置 且减仓比例大于0小于100 + if nextExt.Id > 0 && nextExt.AddType == 2 && nextExt.AddPositionVal.Cmp(decimal.Zero) > 0 && nextExt.AddPositionVal.Cmp(decimal.Zero) < 100 { + num := totalNum.Mul(nextExt.AddPositionVal.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + // percentag = positionData.TotalLoss.Div(num).Div(price).Mul(decimal.NewFromInt(100)).Truncate(2) + nextOrder.Num = num.String() + } + // percentag = nextExt.PriceRatio.Add(percentag) + // nextOrder.Rate = percentag.String() + // nextOrder.Price = price.Mul(decimal.NewFromInt(1).Add(percentag.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.AmountDigit)).String() + + //减仓待触发 + processSpotReduceOrder(nextOrder) + return false } // 获取主单可用数量 @@ -344,8 +363,16 @@ func getSpotPositionNum(apiUserInfo DbModels.LineApiUser, preOrder *DbModels.Lin } // 主单取消 -func handleMainOrderCancel(preOrder *DbModels.LinePreOrder) { - preOrderKey := fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE) +// symbolType 1:现货 2:合约 +func handleMainOrderCancel(db *gorm.DB, preOrder *DbModels.LinePreOrder, symboType int) { + var preOrderKey string + + if symboType == 1 { + preOrderKey = fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE) + } else { + preOrderKey = fmt.Sprintf(rediskey.PreFutOrderList, global.EXCHANGE_BINANCE) + } + preSpotOrders, _ := helper.DefaultRedis.GetAllList(preOrderKey) preOrderCache := DbModels.LinePreOrder{} @@ -356,10 +383,17 @@ func handleMainOrderCancel(preOrder *DbModels.LinePreOrder) { sonic.Unmarshal([]byte(v), &preOrderCache) if preOrderCache.Pid == preOrder.Id { - _, err := helper.DefaultRedis.LRem(preOrderKey, v) + var count int64 + db.Model(&models.LinePreOrder{}). + Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6", + preOrder.ApiId, preOrder.Site, preOrder.Symbol, preOrder.SymbolType, preOrder.ExchangeType).Count(&count) - if err != nil { - logger.Errorf("订单回调失败, 回调订单号:%s 删除缓存失败:%v", preOrder.OrderSn, err) + if count == 0 { + _, err := helper.DefaultRedis.LRem(preOrderKey, v) + + if err != nil { + logger.Errorf("订单回调失败, 回调订单号:%s 删除缓存失败:%v", preOrder.OrderSn, err) + } } } } @@ -549,9 +583,45 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } } } + + //加仓待触发 + addPositionOrders := make([]DbModels.LinePreOrder, 0) + + if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_category=3 AND order_type=0 AND status=0", preOrder.Id).Order("rate asc").Find(&addPositionOrders).Error; err != nil { + logger.Errorf("handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + + keySpotAddPosition := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) + + for _, addPositionOrder := range addPositionOrders { + addPositionData := AddPositionList{ + Id: addPositionOrder.Id, + OrderSn: addPositionOrder.OrderSn, + MainId: addPositionOrder.MainId, + Pid: addPositionOrder.Pid, + Price: utility.StrToDecimal(addPositionOrder.Price), + ApiId: addPositionOrder.ApiId, + Symbol: addPositionOrder.Symbol, + Side: addPositionOrder.Site, + SymbolType: addPositionOrder.SymbolType, + } + + addVal, err := sonic.MarshalString(addPositionData) + + if err != nil { + logger.Errorf("handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + return + } + + if err := helper.DefaultRedis.RPushList(keySpotAddPosition, addVal); err != nil { + logger.Errorf("handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v", preOrder.OrderSn, err) + } + } + } processTakeProfitAndStopLossOrders(db, preOrder, &positionData, preOrder.Id, true) + } // 解析订单状态 @@ -623,6 +693,11 @@ func updateOrderStatus(db *gorm.DB, preOrder *models.LinePreOrder, status int, r func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrder, positionData *positiondto.PositionDto, extOrderId int, fist bool) { orders := []models.LinePreOrder{} tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) + mainId := preOrder.Id + + if preOrder.MainId > 0 { + mainId = preOrder.MainId + } if tradeSet.Coin == "" { logger.Error("获取交易对失败") @@ -654,9 +729,11 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd spotApi := SpotRestApi{} orderExt := models.LinePreOrderExt{} db.Model(&orderExt).Where("order_id =?", extOrderId).First(&orderExt) + num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)) + //止盈止损 for _, order := range orders { - order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() + order.Num = num.String() if fist && order.OrderCategory == 1 && orderExt.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && orderExt.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) != 0 { //主单第一次且止盈数量不是100% 止盈数量 @@ -694,10 +771,13 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd } processStopLossOrder(order) - case 4: //减仓 - processSpotReduceOrder(order) + // case 4: //减仓 + // processSpotReduceOrder(order) } } + + //待触发减仓单 + nextSpotReduceTrigger(db, mainId, num, tradeSet) } // 根据下单百分比计算价格