package binanceservice import ( "context" "errors" "fmt" DbModels "go-admin/app/admin/models" "go-admin/app/admin/service/dto" "go-admin/common/const/rediskey" "go-admin/common/global" "go-admin/common/helper" "go-admin/models" "go-admin/pkg/utility" "go-admin/services/cacheservice" "strings" "time" "github.com/bytedance/sonic" log "github.com/go-admin-team/go-admin-core/logger" "github.com/shopspring/decimal" "gorm.io/gorm" ) /* 判断是否触发 */ func JudgeSpotPrice(trade models.TradeSet) { key := fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE) preOrderVal, _ := helper.DefaultRedis.GetAllList(key) db := GetDBConnection() if len(preOrderVal) == 0 { return } spotApi := SpotRestApi{} for _, item := range preOrderVal { preOrder := dto.PreOrderRedisList{} if err := sonic.Unmarshal([]byte(item), &preOrder); err != nil { log.Error("反序列化失败") continue } if preOrder.Symbol == trade.Coin+trade.Currency { orderPrice, _ := decimal.NewFromString(preOrder.Price) tradePrice, _ := decimal.NewFromString(trade.LastPrice) //买入 if strings.ToUpper(preOrder.Site) == "BUY" && orderPrice.Cmp(tradePrice) >= 0 && orderPrice.Cmp(decimal.Zero) > 0 && tradePrice.Cmp(decimal.Zero) > 0 { SpotOrderLock(db, &preOrder, item, spotApi) } } } } // 分布式锁下单 // v 预下单信息 // item 预下单源文本 func SpotOrderLock(db *gorm.DB, v *dto.PreOrderRedisList, item string, spotApi SpotRestApi) { lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, v.ApiId, v.Symbol), 20, 5, 100*time.Millisecond) if ok, err := lock.AcquireWait(context.Background()); err != nil { log.Error("获取锁失败", err) return } else if ok { defer lock.Release() key := fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE) preOrder := DbModels.LinePreOrder{} if err := db.Where("id = ?", v.Id).First(&preOrder).Error; err != nil { log.Error("获取预下单失败", err) if errors.Is(err, gorm.ErrRecordNotFound) { log.Error("不存在待触发主单", item) helper.DefaultRedis.LRem(key, item) } return } hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item) if !hasrecord { log.Error("不存在待触发主单", item) return } price, _ := decimal.NewFromString(v.Price) num, _ := decimal.NewFromString(preOrder.Num) params := OrderPlacementService{ ApiId: v.ApiId, Symbol: v.Symbol, Side: v.Site, Type: preOrder.MainOrderType, TimeInForce: "GTC", Price: price, Quantity: num, NewClientOrderId: v.OrderSn, } preOrderVal, _ := sonic.MarshalString(&v) if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil { log.Error("下单失败", v.Symbol, " err:", err) err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status =0", preOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error if err != nil { log.Error("下单失败后修改订单失败") } if preOrderVal != "" { if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil { log.Error("删除redis 预下单失败:", err) } } return } if preOrderVal != "" { if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil { log.Error("删除redis 预下单失败:", err) } } if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? ", preOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1") } return } else { log.Error("获取锁失败") return } } // 判断是否触发止损 func JudgeSpotStopLoss(trade models.TradeSet) { key := fmt.Sprintf(rediskey.SpotStopLossList, global.EXCHANGE_BINANCE) stopLossVal, _ := helper.DefaultRedis.GetAllList(key) if len(stopLossVal) == 0 { return } db := GetDBConnection() spotApi := SpotRestApi{} setting, err := cacheservice.GetSystemSetting(db) if err != nil { log.Error("获取系统设置失败") return } tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, trade.Coin+trade.Currency, 0) if err != nil { log.Error("获取交易设置失败") return } for _, item := range stopLossVal { stopOrder := dto.StopLossRedisList{} if err := sonic.Unmarshal([]byte(item), &stopOrder); err != nil { log.Error("反序列化失败") continue } if stopOrder.Symbol == trade.Coin+trade.Currency { orderPrice := stopOrder.Price tradePrice, _ := decimal.NewFromString(trade.LastPrice) //买入 if strings.ToUpper(stopOrder.Site) == "SELL" && orderPrice.Cmp(tradePrice) >= 0 && orderPrice.Cmp(decimal.Zero) > 0 && tradePrice.Cmp(decimal.Zero) > 0 { SpotStopLossTrigger(db, stopOrder, spotApi, setting, tradeSet, key, item) } } } } // 触发现货止损 func SpotStopLossTrigger(db *gorm.DB, stopOrder dto.StopLossRedisList, spotApi SpotRestApi, setting DbModels.LineSystemSetting, tradeSet models.TradeSet, key string, item string) { lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, stopOrder.ApiId, stopOrder.Symbol), 20, 5, 100*time.Millisecond) if ok, err := lock.AcquireWait(context.Background()); err != nil { log.Error("获取锁失败", err) return } else if ok { defer lock.Release() takeOrder := DbModels.LinePreOrder{} if err := db.Model(&DbModels.LinePreOrder{}).Where("pid =? AND order_type =1", stopOrder.PId).Find(&takeOrder).Error; err != nil { log.Error("查询止盈单失败") return } hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item) if !hasrecord { log.Debug("减仓缓存中不存在", item) return } apiInfo, _ := GetApiInfo(takeOrder.ApiId) if apiInfo.Id == 0 { log.Error("现货止损 查询api用户不存在") return } var err error for x := 1; x <= 4; x++ { err = spotApi.CancelOpenOrderByOrderSn(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) if err == nil || strings.Contains(err.Error(), "该交易对没有订单") { err = nil break } } if err != nil { log.Error("现货止损撤单失败", err) return } stopPreOrder, _ := GetOrderById(db, stopOrder.Id) price := stopOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.StopLossPremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) num := utility.StrToDecimal(takeOrder.Num).Truncate(int32(tradeSet.AmountDigit)) params := OrderPlacementService{ ApiId: takeOrder.ApiId, Side: takeOrder.Site, Type: "LIMIT", TimeInForce: "GTC", Symbol: takeOrder.Symbol, Price: price, Quantity: num, NewClientOrderId: stopPreOrder.OrderSn, } if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil { log.Errorf("现货止损挂单失败 id:%s err:%v", stopOrder.Id, err) } else { if err := db.Model(&DbModels.LinePreOrder{}).Where("id = ? ", stopOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { log.Errorf("现货止损更新状态失败 id:%s err:%v", stopOrder.Id, err) } } if _, err := helper.DefaultRedis.LRem(key, item); err != nil { log.Errorf("现货止损 删除缓存失败 id:%v err:%v", stopOrder.Id, err) } } else { log.Error("获取锁失败") } } // 判断是否触发现货减仓 func JudgeSpotReduce(trade models.TradeSet) { key := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE) db := GetDBConnection() spotApi := SpotRestApi{} setting, err := cacheservice.GetSystemSetting(db) if err != nil { log.Error("获取系统设置失败") return } tradePrice, _ := decimal.NewFromString(trade.LastPrice) //减仓单减仓策略 orderReduceVal, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.SpotOrderReduceStrategyList, global.EXCHANGE_BINANCE)) reduceOrderStrategy := dto.LineOrderReduceStrategyResp{} for _, item := range orderReduceVal { sonic.Unmarshal([]byte(item), &reduceOrderStrategy) for _, item2 := range reduceOrderStrategy.Items { if reduceOrderStrategy.Symbol == trade.Coin+trade.Currency { //买入 if strings.ToUpper(reduceOrderStrategy.Side) == "SELL" && item2.TriggerPrice.Cmp(tradePrice) >= 0 && item2.TriggerPrice.Cmp(decimal.Zero) > 0 && tradePrice.Cmp(decimal.Zero) > 0 { //todo 生成订单并触发 // SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item) } } } } //减仓单 reduceVal, _ := helper.DefaultRedis.GetAllList(key) for _, item := range reduceVal { reduceOrder := ReduceListItem{} if err := sonic.Unmarshal([]byte(item), &reduceOrder); err != nil { log.Error("反序列化失败") continue } if reduceOrder.Symbol == trade.Coin+trade.Currency { orderPrice := reduceOrder.Price //买入 if strings.ToUpper(reduceOrder.Side) == "SELL" && orderPrice.Cmp(tradePrice) >= 0 && orderPrice.Cmp(decimal.Zero) > 0 && tradePrice.Cmp(decimal.Zero) > 0 { SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item) } } } } // 触发现货减仓 func SpotReduceTrigger(db *gorm.DB, reduceOrder ReduceListItem, spotApi SpotRestApi, setting DbModels.LineSystemSetting, key, item string) { tradeSet, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, 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 { log.Error("获取锁失败", err) return } else if ok { defer lock.Release() takeOrders := make([]DbModels.LinePreOrder, 0) if err := db.Model(&DbModels.LinePreOrder{}).Where("main_id =? AND order_type =1 AND status IN (1,5)", reduceOrder.MainId).Find(&takeOrders).Error; err != nil { log.Error("查询止盈单失败") return } hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item) if !hasrecord { log.Debug("减仓缓存中不存在", item) return } apiInfo, _ := GetApiInfo(reduceOrder.ApiId) if apiInfo.Id == 0 { log.Error("现货减仓 查询api用户不存在") return } for _, takeOrder := range takeOrders { err := CancelOpenOrderByOrderSnLoop(apiInfo, takeOrder.Symbol, takeOrder.OrderSn) if err != nil { log.Error("现货止盈撤单失败", err) return } } price := reduceOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.ReducePremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) params := OrderPlacementService{ ApiId: reduceOrder.ApiId, Side: reduceOrder.Side, Type: "LIMIT", TimeInForce: "GTC", Symbol: reduceOrder.Symbol, Price: price, Quantity: reduceOrder.Num, NewClientOrderId: reduceOrder.OrderSn, } 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) } } else { if err := db.Model(&DbModels.LinePreOrder{}).Where("id =?", reduceOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { log.Error("更新预下单触发事件失败 ordersn:", reduceOrder.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}). Where("id =? AND status =0", reduceOrder.Id). Updates(map[string]interface{}{"status": 1}).Error; err != nil { log.Errorf("修改现货减仓状态失败 id:%s err:%v", reduceOrder.Id, err) } //处理减仓单减仓策略 CacheOrderStrategyAndReCreate(db, reduceOrder, 1, tradeSet, setting) } if _, err := helper.DefaultRedis.LRem(key, item); err != nil { log.Errorf("现货减仓 删除缓存失败 id:%v err:%v", reduceOrder.Id, err) } } else { log.Error("获取锁失败") } } // 判断现货加仓 func JudgeSpotAddPosition(trade models.TradeSet) { key := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) preOrderVal, _ := helper.DefaultRedis.GetAllList(key) db := GetDBConnection() if len(preOrderVal) == 0 { return } spotApi := SpotRestApi{} for _, item := range preOrderVal { preOrder := AddPositionList{} if err := sonic.Unmarshal([]byte(item), &preOrder); err != nil { log.Error("反序列化失败") continue } if preOrder.Symbol == trade.Coin+trade.Currency { orderPrice := preOrder.Price tradePrice, _ := decimal.NewFromString(trade.LastPrice) //买入 if strings.ToUpper(preOrder.Side) == "BUY" && orderPrice.Cmp(tradePrice) >= 0 && orderPrice.Cmp(decimal.Zero) > 0 && tradePrice.Cmp(decimal.Zero) > 0 { SpotAddPositionTrigger(db, &preOrder, item, spotApi) } } } } func SpotAddPositionTrigger(db *gorm.DB, v *AddPositionList, item string, spotApi SpotRestApi) { lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, v.ApiId, v.Symbol), 20, 5, 100*time.Millisecond) if ok, err := lock.AcquireWait(context.Background()); err != nil { log.Error("获取锁失败", err) return } else if ok { defer lock.Release() setting, _ := cacheservice.GetSystemSetting(db) tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, v.Symbol, 0) if tradeSet.LastPrice == "" { log.Errorf("现货加仓触发 查询交易对失败 交易对:%s ordersn:%s", v.Symbol, v.OrderSn) return } key := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE) preOrder := DbModels.LinePreOrder{} if err := db.Where("id = ?", v.Id).First(&preOrder).Error; err != nil { log.Error("获取预下单失败", err) if errors.Is(err, gorm.ErrRecordNotFound) { log.Error("不存在待触发加仓主单", item) helper.DefaultRedis.LRem(key, item) } return } hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item) if !hasrecord { log.Error("不存在待触发加仓主单", item) return } price := v.Price num, _ := decimal.NewFromString(preOrder.Num) if setting.AddPositionPremium.Cmp(decimal.Zero) > 0 { price = price.Mul(decimal.NewFromInt(1).Sub(setting.AddPositionPremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)) } params := OrderPlacementService{ ApiId: v.ApiId, Symbol: v.Symbol, Side: v.Side, Type: preOrder.MainOrderType, TimeInForce: "GTC", Price: price, Quantity: num.Truncate(int32(tradeSet.AmountDigit)), NewClientOrderId: v.OrderSn, } preOrderVal, _ := sonic.MarshalString(&v) if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil { log.Error("下单失败", v.Symbol, " err:", err) err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status =0", preOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error if err != nil { log.Error("下单失败后修改订单失败") } if preOrderVal != "" { if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil { log.Error("删除redis 预下单失败:", err) } } return } if preOrderVal != "" { if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil { log.Error("删除redis 预下单失败:", err) } } if err := db.Model(&DbModels.LinePreOrder{}).Where("id =?", preOrder.Id).Updates(map[string]interface{}{"trigger_time": time.Now()}).Error; err != nil { log.Error("更新预下单触发事件失败 ordersn:", v.OrderSn) } if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status ='0'", preOrder.Id).Updates(map[string]interface{}{"status": "1"}).Error; err != nil { log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1") } return } else { log.Error("获取锁失败") return } }