package binanceservice import ( "context" "errors" "fmt" "go-admin/app/admin/models" "go-admin/app/admin/service/dto" "go-admin/common/const/rediskey" "go-admin/common/global" "go-admin/common/helper" models2 "go-admin/models" "go-admin/pkg/utility" "go-admin/services/cacheservice" "time" "github.com/bytedance/sonic" "github.com/go-admin-team/go-admin-core/sdk/service" "github.com/shopspring/decimal" ) type BinanceStrategyOrderService struct { service.Service } // 判断是否触发波段订单 func (e *BinanceStrategyOrderService) TriggerStrategyOrder(exchangeType string) { //现货 orderStrs, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.StrategyFutOrderList, exchangeType)) e.DoJudge(orderStrs, 1, exchangeType) //合约 futOrdedrStrs, _ := helper.DefaultRedis.GetAllList(fmt.Sprintf(rediskey.StrategyFutOrderList, exchangeType)) e.DoJudge(futOrdedrStrs, 2, exchangeType) } // 处理订单 func (e *BinanceStrategyOrderService) DoJudge(orderStrs []string, symbolType int, exchangeType string) { for _, orderStr := range orderStrs { var lockKey string orderItem := dto.StrategyOrderRedisList{} err := sonic.Unmarshal([]byte(orderStr), &orderItem) if err != nil || orderItem.Symbol == "" { continue } if symbolType == 1 { lockKey = rediskey.StrategySpotTriggerLock } else { lockKey = rediskey.StrategyFutTriggerLock } lock := helper.NewRedisLock(fmt.Sprintf(lockKey, orderItem.ApiId, orderItem.Symbol), 200, 50, 100*time.Millisecond) if ok, err := lock.AcquireWait(context.Background()); err != nil { e.Log.Debug("获取锁失败", err) return } else if ok { defer lock.Release() //判断是否符合条件 success, err := e.JudgeStrategy(orderItem, 1, exchangeType) if err != nil { e.Log.Errorf("order_id:%d err:%v", orderItem.Id, err) } if success { e.TriggerOrder(orderItem, symbolType) } } } } // 判断是否符合触发条件 func (e *BinanceStrategyOrderService) JudgeStrategy(order dto.StrategyOrderRedisList, symbolType int, exchangeType string) (bool, error) { var symbolKey string result := false nowUtc := time.Now().UnixMilli() switch symbolType { case 1: symbolKey = fmt.Sprintf(rediskey.SpotTickerLastPrice, exchangeType, order.Symbol) case 2: symbolKey = fmt.Sprintf(rediskey.FutureTickerLastPrice, exchangeType, order.Symbol) } lastUtc := nowUtc - (int64(order.TimeSlotStart) * 60 * 1000) beforePrice, _ := helper.DefaultRedis.GetNextAfterScore(symbolKey, float64(lastUtc)) lastPrices, _ := helper.DefaultRedis.GetLastSortSet(symbolKey) if beforePrice == "" || len(lastPrices) == 0 { return result, errors.New("获取交易对起止价格失败") } score := lastPrices[0].Score startPrice := utility.StrToDecimal(beforePrice) lastPrice := utility.StrToDecimal(lastPrices[0].Member.(string)) //时间差超过10s if (nowUtc-int64(score))/1000 > 10 { return result, fmt.Errorf("时间差超过 %ss", "10") } if startPrice.Cmp(decimal.Zero) == 0 || lastPrice.Cmp(decimal.Zero) == 0 { return result, errors.New("获取交易对起止价格有一个为0") } percentag := lastPrice.Div(startPrice).Sub(decimal.NewFromInt(1)).Truncate(6) //价格没有变动 if percentag.Cmp(decimal.Zero) == 0 { return result, nil } //满足条件 switch order.CompareType { case 1: result = percentag.Mul(decimal.NewFromInt(100)).Cmp(order.Percentag) > 0 case 2: result = percentag.Mul(decimal.NewFromInt(100)).Cmp(order.Percentag) >= 0 case 5: result = percentag.Mul(decimal.NewFromInt(100)).Cmp(order.Percentag) == 0 default: return result, errors.New("没有对应的类型") } return result, nil } // 触发委托单 func (e *BinanceStrategyOrderService) TriggerOrder(order dto.StrategyOrderRedisList, symbolType int) error { orders := make([]models.LinePreOrder, 0) if err := e.Orm.Model(&models.LinePreOrder{}).Where("main_id =?", order.Id).Find(&orders).Error; err != nil { e.Log.Errorf("order_id:%d 获取委托单失败:%s", order.Id, err.Error()) return err } setting, _ := cacheservice.GetSystemSetting(e.Orm) if setting.Id == 0 { return errors.New("获取系统设置失败") } tradeSet, _ := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, symbolType) if tradeSet.Coin == "" { return errors.New("获取交易对行情失败") } lastPrice := utility.StrToDecimal(tradeSet.LastPrice) if lastPrice.Cmp(decimal.Zero) <= 0 { return errors.New("最新成交价小于等于0") } var mainOrder models.LinePreOrder for _, v := range orders { if v.MainId == 0 { mainOrder = v break } } GetOrderByPid(&mainOrder, orders, mainOrder.Id) return nil } // 重新计算订单单价、数量 // tradeSet 交易对行情 // mainOrder 主订单 // setting 系统设置 func (e *BinanceStrategyOrderService) RecalculateOrder(tradeSet models2.TradeSet, mainOrder *models.LinePreOrder, setting models.LineSystemSetting) error { exts := make([]models.LinePreOrderExt, 0) if err := e.Orm.Model(models.LinePreOrderExt{}).Where("main_id =?", mainOrder.Id).Find(&exts).Error; err != nil { return errors.New("获取拓展信息失败") } lastPrice := utility.StrToDecimal(tradeSet.LastPrice) rate := utility.StrToDecimal(mainOrder.Rate) newPrice := lastPrice.Mul(decimal.NewFromInt(1).Add(rate.Div(decimal.NewFromInt(100)).Truncate(4))).Truncate(int32(tradeSet.PriceDigit)) buyPrice := utility.StrToDecimal(mainOrder.BuyPrice) totalNum := buyPrice.Div(newPrice).Truncate(int32(tradeSet.AmountDigit)) mainOrder.SignPrice = lastPrice.String() mainOrder.Price = newPrice.String() mainOrder.Num = totalNum.String() for index := range mainOrder.Childs { //主单止盈 if mainOrder.Child[index].OrderType == 1 { var ext models.LinePreOrderExt for _, v := range exts { if v.OrderId == mainOrder.Child[index].Id { ext = v break } } if ext.Id > 0 { var percent decimal.Decimal //多 if mainOrder.Site == "BUY" { percent = decimal.NewFromInt(100).Add(ext.TakeProfitRatio) } else { percent = decimal.NewFromInt(100).Sub(ext.StopLossRatio) } childPrice := lastPrice.Mul(percent.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.PriceDigit)) mainOrder.Child[index].Price = childPrice.String() } } else { //todo 重新计算 } } return nil } // 递归订单树 func GetOrderByPid(order *models.LinePreOrder, orders []models.LinePreOrder, pid int) { for _, v := range orders { if v.Pid == pid { GetOrderByPid(&v, orders, v.Id) order.Childs = append(order.Childs, v) } } }