package service

import (
	"errors"
	"fmt"
	"go-admin/common/const/rediskey"
	"go-admin/common/global"
	"go-admin/common/helper"
	models2 "go-admin/models"
	"go-admin/pkg/utility"
	"go-admin/pkg/utility/snowflakehelper"
	"go-admin/services/binanceservice"
	"strconv"
	"strings"
	"time"

	"github.com/bytedance/sonic"
	"github.com/jinzhu/copier"
	"github.com/shopspring/decimal"

	"github.com/go-admin-team/go-admin-core/logger"
	"github.com/go-admin-team/go-admin-core/sdk/service"
	"gorm.io/gorm"

	"go-admin/app/admin/models"
	"go-admin/app/admin/service/dto"
	"go-admin/common/actions"
	cDto "go-admin/common/dto"
)

type LinePreOrder struct {
	service.Service
}

// GetPage 获取LinePreOrder列表
func (e *LinePreOrder) GetPage(c *dto.LinePreOrderGetPageReq, p *actions.DataPermission, list *[]models.LinePreOrder, count *int64) error {
	var err error
	var data models.LinePreOrder
	tx := e.Orm.Model(&data).
		Scopes(
			cDto.MakeCondition(c.GetNeedSearch()),
			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
			actions.Permission(data.TableName(), p),
		).Where("pid = 0").Joins("JOIN line_api_user on line_api_user.id = line_pre_order.api_id").
		Joins("JOIN line_pre_order_status on line_pre_order_status.order_sn = line_pre_order.order_sn").
		Select("line_pre_order.*,line_api_user.api_name,line_pre_order_status.add_position_status,line_pre_order_status.reduce_status")
	if c.AddPositionStatus >= 0 {
		tx.Where("line_pre_order_status.add_position_status = ?", c.AddPositionStatus)
	}

	if c.HedgeStatus >= 0 {
		tx.Where("line_pre_order_status.reduce_status = ?", c.HedgeStatus)
	}

	err = tx.
		Find(list).Limit(-1).Offset(-1).
		Count(count).Error
	if err != nil {
		e.Log.Errorf("LinePreOrderService GetPage error:%s \r\n", err)
		return err
	}
	for i, order := range *list {
		var childNum int64
		e.Orm.Model(&models.LinePreOrder{}).Where("pid = ?", order.Id).Count(&childNum)
		(*list)[i].ChildNum = childNum
	}
	return nil
}

// GetOrderPage 获取LinePreOrder列表
func (e *LinePreOrder) GetOrderPage(c *dto.LinePreOrderGetPageReq, p *actions.DataPermission, list *[]models.LinePreOrder, count *int64) error {
	var err error
	var data models.LinePreOrder
	tx := e.Orm.Model(&data).
		Scopes(
			cDto.MakeCondition(c.GetNeedSearch()),
			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
			actions.Permission(data.TableName(), p),
		).Where("pid = 0 AND status != '0'").Joins("JOIN line_api_user on line_api_user.id = line_pre_order.api_id").
		Joins("JOIN line_pre_order_status on line_pre_order_status.order_id = line_pre_order.id").
		Select("line_pre_order.*,line_api_user.api_name,line_pre_order_status.add_position_status,line_pre_order_status.reduce_status")
	if c.AddPositionStatus >= 0 {
		tx.Where("line_pre_order_status.add_position_status = ?", c.AddPositionStatus)
	}

	if c.HedgeStatus >= 0 {
		tx.Where("line_pre_order_status.reduce_status = ?", c.HedgeStatus)
	}
	err = tx.
		Find(list).Limit(-1).Offset(-1).
		Count(count).Error
	//err = e.Orm.Model(&data).
	//	Scopes(
	//		cDto.MakeCondition(c.GetNeedSearch()),
	//		cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
	//		actions.Permission(data.TableName(), p),
	//	).Where("pid = 0 AND status != '0'").Joins("JOIN line_api_user on line_api_user.id = line_pre_order.api_id").Select("line_pre_order.*,line_api_user.api_name").
	//	Find(list).Limit(-1).Offset(-1).
	//	Count(count).Error
	if err != nil {
		e.Log.Errorf("LinePreOrderService GetChildPage error:%s \r\n", err)
		return err
	}
	for i, order := range *list {
		var childNum int64
		e.Orm.Model(&models.LinePreOrder{}).Where("pid = ?", order.Id).Count(&childNum)
		(*list)[i].ChildNum = childNum
	}
	return nil
}

// GetAllPage 获取LinePreOrder列表
func (e *LinePreOrder) GetAllPage(c *dto.LinePreOrderGetPageReq, p *actions.DataPermission, list *[]models.LinePreOrder, count *int64) error {
	var err error
	var data models.LinePreOrder

	err = e.Orm.Model(&data).
		Scopes(
			cDto.MakeCondition(c.GetNeedSearch()),
			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
			actions.Permission(data.TableName(), p),
		).
		Find(list).Limit(-1).Offset(-1).
		Count(count).Error
	if err != nil {
		e.Log.Errorf("LinePreOrderService GetPage error:%s \r\n", err)
		return err
	}
	return nil
}

// GetChildList 获取子订单列表
func (e *LinePreOrder) GetChildList(req *dto.GetChildOrderReq, p *actions.DataPermission, order *[]models.LinePreOrder) error {
	err := e.Orm.Model(&models.LinePreOrder{}).Where("pid = ?", req.Id).
		Order("id asc").
		Joins("JOIN line_api_user on line_api_user.id = line_pre_order.api_id").Select("line_pre_order.*,line_api_user.api_name").
		Find(order).Error
	if err != nil {
		e.Log.Errorf("LinePreOrderService GetPage error:%s \r\n", err)
		return err
	}
	ids := make([]int, 0)

	for _, v := range *order {
		ids = append(ids, v.Id)
	}

	if len(ids) > 0 {
		var counts []dto.LinePreOrderChildCount

		if err := e.Orm.Model(&models.LinePreOrder{}).Where("pid in ?", ids).Group("pid").Select("pid as id,count(*) as count").Find(&counts).Error; err != nil {
			logger.Errorf("获取子订单数量失败 %v", err)
			return nil
		}

		for index := range *order {
			for _, v := range counts {
				if v.Id == (*order)[index].Id {
					(*order)[index].ChildNum = int64(v.Count)
				}
			}
		}
	}

	return nil

}

// Get 获取LinePreOrder对象
func (e *LinePreOrder) Get(d *dto.LinePreOrderGetReq, p *actions.DataPermission, model *models.LinePreOrder) error {
	var data models.LinePreOrder

	err := e.Orm.Model(&data).
		Scopes(
			actions.Permission(data.TableName(), p),
		).
		First(model, d.GetId()).Error
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		err = errors.New("查看对象不存在或无权查看")
		e.Log.Errorf("Service GetLinePreOrder error:%s \r\n", err)
		return err
	}
	if err != nil {
		e.Log.Errorf("db error:%s", err)
		return err
	}
	return nil
}

// Insert 创建LinePreOrder对象
func (e *LinePreOrder) Insert(c *dto.LinePreOrderInsertReq) error {
	var err error
	var data models.LinePreOrder
	c.Generate(&data)
	err = e.Orm.Create(&data).Error
	if err != nil {
		e.Log.Errorf("LinePreOrderService Insert error:%s \r\n", err)
		return err
	}
	return nil
}

// Update 修改LinePreOrder对象
func (e *LinePreOrder) Update(c *dto.LinePreOrderUpdateReq, p *actions.DataPermission) error {
	var err error
	var data = models.LinePreOrder{}
	e.Orm.Scopes(
		actions.Permission(data.TableName(), p),
	).First(&data, c.GetId())
	c.Generate(&data)

	db := e.Orm.Save(&data)
	if err = db.Error; err != nil {
		e.Log.Errorf("LinePreOrderService Save error:%s \r\n", err)
		return err
	}
	if db.RowsAffected == 0 {
		return errors.New("无权更新该数据")
	}
	return nil
}

// Remove 删除LinePreOrder
func (e *LinePreOrder) Remove(d *dto.LinePreOrderDeleteReq, p *actions.DataPermission) error {
	var data models.LinePreOrder
	var list []models.LinePreOrder
	e.Orm.Model(&models.LinePreOrder{}).Where("id in ?", d.GetId()).Find(&list)
	//for _, order := range list {
	//if order.Status != "0" {
	//	return errors.New(fmt.Sprintf("订单id %d 已被触发 无法被删除", order.Id))
	//}
	//}
	db := e.Orm.Model(&data).
		Scopes(
			actions.Permission(data.TableName(), p),
		).Unscoped().Delete(&data, d.GetId())
	if err := db.Error; err != nil {
		e.Log.Errorf("Service RemoveLinePreOrder error:%s \r\n", err)
		return err
	}
	ints := make([]int, 0)
	apiIds := make([]int, 0)

	if db.RowsAffected == 0 {
		return errors.New("无权删除该数据")
	}

	//删除的缓存
	for _, order := range list {
		redisList := dto.PreOrderRedisList{
			Id:          order.Id,
			Symbol:      order.Symbol,
			Price:       order.Price,
			Site:        order.Site,
			ApiId:       order.ApiId,
			OrderSn:     order.OrderSn,
			QuoteSymbol: order.QuoteSymbol,
		}

		if !utility.ContainsInt(apiIds, order.ApiId) {
			apiIds = append(apiIds, order.ApiId)
		}

		tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, fmt.Sprintf(global.TICKER_SPOT, order.ExchangeType, order.Symbol))
		redisList.Price = utility.StringToDecimal(redisList.Price).Truncate(int32(tradeSet.PriceDigit)).String()
		marshal, _ := sonic.Marshal(redisList)
		if order.SymbolType == 1 {
			listKey := fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType)
			helper.DefaultRedis.LRem(listKey, string(marshal))
		} else {
			listKey := fmt.Sprintf(rediskey.PreFutOrderList, order.ExchangeType)
			helper.DefaultRedis.LRem(listKey, string(marshal))
		}

		binanceservice.MainClosePositionClearCache(order.Id, order.SymbolType)

		ints = append(ints, order.Id)
	}

	if len(ints) > 0 {
		e.Orm.Model(&models.LinePreOrder{}).Where("main_id >0 AND main_id in ?", ints).Unscoped().Delete(&models.LinePreOrder{})
	}
	return nil
}

// AddPreOrder 单个添加
func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataPermission, errs *[]error, tickerSymbol string) error {
	apiUserIds := strings.Split(req.ApiUserId, ",")
	if req.SaveTemplate == "2" || req.SaveTemplate == "1" { //2 = 只保存模板 1= 保存模板并下单
		var templateLog dto.LineAddPreOrderReq
		copier.Copy(&templateLog, req)
		//templateLog := *req
		templateLog.SaveTemplate = "0"
		templateLog.TemplateName = ""
		marshal, _ := sonic.Marshal(templateLog)
		saveTemplateParams := models.LineOrderTemplateLogs{
			Name:   req.TemplateName,
			UserId: 0,
			Params: string(marshal),
			Type:   1,
			Switch: "0",
		}
		e.Orm.Model(&models.LineOrderTemplateLogs{}).Create(&saveTemplateParams)
	}
	if req.SaveTemplate == "2" {
		return nil
	}
	var key string
	if req.SymbolType == global.SYMBOL_SPOT {
		key = fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, req.Symbol)
	} else {
		key = fmt.Sprintf(global.TICKER_FUTURES, req.ExchangeType, req.Symbol)
	}

	for _, id := range apiUserIds {
		if req.Site == "SELL" && req.SymbolType == global.SYMBOL_SPOT {
			*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 现货不支持卖出操作", id, req.Symbol))
			continue
		}
		var AddOrder models.LinePreOrder
		var profitOrder models.LinePreOrder
		var stopOrder models.LinePreOrder

		//获取交易对
		tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key)
		orderCount := e.CheckRepeatOrder(req.SymbolType, id, req.Site, tradeSet.Coin)
		if orderCount > 0 {
			*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 该交易对已存在,请勿重复下单", id, req.Symbol))
			continue
		}
		tickerPrice := utility.StrToDecimal(tradeSet.LastPrice)
		if tickerPrice.Equal(decimal.Zero) { //redis 没有这个值
			*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 交易行情出错", id, req.Symbol))
			continue
		}

		AddOrder.ExchangeType = req.ExchangeType
		AddOrder.OrderCategory = 1
		AddOrder.SignPriceType = "new"
		AddOrder.BuyPrice = req.BuyPrice     //购买多少U
		AddOrder.SymbolType = req.SymbolType //交易对类型1= 现货 2 = 合约
		AddOrder.OrderType = req.OrderType   //订单类型
		AddOrder.CoverType = req.CoverType   //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货
		if req.ExpireHour == 0 {             //过期时间默认为4个小时
			req.ExpireHour = 4
		}
		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

			if strings.ToUpper(req.Site) == "BUY" { //购买方向
				//实际下单价格
				truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit))
				AddOrder.Price = truncate.String()
			} else {
				truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit))
				AddOrder.Price = truncate.String()
			}

		} else { //实际价格下单
			AddOrder.Price = utility.StringToDecimal(req.Price).Truncate(int32(tradeSet.PriceDigit)).String()
			AddOrder.SignPriceType = req.PricePattern
			AddOrder.Rate = "0"
		}
		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本位
		if symbolInfo.QuoteAsset != "USDT" { //不是U本位
			//获取币本位兑换u的价格
			ticker2 := models2.TradeSet{}
			tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(symbolInfo.QuoteAsset+"USDT")))

			if tickerVal == "" {
				logger.Error("查询行情失败")
				continue
			}

			err := sonic.Unmarshal([]byte(tickerVal), &ticker2)

			if ticker2.LastPrice == "" {
				logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(symbolInfo.QuoteAsset+"USDT"), err)
				continue
			}
			//LTCBTC --> LTCUSDT
			uTickerPrice, _ := decimal.NewFromString(ticker2.LastPrice) //94069
			//换算成U
			//div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365
			//在换算成对应交易对对应的价值
			//LTCBTC --> LTCUSDT == LTCUSDT -- 100.502
			div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice))
			//计算下单数量
			AddOrder.Num = buyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String()
		} else {
			fromString, _ := decimal.NewFromString(AddOrder.Price)
			AddOrder.Num = buyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String()
		}
		if utility.StringToFloat64(AddOrder.Num) < tradeSet.MinQty {
			*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 小于最小下单数量", id, req.Symbol))
			continue
		}
		AddOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
		AddOrder.ApiId, _ = strconv.Atoi(id)
		AddOrder.Symbol = req.Symbol
		AddOrder.QuoteSymbol = symbolInfo.QuoteAsset
		AddOrder.Pid = 0
		AddOrder.GroupId = "0"
		AddOrder.Status = 0
		copier.Copy(&profitOrder, &AddOrder)
		copier.Copy(&stopOrder, &AddOrder)

		preOrderStatus := models.LinePreOrderStatus{}
		preOrderStatus.OrderSn = AddOrder.OrderSn

		//订单配置信息
		preOrderExts := make([]models.LinePreOrderExt, 0)
		defultExt := models.LinePreOrderExt{
			TakeProfitRatio:       utility.StringToDecimal(req.Profit),
			ReducePriceRatio:      req.ReducePriceRatio,
			ReduceNumRatio:        req.ReduceNumRatio,
			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)

		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.LossEndPercent = req.ReducePriceRatio
		mainParam.RemainingQuantity = mainAmount
		e.CalculateBreakEvenRatio(&mainParam, &calculateResp)
		mainParam.RemainingQuantity = calculateResp.RemainingQuantity //mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
		mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU   //buyPrice.Mul(req.ReducePriceRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.PriceDigit))
		req.ReduceReTakeProfitRatio = calculateResp.Ratio
		mainParam.LossBeginPercent = req.ReducePriceRatio
		defultExt.ReduceReTakeRatio = calculateResp.Ratio

		for index, addPosition := range req.Ext {
			ext := models.LinePreOrderExt{
				TakeProfitRatio:       addPosition.TakeProfitRatio,
				ReducePriceRatio:      addPosition.ReducePriceRatio,
				ReduceNumRatio:        addPosition.ReduceNumRatio,
				ReduceTakeProfitRatio: addPosition.ReduceTakeProfitRatio,
				ReduceStopLossRatio:   addPosition.ReduceStopLossRatio,
				AddPositionPriceRatio: addPosition.AddPositionPriceRatio,
				AddPositionOrderType:  addPosition.AddPositionOrderType,
				AddPositionType:       addPosition.AddPositionType,
				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)
		}

		//事务添加
		e.Orm.Transaction(func(tx *gorm.DB) error {
			err := tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&AddOrder).Error
			if err != nil {
				*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 生成订单失败", id, req.Symbol))
				return err
			}

			preOrderStatus.OrderId = AddOrder.Id

			//加仓、减仓状态
			tx.Model(&models.LinePreOrderStatus{}).Create(&preOrderStatus)

			list := dto.PreOrderRedisList{
				Id:          AddOrder.Id,
				Symbol:      AddOrder.Symbol,
				Price:       AddOrder.Price,
				Site:        AddOrder.Site,
				ApiId:       AddOrder.ApiId,
				OrderSn:     AddOrder.OrderSn,
				QuoteSymbol: AddOrder.QuoteSymbol,
			}
			marshal, _ := sonic.Marshal(&list)
			var preKey string
			if AddOrder.SymbolType == global.SYMBOL_SPOT {
				preKey = fmt.Sprintf(rediskey.PreSpotOrderList, AddOrder.ExchangeType)
			} else {
				preKey = fmt.Sprintf(rediskey.PreFutOrderList, AddOrder.ExchangeType)
			}
			helper.DefaultRedis.LPushList(preKey, string(marshal))

			//是否有止盈止损订单
			if req.Profit != "" {
				if strings.ToUpper(req.Site) == "BUY" {
					profitOrder.Site = "SELL"
					profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 + utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
				} else {
					profitOrder.Site = "BUY"
					profitOrder.Price = decimal.NewFromFloat(utility.StringToFloat64(AddOrder.Price) * (1 - utility.StringToFloat64(req.Profit)/100)).Truncate(int32(tradeSet.PriceDigit)).String()
				}
				profitOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
				profitOrder.Pid = AddOrder.Id
				profitOrder.OrderType = 1
				profitOrder.Status = 0
				profitOrder.Rate = req.Profit
				profitOrder.MainId = AddOrder.Id

				tx.Model(&models.LinePreOrder{}).Omit("id", "save_template", "template_name").Create(&profitOrder)
			}

			if req.ReducePriceRatio.Cmp(decimal.Zero) > 0 {
				if strings.ToUpper(req.Site) == "BUY" {
					stopOrder.Site = "SELL"
					stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Sub(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
				} else {
					stopOrder.Site = "BUY"
					stopOrder.Price = utility.StrToDecimal(AddOrder.Price).Mul(decimal.NewFromInt(1).Add(req.ReducePriceRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
				}
				stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
				stopOrder.Pid = AddOrder.Id
				stopOrder.MainId = AddOrder.Id
				stopOrder.OrderType = 4
				stopOrder.Status = 0
				stopOrder.BuyPrice = "0"
				stopOrder.Rate = req.ReducePriceRatio.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)

				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
				}

				if err := e.Orm.Create(&addPosition).Error; err != nil {
					logger.Error("保存加仓单失败")
					return err
				}

				preOrderExts[index].OrderId = addPosition.Id
				//止盈、减仓
				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
						}
					}
				}
			}

			err = tx.Model(&models.LinePreOrderExt{}).Create(&preOrderExts).Error
			if err != nil {
				return err
			}
			return nil
		})
	}

	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.Truncate(int32(tradeSet.AmountDigit))
	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.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 {
			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"
		} 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(symbolType int, apiUserId, site, baseCoin string) int64 {
	var count int64
	e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND pid=0 AND symbol like ? AND symbol_type = ? AND site = ? AND `status` IN (1,5,6)", apiUserId, baseCoin+"%", symbolType, site).Count(&count)
	return count
}

// AddBatchPreOrder 批量添加
func (e *LinePreOrder) AddBatchPreOrder(batchReq *dto.LineBatchAddPreOrderReq, p *actions.DataPermission, errs *[]error) error {
	if batchReq.SaveTemplate == "2" || batchReq.SaveTemplate == "1" { //2 = 只保存模板 1= 保存模板并下单
		var templateLog dto.LineBatchAddPreOrderReq
		copier.Copy(&templateLog, batchReq)
		//templateLog = *batchReq
		templateLog.SaveTemplate = "0"
		templateLog.TemplateName = ""
		marshal, _ := sonic.Marshal(templateLog)
		saveTemplateParams := models.LineOrderTemplateLogs{
			Name:   batchReq.TemplateName,
			UserId: 0,
			Params: string(marshal),
			Type:   2,
			Switch: "0",
		}
		e.Orm.Model(&models.LineOrderTemplateLogs{}).Create(&saveTemplateParams)
	}

	if batchReq.SaveTemplate == "2" {
		return nil
	}

	if batchReq.SymbolGroupId != "" {
		var symbolGroupInfo models.LineSymbolGroup
		e.Orm.Model(&models.LineSymbolGroup{}).Where("id = ?", utility.StringToInt(batchReq.SymbolGroupId)).Find(&symbolGroupInfo)
		if symbolGroupInfo.Id <= 0 || symbolGroupInfo.Symbol == "" {
			*errs = append(*errs, errors.New(fmt.Sprintf("选择的交易对组:%s不存在或交易对组的交易对为空", batchReq.SymbolGroupId)))
			return nil
		}
		batchReq.Symbol = symbolGroupInfo.Symbol
	}

	//脚本次数
	if batchReq.OrderNum > 0 {
		var tickerSymbol string
		if batchReq.SymbolType == global.SYMBOL_SPOT {
			tickerSymbol = helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
		} else {
			tickerSymbol = helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val()
		}
		apiUserIds := strings.Split(batchReq.ApiUserId, ",")
		if batchReq.Script == "1" {
			//scriptLogs := make([]models.LinePreScript, 0)
			logParams := *batchReq
			for _, id := range apiUserIds {
				for j := 1; j <= batchReq.OrderNum; j++ {
					var log models.LinePreScript
					logParams.SaveTemplate = "0"
					logParams.TemplateName = ""
					logParams.Script = ""
					marshal, _ := sonic.Marshal(logParams)
					log.ApiId = int64(utility.StringToInt(id))
					log.ScriptNum = int64(j)
					log.ScriptParams = string(marshal)
					log.AdminId = 0
					log.Status = "0"
					//scriptLogs = append(scriptLogs, log)
					err := e.Orm.Model(&models.LinePreScript{}).Create(&log).Error
					if err != nil {
						*errs = append(*errs, fmt.Errorf("记录脚本失败:%+v", err.Error()))
						return nil
					}
					helper.DefaultRedis.RPushList(rediskey.PreOrderScriptList, utility.IntToString(log.Id))
				}
			}

			return nil
		}
		for _, id := range apiUserIds {
			for j := 0; j < batchReq.OrderNum; j++ {
				symbols := strings.Split(batchReq.Symbol, ",")
				for _, symbol := range symbols {
					var req dto.LineAddPreOrderReq
					req.ExchangeType = batchReq.ExchangeType
					req.OrderType = batchReq.OrderType
					req.Symbol = symbol
					req.ApiUserId = id
					req.Site = batchReq.Site
					req.BuyPrice = batchReq.BuyPrice
					req.PricePattern = batchReq.PricePattern
					req.Price = batchReq.Price
					req.Profit = batchReq.Profit
					req.Ext = batchReq.Ext
					req.SymbolType = batchReq.SymbolType
					// req.StopPrice = batchReq.StopPrice
					req.ReducePriceRatio = batchReq.ReducePriceRatio
					req.PriceType = batchReq.PriceType
					req.CoverType = batchReq.CoverType
					req.ExpireHour = batchReq.ExpireHour
					req.MainOrderType = batchReq.MainOrderType
					req.ReduceNumRatio = batchReq.ReduceNumRatio
					req.ReduceStopLossRatio = batchReq.ReduceStopLossRatio
					req.ReduceTakeProfitRatio = batchReq.ReduceTakeProfitRatio

					e.AddPreOrder(&req, p, errs, tickerSymbol)
				}
			}
		}
		return nil
	} else {
		*errs = append(*errs, errors.New("请选择运行次数"))
		return nil
	}
}

// QuickAddPreOrder 模板快速下单
func (e *LinePreOrder) QuickAddPreOrder(quickReq *dto.QuickAddPreOrderReq, p *actions.DataPermission, errs *[]error) error {
	templateLogs := make([]models.LineOrderTemplateLogs, 0)
	e.Orm.Model(&models.LineOrderTemplateLogs{}).Where("id in ?", strings.Split(quickReq.Ids, ",")).Find(&templateLogs)
	for _, log := range templateLogs {
		//单独添加
		if log.Type == 1 {
			var addPreOrderParams dto.LineAddPreOrderReq
			sonic.Unmarshal([]byte(log.Params), &addPreOrderParams)

			var tickerSymbol string
			if addPreOrderParams.OrderType == global.SYMBOL_SPOT {
				tickerSymbol = helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
			} else {
				tickerSymbol = helper.DefaultRedis.Get(rediskey.FutSymbolTicker).Val()
			}
			err := e.AddPreOrder(&addPreOrderParams, p, errs, tickerSymbol)
			if err != nil {
				*errs = append(*errs, fmt.Errorf("api_id:%s 获取交易对:%s 生成订单失败", addPreOrderParams.ApiUserId, addPreOrderParams.Symbol))
				continue
			}
		}

		//批量添加
		if log.Type == 2 {
			var batchAddPreOrder dto.LineBatchAddPreOrderReq
			sonic.Unmarshal([]byte(log.Params), &batchAddPreOrder)
			e.AddBatchPreOrder(&batchAddPreOrder, p, errs)
		}
	}
	return nil
}

// Lever 设置杠杆
func (e *LinePreOrder) Lever(req *dto.LeverReq, p *actions.DataPermission, errs *[]error) {
	users := make([]models.LineApiUser, 0)
	err := e.Orm.Model(&models.LineApiUser{}).Where("id in ? AND exchange_type = ?", strings.Split(req.ApiUserIds, ","), req.ExchangeType).Find(&users).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		*errs = append(*errs, errors.New(fmt.Sprintf("设置杠杆失败:%+v", err.Error())))
		return
	}
	var symbols string
	if req.Symbol != "" {
		symbols = req.Symbol
	} else {
		var symbolGroupInfo models.LineSymbolGroup
		e.Orm.Model(&models.LineSymbolGroup{}).Where("id = ? AND exchange_type = ?", req.GroupId, req.ExchangeType).Find(&symbolGroupInfo)
		symbols = symbolGroupInfo.Symbol
	}

	if req.IsAll == 1 {
		lineSymbols := make([]models.LineSymbol, 0)
		futSymbols := make([]string, 0)
		e.Orm.Model(&models.LineSymbol{}).Where("type = '2' AND switch = '1' AND exchange_type = ?", req.ExchangeType).Find(&lineSymbols)
		for _, symbol := range lineSymbols {
			futSymbols = append(futSymbols, symbol.Symbol)
		}
		if len(futSymbols) != 0 {
			symbols = strings.Join(futSymbols, ",")
		}
	}

	if req.ExchangeType == global.EXCHANGE_BINANCE {
		e.SetBinanceLever(req, users, symbols, errs)
	}

	return
}

// SetBinanceLever 设置币安杠杆
func (e *LinePreOrder) SetBinanceLever(req *dto.LeverReq, users []models.LineApiUser, symbols string, errs *[]error) {
	for _, user := range users {
		var client *helper.BinanceClient
		if user.UserPass == "" {
			client, _ = helper.NewBinanceClient(user.ApiKey, user.ApiSecret, "", user.IpAddress)
		} else {
			client, _ = helper.NewBinanceClient(user.ApiKey, user.ApiSecret, "socks5", user.UserPass+"@"+user.IpAddress)
		}
		//client.SendFuturesRequestAuth("/")
		symbolsSlice := strings.Split(symbols, ",")
		for _, s := range symbolsSlice {
			params := map[string]string{
				"leverage": utility.IntToString(req.Leverage),
				"symbol":   s,
			}
			resp, _, err := client.SendFuturesRequestAuth("/fapi/v1/leverage", "POST", params)
			if err != nil {
				*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 交易对:%s 设置杠杆失败:%+v", user.Id, s, err.Error())))
				continue
			}
			var dataMap map[string]interface{}
			if err := sonic.Unmarshal(resp, &dataMap); err != nil {
				*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 交易对:%s 设置杠杆失败:%+v", user.Id, s, err.Error())))
				continue
			}

			if _, ok := dataMap["leverage"]; !ok {
				*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 交易对:%s 设置杠杆失败:%+v", user.Id, s, dataMap["message"])))
				continue
			}
		}
	}
}

// SetBinanceMarginType 设置币安保证金模式
func (e *LinePreOrder) SetBinanceMarginType(req *dto.MarginTypeReq, users []models.LineApiUser, symbols string, errs *[]error) {
	for _, user := range users {
		var client *helper.BinanceClient
		if user.UserPass == "" {
			client, _ = helper.NewBinanceClient(user.ApiKey, user.ApiSecret, "", user.IpAddress)
		} else {
			client, _ = helper.NewBinanceClient(user.ApiKey, user.ApiSecret, "socks5", user.UserPass+"@"+user.IpAddress)
		}
		//client.SendFuturesRequestAuth("/")
		symbolsSlice := strings.Split(symbols, ",")
		for _, s := range symbolsSlice {
			params := map[string]string{
				"marginType": req.MarginType,
				"symbol":     s,
			}
			resp, _, err := client.SendFuturesRequestAuth("/fapi/v1/marginType", "POST", params)
			if err != nil {
				*errs = append(*errs, fmt.Errorf("api_id:%d 交易对:%s 设置仓位失败:%+v", user.Id, s, err.Error()))
				continue
			}
			var dataMap map[string]interface{}
			if err := sonic.Unmarshal(resp, &dataMap); err != nil {
				*errs = append(*errs, fmt.Errorf("api_id:%d 交易对:%s 设置仓位失败:%+v", user.Id, s, err.Error()))
				continue
			}
			code, ok := dataMap["code"]
			if !ok {
				*errs = append(*errs, fmt.Errorf("api_id:%d 交易对:%s 设置仓位失败:%+v", user.Id, s, dataMap["message"]))
				continue
			}
			if code.(float64) != 200 {
				*errs = append(*errs, fmt.Errorf("api_id:%d 交易对:%s 设置仓位失败:%+v", user.Id, s, dataMap["message"]))
				continue
			}
		}
	}
}

// MarginType 设置仓位模式
func (e *LinePreOrder) MarginType(req *dto.MarginTypeReq, p *actions.DataPermission, errs *[]error) {
	users := make([]models.LineApiUser, 0)
	err := e.Orm.Model(&models.LineApiUser{}).Where("id in  ? ", strings.Split(req.ApiUserIds, ",")).Find(&users).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		*errs = append(*errs, errors.New(fmt.Sprintf("设置杠杆失败:%+v", err.Error())))
		return
	}
	var symbols string
	if req.Symbol != "" {
		symbols = req.Symbol
	} else {
		var symbolGroupInfo models.LineSymbolGroup
		e.Orm.Model(&models.LineSymbolGroup{}).Where("id = ?", req.GroupId).Find(&symbolGroupInfo)
		symbols = symbolGroupInfo.Symbol
	}

	if req.IsAll == 1 {
		lineSymbols := make([]models.LineSymbol, 0)
		futSymbols := make([]string, 0)
		e.Orm.Model(&models.LineSymbol{}).Where("type = '2' AND switch = '1'").Find(&lineSymbols)
		for _, symbol := range lineSymbols {
			futSymbols = append(futSymbols, symbol.Symbol)
		}
		if len(futSymbols) != 0 {
			symbols = strings.Join(futSymbols, ",")
		}
	}

	if req.ExchangeType == global.EXCHANGE_BINANCE {
		e.SetBinanceMarginType(req, users, symbols, errs)
	}
}

// CancelOpenOrder 取消委托
func (e *LinePreOrder) CancelOpenOrder(req *dto.CancelOpenOrderReq, errs *[]error) {
	newClientOrderIdList := make([]string, 0)
	var apiUserInfo models.LineApiUser
	e.Orm.Model(&models.LineApiUser{}).Where("id = ?", req.ApiId).Find(&apiUserInfo)
	futApi := binanceservice.FutRestApi{}
	spotApi := binanceservice.SpotRestApi{}

	//取消指定订单号的委托
	if req.OrderSn != "" && req.Symbol != "" {
		var err error
		var orderInfo models.LinePreOrder
		e.Orm.Model(&models.LinePreOrder{}).Where("symbol = ? AND order_sn = ? AND status = '5'", req.Symbol, req.OrderSn).Find(&orderInfo)
		if orderInfo.Id <= 0 {
			*errs = append(*errs, fmt.Errorf("取消委托失败:%+v", "未找到可以取消的委托"))
			return
		}
		if req.OrderType == 1 { //现货
			err = spotApi.CancelOpenOrderByOrderSn(apiUserInfo, req.Symbol, req.OrderSn)
		} else {
			newClientOrderIdList = append(newClientOrderIdList, req.OrderSn)
			err = futApi.CancelBatchFutOrder(apiUserInfo, req.Symbol, newClientOrderIdList)
		}
		if err != nil {
			*errs = append(*errs, fmt.Errorf("取消委托失败:%+v", err.Error()))
			return
		}
	} else {
		var orderList []models.LinePreOrder
		e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND main_id = 0 status = '5'", req.ApiId).Find(&orderList)
		if len(orderList) <= 0 {
			*errs = append(*errs, fmt.Errorf("没有可撤销的委托"))
			return
		}
		for _, order := range orderList {

			if order.SymbolType == global.SYMBOL_SPOT {
				err := spotApi.CancelOpenOrderByOrderSn(apiUserInfo, req.Symbol, req.OrderSn)
				if err != nil {
					*errs = append(*errs, fmt.Errorf("取消委托失败:%+v", err.Error()))
					continue
				}
			} else {
				err := futApi.CancelBatchFutOrder(apiUserInfo, order.Symbol, []string{order.OrderSn})
				if err != nil {
					*errs = append(*errs, fmt.Errorf("取消委托失败:%+v", err.Error()))
					continue
				}
			}
		}
	}
}

// ClearAll 一键清除数据
func (e *LinePreOrder) ClearAll() error {
	_, err := helper.DefaultRedis.BatchDeleteKeys([]string{rediskey.PreSpotOrderList, rediskey.PreFutOrderList})
	if err != nil {
		e.Log.Errorf("Service RemoveLinePreOrder error:%s \r\n", err)
		return err
	}
	prefixs := []string{
		"spot_stoploss_list",
		"futures_stoploss_list",
		"spot_add_position_list",
		"futures_add_position_list",
		"api_user_hold",
		"spot_trigger_lock",
		"fut_trigger_lock",
		"fut_trigger_stop_lock",
		"spot_trigger_stop_lock",
		"spot_addposition_trigger",
		"fut_addposition_trigger",
		"spot_hedge_close_position",
		"futures_hedge_close_position",
		"spot_callback",
		"fut_callback",
		"holde_a",
		"holde_b",
		"stop_loss_markt",
		"_PreSpotOrderList_",
		"_PreFutOrderList_",
		"spot_reduce_list",
		"futures_reduce_list",
	}
	err = helper.DefaultRedis.DeleteKeysByPrefix(prefixs...)
	if err != nil {
		e.Log.Errorf("Service RemoveLinePreOrder error:%s \r\n", err)
		return err
	}

	e.Orm.Model(&models.LinePreOrder{}).Exec("TRUNCATE TABLE line_pre_order")        //订单表
	e.Orm.Model(&models.LinePreOrder{}).Exec("TRUNCATE TABLE line_pre_order_status") //订单拓展状态
	e.Orm.Model(&models.LinePreOrder{}).Exec("TRUNCATE TABLE line_pre_order_ext")    //订单拓展配置
	return err
}

// GetTargetSymbol 获取目标交易对信息
func (e *LinePreOrder) GetTargetSymbol(symbol string, symbolType int) (string, bool, models.LineSymbol, error) {
	var targetSymbol string
	var notUsdt bool
	var symbolInfo models.LineSymbol

	// 处理非 USDT 交易对
	if !strings.HasSuffix(symbol, "USDT") {
		notUsdt = true
		if err := e.Orm.Model(&models.LineSymbol{}).Where("symbol = ? AND type = ?", symbol, utility.IntToString(symbolType)).Find(&symbolInfo).Error; err != nil {
			return "", false, models.LineSymbol{}, err
		}
		if symbolInfo.Id <= 0 {
			return "", false, models.LineSymbol{}, fmt.Errorf("未找到交易对信息")
		}
		targetSymbol = symbolInfo.BaseAsset + "USDT"
	} else {
		targetSymbol = symbol
	}

	return targetSymbol, notUsdt, symbolInfo, nil
}

func (e *LinePreOrder) GetOrderInfo(req dto.ManuallyCover, symbol, orderType, site, status string) (models.LinePreOrder, error) {
	var orderInfo models.LinePreOrder
	if err := e.Orm.Model(models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND site = ? AND status = ?", req.ApiId, symbol, orderType, site, status).Find(&orderInfo).Error; err != nil {
		return models.LinePreOrder{}, err
	}
	if orderInfo.Id <= 0 {
		return models.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
	}
	return orderInfo, nil
}

func (e *LinePreOrder) GetFutOrderInfo(req dto.ManuallyCover, symbol, orderType, status string) (models.LinePreOrder, error) {
	var orderInfo models.LinePreOrder
	if err := e.Orm.Model(models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND status = ? AND cover_type = 2", req.ApiId, symbol, orderType, status).Find(&orderInfo).Error; err != nil {
		return models.LinePreOrder{}, err
	}
	if orderInfo.Id <= 0 {
		return models.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
	}
	return orderInfo, nil
}

// GetFutSpotOrderInfo 获取合约对现货的订单信息
func (e *LinePreOrder) GetFutSpotOrderInfo(req dto.ManuallyCover, symbol, orderType, status string) (models.LinePreOrder, error) {
	var orderInfo models.LinePreOrder
	if err := e.Orm.Model(models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND order_type = ? AND status = ? AND cover_type = 3", req.ApiId, symbol, orderType, status).Find(&orderInfo).Error; err != nil {
		return models.LinePreOrder{}, err
	}
	if orderInfo.Id <= 0 {
		return models.LinePreOrder{}, fmt.Errorf("未找到主仓信息")
	}
	return orderInfo, nil
}

// CalculateAmount 计算加仓数量
func (e *LinePreOrder) CalculateAmount(req dto.ManuallyCover, totalNum, lastPrice decimal.Decimal, amountDigit int, notUsdt bool, symbolInfo models.LineSymbol) (decimal.Decimal, error) {
	var amt decimal.Decimal
	if req.CoverType == 1 {
		decimalValue := utility.StringToDecimal(req.Value).Div(decimal.NewFromInt(100))
		amt = totalNum.Mul(decimalValue)
	} else {
		decimalValue := utility.StringToDecimal(req.Value)
		if notUsdt {
			tickerSymbolMaps := make([]dto.Ticker, 0)
			tickerSymbol := helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
			if err := sonic.Unmarshal([]byte(tickerSymbol), &tickerSymbolMaps); err != nil {
				return decimal.Zero, err
			}

			var tickerPrice decimal.Decimal
			for _, symbolMap := range tickerSymbolMaps {
				if symbolMap.Symbol == strings.ToUpper(symbolInfo.BaseAsset+"USDT") {
					tickerPrice, _ = decimal.NewFromString(symbolMap.Price)
					break
				}
			}

			for _, symbolMap := range tickerSymbolMaps {
				if symbolMap.Symbol == strings.ToUpper(symbolInfo.QuoteAsset+"USDT") {
					uTickerPrice, _ := decimal.NewFromString(symbolMap.Price)
					div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice))
					amt = decimalValue.Div(div)
					break
				}
			}
		} else {
			amt = decimalValue.Div(lastPrice)
		}
	}
	return amt.Truncate(int32(amountDigit)), nil
}

// SpotClosePosition 现货单个交易对平仓
func (e *LinePreOrder) SpotClosePosition(position *dto.ClosePosition, errs *[]error) {
	var apiUserInfo models.LineApiUser
	e.Orm.Model(&models.LineApiUser{}).Where("id = ?", position.ApiId).Find(&apiUserInfo)
	client := binanceservice.GetClient(&apiUserInfo)

	resp, _, err := client.SendSpotAuth("/api/v3/account", "GET", map[string]interface{}{
		"omitZeroBalances": true,
	})

	if err != nil {
		*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 获取账户信息失败", position.ApiId)))
		return
	}
	var balanceInfo binanceservice.SpotAccountInfo
	sonic.Unmarshal(resp, &balanceInfo)

	if len(balanceInfo.Balances) == 0 {
		*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 没有可平仓的交易对", position.ApiId)))
	}

	//查询已经开仓的现货交易对
	var spotList []models.LinePreOrder
	if position.Symbol == "" { //全平
		e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ?  AND symbol_type =1 AND status = 6 AND pid = 0 AND order_type = 0", position.ApiId).Find(&spotList)
	} else {
		e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ?  AND symbol_type =1 AND symbol = ? AND status = 6 AND pid = 0 AND order_type = 0", position.ApiId, position.Symbol).Find(&spotList)
	}
	if len(spotList) <= 0 {
		*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 没有可平仓的交易对", position.ApiId)))
	}
	api := binanceservice.SpotRestApi{}

	if len(spotList) == 0 {
		*errs = append(*errs, errors.New("无仓可平"))
		return
	}

	for _, list := range spotList {
		for _, balance := range balanceInfo.Balances {
			suffix := utility.ReplaceSuffix(list.Symbol, list.QuoteSymbol, "")
			if utility.StringToDecimal(balance.Free).GreaterThan(decimal.Zero) && balance.Asset == suffix {
				//锁仓数量大于0
				if utility.StringToDecimal(balance.Locked).GreaterThan(decimal.Zero) {
					//撤销之前的委托
					client.SendSpotAuth("/api/v3/openOrders", "DELETE", map[string]string{
						"symbol": list.Symbol,
					})
				}
				key := fmt.Sprintf(global.TICKER_SPOT, list.ExchangeType, list.Symbol)
				tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key)
				total := utility.StringToDecimal(balance.Free).Add(utility.StringToDecimal(balance.Locked)).Truncate(int32(tradeSet.AmountDigit))
				lastPrice := api.GetSpotSymbolLastPrice(list.Symbol)
				var price decimal.Decimal
				paramsMaps := make(map[string]string)
				if total.GreaterThan(decimal.NewFromFloat(tradeSet.MinQty)) {
					paramsMaps = map[string]string{
						"symbol":           list.Symbol,
						"side":             "SELL",
						"quantity":         total.String(),
						"type":             "LIMIT",
						"newClientOrderId": utility.Int64ToString(snowflakehelper.GetOrderId()),
						"timeInForce":      "GTC",
					}
					price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
					paramsMaps["price"] = price.String()
				} else {
					*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 下单数量小于最小下单数量", position.ApiId)))
					continue
				}
				order := models.LinePreOrder{
					ExchangeType:  global.EXCHANGE_BINANCE,
					Pid:           list.Id,
					MainId:        list.Id,
					ApiId:         list.ApiId,
					GroupId:       "0",
					Symbol:        list.Symbol,
					SymbolType:    position.CloseType,
					QuoteSymbol:   list.QuoteSymbol,
					SignPrice:     lastPrice.String(),
					SignPriceType: "new",
					Rate:          position.Rate,
					Price:         price.String(),
					Num:           total.String(),
					BuyPrice:      "0",
					Site:          "SELL",
					OrderSn:       paramsMaps["newClientOrderId"],
					OrderType:     3,
					Desc:          "",
					Status:        0,
					CoverType:     list.CoverType,
					ExpireTime:    time.Now().Add(time.Hour * 24 * 30),
					MainOrderType: list.MainOrderType,
					Child:         nil,
					OrderCategory: 1,
				}
				err := e.Orm.Model(&models.LinePreOrder{}).Create(&order).Error
				if err != nil {
					*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 生成订单失败:%s", position.ApiId, err.Error())))
					continue
				}

				//下订单
				_, _, err = client.SendSpotAuth("/api/v3/order", "POST", paramsMaps)
				if err != nil {
					*errs = append(*errs, errors.New(fmt.Sprintf("api_id:%d 币安下订单失败:%s", position.ApiId, err.Error())))
					continue
				}
				binanceservice.MainClosePositionClearCache(list.Id, list.SymbolType)
			}
		}
	}
}

// FutClosePosition 合约平仓
func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]error) {
	var apiUserInfo models.LineApiUser
	e.Orm.Model(&models.LineApiUser{}).Where("id = ?", position.ApiId).Find(&apiUserInfo)
	//client := binanceservice.GetClient(&apiUserInfo)

	//查询已经开仓的合约交易对
	var futList []models.LinePreOrder
	if position.Symbol == "" {
		e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND status = 6 AND symbol_type =2 AND order_type =0 AND main_id = 0", position.ApiId).Find(&futList)
	} else {
		e.Orm.Model(&models.LinePreOrder{}).Where("api_id = ? AND symbol = ? AND symbol_type =2 AND status = 6 AND order_type = 0 AND main_id = 0", position.ApiId, position.Symbol).Find(&futList)
	}
	if len(futList) <= 0 {
		*errs = append(*errs, fmt.Errorf("api_id:%d 没有可平仓的交易对", position.ApiId))
		return
	}

	api := binanceservice.FutRestApi{}

	if len(futList) == 0 {
		*errs = append(*errs, errors.New("无仓可平"))
		return
	}

	for _, list := range futList {
		risks, err := api.GetPositionV3(&apiUserInfo, list.Symbol)
		if err != nil {
			*errs = append(*errs, fmt.Errorf("api_id:%d 获取仓位信息时 没有可平仓的交易对 err: %s", position.ApiId, err))
			continue
		}

		key := fmt.Sprintf(global.TICKER_FUTURES, list.ExchangeType, list.Symbol)
		tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, key)
		lastPrice := api.GetFutSymbolLastPrice(list.Symbol)
		parentId := list.Id

		if list.Pid > 0 {
			parentId = list.Pid
		}

		for _, risk := range risks {
			positionAmt := utility.StringToDecimal(risk.PositionAmt)

			var riskSide string
			var orderSide string
			if positionAmt.GreaterThan(decimal.Zero) {
				riskSide = "BUY"
			} else {
				riskSide = "SELL"
			}
			if riskSide == list.Site {
				var price decimal.Decimal
				if list.Site == "BUY" && risk.PositionSide == "LONG" { //做多
					//根据仓位数量去平多
					orderSide = "SELL"
					price = lastPrice.Mul(decimal.NewFromInt(1).Add(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
				} else if list.Site == "SELL" && risk.PositionSide == "SHORT" {
					orderSide = "BUY"
					price = lastPrice.Mul(decimal.NewFromInt(1).Sub(utility.StringToDecimal(position.Rate).Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
				}

				if price.LessThanOrEqual(decimal.Zero) {
					continue
				}

				if positionAmt.Abs().LessThanOrEqual(decimal.NewFromFloat(tradeSet.MinQty)) {
					*errs = append(*errs, fmt.Errorf("api_id:%d 下单数量小于最小下单数量", position.ApiId))
					continue
				}

				order := models.LinePreOrder{
					Pid:           parentId,
					ExchangeType:  list.ExchangeType,
					ApiId:         list.ApiId,
					MainId:        list.Id,
					GroupId:       "0",
					Symbol:        list.Symbol,
					SymbolType:    position.CloseType,
					QuoteSymbol:   list.QuoteSymbol,
					SignPrice:     lastPrice.String(),
					SignPriceType: "new",
					Rate:          position.Rate,
					Price:         price.String(),
					Num:           positionAmt.Abs().String(),
					BuyPrice:      "0",
					Site:          orderSide,
					OrderSn:       utility.Int64ToString(snowflakehelper.GetOrderId()),
					OrderType:     3,
					Desc:          "",
					Status:        0,
					CoverType:     list.CoverType,
					ExpireTime:    time.Now().Add(24 * 30 * time.Hour),
					MainOrderType: list.MainOrderType,
					Child:         nil,
					OrderCategory: 1,
				}
				err = e.Orm.Model(&models.LinePreOrder{}).Create(&order).Error
				if err != nil {
					*errs = append(*errs, fmt.Errorf("api_id:%d 生成平仓单错误:%s", position.ApiId, err))
					continue
				}

				//撤销合约的委托
				api.CancelAllFutOrder(apiUserInfo, list.Symbol)
				//side=BUY&positionSide=LONG是开多，
				//side=SELL&positionSide=LONG是平多，
				//side=SELL&positionSide=SHORT是开空，
				//side=BUY&positionSide=SHORT是平空。
				if orderSide != "" {
					if orderSide == "BUY" { //平空
						err = api.ClosePosition(list.Symbol, order.OrderSn, utility.StringToDecimal(order.Num), "BUY", "SHORT", apiUserInfo, "LIMIT", "0", price)
					} else { // 平多
						err = api.ClosePosition(list.Symbol, order.OrderSn, utility.StringToDecimal(order.Num), "SELL", "LONG", apiUserInfo, "LIMIT", "0", price)
					}
					if err != nil {
						*errs = append(*errs, fmt.Errorf("api_id:%d 币安平仓单错误:%s", position.ApiId, err))
						continue
					}
					// 主单平仓删除缓存
					binanceservice.MainClosePositionClearCache(list.Id, list.SymbolType)
				}
			}
		}
	}

}

// ClearUnTriggered 清除待触发的交易对
func (e *LinePreOrder) ClearUnTriggered() error {
	var orderLists []models.LinePreOrder
	e.Orm.Model(&models.LinePreOrder{}).Where("main_id = 0 AND pid = 0 AND status = '0'").Find(&orderLists).Unscoped().Delete(&models.LinePreOrder{})

	for _, order := range orderLists {
		redisList := dto.PreOrderRedisList{
			Id:          order.Id,
			Symbol:      order.Symbol,
			Price:       order.Price,
			Site:        order.Site,
			ApiId:       order.ApiId,
			OrderSn:     order.OrderSn,
			QuoteSymbol: order.QuoteSymbol,
		}
		tradeSet, _ := helper.GetObjString[models2.TradeSet](helper.DefaultRedis, fmt.Sprintf(global.TICKER_SPOT, order.ExchangeType, order.Symbol))
		redisList.Price = utility.StringToDecimal(redisList.Price).Truncate(int32(tradeSet.PriceDigit)).String()
		marshal, _ := sonic.Marshal(redisList)
		if order.SymbolType == 1 {
			key := fmt.Sprintf(rediskey.PreFutOrderList, order.ExchangeType)
			helper.DefaultRedis.LRem(key, string(marshal))
		} else {
			key := fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType)
			helper.DefaultRedis.LRem(key, string(marshal))
		}
		e.Orm.Model(&models.LinePreOrder{}).Where("main_id = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{})
	}
	return nil
}

func (e *LinePreOrder) QueryOrder(req *dto.QueryOrderReq) (res interface{}, err error) {
	var apiUserInfo models.LineApiUser
	e.Orm.Model(&models.LineApiUser{}).Where("id = ?", req.ApiId).Find(&apiUserInfo)
	var client *helper.BinanceClient

	if apiUserInfo.UserPass == "" {
		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress)
	} else {
		client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress)
	}
	params := map[string]string{"symbol": req.Symbol, "origClientOrderId": req.OrderSn}
	if req.OrderType == 1 { //现货
		var resp dto.QuickAddPreOrderReq
		auth, code, err := client.SendSpotAuth("/api/v3/order", "GET", params)
		if code != 200 {
			return nil, err
		}

		if err != nil {
			return nil, err
		}
		sonic.Unmarshal(auth, &resp)
		return resp, nil
	}

	if req.OrderType == 2 {
		var resp dto.FutQueryOrderResp
		auth, code, err := client.SendFuturesRequestAuth("/fapi/v1/order", "GET", params)
		if code != 200 {
			return nil, err
		}
		if err != nil {
			return nil, err
		}
		sonic.Unmarshal(auth, &resp)
		return resp, nil
	}
	return nil, err
}

func (e *LinePreOrder) QueryAiCoinPrice(req *dto.QueryAiCoinPriceReq) (models.LineDirection, error) {
	var info models.LineDirection
	err := e.Orm.Model(&models.LineDirection{}).Where("symbol = ?", req.Symbol).Find(&info).Error
	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).Truncate(int32(tradeSet.AmountDigit))
	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.LossEndPercent = req.ReducePriceRatio
	mainParam.RemainingQuantity = mainAmount
	e.CalculateBreakEvenRatio(&mainParam, &calculateResp)
	mainParam.RemainingQuantity = calculateResp.RemainingQuantity
	mainParam.TotalLossAmountU = calculateResp.TotalLossAmountU
	req.ReduceReTakeProfitRatio = calculateResp.Ratio
	mainParam.LossBeginPercent = req.ReducePriceRatio

	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

	if req.LossEndPercent.Cmp(req.LossBeginPercent) < 0 {
		return errors.New("截至亏损百分比必须大于开始亏损百分比")
	}

	if req.SymbolType == 1 {
		val, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, req.Symbol))

		sonic.Unmarshal([]byte(val), &tradeSet)
	} else if req.SymbolType == 2 {
		val, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_FUTURES, req.ExchangeType, req.Symbol))

		sonic.Unmarshal([]byte(val), &tradeSet)
	} else {
		return errors.New("symbolType error")
	}

	if tradeSet.LastPrice == "" {
		return errors.New("没有找到交易对行情")
	}

	var addPositionBuyPrice decimal.Decimal

	if req.AddPositionType == 1 {
		addPositionBuyPrice = req.BuyPrice.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(2)
	} else {
		addPositionBuyPrice = req.AddPositionVal.Truncate(2)
	}

	var percentDiff decimal.Decimal
	var reduceAmount decimal.Decimal
	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))

	//计算价格下跌价差
	if req.LossEndPercent.Cmp(req.LossBeginPercent) > 0 {
		percentDiff = req.LossEndPercent.Sub(req.LossBeginPercent).Abs()
	}

	totalAmount := req.RemainingQuantity.Add(addPositionAmount)
	lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.PriceDigit))

	//计算减仓数量
	if req.ReducePercent.Cmp(decimal.NewFromInt(0)) > 0 {
		reduceAmount = totalAmount.Mul(req.ReducePercent.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit))
	}

	data.RemainingQuantity = totalAmount.Sub(reduceAmount)
	data.TotalLossAmountU = lossAmountU.Add(req.TotalLossAmountU)

	//计算百分比
	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
}

// 手动加仓
func (e *LinePreOrder) AddPosition(req *dto.LinePreOrderAddPositionReq) error {
	lastPositionOrder := models.LinePreOrder{}
	var tradeSet models2.TradeSet

	if req.OrderType == 1 {
		tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 0)
	} else if req.OrderType == 2 {
		tradeSet, _ = binanceservice.GetTradeSet(req.Symbol, 1)
	} else {
		return fmt.Errorf("交易对：%s 订单类型错误", req.Symbol)
	}

	if tradeSet.LastPrice == "" {
		return fmt.Errorf("交易对：%s 交易对配置错误", req.Symbol)
	}

	if err := e.Orm.Model(&lastPositionOrder).Where("symbol =? AND status = 6 AND site =? AND api_id =? AND  symbol_type =? AND exchange_type=?",
		req.Symbol, req.Site, req.ApiUserId, req.OrderType, req.ExchangeType).Error; err != nil {
		logger.Errorf("交易对：%s查询已开仓订单失败", req.Symbol)
		return fmt.Errorf("交易对：%s 没有已开仓的订单", req.Symbol)
	}

	ext := models.LinePreOrderExt{
		MainOrderId:           lastPositionOrder.Id,
		TakeProfitRatio:       req.Profit,
		ReducePriceRatio:      req.ReducePriceRatio,
		ReduceNumRatio:        req.ReduceNumRatio,
		ReduceTakeProfitRatio: req.ReduceTakeProfitRatio,
		ReduceStopLossRatio:   req.ReduceStopLossRatio,
		AddPositionOrderType:  req.AddPositionOrderType,
		AddPositionType:       2,
		AddPositionVal:        req.BuyPrice,
	}

	addPosition := models.LinePreOrder{
		SignPrice:     tradeSet.LastPrice,
		Pid:           lastPositionOrder.Id,
		MainId:        lastPositionOrder.Id,
		Symbol:        req.Symbol,
		QuoteSymbol:   tradeSet.Currency,
		SignPriceU:    utility.StrToDecimal(tradeSet.LastPrice),
		ApiId:         req.ApiUserId,
		Site:          req.Site,
		ExchangeType:  req.ExchangeType,
		OrderType:     0,
		OrderCategory: 3,
		BuyPrice:      req.BuyPrice.String(),
		Status:        0,
		SymbolType:    req.OrderType,
		MainOrderType: req.AddPositionOrderType,
		ExpireTime:    time.Now().Add(4),
		OrderSn:       utility.Int64ToString(snowflakehelper.GetOrderId()),
	}

	tickerPrice := utility.StrToDecimal(tradeSet.LastPrice)
	if req.PricePattern == "percentage" {
		addPosition.Rate = req.Price.String()
		priceRate := req.Price.Div(decimal.NewFromInt(100)) //下单价除100 =0.1

		if strings.ToUpper(req.Site) == "BUY" { //购买方向
			//实际下单价格
			truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit))
			addPosition.Price = truncate.String()
		} else {
			truncate := tickerPrice.Mul(decimal.NewFromInt(1).Add(priceRate)).Truncate(int32(tradeSet.PriceDigit))
			addPosition.Price = truncate.String()
		}

	} else { //实际价格下单
		addPosition.Price = req.Price.Truncate(int32(tradeSet.PriceDigit)).String()
		addPosition.SignPriceType = req.PricePattern
		addPosition.Rate = "0"
	}
	if tradeSet.Currency != "USDT" { //不是U本位
		//获取币本位兑换u的价格
		ticker2 := models2.TradeSet{}
		tickerVal, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.TICKER_SPOT, req.ExchangeType, strings.ToUpper(tradeSet.Coin+"USDT")))

		if tickerVal == "" {
			logger.Error("查询行情失败")
			return fmt.Errorf("交易对：%s 获取u本位行情失败", req.Symbol)
		}

		err := sonic.Unmarshal([]byte(tickerVal), &ticker2)

		if ticker2.LastPrice == "" {
			logger.Errorf("查询行情失败 %s err:%v", strings.ToUpper(tradeSet.Coin+"USDT"), err)
			return fmt.Errorf("交易对：%s 获取u本位行情 反序列化失败", req.Symbol)
		}
		//LTCBTC --> LTCUSDT
		uTickerPrice := utility.StrToDecimal(ticker2.LastPrice) //94069
		//换算成U
		//div := decimal.NewFromInt(1).Div(uTickerPrice) //0.0000106365
		//在换算成对应交易对对应的价值
		//LTCBTC --> LTCUSDT == LTCUSDT -- 100.502
		div := tickerPrice.Div(decimal.NewFromInt(1).Div(uTickerPrice))
		//计算下单数量
		addPosition.Num = req.BuyPrice.Div(div).Truncate(int32(tradeSet.AmountDigit)).String()
	} else {
		fromString, _ := decimal.NewFromString(addPosition.Price)
		addPosition.Num = req.BuyPrice.Div(fromString).Truncate(int32(tradeSet.AmountDigit)).String()
	}
	//事务保存
	err := e.Orm.Transaction(func(tx *gorm.DB) error {
		//添加加仓单
		if err := tx.Create(&addPosition).Error; err != nil {
			return err
		}

		//止盈、减仓
		orders, err := makeFuturesTakeAndReduce(&addPosition, ext, 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 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 && ext.ReduceNumRatio.Cmp(decimal.NewFromInt(100)) < 0 {
				reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), ext, 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
				}
			}
		}

		ext.OrderId = addPosition.Id
		if err := tx.Create(&ext).Error; err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		logger.Errorf("交易对：%s 添加加仓订单失败", req.Symbol)
		return fmt.Errorf("交易对：%s 添加加仓订单失败", req.Symbol)
	}

	return nil
}
