From 97037adbbb63f3025086e91d260da9242bbecf62 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 11 Feb 2025 18:03:30 +0800 Subject: [PATCH] 1 --- services/binanceservice/futuresbinancerest.go | 7 ++ .../binanceservice/futuresjudgeservice.go | 29 ++++--- services/binanceservice/futuresrest.go | 78 ++++++++++++++++++- services/binanceservice/orderservice.go | 2 +- services/binanceservice/spotjudgeservice.go | 33 +++++--- services/binanceservice/spotreset.go | 73 ++++++++++------- 6 files changed, 169 insertions(+), 53 deletions(-) diff --git a/services/binanceservice/futuresbinancerest.go b/services/binanceservice/futuresbinancerest.go index 4f68390..c1ae766 100644 --- a/services/binanceservice/futuresbinancerest.go +++ b/services/binanceservice/futuresbinancerest.go @@ -443,6 +443,13 @@ func (e FutRestApi) OrderPlace(orm *gorm.DB, params FutOrderPlace) error { paramsMaps["workingType"] = "MARK_PRICE" } + if strings.ToUpper(params.OrderType) == "TAKE_PROFIT" { + paramsMaps["timeInForce"] = "GTC" + paramsMaps["price"] = params.Price.String() + paramsMaps["stopprice"] = params.Profit.String() + paramsMaps["workingType"] = "MARK_PRICE" + } + if strings.ToUpper(params.OrderType) == "STOP_MARKET" { paramsMaps["stopprice"] = params.StopPrice.String() paramsMaps["workingType"] = "MARK_PRICE" diff --git a/services/binanceservice/futuresjudgeservice.go b/services/binanceservice/futuresjudgeservice.go index abd751d..63085c7 100644 --- a/services/binanceservice/futuresjudgeservice.go +++ b/services/binanceservice/futuresjudgeservice.go @@ -153,7 +153,7 @@ func JudgeFuturesReduce(trade models.TradeSet) { } db := GetDBConnection() - spotApi := SpotRestApi{} + futApi := FutRestApi{} setting, err := GetSystemSetting(db) if err != nil { @@ -161,13 +161,6 @@ func JudgeFuturesReduce(trade models.TradeSet) { return } - tradeSet, err := GetTradeSet(trade.Coin+trade.Currency, 0) - - if err != nil { - log.Error("获取交易设置失败") - return - } - for _, item := range reduceVal { reduceOrder := ReduceListItem{} if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil { @@ -184,14 +177,20 @@ func JudgeFuturesReduce(trade models.TradeSet) { ((strings.ToUpper(reduceOrder.Side) == "SELL" && orderPrice.Cmp(tradePrice) >= 0) || (strings.ToUpper(reduceOrder.Side) == "BUY" && orderPrice.Cmp(tradePrice) <= 0)) { - SpotReduceTrigger(db, reduceOrder, spotApi, setting, tradeSet, key, item) + FuturesReduceTrigger(db, reduceOrder, futApi, setting, key, item) } } } } // 触发合约减仓 -func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRestApi, setting DbModels.LineSystemSetting, tradeSet models.TradeSet, key, item string) { +func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRestApi, setting DbModels.LineSystemSetting, key, item string) { + tradeSet, _ := GetTradeSet(reduceOrder.Symbol, 1) + + if tradeSet.LastPrice == "" { + return + } + lock := helper.NewRedisLock(fmt.Sprintf(rediskey.FutTrigger, reduceOrder.ApiId, reduceOrder.Symbol), 20, 5, 100*time.Millisecond) if ok, err := lock.AcquireWait(context.Background()); err != nil { @@ -205,6 +204,13 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes return } + hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item) + + if !hasrecord { + log.Debug("减仓缓存中不存在", item) + return + } + apiInfo, _ := GetApiInfo(takeOrder.ApiId) if apiInfo.Id == 0 { @@ -216,7 +222,8 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes for x := 1; x <= 4; x++ { err = futApi.CancelFutOrder(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) - if err == nil { + if err == nil || strings.Contains(err.Error(), "取消订单被拒绝") { + err = nil break } diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index 714c27d..efde2eb 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -37,7 +37,7 @@ func ChangeFutureOrder(mapData map[string]interface{}) { return } - if originOrderSn != "" { + if originOrderSn != nil && originOrderSn != "" { orderSn = originOrderSn } @@ -375,6 +375,17 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { return } + if tradeSet.Coin == "" { + logger.Errorf("获取交易对配置失败, 回调订单号:%s", preOrder.OrderSn) + return + } + + //预生成加仓单 + shouldReturn := createFutPreAddPosition(preOrder, db, tradeSet) + if shouldReturn { + 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) { @@ -413,6 +424,65 @@ 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, &v) + + 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 + var percentage decimal.Decimal + + if data.Site == "BUY" { + percentage = decimal.NewFromInt(1).Add(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) + } else { + percentage = decimal.NewFromInt(1).Sub(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) + } + data.Price = price.Mul(percentage).Truncate(int32(tradeSet.PriceDigit)).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{} @@ -475,6 +545,12 @@ func makeFuturesTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, trad 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() + } + orders = append(orders, stopOrder) } diff --git a/services/binanceservice/orderservice.go b/services/binanceservice/orderservice.go index 89df671..c103f5f 100644 --- a/services/binanceservice/orderservice.go +++ b/services/binanceservice/orderservice.go @@ -107,7 +107,7 @@ func GetLastStop(db *gorm.DB, pid int) (DbModels.LinePreOrder, error) { // mainId 主单Id func GetOrderExts(db *gorm.DB, mainId int) ([]models.LinePreOrderExt, error) { result := make([]models.LinePreOrderExt, 0) - if err := db.Model(&result).Where("main_id =?", mainId).Find(&result).Error; err != nil { + if err := db.Model(&result).Where("main_order_id =?", mainId).Find(&result).Error; err != nil { return result, err } diff --git a/services/binanceservice/spotjudgeservice.go b/services/binanceservice/spotjudgeservice.go index 98bd908..a417ab0 100644 --- a/services/binanceservice/spotjudgeservice.go +++ b/services/binanceservice/spotjudgeservice.go @@ -192,6 +192,13 @@ func SpotStopLossTrigger(db *gorm.DB, stopOrder dto.StopLossRedisList, spotApi S return } + hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item) + + if !hasrecord { + log.Debug("减仓缓存中不存在", item) + return + } + apiInfo, _ := GetApiInfo(takeOrder.ApiId) if apiInfo.Id == 0 { @@ -259,13 +266,6 @@ func JudgeSpotReduce(trade models.TradeSet) { return } - tradeSet, err := GetTradeSet(trade.Coin+trade.Currency, 0) - - if err != nil { - log.Error("获取交易设置失败") - return - } - for _, item := range reduceVal { reduceOrder := ReduceListItem{} if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil { @@ -282,14 +282,20 @@ func JudgeSpotReduce(trade models.TradeSet) { orderPrice.Cmp(decimal.Zero) > 0 && tradePrice.Cmp(decimal.Zero) > 0 { - SpotReduceTrigger(db, reduceOrder, spotApi, setting, tradeSet, key, item) + SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item) } } } } // 触发现货减仓 -func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRestApi, setting DbModels.LineSystemSetting, tradeSet models.TradeSet, key, item string) { +func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRestApi, setting DbModels.LineSystemSetting, key, item string) { + tradeSet, err := GetTradeSet(reduceOrder.Symbol, 0) + + if err != nil { + log.Error("获取交易设置失败") + return + } lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, reduceOrder.ApiId, reduceOrder.Symbol), 20, 5, 100*time.Millisecond) if ok, err := lock.AcquireWait(context.Background()); err != nil { @@ -302,6 +308,12 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest log.Error("查询止盈单失败") return } + hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item) + + if !hasrecord { + log.Debug("减仓缓存中不存在", item) + return + } apiInfo, _ := GetApiInfo(takeOrder.ApiId) @@ -325,7 +337,6 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest return } price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) - num := utility.StrToDecimal(takeOrder.Num).Truncate(int32(tradeSet.AmountDigit)) params := OrderPlacementService{ ApiId: reduceOrder.ApiId, @@ -334,7 +345,7 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest TimeInForce: "GTC", Symbol: reduceOrder.Symbol, Price: price, - Quantity: num, + Quantity: reduceOrder.Num, NewClientOrderId: reduceOrder.OrderSn, } diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index a0c2fef..323f0f1 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -37,7 +37,7 @@ func ChangeSpotOrder(mapData map[string]interface{}) { return } - if originOrderSn != "" { + if originOrderSn != nil && originOrderSn != "" { orderSn = originOrderSn } @@ -177,7 +177,7 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { takeProfitOrder.Id = 0 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.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 @@ -462,9 +462,9 @@ func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { var percentage decimal.Decimal if data.Site == "BUY" { - percentage = decimal.NewFromInt(1).Add(v.AddPositionPriceRatio) + percentage = decimal.NewFromInt(1).Add(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) } else { - percentage = decimal.NewFromInt(1).Sub(v.AddPositionPriceRatio) + percentage = decimal.NewFromInt(1).Sub(v.AddPositionPriceRatio.Div(decimal.NewFromInt(100))) } data.Price = price.Mul(percentage).Truncate(int32(tradeSet.PriceDigit)).String() @@ -563,13 +563,26 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd return } + apiInfo, err := GetApiInfo(preOrder.ApiId) + if apiInfo.Id == 0 { + logger.Error("订单回调查询apiuserinfo失败 err:", err) + return + } + + num, err := getSpotPositionNum(apiInfo, preOrder, tradeSet) + + if err != nil { + logger.Error("订单回调查询持仓数量失败:", err) + num = utility.StrToDecimal(preOrder.Num) + } + if err := db.Model(&DbModels.LinePreOrder{}). Where("pid = ? AND order_category = 1 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 = makeSpotTakeAndReduce(preOrder, db, tradeSet, orders) + orders, err = makeSpotTakeAndReduce(preOrder, db, tradeSet, orders, num) if err != nil { return @@ -577,52 +590,49 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd } spotApi := SpotRestApi{} - num, _ := decimal.NewFromString(preOrder.Num) for i, order := range orders { if i >= 2 { // 最多处理 2 个订单 break } - if err := db.Model(&order).Update("num", num).Error; err != nil { + order.Num = num.Mul(decimal.NewFromFloat(0.998)).Truncate(int32(tradeSet.AmountDigit)).String() + + if order.OrderType == 4 { + ext := DbModels.LinePreOrderExt{} + db.Model(&ext).Where("order_id=?", preOrder.Id).Find(&ext) + + if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 { + order.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String() + } + } + + if err := db.Model(&order).Update("num", order.Num).Error; err != nil { logger.Errorf("修改止盈止损数量失败 订单号:%s err:%v", order.OrderSn, err) } switch order.OrderType { case 1: // 止盈 - processTakeProfitOrder(db, spotApi, order, num) + processTakeProfitOrder(db, spotApi, order) case 2: // 止损 processStopLossOrder(order) case 4: //减仓 - processSpotReduceOrder(order, num) - + processSpotReduceOrder(order) } } } // 构建现货止盈、减仓单 -func makeSpotTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet, orders []DbModels.LinePreOrder) ([]DbModels.LinePreOrder, error) { +func makeSpotTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet, orders []DbModels.LinePreOrder, num decimal.Decimal) ([]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, err := getSpotPositionNum(apiInfo, preOrder, tradeSet) - - if err != nil { - logger.Error("订单回调查询持仓数量失败:", err) - return nil, errors.New("订单回调查询持仓数量失败") - } //止盈单 if ext.TakeProfitRatio.Cmp(decimal.Zero) > 0 { @@ -658,7 +668,12 @@ func makeSpotTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSe 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() + } orders = append(orders, stopOrder) } @@ -683,7 +698,7 @@ func makeSpotTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSe } // 现货减仓 -func processSpotReduceOrder(preOrder DbModels.LinePreOrder, num decimal.Decimal) { +func processSpotReduceOrder(preOrder DbModels.LinePreOrder) { key := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE) item := ReduceListItem{ Id: preOrder.Id, @@ -691,7 +706,7 @@ func processSpotReduceOrder(preOrder DbModels.LinePreOrder, num decimal.Decimal) Pid: preOrder.Pid, MainId: preOrder.MainId, Price: utility.StrToDecimal(preOrder.Price), - Num: num, + Num: utility.StrToDecimal(preOrder.Num), Side: preOrder.Site, Symbol: preOrder.Symbol, OrderSn: preOrder.OrderSn, @@ -711,7 +726,7 @@ func processSpotReduceOrder(preOrder DbModels.LinePreOrder, num decimal.Decimal) } // 处理止盈订单 -func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LinePreOrder, num decimal.Decimal) { +func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LinePreOrder) { tradeSet, _ := GetTradeSet(order.Symbol, 0) if tradeSet.Coin == "" { @@ -726,7 +741,7 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP Symbol: order.Symbol, Side: order.Site, Price: price.Truncate(int32(tradeSet.PriceDigit)), - Quantity: num.Truncate(int32(tradeSet.AmountDigit)), + Quantity: utility.StrToDecimal(order.Num), Type: "TAKE_PROFIT_LIMIT", TimeInForce: "GTC", StopPrice: price.Truncate(int32(tradeSet.PriceDigit)), @@ -757,7 +772,7 @@ func processTakeProfitOrder(db *gorm.DB, spotApi SpotRestApi, order models.LineP } } else { if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? and status ='0'", order.Id). - Updates(map[string]interface{}{"status": "1", "num": num.String()}).Error; err != nil { + Updates(map[string]interface{}{"status": "1", "num": order.Num}).Error; err != nil { logger.Error("现货止盈下单成功,更新状态失败:", order.OrderSn, " err:", err) } }