From 08a7da607ffdc458c6389c2192a7a3adbab5ecb2 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 18 Feb 2025 09:59:51 +0800 Subject: [PATCH] 1 --- app/admin/apis/line_pre_order.go | 8 +- app/admin/models/line_pre_order_ext.go | 4 + app/admin/router/line_pre_order.go | 2 +- app/admin/service/dto/line_pre_order.go | 31 +- app/admin/service/dto/line_pre_order_ext.go | 20 +- app/admin/service/line_pre_order.go | 359 +++++++++++++++++++- app/jobs/jobs.go | 2 +- services/binanceservice/futuresrest.go | 277 +-------------- services/binanceservice/spotreset.go | 265 +-------------- 9 files changed, 420 insertions(+), 548 deletions(-) diff --git a/app/admin/apis/line_pre_order.go b/app/admin/apis/line_pre_order.go index 3b7d15a..367354a 100644 --- a/app/admin/apis/line_pre_order.go +++ b/app/admin/apis/line_pre_order.go @@ -606,7 +606,7 @@ func (e LinePreOrder) QueryAiCoinPrice(c *gin.Context) { // 计算回本盈利比例 func (e LinePreOrder) CalculateBreakEevenRatio(c *gin.Context) { s := service.LinePreOrder{} - req := dto.CalculateBreakEevenRatioReq{} + req := dto.LineAddPreOrderReq{} err := e.MakeContext(c). MakeOrm(). @@ -619,12 +619,12 @@ func (e LinePreOrder) CalculateBreakEevenRatio(c *gin.Context) { return } - data := dto.CalculateBreakEvenRatioResp{} - err = s.CalculateBreakEvenRatio(&req, &data) + // data := dto.CalculateBreakEvenRatioResp{} + _, err = s.GenerateOrder(&req) if err != nil { e.Error(500, err, err.Error()) return } - e.OK(data, "操作成功") + e.OK(req, "操作成功") } diff --git a/app/admin/models/line_pre_order_ext.go b/app/admin/models/line_pre_order_ext.go index 9872e1e..308e109 100644 --- a/app/admin/models/line_pre_order_ext.go +++ b/app/admin/models/line_pre_order_ext.go @@ -12,6 +12,7 @@ type LinePreOrderExt struct { MainOrderId int `json:"mainOrderId" gorm:"type:bigint;comment:主单id"` OrderId int `json:"orderId" gorm:"type:bigint;comment:订单id"` TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" gorm:"type:decimal(10,2);comment:止盈百分比"` + ReTakeRatio decimal.Decimal `json:"reTakeRatio" gorm:"type:decimal(10,2);comment:亏损回本止盈百分比"` ReduceOrderType string `json:"reduceOrderType" gorm:"type:varchar(20);comment:减仓类型 LIMIT-限价 MARKET-市价"` ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" gorm:"type:decimal(10,2);comment:减仓价格百分比"` ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" gorm:"type:decimal(10,2);comment:减仓数量百分比"` @@ -21,6 +22,9 @@ type LinePreOrderExt struct { AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" gorm:"type:decimal(10,2);comment:加仓价格百分比"` AddPositionType int `json:"addPositionType" gorm:"type:int;comment:加仓类型 1-百分比 2-实际金额"` AddPositionVal decimal.Decimal `json:"addPositionVal" gorm:"type:decimal(10,2);comment:加仓值"` + ReduceReTakeRatio decimal.Decimal `json:"reduceReTakeRatio" gorm:"type:decimal(10,2);comment:减仓后亏损回本止盈百分比"` + TotalAfterAdding decimal.Decimal `json:"totalAfterAdding" gorm:"-"` //加仓后总数 + TotalAfterReducing decimal.Decimal `json:"totalAfterReducing" gorm:"-"` //减仓后总数 models.ModelTime models.ControlBy } diff --git a/app/admin/router/line_pre_order.go b/app/admin/router/line_pre_order.go index 589f877..afc9f68 100644 --- a/app/admin/router/line_pre_order.go +++ b/app/admin/router/line_pre_order.go @@ -38,7 +38,7 @@ func registerLinePreOrderRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTM r.POST("clearUnTriggered", actions.PermissionAction(), api.ClearUnTriggered) // 清除待触发的交易对 r.POST("aiCoinPrice", actions.PermissionAction(), api.QueryAiCoinPrice) //获取aiCoin买入点 - r.GET("/calculate", api.CalculateBreakEevenRatio) //计算亏损后止盈百分比 + r.POST("/calculate", api.CalculateBreakEevenRatio) //计算亏损后止盈百分比 } } diff --git a/app/admin/service/dto/line_pre_order.go b/app/admin/service/dto/line_pre_order.go index 31a97bb..3375641 100644 --- a/app/admin/service/dto/line_pre_order.go +++ b/app/admin/service/dto/line_pre_order.go @@ -191,18 +191,20 @@ type LineAddPreOrderReq struct { Price string `json:"price"` //下单价百分比 Profit string `json:"profit"` //止盈价 // StopPrice string `json:"stop_price"` //止损价 - PriceType string `json:"price_type"` //价格类型 - SaveTemplate string `json:"save_template"` //是否保存模板 - TemplateName string `json:"template_name"` //模板名字 - SymbolType int `json:"symbol_type"` //交易对类型 1-现货 2-合约 - CoverType int `json:"cover_type"` //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货 - ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 - MainOrderType string `json:"main_order_type"` //主单类型:限价(LIMIT)或市价(MARKET) - ReducePriceRatio decimal.Decimal `json:"reduce_price"` //主单减仓价格百分比 - ReduceNumRatio decimal.Decimal `json:"reduce_num"` //主单减仓数量百分比 - ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 - ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 - Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段 + PriceType string `json:"price_type"` //价格类型 + SaveTemplate string `json:"save_template"` //是否保存模板 + TemplateName string `json:"template_name"` //模板名字 + SymbolType int `json:"symbol_type"` //交易对类型 1-现货 2-合约 + CoverType int `json:"cover_type"` //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货 + ExpireHour int `json:"expire_hour"` // 过期时间 单位小时 + MainOrderType string `json:"main_order_type"` //主单类型:限价(LIMIT)或市价(MARKET) + ReducePriceRatio decimal.Decimal `json:"reduce_price"` //主单减仓价格百分比 + ReduceNumRatio decimal.Decimal `json:"reduce_num"` //主单减仓数量百分比 + ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比 + ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比 + ReduceReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"减仓后亏损回本止盈百分比"` + + Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段 } func (req LineAddPreOrderReq) CheckParams() error { @@ -232,6 +234,11 @@ func (req LineAddPreOrderReq) CheckParams() error { return nil } +type LineTreeOrder struct { + models.LinePreOrder + Childs []models.LinePreOrder `json:"childs"` +} + // LineBatchAddPreOrderReq 批量添加订单请求参数 type LineBatchAddPreOrderReq struct { ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type diff --git a/app/admin/service/dto/line_pre_order_ext.go b/app/admin/service/dto/line_pre_order_ext.go index 793a9c3..14cb24c 100644 --- a/app/admin/service/dto/line_pre_order_ext.go +++ b/app/admin/service/dto/line_pre_order_ext.go @@ -37,15 +37,17 @@ func (m *LinePreOrderExtGetPageReq) GetNeedSearch() interface{} { } type LineAddPreOrderExtReq struct { - TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` - ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比"` - ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比"` - ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比"` - ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` - AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比"` - AddPositionOrderType string `json:"addPositionOrderType" comment:"加仓订单类型 LIMIT-限价 MARKET-市价"` - AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` - AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` + TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"` + ReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"亏损回本止盈百分比"` + ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比"` + ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比"` + ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比"` + ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"` + ReduceReTakeProfitRatio decimal.Decimal `json:"reduceReTakeProfitRatio" comment:"减仓后回本止盈百分比"` + AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比"` + AddPositionOrderType string `json:"addPositionOrderType" comment:"加仓订单类型 LIMIT-限价 MARKET-市价"` + AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"` + AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"` } type LinePreOrderExtInsertReq struct { diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 88e517d..71c6342 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -347,11 +347,13 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP AddOrder.ExpireTime = time.Now().Add(time.Duration(req.ExpireHour) * time.Hour) //过期时间 AddOrder.MainOrderType = req.MainOrderType AddOrder.Site = req.Site + AddOrder.SignPrice = tickerPrice.String() + if req.PricePattern == "percentage" { AddOrder.Rate = req.Price orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10% priceRate := orderPrice.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 - AddOrder.SignPrice = tickerPrice.String() + if strings.ToUpper(req.Site) == "BUY" { //购买方向 //实际下单价格 truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit)) @@ -363,11 +365,10 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP } else { //实际价格下单 AddOrder.Price = utility.StringToDecimal(req.Price).Truncate(int32(tradeSet.PriceDigit)).String() - AddOrder.SignPrice = req.Price AddOrder.SignPriceType = req.PricePattern AddOrder.Rate = "0" } - buyPrice, _ := decimal.NewFromString(req.BuyPrice) //购买多少U + buyPrice := utility.StrToDecimal(req.BuyPrice) //购买多少U var symbolInfo models.LineSymbol e.Orm.Model(&models.LineSymbol{}).Where("type = ? AND symbol = ?", req.SymbolType, req.Symbol).Find(&symbolInfo) //计算购买数量 判断是否是否是U本位 @@ -426,9 +427,31 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP ReduceTakeProfitRatio: req.ReduceTakeProfitRatio, ReduceStopLossRatio: req.ReduceStopLossRatio, } + mainPrice := utility.StringToDecimal(AddOrder.Price) + mainAmount := buyPrice.Div(mainPrice) + defultExt.TotalAfterReducing = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit)) preOrderExts = append(preOrderExts, defultExt) - for _, addPosition := range req.Ext { + calculateResp := dto.CalculateBreakEvenRatioResp{} + mainParam := dto.CalculateBreakEevenRatioReq{ + Price: mainPrice, + ExchangeType: req.ExchangeType, + Symbol: req.Symbol, + SymbolType: req.SymbolType, + BuyPrice: buyPrice, + LossBeginPercent: decimal.Zero, + LossEndPercent: req.ReducePriceRatio, + AddPositionType: 2, + AddPositionVal: decimal.Zero, + ReducePercent: req.ReduceNumRatio, + } + + //计算减仓后 + mainParam.LossBeginPercent = req.ReducePriceRatio + mainParam.RemainingQuantity = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + mainParam.TotalLossAmountU = buyPrice.Mul(req.ReducePriceRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.PriceDigit)) + + for index, addPosition := range req.Ext { ext := models.LinePreOrderExt{ TakeProfitRatio: addPosition.TakeProfitRatio, ReducePriceRatio: addPosition.ReducePriceRatio, @@ -441,6 +464,30 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP AddPositionVal: addPosition.AddPositionVal, } + mainParam.LossEndPercent = req.Ext[index].AddPositionPriceRatio + mainParam.AddPositionType = req.Ext[index].AddPositionType + mainParam.AddPositionVal = req.Ext[index].AddPositionVal + mainParam.ReducePercent = decimal.Zero + e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + + ext.TotalAfterAdding = calculateResp.RemainingQuantity + req.Ext[index].ReTakeProfitRatio = calculateResp.Ratio + mainParam.LossBeginPercent = req.Ext[index].AddPositionPriceRatio + mainParam.RemainingQuantity = calculateResp.RemainingQuantity + mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU + mainParam.LossEndPercent = req.Ext[index].ReducePriceRatio + mainParam.AddPositionVal = decimal.Zero + mainParam.ReducePercent = req.Ext[index].ReduceNumRatio + e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + + req.Ext[index].ReduceReTakeProfitRatio = calculateResp.Ratio + mainParam.LossBeginPercent = req.Ext[index].ReducePriceRatio + mainParam.RemainingQuantity = calculateResp.RemainingQuantity + mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU + + ext.TotalAfterReducing = calculateResp.RemainingQuantity + ext.ReTakeRatio = req.Ext[index].ReTakeProfitRatio + ext.ReduceReTakeRatio = req.Ext[index].ReduceReTakeProfitRatio preOrderExts = append(preOrderExts, ext) } @@ -520,10 +567,77 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP stopOrder.OrderType = 4 stopOrder.Status = 0 stopOrder.Rate = req.ReducePriceRatio.String() + stopOrder.Num = utility.StrToDecimal(AddOrder.Num).Mul(req.ReduceNumRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.AmountDigit)).String() 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 { + logger.Errorf("主单减仓生成止盈、减仓失败 err:%v", err) + return err + } else if len(newOrders) > 0 { + if err := e.Orm.Create(&newOrders).Error; err != nil { + logger.Errorf("主单减仓保存止盈、减仓失败 err:%v", err) + return err + } + } + } } + //添加止盈单 + for index, v := range preOrderExts { + preOrderExts[index].MainOrderId = AddOrder.Id + if index == 0 { + preOrderExts[index].OrderId = AddOrder.Id + continue + } + + addPosition := createPreAddPosition(&AddOrder, v, tradeSet) + + if addPosition.OrderSn == "" { + continue + } + + preOrderExts[index].OrderId = addPosition.Id + if err := e.Orm.Create(&addPosition).Error; err != nil { + logger.Error("保存加仓单失败") + return err + } + + //止盈、减仓 + orders, err := makeFuturesTakeAndReduce(&addPosition, v, tradeSet) + + if err != nil { + logger.Error("构造加仓单止盈、减仓失败") + return err + } + + if err := e.Orm.Create(&orders).Error; err != nil { + logger.Error("保存加仓单止盈、减仓失败") + return err + } + + for index := range orders { + //减仓单且 减仓比例大于0 小于100 就冲下止盈止损 + if orders[index].OrderType == 4 && v.ReduceNumRatio.Cmp(decimal.Zero) > 0 && v.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 { + reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet) + + if err != nil { + logger.Error("生产加仓单止盈、减仓失败") + return err + } + + if len(reduceChildOrders) == 0 { + continue + } + + if err := e.Orm.Create(&reduceChildOrders).Error; err != nil { + logger.Error("报错减仓后止盈止损失败") + return err + } + } + } + } return nil }) } @@ -531,6 +645,160 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP return nil } +// 生成加仓单 +func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderExt, tradeSet models2.TradeSet) models.LinePreOrder { + data := models.LinePreOrder{} + //主单类型 + if v.AddPositionVal.Cmp(decimal.Zero) <= 0 { + logger.Errorf("预生成加仓单失败, 主订单号:%s 加仓数值不大于0", preOrder.OrderSn) + return data + } + + price := utility.StrToDecimal(preOrder.Price) + copier.Copy(&data, preOrder) + + data.Id = 0 + data.Pid = preOrder.Id + data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + data.MainId = preOrder.Id + data.CreatedAt = time.Now() + data.MainOrderType = v.AddPositionOrderType + data.Status = 0 + data.OrderCategory = 3 + data.Rate = v.AddPositionPriceRatio.String() + var percentage decimal.Decimal + + if data.Site == "BUY" { + percentage = decimal.NewFromInt(1).Sub(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) + } else { + percentage = decimal.NewFromInt(1).Add(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) + } + + dataPrice := price.Mul(percentage).Truncate(int32(tradeSet.PriceDigit)) + 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() + 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() + } + + return data +} + +// 构建合约止盈、减仓单 +func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { + num := ext.TotalAfterAdding + orders := make([]models.LinePreOrder, 0) + //止盈单 + profitOrder := models.LinePreOrder{} + copier.Copy(&profitOrder, preOrder) + + profitOrder.Id = 0 + profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) + profitOrder.Pid = preOrder.Id + profitOrder.OrderType = 1 + profitOrder.Status = 0 + profitOrder.MainId = preOrder.MainId + profitOrder.Num = num.Truncate(int32(tradeSet.AmountDigit)).String() + profitOrder.BuyPrice = "0" + // profitOrder.Rate = ext.TakeProfitRatio.String() + + //止盈需要累加之前的亏损 + profitOrder.Rate = ext.TakeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String() + + if strings.ToUpper(preOrder.Site) == "BUY" { + profitOrder.Site = "SELL" + } else { + profitOrder.Site = "BUY" + } + + binanceservice.SetPrice(&profitOrder, preOrder, tradeSet) + orders = append(orders, profitOrder) + + //减仓单 + if ext.ReducePriceRatio.Cmp(decimal.Zero) > 0 { + stopOrder := models.LinePreOrder{} + copier.Copy(&stopOrder, preOrder) + + stopOrder.Id = 0 + stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) + stopOrder.Pid = preOrder.Id + stopOrder.MainId = preOrder.MainId + stopOrder.OrderType = 4 + stopOrder.Status = 0 + stopOrder.Rate = ext.ReducePriceRatio.String() + stopOrder.Num = num.String() + stopOrder.BuyPrice = "0" + + if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { + stopOrder.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() + } + if strings.ToUpper(preOrder.Site) == "BUY" { + stopOrder.Site = "SELL" + } else { + stopOrder.Site = "BUY" + } + + binanceservice.SetPrice(&stopOrder, preOrder, tradeSet) + orders = append(orders, stopOrder) + } + + return orders, nil +} + +// 构建减仓后止盈止损 +func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { + orders := make([]models.LinePreOrder, 0) + takeProfitOrder := models.LinePreOrder{} + copier.Copy(&takeProfitOrder, parentOrder) + takeProfitOrder.Id = 0 + takeProfitOrder.Pid = parentOrder.Id + takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + takeProfitOrder.Status = 0 + takeProfitOrder.OrderType = 1 + takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() + takeProfitOrder.SignPrice = parentOrder.Price + takeProfitOrder.CreatedAt = time.Now() + takeProfitOrder.BuyPrice = "0" + takeProfitOrder.MainOrderType = "LIMIT" + takeProfitOrder.Num = ext.TotalAfterReducing.Truncate(int32(tradeSet.AmountDigit)).String() + // takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() + //止盈需要累加之前的亏损 + takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.Add(ext.ReduceReTakeRatio).String() + takeProfitOrder.BuyPrice = "0" + + binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet) + orders = append(orders, takeProfitOrder) + + //有止损单 + if ext.ReduceStopLossRatio.Cmp(decimal.Zero) > 0 { + var stoploss models.LinePreOrder + + copier.Copy(&stoploss, parentOrder) + stoploss.Id = 0 + stoploss.Pid = parentOrder.Id + stoploss.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + stoploss.Status = 0 + stoploss.CreatedAt = time.Now() + stoploss.OrderType = 2 + stoploss.SignPrice = parentOrder.Price + stoploss.BuyPrice = "0" + stoploss.Rate = ext.ReduceStopLossRatio.String() + stoploss.MainOrderType = "LIMIT" + stoploss.Num = ext.TotalAfterReducing.Truncate(int32(tradeSet.AmountDigit)).String() + stoploss.BuyPrice = "0" + + binanceservice.SetPrice(&stoploss, parentOrder, tradeSet) + orders = append(orders, stoploss) + } + + return orders, nil +} + // CheckRepeatOrder 检查重复下单 检查基础货币 func (e *LinePreOrder) CheckRepeatOrder(orderType int, apiUserId, site, baseCoin string) int64 { var count int64 @@ -1317,6 +1585,83 @@ func (e *LinePreOrder) QueryAiCoinPrice(req *dto.QueryAiCoinPriceReq) (models.Li return info, err } +// 根据请求参数重新生成亏损回本止盈百分比 +func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) ([]models.LineDirection, error) { + var tradeSet models2.TradeSet + var tickerPrice decimal.Decimal + + if req.SymbolType == 1 { + tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0) + } else { + tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1) + } + + if tradeSet.LastPrice == "" { + return nil, errors.New("获取不到交易对信息") + } + + var price decimal.Decimal + tickerPrice = utility.StrToDecimal(tradeSet.LastPrice) + if req.PricePattern == "percentage" { + orderPrice, _ := decimal.NewFromString(req.Price) //下单价百分比 10% + priceRate := orderPrice.Div(decimal.NewFromInt(100)) //下单价除100 =0.1 + if strings.ToUpper(req.Site) == "BUY" { //购买方向 + //实际下单价格 + price = tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit)) + } else { + price = tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit)) + } + + } else { //实际价格下单 + price = utility.StringToDecimal(req.Price).Truncate(int32(tradeSet.PriceDigit)) + } + + buyPrice := utility.StrToDecimal(req.BuyPrice) + mainAmount := buyPrice.Div(price) + calculateResp := dto.CalculateBreakEvenRatioResp{} + mainParam := dto.CalculateBreakEevenRatioReq{ + Price: price, + ExchangeType: req.ExchangeType, + Symbol: req.Symbol, + SymbolType: req.SymbolType, + BuyPrice: buyPrice, + LossBeginPercent: decimal.Zero, + LossEndPercent: req.ReducePriceRatio, + AddPositionType: 2, + AddPositionVal: decimal.Zero, + ReducePercent: req.ReduceNumRatio, + } + + //计算减仓后 + mainParam.LossBeginPercent = req.ReducePriceRatio + mainParam.RemainingQuantity = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)) + mainParam.TotalLossAmountU = buyPrice.Mul(req.ReducePriceRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.PriceDigit)) + + for index := range req.Ext { + mainParam.LossEndPercent = req.Ext[index].AddPositionPriceRatio + mainParam.AddPositionType = req.Ext[index].AddPositionType + mainParam.AddPositionVal = req.Ext[index].AddPositionVal + mainParam.ReducePercent = decimal.Zero + e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + + req.Ext[index].ReTakeProfitRatio = calculateResp.Ratio + mainParam.LossBeginPercent = req.Ext[index].AddPositionPriceRatio + mainParam.RemainingQuantity = calculateResp.RemainingQuantity + mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU + mainParam.LossEndPercent = req.Ext[index].ReducePriceRatio + mainParam.AddPositionVal = decimal.Zero + mainParam.ReducePercent = req.Ext[index].ReduceNumRatio + e.CalculateBreakEvenRatio(&mainParam, &calculateResp) + + req.Ext[index].ReduceReTakeProfitRatio = calculateResp.Ratio + mainParam.LossBeginPercent = req.Ext[index].ReducePriceRatio + mainParam.RemainingQuantity = calculateResp.RemainingQuantity + mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU + } + + return nil, nil +} + // 计算亏损百分比 func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatioReq, data *dto.CalculateBreakEvenRatioResp) error { var tradeSet models2.TradeSet @@ -1351,7 +1696,7 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio var percentDiff decimal.Decimal var reduceAmount decimal.Decimal - nowPrice := req.Price.Mul(decimal.NewFromInt(1).Sub(req.LossEndPercent.Div(decimal.NewFromInt(100).Truncate(4)))) + nowPrice := req.Price.Mul(decimal.NewFromInt(1).Sub(req.LossEndPercent.Div(decimal.NewFromInt(100)).Truncate(4))).Truncate(int32(tradeSet.PriceDigit)) addPositionAmount := addPositionBuyPrice.Div(nowPrice).Truncate(int32(tradeSet.AmountDigit)) //计算价格下跌价差 @@ -1360,7 +1705,7 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio } totalAmount := req.RemainingQuantity.Add(addPositionAmount) - lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100)).Truncate(4)).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.AmountDigit)) + lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.AmountDigit)) //计算减仓数量 if req.ReducePercent.Cmp(decimal.NewFromInt(0)) > 0 { @@ -1373,6 +1718,8 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio //计算百分比 if data.RemainingQuantity.Cmp(decimal.Zero) > 0 { data.Ratio = data.TotalLossAmountU.Div(data.RemainingQuantity).Div(nowPrice).Mul(decimal.NewFromInt(100)).Truncate(2) + } else { + data.Ratio = decimal.Zero } return nil diff --git a/app/jobs/jobs.go b/app/jobs/jobs.go index 91a51e7..a566887 100644 --- a/app/jobs/jobs.go +++ b/app/jobs/jobs.go @@ -158,7 +158,7 @@ func (t LimitOrderTimeoutDuration) Exec(arg interface{}) error { } limitOrderTimeoutDuration := utility.StringAsInt64(resp.ConfigValue) orders := make([]models.LinePreOrder, 0) - err := db.Model(&models.LinePreOrder{}).Where("status = '5' AND main_order_type = 'LIMIT' AND order_type in ('0','4') AND order_category = 3 AND updated_at < ?", time.Now().Add(-time.Duration(limitOrderTimeoutDuration)*time.Second)).Find(&orders).Error + err := db.Model(&models.LinePreOrder{}).Where("status = '5' AND main_order_type = 'LIMIT' AND order_type in ('4') AND order_category = 3 AND updated_at < ?", time.Now().Add(-time.Duration(limitOrderTimeoutDuration)*time.Second)).Find(&orders).Error if err != nil { return err } diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 2ce3df8..b23fd23 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -12,14 +12,11 @@ import ( "go-admin/common/helper" models2 "go-admin/models" "go-admin/pkg/utility" - "go-admin/pkg/utility/snowflakehelper" - "strconv" "strings" "time" "github.com/bytedance/sonic" "github.com/go-admin-team/go-admin-core/logger" - "github.com/jinzhu/copier" "github.com/shopspring/decimal" "gorm.io/gorm" ) @@ -115,12 +112,6 @@ func handleFutOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderSta // 减仓回调 func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { apiUserInfo, _ := GetApiInfo(preOrder.ApiId) - mainId := preOrder.Id - - if preOrder.MainId > 0 { - mainId = preOrder.MainId - } - if apiUserInfo.Id == 0 { logger.Errorf("handleMainReduceFilled 获取api信息失败,订单号:%s", preOrder.OrderSn) return @@ -161,74 +152,12 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } orders := make([]models.LinePreOrder, 0) - rate := utility.StringAsFloat(preOrder.Rate) - ext := models.LinePreOrderExt{} - //获取订单配置 - db.Model(&ext).Where("order_id =?", preOrder.Pid).First(&ext) - - // 不是100%减仓 就需要挂止盈止损 - if rate >= 100 { - removeFutLossAndAddPosition(preOrder) - ids := []int{preOrder.MainId, preOrder.Pid} - if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6", ids).Update("status", 9).Error; err != nil { - logger.Info("100%减仓完毕,终结流程") - } + if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status = 0", preOrder.Id).Find(&orders).Error; err != nil { + logger.Errorf("handleMainReduceFilled 获取待触发订单失败,订单号:%s", preOrder.OrderSn) return } - totalLossAmountU, _ := GetTotalLossAmount(db, mainId) totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) - - takeProfitOrder := models.LinePreOrder{} - copier.Copy(&takeProfitOrder, &preOrder) - takeProfitOrder.Id = 0 - takeProfitOrder.Pid = preOrder.Id - takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) - takeProfitOrder.Status = 0 - takeProfitOrder.Price = price.Mul(decimal.NewFromInt(1).Add(ext.TakeProfitRatio)).Truncate(int32(tradeSet.PriceDigit)).String() - takeProfitOrder.OrderType = 1 - takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() - takeProfitOrder.SignPrice = preOrder.Price - takeProfitOrder.CreatedAt = time.Now() - takeProfitOrder.BuyPrice = "0" - takeProfitOrder.MainOrderType = "LIMIT" - takeProfitOrder.Num = totalNum.String() - takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() - //止盈需要累加之前的亏损 - if totalLossAmountU.Cmp(decimal.Zero) > 0 { - percent := totalLossAmountU.Div(totalNum).Div(price).Abs() - takeProfitOrder.Rate = percent.Mul(decimal.NewFromInt(100)).Add(ext.ReduceTakeProfitRatio).Truncate(2).String() - } - - setPrice(&takeProfitOrder, preOrder, tradeSet) - orders = append(orders, takeProfitOrder) - - //有止损单 - if ext.ReduceStopLossRatio.Cmp(decimal.Zero) > 0 { - var stoploss models.LinePreOrder - - copier.Copy(&stoploss, &preOrder) - stoploss.Id = 0 - stoploss.Pid = preOrder.Id - stoploss.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) - stoploss.Status = 0 - stoploss.CreatedAt = time.Now() - stoploss.OrderType = 2 - stoploss.SignPrice = preOrder.Price - stoploss.BuyPrice = "0" - stoploss.Rate = ext.ReduceStopLossRatio.String() - stoploss.MainOrderType = "LIMIT" - stoploss.Num = totalNum.String() - - setPrice(&stoploss, preOrder, tradeSet) - orders = append(orders, stoploss) - } - - if err := db.Create(&orders).Error; err != nil { - logger.Errorf("handleMainReduceFilled 创建止盈止损单失败:%v", err) - return - } - futApi := FutRestApi{} for _, v := range orders { if v.OrderType == 1 { @@ -435,37 +364,27 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { return } - //预生成加仓单 - shouldReturn := createFutPreAddPosition(preOrder, db, tradeSet) - if shouldReturn { - return - } - if preOrder.OrderCategory == 3 { if err := cancelSymbolTakeAndStop(db, preOrder.MainId, preOrder.SymbolType); err != nil { logger.Errorf("取消止盈止损订单失败 orderSn:%s err:%v", preOrder.OrderSn, err) } } - + apiInfo, err := GetApiInfo(preOrder.ApiId) + if apiInfo.Id == 0 { + logger.Error("订单回调查询apiuserinfo失败 err:", err) + return + } if err := db.Model(&DbModels.LinePreOrder{}). Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id). Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { logger.Error("订单回调查询止盈止损单失败:", err) return - } else if len(orders) == 0 && preOrder.OrderCategory == 3 { - orders, err = makeFuturesTakeAndReduce(preOrder, db, tradeSet, orders) - if err != nil { - return - } } futApi := FutRestApi{} - num, _ := decimal.NewFromString(preOrder.Num) + num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet) - for i, order := range orders { - if i >= 2 { // 最多处理 2 个订单 - break - } + for _, order := range orders { price := utility.StrToDecimal(order.Price).Truncate(int32(tradeSet.PriceDigit)) num = num.Truncate(int32(tradeSet.AmountDigit)) order.Price = price.String() @@ -495,184 +414,6 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { } } -// 生成加仓单 -func createFutPreAddPosition(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet) bool { - //主单类型 - if preOrder.OrderCategory == 1 { - orderExts, err := GetOrderExts(db, preOrder.Id) - - if err != nil { - logger.Errorf("预生成加仓单失败, 回调订单号:%s 获取主单拓展配置失败:%v", preOrder.OrderSn, err) - return true - } - - price := utility.StrToDecimal(preOrder.Price) - for _, v := range orderExts { - if v.OrderId == 0 { - var data DbModels.LinePreOrder - - copier.Copy(&data, &preOrder) - - data.Id = 0 - data.Pid = preOrder.Id - data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) - data.MainId = preOrder.Id - data.CreatedAt = time.Now() - data.MainOrderType = v.AddPositionOrderType - data.Status = 0 - data.OrderCategory = 3 - data.Rate = v.AddPositionPriceRatio.String() - var percentage decimal.Decimal - - if data.Site == "BUY" { - percentage = decimal.NewFromInt(1).Sub(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) - } else { - percentage = decimal.NewFromInt(1).Add(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) - } - - dataPrice := price.Mul(percentage).Truncate(int32(tradeSet.PriceDigit)) - data.Price = dataPrice.String() - - if v.AddPositionType == 1 { - data.Num = utility.StrToDecimal(preOrder.Num).Truncate(int32(tradeSet.AmountDigit)).String() - data.BuyPrice = "0" - } else { - data.BuyPrice = v.AddPositionVal.String() - data.Num = v.AddPositionVal.Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() - } - - err := db.Transaction(func(tx *gorm.DB) error { - if err2 := tx.Create(&data).Error; err2 != nil { - return err2 - } - - v.OrderId = data.Id - if err2 := tx.Model(&v).Update("order_id", data.Id).Error; err2 != nil { - return err2 - } - - return nil - }) - - if err != nil { - logger.Errorf("预生成加仓单失败, 回调订单号:%s 预生成加仓单失败:%v", preOrder.OrderSn, err) - return true - } - } - } - } - - return false -} - -// 构建合约止盈、减仓单 -func makeFuturesTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet, orders []DbModels.LinePreOrder) ([]DbModels.LinePreOrder, error) { - ext := models.LinePreOrderExt{} - apiInfo, err := GetApiInfo(preOrder.ApiId) - price := utility.StrToDecimal(preOrder.Price) - - if apiInfo.Id == 0 { - logger.Error("订单回调查询apiuserinfo失败 err:", err) - return nil, errors.New("订单回调查询apiuserinfo失败") - } - - if err := db.Model(&ext).Where("order_id = ?", preOrder.Id).First(&ext).Error; err != nil { - logger.Error("订单回调查询止盈止损单扩展表失败:", err) - return nil, errors.New("订单回调查询止盈止损单扩展表失败") - } - - totalLossAmountU, _ := GetTotalLossAmount(db, preOrder.MainId) - num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet) - - //止盈单 - if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 { - profitOrder := models.LinePreOrder{} - copier.Copy(&profitOrder, preOrder) - - profitOrder.Id = 0 - profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) - profitOrder.Pid = preOrder.Id - profitOrder.OrderType = 1 - profitOrder.Status = 0 - profitOrder.MainId = ext.MainOrderId - profitOrder.Num = num.String() - profitOrder.Rate = ext.TakeProfitRatio.String() - - //止盈需要累加之前的亏损 - if totalLossAmountU.Cmp(decimal.Zero) > 0 { - percent := totalLossAmountU.Div(num).Div(price).Abs() - profitOrder.Rate = percent.Mul(decimal.NewFromInt(100)).Add(ext.TakeProfitRatio).Truncate(2).String() - } - - if strings.ToUpper(preOrder.Site) == "BUY" { - profitOrder.Site = "SELL" - } else { - profitOrder.Site = "BUY" - } - - setPrice(&profitOrder, preOrder, tradeSet) - orders = append(orders, profitOrder) - } - - //减仓单 - if ext.ReducePriceRatio.Cmp(decimal.Zero) > 0 { - stopOrder := models.LinePreOrder{} - copier.Copy(&stopOrder, preOrder) - - stopOrder.Id = 0 - stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) - stopOrder.Pid = preOrder.Id - stopOrder.MainId = ext.MainOrderId - stopOrder.OrderType = 4 - stopOrder.Status = 0 - stopOrder.Rate = ext.ReducePriceRatio.String() - stopOrder.Num = num.String() - - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - stopOrder.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() - } else { - stopOrder.Num = num.String() - } - - if strings.ToUpper(preOrder.Site) == "BUY" { - stopOrder.Site = "SELL" - } else { - stopOrder.Site = "BUY" - } - - setPrice(&stopOrder, preOrder, tradeSet) - orders = append(orders, stopOrder) - } - - for index := range orders { - orderRate := utility.StrToDecimal(orders[index].Rate) - orderType := orders[index].OrderType - - if strings.ToUpper(preOrder.Site) == "BUY" { - orders[index].Site = "SELL" - } else { - orders[index].Site = "BUY" - } - - switch { - //做多止盈、做空止损或减仓 - case (orderType == 1 && preOrder.Site == "BUY"), ((orderType == 2 || orderType == 4) && preOrder.Site == "SELL"): - orders[index].Price = utility.StrToDecimal(preOrder.Price).Mul(decimal.NewFromInt(1).Add(orderRate.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() - //做多止损或减仓、做空止盈 - case ((orderType == 2 || orderType == 4) && preOrder.Site == "BUY"), (orderType == 1 && preOrder.Site == "SELL"): - orders[index].Price = utility.StrToDecimal(preOrder.Price).Mul(decimal.NewFromInt(1).Sub(orderRate.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() - } - } - - if len(orders) > 0 { - if err := db.Create(&orders).Error; err != nil { - logger.Error("主单回调,创建止盈、减仓单失败") - return orders, errors.New("主单回调,创建止盈、减仓单失败") - } - } - return orders, nil -} - // 减仓单 func processFutReduceOrder(order DbModels.LinePreOrder, price, num decimal.Decimal) { key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE) diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index 529ba88..c5fd7da 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -12,14 +12,12 @@ import ( "go-admin/common/helper" models2 "go-admin/models" "go-admin/pkg/utility" - "go-admin/pkg/utility/snowflakehelper" "strconv" "strings" "time" "github.com/bytedance/sonic" "github.com/go-admin-team/go-admin-core/logger" - "github.com/jinzhu/copier" "github.com/shopspring/decimal" "gorm.io/gorm" ) @@ -144,11 +142,6 @@ func handleOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderStatus // 主单减仓完毕 func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { apiUserInfo, _ := GetApiInfo(preOrder.ApiId) - mainId := preOrder.Id - - if preOrder.MainId > 0 { - mainId = preOrder.MainId - } if apiUserInfo.Id == 0 { logger.Errorf("handleMainReduceFilled 获取api信息失败,订单号:%s", preOrder.OrderSn) @@ -162,13 +155,8 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { return } - price := utility.StrToDecimal(preOrder.Price) - parentOrder, _ := GetOrderById(db, preOrder.Pid) orders := make([]models.LinePreOrder, 0) rate := utility.StringAsFloat(preOrder.Rate) - ext := models.LinePreOrderExt{} - //获取订单配置 - db.Model(&ext).Where("order_id =?", preOrder.Pid).First(&ext) // 100%减仓 终止流程 if rate >= 100 { @@ -179,95 +167,36 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } return } - totalLossAmountU, _ := GetTotalLossAmount(db, mainId) + totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet) - takeProfitOrder := models.LinePreOrder{} - copier.Copy(&takeProfitOrder, &preOrder) - takeProfitOrder.Id = 0 - takeProfitOrder.Pid = preOrder.Id - takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) - takeProfitOrder.Status = 0 - // takeProfitOrder.Price = price.Mul(decimal.NewFromInt(1).Add(ext.TakeProfitRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() - takeProfitOrder.OrderType = 1 - takeProfitOrder.Rate = "100" - takeProfitOrder.SignPrice = preOrder.Price - takeProfitOrder.CreatedAt = time.Now() - takeProfitOrder.BuyPrice = "0" - takeProfitOrder.MainOrderType = "LIMIT" - takeProfitOrder.Num = totalNum.String() - takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String() - //止盈需要累加之前的亏损 - if totalLossAmountU.Cmp(decimal.Zero) > 0 { - percent := totalLossAmountU.Div(totalNum).Div(price).Abs() - takeProfitOrder.Rate = percent.Mul(decimal.NewFromInt(100)).Add(ext.ReduceTakeProfitRatio).Truncate(2).String() - } - - setPrice(&takeProfitOrder, preOrder, tradeSet) - orders = append(orders, takeProfitOrder) - - //有止损单 - if ext.ReduceStopLossRatio.Cmp(decimal.Zero) > 0 { - var stoploss models.LinePreOrder - - copier.Copy(&stoploss, &preOrder) - stoploss.Id = 0 - stoploss.Pid = preOrder.Id - stoploss.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) - stoploss.Status = 0 - stoploss.CreatedAt = time.Now() - stoploss.OrderType = 2 - stoploss.SignPrice = preOrder.Price - stoploss.BuyPrice = "0" - stoploss.Rate = ext.ReduceStopLossRatio.String() - stoploss.MainOrderType = "LIMIT" - stoploss.Num = totalNum.String() - // stoploss.Price = price.Mul(decimal.NewFromInt(1).Sub(ext.ReduceStopLossRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String() - setPrice(&stoploss, preOrder, tradeSet) - orders = append(orders, stoploss) - } - - if err := db.Create(&orders).Error; err != nil { - logger.Errorf("handleMainReduceFilled 创建止盈止损单失败:%v", err) + if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status=0", preOrder.Id).Find(&orders).Error; err != nil { + logger.Errorf("获取减仓单止盈止损失败 err:%v", err) return } spotApi := SpotRestApi{} - paramsMap := OrderPlacementService{ - ApiId: takeProfitOrder.ApiId, - Symbol: takeProfitOrder.Symbol, - Side: takeProfitOrder.Site, - Type: "LIMIT", - TimeInForce: "GTC", - Price: utility.StrToDecimal(takeProfitOrder.Price), - Quantity: totalNum, - NewClientOrderId: takeProfitOrder.OrderSn, - StopPrice: utility.StrToDecimal(takeProfitOrder.Price), - } - if err := spotApi.OrderPlaceLoop(db, paramsMap, 3); err != nil { - logger.Errorf("减仓后重下止盈失败 减仓order_sn:%s err:%v", preOrder.OrderSn, err) - if err2 := db.Model(&takeProfitOrder).Updates(map[string]interface{}{"status": 2, "": err.Error()}).Error; err2 != nil { - logger.Errorf("handleMainReduceFilled 更新止盈单失败:%v", err2) - } - } + for index := range orders { + orders[index].Num = totalNum.Truncate(int32(tradeSet.AmountDigit)).String() - for _, item := range orders { - if item.OrderType == 2 { - processStopLossOrder(item) + if orders[index].OrderType == 1 { + processTakeProfitOrder(db, spotApi, orders[index]) + } else if orders[index].OrderType == 2 { + processStopLossOrder(orders[index]) } } //计算实际亏损 - if parentOrder.Price != "" { - parentPrice := utility.StrToDecimal(parentOrder.Price) - reduceNum := utility.StrToDecimal(preOrder.Num) - lossAmountU := price.Sub(parentPrice).Abs().Mul(reduceNum) //.Truncate(int32(tradeSet.PriceDigit)) + // if parentOrder.Price != "" { + // parentPrice := utility.StrToDecimal(parentOrder.Price) + // reduceNum := utility.StrToDecimal(preOrder.Num) + // lossAmountU := price.Sub(parentPrice).Abs().Mul(reduceNum) //.Truncate(int32(tradeSet.PriceDigit)) - if err := db.Model(&parentOrder).Update("loss_amount", lossAmountU).Error; err != nil { - logger.Errorf("修改主单实际亏损失败 订单号:%s err:%v", parentOrder.OrderSn, err) - } - } + // if err := db.Model(&parentOrder).Update("loss_amount", lossAmountU).Error; err != nil { + // logger.Errorf("修改主单实际亏损失败 订单号:%s err:%v", parentOrder.OrderSn, err) + // } + // } //加仓待触发 addPositionOrder := DbModels.LinePreOrder{} @@ -508,78 +437,6 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) { // 主单成交 func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { processTakeProfitAndStopLossOrders(db, preOrder) - tradeSet, _ := GetTradeSet(preOrder.Symbol, 0) - - if tradeSet.Coin == "" { - logger.Errorf("获取交易对配置失败, 回调订单号:%s", preOrder.OrderSn) - return - } - - //主单类型 - if preOrder.OrderCategory == 1 { - //预生成加仓单 - orderExts, err := GetOrderExts(db, preOrder.Id) - - if err != nil { - logger.Errorf("预生成加仓单失败, 回调订单号:%s 获取主单拓展配置失败:%v", preOrder.OrderSn, err) - return - } - - price := utility.StrToDecimal(preOrder.Price) - for _, v := range orderExts { - if v.OrderId == 0 { - var data DbModels.LinePreOrder - - copier.Copy(&data, &preOrder) - - data.Id = 0 - data.Pid = preOrder.Id - data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) - data.MainId = preOrder.Id - data.CreatedAt = time.Now() - data.MainOrderType = v.AddPositionOrderType - data.Status = 0 - data.OrderCategory = 3 - data.Rate = v.AddPositionPriceRatio.String() - var percentage decimal.Decimal - - if data.Site == "BUY" { - percentage = decimal.NewFromInt(1).Sub(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) - } else { - percentage = decimal.NewFromInt(1).Add(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) - } - - dataPrice := price.Mul(percentage).Truncate(int32(tradeSet.PriceDigit)) - data.Price = dataPrice.String() - - if v.AddPositionType == 1 { - data.Num = preOrder.Num - data.BuyPrice = "0" - } else { - data.BuyPrice = v.AddPositionVal.String() - data.Num = v.AddPositionVal.Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String() - } - - err := db.Transaction(func(tx *gorm.DB) error { - if err2 := tx.Create(&data).Error; err2 != nil { - return err2 - } - - v.OrderId = data.Id - if err2 := tx.Model(&v).Update("order_id", data.Id).Error; err2 != nil { - return err2 - } - - return nil - }) - - if err != nil { - logger.Errorf("预生成加仓单失败, 回调订单号:%s 预生成加仓单失败:%v", preOrder.OrderSn, err) - return - } - } - } - } } // 解析订单状态 @@ -674,12 +531,6 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd Find(&orders).Error; err != nil && errors.Is(err, gorm.ErrRecordNotFound) { logger.Error("订单回调查询止盈止损单失败:", err) return - } else if len(orders) == 0 && preOrder.OrderCategory == 3 { - orders, err = makeSpotTakeAndReduce(preOrder, db, tradeSet, num) - - if err != nil { - return - } } spotApi := SpotRestApi{} @@ -711,88 +562,8 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd } } -// 构建现货止盈、减仓单 -func makeSpotTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet, num decimal.Decimal) ([]DbModels.LinePreOrder, error) { - ext := models.LinePreOrderExt{} - price := utility.StrToDecimal(preOrder.Price) - orders := make([]DbModels.LinePreOrder, 0) - - if err := db.Model(&ext).Where("order_id = ?", preOrder.Id).First(&ext).Error; err != nil { - logger.Error("订单回调查询止盈止损单扩展表失败:", err) - return nil, errors.New("订单回调查询止盈止损单扩展表失败") - } - - totalLossAmountU, _ := GetTotalLossAmount(db, preOrder.MainId) - - //止盈单 - if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 { - profitOrder := models.LinePreOrder{} - copier.Copy(&profitOrder, preOrder) - - profitOrder.Id = 0 - profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) - profitOrder.Pid = preOrder.Id - profitOrder.OrderType = 1 - profitOrder.Status = 0 - profitOrder.Rate = ext.TakeProfitRatio.String() - profitOrder.MainId = ext.MainOrderId - profitOrder.Num = num.String() - - //止盈需要累加之前的亏损 - if totalLossAmountU.Cmp(decimal.Zero) > 0 { - percent := totalLossAmountU.Div(num).Div(price).Abs() - profitOrder.Rate = percent.Mul(decimal.NewFromInt(100)).Add(ext.TakeProfitRatio).Truncate(2).String() - } - - if strings.ToUpper(preOrder.Site) == "BUY" { - profitOrder.Site = "SELL" - } else { - profitOrder.Site = "BUY" - } - - setPrice(&profitOrder, preOrder, tradeSet) - orders = append(orders, profitOrder) - } - - //减仓单 - if ext.ReducePriceRatio.Cmp(decimal.Zero) > 0 { - stopOrder := models.LinePreOrder{} - copier.Copy(&stopOrder, preOrder) - - stopOrder.Id = 0 - stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10) - stopOrder.Pid = preOrder.Id - stopOrder.MainId = ext.MainOrderId - stopOrder.OrderType = 4 - stopOrder.Status = 0 - stopOrder.Rate = ext.ReducePriceRatio.String() - - if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { - stopOrder.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() - } else { - stopOrder.Num = num.String() - } - if strings.ToUpper(preOrder.Site) == "BUY" { - stopOrder.Site = "SELL" - } else { - stopOrder.Site = "BUY" - } - - setPrice(&stopOrder, preOrder, tradeSet) - orders = append(orders, stopOrder) - } - - if len(orders) > 0 { - if err := db.Create(&orders).Error; err != nil { - logger.Error("主单回调,创建止盈、减仓单失败") - return orders, errors.New("主单回调,创建止盈、减仓单失败") - } - } - return orders, nil -} - // 根据下单百分比计算价格 -func setPrice(order *models.LinePreOrder, preOrder *models.LinePreOrder, tradeSet models2.TradeSet) { +func SetPrice(order *models.LinePreOrder, preOrder *models.LinePreOrder, tradeSet models2.TradeSet) { orderType := order.OrderType itemSide := order.Site rate := utility.StrToDecimal(order.Rate)