From 935fc36b5ab57073ba30e69d7b09acde9cba42b2 Mon Sep 17 00:00:00 2001 From: hucan <951870319@qq.com> Date: Tue, 18 Feb 2025 15:40:45 +0800 Subject: [PATCH] 1 --- app/admin/service/line_pre_order.go | 10 +- app/jobs/jobs.go | 116 +++++++++++------- common/const/rediskey/redis_key.go | 1 + .../binanceservice/futuresjudgeservice.go | 6 + services/binanceservice/futuresrest.go | 3 +- services/binanceservice/spotjudgeservice.go | 10 +- services/binanceservice/spotreset.go | 2 +- 7 files changed, 95 insertions(+), 53 deletions(-) diff --git a/app/admin/service/line_pre_order.go b/app/admin/service/line_pre_order.go index 71c6342..d1ad2a1 100644 --- a/app/admin/service/line_pre_order.go +++ b/app/admin/service/line_pre_order.go @@ -567,7 +567,8 @@ 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() + stopNum := utility.StrToDecimal(AddOrder.Num).Mul(req.ReduceNumRatio.Div(decimal.NewFromInt(100)).Truncate(4)) + stopOrder.Num = stopNum.Truncate(int32(tradeSet.AmountDigit)).String() tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&stopOrder) @@ -691,7 +692,7 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx // 构建合约止盈、减仓单 func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) { - num := ext.TotalAfterAdding + num := ext.TotalAfterAdding.Truncate(int32(tradeSet.AmountDigit)) orders := make([]models.LinePreOrder, 0) //止盈单 profitOrder := models.LinePreOrder{} @@ -703,7 +704,7 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO profitOrder.OrderType = 1 profitOrder.Status = 0 profitOrder.MainId = preOrder.MainId - profitOrder.Num = num.Truncate(int32(tradeSet.AmountDigit)).String() + profitOrder.Num = num.String() profitOrder.BuyPrice = "0" // profitOrder.Rate = ext.TakeProfitRatio.String() @@ -735,7 +736,8 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO 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() + stopNum := num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))) + stopOrder.Num = stopNum.Truncate(int32(tradeSet.AmountDigit)).String() } if strings.ToUpper(preOrder.Site) == "BUY" { stopOrder.Site = "SELL" diff --git a/app/jobs/jobs.go b/app/jobs/jobs.go index a566887..59c0001 100644 --- a/app/jobs/jobs.go +++ b/app/jobs/jobs.go @@ -1,12 +1,15 @@ package jobs import ( + "context" "errors" "fmt" "go-admin/app/admin/models" "go-admin/app/admin/service" "go-admin/app/admin/service/dto" + "go-admin/common/const/rediskey" "go-admin/common/global" + "go-admin/common/helper" "go-admin/pkg/utility" "go-admin/pkg/utility/snowflakehelper" "go-admin/services/binanceservice" @@ -15,7 +18,6 @@ import ( "strings" "time" - "github.com/jinzhu/copier" "github.com/shopspring/decimal" "github.com/go-admin-team/go-admin-core/sdk" @@ -156,37 +158,46 @@ func (t LimitOrderTimeoutDuration) Exec(arg interface{}) error { if resp.ConfigValue == "" { return nil } - 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 ('4') AND order_category = 3 AND updated_at < ?", time.Now().Add(-time.Duration(limitOrderTimeoutDuration)*time.Second)).Find(&orders).Error - if err != nil { - return err - } - spotApi := binanceservice.SpotRestApi{} - futApi := binanceservice.FutRestApi{} - for _, order := range orders { - var apiUserinfo models.LineApiUser - db.Model(&models.LineApiUser{}).Where("id = ?", order.ApiId).Find(&apiUserinfo) - //现货 - if order.SymbolType == global.SYMBOL_SPOT { - if order.ExchangeType == global.EXCHANGE_BINANCE { - err := t.ReSpotOrderPlace(db, order, apiUserinfo, spotApi) - if err != nil { - continue + + lock := helper.NewRedisLock(rediskey.JobReOrderTrigger, 20, 5, 100*time.Millisecond) + + if ok, err := lock.AcquireWait(context.Background()); err != nil { + logger.Error("获取锁失败", err) + return nil + } else if ok { + defer lock.Release() + 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 ('4') AND updated_at < ?", time.Now().Add(-time.Duration(limitOrderTimeoutDuration)*time.Second)).Find(&orders).Error + if err != nil { + return err + } + spotApi := binanceservice.SpotRestApi{} + futApi := binanceservice.FutRestApi{} + for _, order := range orders { + var apiUserinfo models.LineApiUser + db.Model(&models.LineApiUser{}).Where("id = ?", order.ApiId).Find(&apiUserinfo) + //现货 + if order.SymbolType == global.SYMBOL_SPOT { + if order.ExchangeType == global.EXCHANGE_BINANCE { + err := t.ReSpotOrderPlace(db, order, apiUserinfo, spotApi) + if err != nil { + continue + } } } - } - //合约 - if order.SymbolType == global.SYMBOL_FUTURES { - if order.ExchangeType == global.EXCHANGE_BINANCE { - err := t.ReFutOrderPlace(db, order, apiUserinfo, futApi) - if err != nil { - continue + //合约 + if order.SymbolType == global.SYMBOL_FUTURES { + if order.ExchangeType == global.EXCHANGE_BINANCE { + err := t.ReFutOrderPlace(db, order, apiUserinfo, futApi) + if err != nil { + continue + } } } - } + } } return nil } @@ -195,7 +206,7 @@ func (t LimitOrderTimeoutDuration) Exec(arg interface{}) error { func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.LinePreOrder, apiUserinfo models.LineApiUser, spotApi binanceservice.SpotRestApi) error { var err error for i := 0; i < 3; i++ { - err := spotApi.CancelOpenOrderByOrderSn(apiUserinfo, order.Symbol, order.OrderSn) + err = spotApi.CancelOpenOrderByOrderSn(apiUserinfo, order.Symbol, order.OrderSn) if err == nil || strings.Contains(err.Error(), "该交易对没有订单") { break } @@ -206,13 +217,18 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li } else { newClientOrderId := snowflakehelper.GetOrderId() - var newOrder models.LinePreOrder - copier.Copy(&newOrder, order) - newOrder.Id = 0 - newOrder.OrderSn = utility.Int64ToString(newClientOrderId) - newOrder.CreatedAt = time.Now() - newOrder.MainOrderType = "MARKET" - err = db.Model(&models.LinePreOrder{}).Create(&newOrder).Error + order.Desc = fmt.Sprintf("取消限价单,重下市价单源订单号:%s ", order.OrderSn) + order.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId()) + order.MainOrderType = "MARKET" + // var newOrder models.LinePreOrder + // copier.Copy(&newOrder, order) + // newOrder.Id = 0 + // newOrder.OrderSn = utility.Int64ToString(newClientOrderId) + // newOrder.CreatedAt = time.Now() + // newOrder.MainOrderType = "MARKET" + // err = db.Model(&models.LinePreOrder{}).Create(&newOrder).Error + err := db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn, "main_order_type": order.MainOrderType}).Error + if err != nil { logger.Error(fmt.Sprintf("生成新市价单失败 err:%+v", err)) return err @@ -224,12 +240,13 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li Type: "MARKET", TimeInForce: "GTC", Price: utility.StringToDecimal(order.Price), + StopPrice: utility.StrToDecimal(order.Price), Quantity: utility.StringToDecimal(order.Num), NewClientOrderId: utility.Int64ToString(newClientOrderId), } if err := spotApi.OrderPlace(db, params); err != nil { logger.Error(fmt.Sprintf("重新下市价单失败 err:%+v", err)) - err := db.Model(&models.LinePreOrder{}).Where("id = ? AND status = 0", newOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error + err := db.Model(&order).Updates(map[string]interface{}{"status": "2", "desc": order.Desc + "err:" + err.Error()}).Error if err != nil { logger.Error("下单失败后修改订单失败") @@ -255,19 +272,28 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin return err } else { newClientOrderId := snowflakehelper.GetOrderId() + orderType := "MARKET" + order.Desc = fmt.Sprintf("取消限价单,重下市价单 源订单号:%s", order.OrderSn) + order.OrderSn = utility.Int64ToString(newClientOrderId) - var newOrder models.LinePreOrder - copier.Copy(&newOrder, order) - newOrder.Id = 0 - newOrder.OrderSn = utility.Int64ToString(newClientOrderId) - newOrder.CreatedAt = time.Now() - newOrder.MainOrderType = "MARKET" - err = db.Model(&models.LinePreOrder{}).Create(&newOrder).Error + // var newOrder models.LinePreOrder + // copier.Copy(&newOrder, order) + // newOrder.Id = 0 + // newOrder.OrderSn = utility.Int64ToString(newClientOrderId) + // newOrder.CreatedAt = time.Now() + // newOrder.MainOrderType = "MARKET" + // err = db.Model(&models.LinePreOrder{}).Create(&newOrder).Error + + err = db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn}).Error if err != nil { logger.Error(fmt.Sprintf("生成合约新市价单失败 err:%+v", err)) return err } + if order.OrderType == 4 { + orderType = "STOP_MARKET" + } + params := binanceservice.FutOrderPlace{ ApiId: order.ApiId, Symbol: order.Symbol, @@ -277,14 +303,14 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin SideType: "MARKET", OpenOrder: 0, Profit: decimal.Decimal{}, - StopPrice: decimal.Decimal{}, - OrderType: "MARKET", + StopPrice: utility.StringToDecimal(order.Price), + OrderType: orderType, NewClientOrderId: utility.Int64ToString(newClientOrderId), } if err := futApi.OrderPlace(db, params); err != nil { logger.Error(fmt.Sprintf("重新下合约市价单失败 err:%+v", err)) - err := db.Model(&models.LinePreOrder{}).Where("id = ? AND status = 0", newOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error + err := db.Model(&order).Updates(map[string]interface{}{"status": "2", "desc": order.Desc + " err:" + err.Error()}).Error if err != nil { logger.Error("下单失败后修改订单失败") return err diff --git a/common/const/rediskey/redis_key.go b/common/const/rediskey/redis_key.go index c6d8c41..b4dd0f5 100644 --- a/common/const/rediskey/redis_key.go +++ b/common/const/rediskey/redis_key.go @@ -51,4 +51,5 @@ const ( //需要清理键值---------END----------------- + JobReOrderTrigger = "job_re_order_trigger" //定时取消限价并下市价锁 ) diff --git a/services/binanceservice/futuresjudgeservice.go b/services/binanceservice/futuresjudgeservice.go index 407bcbb..9ef8194 100644 --- a/services/binanceservice/futuresjudgeservice.go +++ b/services/binanceservice/futuresjudgeservice.go @@ -254,6 +254,12 @@ func FuturesReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, futApi FutRes if err := futApi.ClosePositionLoop(reduceOrder.Symbol, reduceOrder.OrderSn, num, reduceOrder.Side, positionSide, apiInfo, "LIMIT", "0", price, 3); err != nil { log.Errorf("合约减仓挂单失败 id:%s err:%v", reduceOrder.Id, err) + + if err2 := db.Model(&DbModels.LinePreOrder{}). + Where("id = ? AND status =0", reduceOrder.Id). + Updates(map[string]interface{}{"status": 2, "desc": err.Error()}).Error; err2 != nil { + log.Errorf("合约减仓更新状态失败 id:%s err:%v", reduceOrder.Id, err2) + } } if _, err := helper.DefaultRedis.LRem(key, item); err != nil { diff --git a/services/binanceservice/futuresrest.go b/services/binanceservice/futuresrest.go index b23fd23..b7f732d 100644 --- a/services/binanceservice/futuresrest.go +++ b/services/binanceservice/futuresrest.go @@ -158,6 +158,7 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) { } totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) + totalNum = totalNum.Truncate(int32(tradeSet.AmountDigit)) futApi := FutRestApi{} for _, v := range orders { if v.OrderType == 1 { @@ -383,10 +384,10 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) { futApi := FutRestApi{} num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet) + num = num.Truncate(int32(tradeSet.AmountDigit)) for _, order := range orders { price := utility.StrToDecimal(order.Price).Truncate(int32(tradeSet.PriceDigit)) - num = num.Truncate(int32(tradeSet.AmountDigit)) order.Price = price.String() if order.OrderType == 4 { diff --git a/services/binanceservice/spotjudgeservice.go b/services/binanceservice/spotjudgeservice.go index 8846797..9836eeb 100644 --- a/services/binanceservice/spotjudgeservice.go +++ b/services/binanceservice/spotjudgeservice.go @@ -217,10 +217,10 @@ func SpotStopLossTrigger(db *gorm.DB, stopOrder dto.StopLossRedisList, spotApi S for x := 1; x <= 4; x++ { err = spotApi.CancelOpenOrderByOrderSn(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) - if err == nil { + if err == nil || strings.Contains(err.Error(), "该交易对没有订单") { + err = nil break } - } if err != nil { @@ -350,6 +350,12 @@ func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRest if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil { log.Errorf("现货减仓挂单失败 id:%s err:%v", reduceOrder.Id, err) + + if err2 := db.Model(&DbModels.LinePreOrder{}). + Where("id =? AND status =0", reduceOrder.Id). + Updates(map[string]interface{}{"status": 2, "desc": err.Error()}).Error; err2 != nil { + log.Errorf("修改现货减仓状态失败 id:%s err:%v", reduceOrder.Id, err2) + } } if _, err := helper.DefaultRedis.LRem(key, item); err != nil { diff --git a/services/binanceservice/spotreset.go b/services/binanceservice/spotreset.go index c5fd7da..916eead 100644 --- a/services/binanceservice/spotreset.go +++ b/services/binanceservice/spotreset.go @@ -527,7 +527,7 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd num := getSpotPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet) if err := db.Model(&DbModels.LinePreOrder{}). - Where("pid = ? AND order_category = 1 AND order_type > 0 AND status = '0' ", preOrder.Id). + 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