This commit is contained in:
2025-02-18 09:59:51 +08:00
parent eb2455f967
commit 08a7da607f
9 changed files with 420 additions and 548 deletions

View File

@ -606,7 +606,7 @@ func (e LinePreOrder) QueryAiCoinPrice(c *gin.Context) {
// 计算回本盈利比例
func (e LinePreOrder) CalculateBreakEevenRatio(c *gin.Context) {
s := service.LinePreOrder{}
req := dto.CalculateBreakEevenRatioReq{}
req := dto.LineAddPreOrderReq{}
err := e.MakeContext(c).
MakeOrm().
@ -619,12 +619,12 @@ func (e LinePreOrder) CalculateBreakEevenRatio(c *gin.Context) {
return
}
data := dto.CalculateBreakEvenRatioResp{}
err = s.CalculateBreakEvenRatio(&req, &data)
// data := dto.CalculateBreakEvenRatioResp{}
_, err = s.GenerateOrder(&req)
if err != nil {
e.Error(500, err, err.Error())
return
}
e.OK(data, "操作成功")
e.OK(req, "操作成功")
}

View File

@ -12,6 +12,7 @@ type LinePreOrderExt struct {
MainOrderId int `json:"mainOrderId" gorm:"type:bigint;comment:主单id"`
OrderId int `json:"orderId" gorm:"type:bigint;comment:订单id"`
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" gorm:"type:decimal(10,2);comment:止盈百分比"`
ReTakeRatio decimal.Decimal `json:"reTakeRatio" gorm:"type:decimal(10,2);comment:亏损回本止盈百分比"`
ReduceOrderType string `json:"reduceOrderType" gorm:"type:varchar(20);comment:减仓类型 LIMIT-限价 MARKET-市价"`
ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" gorm:"type:decimal(10,2);comment:减仓价格百分比"`
ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" gorm:"type:decimal(10,2);comment:减仓数量百分比"`
@ -21,6 +22,9 @@ type LinePreOrderExt struct {
AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" gorm:"type:decimal(10,2);comment:加仓价格百分比"`
AddPositionType int `json:"addPositionType" gorm:"type:int;comment:加仓类型 1-百分比 2-实际金额"`
AddPositionVal decimal.Decimal `json:"addPositionVal" gorm:"type:decimal(10,2);comment:加仓值"`
ReduceReTakeRatio decimal.Decimal `json:"reduceReTakeRatio" gorm:"type:decimal(10,2);comment:减仓后亏损回本止盈百分比"`
TotalAfterAdding decimal.Decimal `json:"totalAfterAdding" gorm:"-"` //加仓后总数
TotalAfterReducing decimal.Decimal `json:"totalAfterReducing" gorm:"-"` //减仓后总数
models.ModelTime
models.ControlBy
}

View File

@ -38,7 +38,7 @@ func registerLinePreOrderRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTM
r.POST("clearUnTriggered", actions.PermissionAction(), api.ClearUnTriggered) // 清除待触发的交易对
r.POST("aiCoinPrice", actions.PermissionAction(), api.QueryAiCoinPrice) //获取aiCoin买入点
r.GET("/calculate", api.CalculateBreakEevenRatio) //计算亏损后止盈百分比
r.POST("/calculate", api.CalculateBreakEevenRatio) //计算亏损后止盈百分比
}
}

View File

@ -191,18 +191,20 @@ type LineAddPreOrderReq struct {
Price string `json:"price"` //下单价百分比
Profit string `json:"profit"` //止盈价
// StopPrice string `json:"stop_price"` //止损价
PriceType string `json:"price_type"` //价格类型
SaveTemplate string `json:"save_template"` //是否保存模板
TemplateName string `json:"template_name"` //模板名字
SymbolType int `json:"symbol_type"` //交易对类型 1-现货 2-合约
CoverType int `json:"cover_type"` //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货
ExpireHour int `json:"expire_hour"` // 过期时间 单位小时
MainOrderType string `json:"main_order_type"` //主单类型:限价(LIMIT)或市价(MARKET)
ReducePriceRatio decimal.Decimal `json:"reduce_price"` //主单减仓价格百分比
ReduceNumRatio decimal.Decimal `json:"reduce_num"` //主单减仓数量百分比
ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比
ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比
Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段
PriceType string `json:"price_type"` //价格类型
SaveTemplate string `json:"save_template"` //是否保存模板
TemplateName string `json:"template_name"` //模板名字
SymbolType int `json:"symbol_type"` //交易对类型 1-现货 2-合约
CoverType int `json:"cover_type"` //对冲类型 0=无对冲 1= 现货对合约 2=合约对合约 3 合约对现货
ExpireHour int `json:"expire_hour"` // 过期时间 单位小时
MainOrderType string `json:"main_order_type"` //主单类型:限价(LIMIT)或市价(MARKET)
ReducePriceRatio decimal.Decimal `json:"reduce_price"` //主单减仓价格百分比
ReduceNumRatio decimal.Decimal `json:"reduce_num"` //主单减仓数量百分比
ReduceTakeProfitRatio decimal.Decimal `json:"reduce_take_profit"` //主单减仓后止盈价百分比
ReduceStopLossRatio decimal.Decimal `json:"reduce_stop_price"` //主单减仓后止损价百分比
ReduceReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"减仓后亏损回本止盈百分比"`
Ext []LineAddPreOrderExtReq `json:"ext"` //拓展字段
}
func (req LineAddPreOrderReq) CheckParams() error {
@ -232,6 +234,11 @@ func (req LineAddPreOrderReq) CheckParams() error {
return nil
}
type LineTreeOrder struct {
models.LinePreOrder
Childs []models.LinePreOrder `json:"childs"`
}
// LineBatchAddPreOrderReq 批量添加订单请求参数
type LineBatchAddPreOrderReq struct {
ExchangeType string `json:"exchange_type"` //交易所类型 字典exchange_type

View File

@ -37,15 +37,17 @@ func (m *LinePreOrderExtGetPageReq) GetNeedSearch() interface{} {
}
type LineAddPreOrderExtReq struct {
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"`
ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比"`
ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比"`
ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止盈百分比"`
ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止百分比"`
AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比"`
AddPositionOrderType string `json:"addPositionOrderType" comment:"加仓订单类型 LIMIT-限价 MARKET-市价"`
AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"`
AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓"`
TakeProfitRatio decimal.Decimal `json:"takeProfitRatio" comment:"止盈百分比"`
ReTakeProfitRatio decimal.Decimal `json:"reTakeProfitRatio" comment:"亏损回本止盈百分比"`
ReducePriceRatio decimal.Decimal `json:"reducePriceRatio" comment:"减仓价格百分比"`
ReduceNumRatio decimal.Decimal `json:"reduceNumRatio" comment:"减仓数量百分比"`
ReduceTakeProfitRatio decimal.Decimal `json:"reduceTakeProfitRatio" comment:"减仓后止百分比"`
ReduceStopLossRatio decimal.Decimal `json:"reduceStopLossRatio" comment:"减仓后止损百分比"`
ReduceReTakeProfitRatio decimal.Decimal `json:"reduceReTakeProfitRatio" comment:"减仓后回本止盈百分比"`
AddPositionPriceRatio decimal.Decimal `json:"addPositionPriceRatio" comment:"加仓价格百分比"`
AddPositionOrderType string `json:"addPositionOrderType" comment:"加仓订单类型 LIMIT-限价 MARKET-市价"`
AddPositionType int `json:"addPositionType" comment:"加仓类型 1-百分比 2-实际金额"`
AddPositionVal decimal.Decimal `json:"addPositionVal" comment:"加仓值"`
}
type LinePreOrderExtInsertReq struct {

View File

@ -347,11 +347,13 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
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
AddOrder.SignPrice = tickerPrice.String()
if strings.ToUpper(req.Site) == "BUY" { //购买方向
//实际下单价格
truncate := tickerPrice.Mul(decimal.NewFromInt(1).Sub(priceRate)).Truncate(int32(tradeSet.PriceDigit))
@ -363,11 +365,10 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
} else { //实际价格下单
AddOrder.Price = utility.StringToDecimal(req.Price).Truncate(int32(tradeSet.PriceDigit)).String()
AddOrder.SignPrice = req.Price
AddOrder.SignPriceType = req.PricePattern
AddOrder.Rate = "0"
}
buyPrice, _ := decimal.NewFromString(req.BuyPrice) //购买多少U
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本位
@ -426,9 +427,31 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
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)
for _, addPosition := range req.Ext {
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.LossBeginPercent = req.ReducePriceRatio
mainParam.RemainingQuantity = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
mainParam.TotalLossAmountU = buyPrice.Mul(req.ReducePriceRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.PriceDigit))
for index, addPosition := range req.Ext {
ext := models.LinePreOrderExt{
TakeProfitRatio: addPosition.TakeProfitRatio,
ReducePriceRatio: addPosition.ReducePriceRatio,
@ -441,6 +464,30 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
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)
}
@ -520,10 +567,77 @@ 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()
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
}
preOrderExts[index].OrderId = addPosition.Id
if err := e.Orm.Create(&addPosition).Error; err != nil {
logger.Error("保存加仓单失败")
return err
}
//止盈、减仓
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
}
}
}
}
return nil
})
}
@ -531,6 +645,160 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
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
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.Truncate(int32(tradeSet.AmountDigit)).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 {
stopOrder.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).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(orderType int, apiUserId, site, baseCoin string) int64 {
var count int64
@ -1317,6 +1585,83 @@ func (e *LinePreOrder) QueryAiCoinPrice(req *dto.QueryAiCoinPriceReq) (models.Li
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)
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.LossBeginPercent = req.ReducePriceRatio
mainParam.RemainingQuantity = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
mainParam.TotalLossAmountU = buyPrice.Mul(req.ReducePriceRatio.Div(decimal.NewFromInt(100)).Truncate(4)).Truncate(int32(tradeSet.PriceDigit))
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
@ -1351,7 +1696,7 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio
var percentDiff decimal.Decimal
var reduceAmount decimal.Decimal
nowPrice := req.Price.Mul(decimal.NewFromInt(1).Sub(req.LossEndPercent.Div(decimal.NewFromInt(100).Truncate(4))))
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))
//计算价格下跌价差
@ -1360,7 +1705,7 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio
}
totalAmount := req.RemainingQuantity.Add(addPositionAmount)
lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100)).Truncate(4)).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.AmountDigit))
lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.AmountDigit))
//计算减仓数量
if req.ReducePercent.Cmp(decimal.NewFromInt(0)) > 0 {
@ -1373,6 +1718,8 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio
//计算百分比
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

View File

@ -158,7 +158,7 @@ func (t LimitOrderTimeoutDuration) Exec(arg interface{}) error {
}
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 ('0','4') AND order_category = 3 AND updated_at < ?", time.Now().Add(-time.Duration(limitOrderTimeoutDuration)*time.Second)).Find(&orders).Error
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
}

View File

@ -12,14 +12,11 @@ import (
"go-admin/common/helper"
models2 "go-admin/models"
"go-admin/pkg/utility"
"go-admin/pkg/utility/snowflakehelper"
"strconv"
"strings"
"time"
"github.com/bytedance/sonic"
"github.com/go-admin-team/go-admin-core/logger"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
@ -115,12 +112,6 @@ func handleFutOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderSta
// 减仓回调
func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
apiUserInfo, _ := GetApiInfo(preOrder.ApiId)
mainId := preOrder.Id
if preOrder.MainId > 0 {
mainId = preOrder.MainId
}
if apiUserInfo.Id == 0 {
logger.Errorf("handleMainReduceFilled 获取api信息失败,订单号:%s", preOrder.OrderSn)
return
@ -161,74 +152,12 @@ func handleReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
}
orders := make([]models.LinePreOrder, 0)
rate := utility.StringAsFloat(preOrder.Rate)
ext := models.LinePreOrderExt{}
//获取订单配置
db.Model(&ext).Where("order_id =?", preOrder.Pid).First(&ext)
// 不是100%减仓 就需要挂止盈止损
if rate >= 100 {
removeFutLossAndAddPosition(preOrder)
ids := []int{preOrder.MainId, preOrder.Pid}
if err := db.Model(&DbModels.LinePreOrder{}).Where("id IN ? AND status =6", ids).Update("status", 9).Error; err != nil {
logger.Info("100%减仓完毕,终结流程")
}
if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status = 0", preOrder.Id).Find(&orders).Error; err != nil {
logger.Errorf("handleMainReduceFilled 获取待触发订单失败,订单号:%s", preOrder.OrderSn)
return
}
totalLossAmountU, _ := GetTotalLossAmount(db, mainId)
totalNum := getFuturesPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet)
takeProfitOrder := models.LinePreOrder{}
copier.Copy(&takeProfitOrder, &preOrder)
takeProfitOrder.Id = 0
takeProfitOrder.Pid = preOrder.Id
takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
takeProfitOrder.Status = 0
takeProfitOrder.Price = price.Mul(decimal.NewFromInt(1).Add(ext.TakeProfitRatio)).Truncate(int32(tradeSet.PriceDigit)).String()
takeProfitOrder.OrderType = 1
takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String()
takeProfitOrder.SignPrice = preOrder.Price
takeProfitOrder.CreatedAt = time.Now()
takeProfitOrder.BuyPrice = "0"
takeProfitOrder.MainOrderType = "LIMIT"
takeProfitOrder.Num = totalNum.String()
takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String()
//止盈需要累加之前的亏损
if totalLossAmountU.Cmp(decimal.Zero) > 0 {
percent := totalLossAmountU.Div(totalNum).Div(price).Abs()
takeProfitOrder.Rate = percent.Mul(decimal.NewFromInt(100)).Add(ext.ReduceTakeProfitRatio).Truncate(2).String()
}
setPrice(&takeProfitOrder, preOrder, tradeSet)
orders = append(orders, takeProfitOrder)
//有止损单
if ext.ReduceStopLossRatio.Cmp(decimal.Zero) > 0 {
var stoploss models.LinePreOrder
copier.Copy(&stoploss, &preOrder)
stoploss.Id = 0
stoploss.Pid = preOrder.Id
stoploss.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
stoploss.Status = 0
stoploss.CreatedAt = time.Now()
stoploss.OrderType = 2
stoploss.SignPrice = preOrder.Price
stoploss.BuyPrice = "0"
stoploss.Rate = ext.ReduceStopLossRatio.String()
stoploss.MainOrderType = "LIMIT"
stoploss.Num = totalNum.String()
setPrice(&stoploss, preOrder, tradeSet)
orders = append(orders, stoploss)
}
if err := db.Create(&orders).Error; err != nil {
logger.Errorf("handleMainReduceFilled 创建止盈止损单失败:%v", err)
return
}
futApi := FutRestApi{}
for _, v := range orders {
if v.OrderType == 1 {
@ -435,37 +364,27 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) {
return
}
//预生成加仓单
shouldReturn := createFutPreAddPosition(preOrder, db, tradeSet)
if shouldReturn {
return
}
if preOrder.OrderCategory == 3 {
if err := cancelSymbolTakeAndStop(db, preOrder.MainId, preOrder.SymbolType); err != nil {
logger.Errorf("取消止盈止损订单失败 orderSn:%s err:%v", preOrder.OrderSn, err)
}
}
apiInfo, err := GetApiInfo(preOrder.ApiId)
if apiInfo.Id == 0 {
logger.Error("订单回调查询apiuserinfo失败 err:", err)
return
}
if err := db.Model(&DbModels.LinePreOrder{}).
Where("pid = ? AND order_type > 0 AND status = '0' ", preOrder.Id).
Find(&orders).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
logger.Error("订单回调查询止盈止损单失败:", err)
return
} else if len(orders) == 0 && preOrder.OrderCategory == 3 {
orders, err = makeFuturesTakeAndReduce(preOrder, db, tradeSet, orders)
if err != nil {
return
}
}
futApi := FutRestApi{}
num, _ := decimal.NewFromString(preOrder.Num)
num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet)
for i, order := range orders {
if i >= 2 { // 最多处理 2 个订单
break
}
for _, order := range orders {
price := utility.StrToDecimal(order.Price).Truncate(int32(tradeSet.PriceDigit))
num = num.Truncate(int32(tradeSet.AmountDigit))
order.Price = price.String()
@ -495,184 +414,6 @@ func handleFutMainOrderFilled(db *gorm.DB, preOrder *models.LinePreOrder) {
}
}
// 生成加仓单
func createFutPreAddPosition(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet) bool {
//主单类型
if preOrder.OrderCategory == 1 {
orderExts, err := GetOrderExts(db, preOrder.Id)
if err != nil {
logger.Errorf("预生成加仓单失败, 回调订单号:%s 获取主单拓展配置失败:%v", preOrder.OrderSn, err)
return true
}
price := utility.StrToDecimal(preOrder.Price)
for _, v := range orderExts {
if v.OrderId == 0 {
var data DbModels.LinePreOrder
copier.Copy(&data, &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 {
data.Num = utility.StrToDecimal(preOrder.Num).Truncate(int32(tradeSet.AmountDigit)).String()
data.BuyPrice = "0"
} else {
data.BuyPrice = v.AddPositionVal.String()
data.Num = v.AddPositionVal.Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String()
}
err := db.Transaction(func(tx *gorm.DB) error {
if err2 := tx.Create(&data).Error; err2 != nil {
return err2
}
v.OrderId = data.Id
if err2 := tx.Model(&v).Update("order_id", data.Id).Error; err2 != nil {
return err2
}
return nil
})
if err != nil {
logger.Errorf("预生成加仓单失败, 回调订单号:%s 预生成加仓单失败:%v", preOrder.OrderSn, err)
return true
}
}
}
}
return false
}
// 构建合约止盈、减仓单
func makeFuturesTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet, orders []DbModels.LinePreOrder) ([]DbModels.LinePreOrder, error) {
ext := models.LinePreOrderExt{}
apiInfo, err := GetApiInfo(preOrder.ApiId)
price := utility.StrToDecimal(preOrder.Price)
if apiInfo.Id == 0 {
logger.Error("订单回调查询apiuserinfo失败 err:", err)
return nil, errors.New("订单回调查询apiuserinfo失败")
}
if err := db.Model(&ext).Where("order_id = ?", preOrder.Id).First(&ext).Error; err != nil {
logger.Error("订单回调查询止盈止损单扩展表失败:", err)
return nil, errors.New("订单回调查询止盈止损单扩展表失败")
}
totalLossAmountU, _ := GetTotalLossAmount(db, preOrder.MainId)
num := getFuturesPositionAvailableQuantity(db, apiInfo, preOrder, tradeSet)
//止盈单
if ext.TakeProfitRatio.Cmp(decimal.Zero) > 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 = ext.MainOrderId
profitOrder.Num = num.String()
profitOrder.Rate = ext.TakeProfitRatio.String()
//止盈需要累加之前的亏损
if totalLossAmountU.Cmp(decimal.Zero) > 0 {
percent := totalLossAmountU.Div(num).Div(price).Abs()
profitOrder.Rate = percent.Mul(decimal.NewFromInt(100)).Add(ext.TakeProfitRatio).Truncate(2).String()
}
if strings.ToUpper(preOrder.Site) == "BUY" {
profitOrder.Site = "SELL"
} else {
profitOrder.Site = "BUY"
}
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 = ext.MainOrderId
stopOrder.OrderType = 4
stopOrder.Status = 0
stopOrder.Rate = ext.ReducePriceRatio.String()
stopOrder.Num = num.String()
if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 {
stopOrder.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String()
} else {
stopOrder.Num = num.String()
}
if strings.ToUpper(preOrder.Site) == "BUY" {
stopOrder.Site = "SELL"
} else {
stopOrder.Site = "BUY"
}
setPrice(&stopOrder, preOrder, tradeSet)
orders = append(orders, stopOrder)
}
for index := range orders {
orderRate := utility.StrToDecimal(orders[index].Rate)
orderType := orders[index].OrderType
if strings.ToUpper(preOrder.Site) == "BUY" {
orders[index].Site = "SELL"
} else {
orders[index].Site = "BUY"
}
switch {
//做多止盈、做空止损或减仓
case (orderType == 1 && preOrder.Site == "BUY"), ((orderType == 2 || orderType == 4) && preOrder.Site == "SELL"):
orders[index].Price = utility.StrToDecimal(preOrder.Price).Mul(decimal.NewFromInt(1).Add(orderRate.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
//做多止损或减仓、做空止盈
case ((orderType == 2 || orderType == 4) && preOrder.Site == "BUY"), (orderType == 1 && preOrder.Site == "SELL"):
orders[index].Price = utility.StrToDecimal(preOrder.Price).Mul(decimal.NewFromInt(1).Sub(orderRate.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
}
}
if len(orders) > 0 {
if err := db.Create(&orders).Error; err != nil {
logger.Error("主单回调,创建止盈、减仓单失败")
return orders, errors.New("主单回调,创建止盈、减仓单失败")
}
}
return orders, nil
}
// 减仓单
func processFutReduceOrder(order DbModels.LinePreOrder, price, num decimal.Decimal) {
key := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE)

View File

@ -12,14 +12,12 @@ import (
"go-admin/common/helper"
models2 "go-admin/models"
"go-admin/pkg/utility"
"go-admin/pkg/utility/snowflakehelper"
"strconv"
"strings"
"time"
"github.com/bytedance/sonic"
"github.com/go-admin-team/go-admin-core/logger"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
@ -144,11 +142,6 @@ func handleOrderByType(db *gorm.DB, preOrder *DbModels.LinePreOrder, orderStatus
// 主单减仓完毕
func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
apiUserInfo, _ := GetApiInfo(preOrder.ApiId)
mainId := preOrder.Id
if preOrder.MainId > 0 {
mainId = preOrder.MainId
}
if apiUserInfo.Id == 0 {
logger.Errorf("handleMainReduceFilled 获取api信息失败,订单号:%s", preOrder.OrderSn)
@ -162,13 +155,8 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
return
}
price := utility.StrToDecimal(preOrder.Price)
parentOrder, _ := GetOrderById(db, preOrder.Pid)
orders := make([]models.LinePreOrder, 0)
rate := utility.StringAsFloat(preOrder.Rate)
ext := models.LinePreOrderExt{}
//获取订单配置
db.Model(&ext).Where("order_id =?", preOrder.Pid).First(&ext)
// 100%减仓 终止流程
if rate >= 100 {
@ -179,95 +167,36 @@ func handleMainReduceFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
}
return
}
totalLossAmountU, _ := GetTotalLossAmount(db, mainId)
totalNum := getSpotPositionAvailableQuantity(db, apiUserInfo, preOrder, tradeSet) //getSpotTotalNum(apiUserInfo, preOrder, tradeSet)
takeProfitOrder := models.LinePreOrder{}
copier.Copy(&takeProfitOrder, &preOrder)
takeProfitOrder.Id = 0
takeProfitOrder.Pid = preOrder.Id
takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
takeProfitOrder.Status = 0
// takeProfitOrder.Price = price.Mul(decimal.NewFromInt(1).Add(ext.TakeProfitRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
takeProfitOrder.OrderType = 1
takeProfitOrder.Rate = "100"
takeProfitOrder.SignPrice = preOrder.Price
takeProfitOrder.CreatedAt = time.Now()
takeProfitOrder.BuyPrice = "0"
takeProfitOrder.MainOrderType = "LIMIT"
takeProfitOrder.Num = totalNum.String()
takeProfitOrder.Rate = ext.ReduceTakeProfitRatio.String()
//止盈需要累加之前的亏损
if totalLossAmountU.Cmp(decimal.Zero) > 0 {
percent := totalLossAmountU.Div(totalNum).Div(price).Abs()
takeProfitOrder.Rate = percent.Mul(decimal.NewFromInt(100)).Add(ext.ReduceTakeProfitRatio).Truncate(2).String()
}
setPrice(&takeProfitOrder, preOrder, tradeSet)
orders = append(orders, takeProfitOrder)
//有止损单
if ext.ReduceStopLossRatio.Cmp(decimal.Zero) > 0 {
var stoploss models.LinePreOrder
copier.Copy(&stoploss, &preOrder)
stoploss.Id = 0
stoploss.Pid = preOrder.Id
stoploss.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
stoploss.Status = 0
stoploss.CreatedAt = time.Now()
stoploss.OrderType = 2
stoploss.SignPrice = preOrder.Price
stoploss.BuyPrice = "0"
stoploss.Rate = ext.ReduceStopLossRatio.String()
stoploss.MainOrderType = "LIMIT"
stoploss.Num = totalNum.String()
// stoploss.Price = price.Mul(decimal.NewFromInt(1).Sub(ext.ReduceStopLossRatio.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
setPrice(&stoploss, preOrder, tradeSet)
orders = append(orders, stoploss)
}
if err := db.Create(&orders).Error; err != nil {
logger.Errorf("handleMainReduceFilled 创建止盈止损单失败:%v", err)
if err := db.Model(&models.LinePreOrder{}).Where("pid =? AND order_type IN (1,2) AND status=0", preOrder.Id).Find(&orders).Error; err != nil {
logger.Errorf("获取减仓单止盈止损失败 err:%v", err)
return
}
spotApi := SpotRestApi{}
paramsMap := OrderPlacementService{
ApiId: takeProfitOrder.ApiId,
Symbol: takeProfitOrder.Symbol,
Side: takeProfitOrder.Site,
Type: "LIMIT",
TimeInForce: "GTC",
Price: utility.StrToDecimal(takeProfitOrder.Price),
Quantity: totalNum,
NewClientOrderId: takeProfitOrder.OrderSn,
StopPrice: utility.StrToDecimal(takeProfitOrder.Price),
}
if err := spotApi.OrderPlaceLoop(db, paramsMap, 3); err != nil {
logger.Errorf("减仓后重下止盈失败 减仓order_sn:%s err:%v", preOrder.OrderSn, err)
if err2 := db.Model(&takeProfitOrder).Updates(map[string]interface{}{"status": 2, "": err.Error()}).Error; err2 != nil {
logger.Errorf("handleMainReduceFilled 更新止盈单失败:%v", err2)
}
}
for index := range orders {
orders[index].Num = totalNum.Truncate(int32(tradeSet.AmountDigit)).String()
for _, item := range orders {
if item.OrderType == 2 {
processStopLossOrder(item)
if orders[index].OrderType == 1 {
processTakeProfitOrder(db, spotApi, orders[index])
} else if orders[index].OrderType == 2 {
processStopLossOrder(orders[index])
}
}
//计算实际亏损
if parentOrder.Price != "" {
parentPrice := utility.StrToDecimal(parentOrder.Price)
reduceNum := utility.StrToDecimal(preOrder.Num)
lossAmountU := price.Sub(parentPrice).Abs().Mul(reduceNum) //.Truncate(int32(tradeSet.PriceDigit))
// if parentOrder.Price != "" {
// parentPrice := utility.StrToDecimal(parentOrder.Price)
// reduceNum := utility.StrToDecimal(preOrder.Num)
// lossAmountU := price.Sub(parentPrice).Abs().Mul(reduceNum) //.Truncate(int32(tradeSet.PriceDigit))
if err := db.Model(&parentOrder).Update("loss_amount", lossAmountU).Error; err != nil {
logger.Errorf("修改主单实际亏损失败 订单号:%s err:%v", parentOrder.OrderSn, err)
}
}
// if err := db.Model(&parentOrder).Update("loss_amount", lossAmountU).Error; err != nil {
// logger.Errorf("修改主单实际亏损失败 订单号:%s err:%v", parentOrder.OrderSn, err)
// }
// }
//加仓待触发
addPositionOrder := DbModels.LinePreOrder{}
@ -508,78 +437,6 @@ func removeSpotLossAndAddPosition(preOrder *DbModels.LinePreOrder) {
// 主单成交
func handleMainOrderFilled(db *gorm.DB, preOrder *DbModels.LinePreOrder) {
processTakeProfitAndStopLossOrders(db, preOrder)
tradeSet, _ := GetTradeSet(preOrder.Symbol, 0)
if tradeSet.Coin == "" {
logger.Errorf("获取交易对配置失败, 回调订单号:%s", preOrder.OrderSn)
return
}
//主单类型
if preOrder.OrderCategory == 1 {
//预生成加仓单
orderExts, err := GetOrderExts(db, preOrder.Id)
if err != nil {
logger.Errorf("预生成加仓单失败, 回调订单号:%s 获取主单拓展配置失败:%v", preOrder.OrderSn, err)
return
}
price := utility.StrToDecimal(preOrder.Price)
for _, v := range orderExts {
if v.OrderId == 0 {
var data DbModels.LinePreOrder
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 {
data.Num = preOrder.Num
data.BuyPrice = "0"
} else {
data.BuyPrice = v.AddPositionVal.String()
data.Num = v.AddPositionVal.Div(dataPrice).Truncate(int32(tradeSet.AmountDigit)).String()
}
err := db.Transaction(func(tx *gorm.DB) error {
if err2 := tx.Create(&data).Error; err2 != nil {
return err2
}
v.OrderId = data.Id
if err2 := tx.Model(&v).Update("order_id", data.Id).Error; err2 != nil {
return err2
}
return nil
})
if err != nil {
logger.Errorf("预生成加仓单失败, 回调订单号:%s 预生成加仓单失败:%v", preOrder.OrderSn, err)
return
}
}
}
}
}
// 解析订单状态
@ -674,12 +531,6 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd
Find(&orders).Error; err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
logger.Error("订单回调查询止盈止损单失败:", err)
return
} else if len(orders) == 0 && preOrder.OrderCategory == 3 {
orders, err = makeSpotTakeAndReduce(preOrder, db, tradeSet, num)
if err != nil {
return
}
}
spotApi := SpotRestApi{}
@ -711,88 +562,8 @@ func processTakeProfitAndStopLossOrders(db *gorm.DB, preOrder *models.LinePreOrd
}
}
// 构建现货止盈、减仓单
func makeSpotTakeAndReduce(preOrder *DbModels.LinePreOrder, db *gorm.DB, tradeSet models2.TradeSet, num decimal.Decimal) ([]DbModels.LinePreOrder, error) {
ext := models.LinePreOrderExt{}
price := utility.StrToDecimal(preOrder.Price)
orders := make([]DbModels.LinePreOrder, 0)
if err := db.Model(&ext).Where("order_id = ?", preOrder.Id).First(&ext).Error; err != nil {
logger.Error("订单回调查询止盈止损单扩展表失败:", err)
return nil, errors.New("订单回调查询止盈止损单扩展表失败")
}
totalLossAmountU, _ := GetTotalLossAmount(db, preOrder.MainId)
//止盈单
if ext.TakeProfitRatio.Cmp(decimal.Zero) > 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.Rate = ext.TakeProfitRatio.String()
profitOrder.MainId = ext.MainOrderId
profitOrder.Num = num.String()
//止盈需要累加之前的亏损
if totalLossAmountU.Cmp(decimal.Zero) > 0 {
percent := totalLossAmountU.Div(num).Div(price).Abs()
profitOrder.Rate = percent.Mul(decimal.NewFromInt(100)).Add(ext.TakeProfitRatio).Truncate(2).String()
}
if strings.ToUpper(preOrder.Site) == "BUY" {
profitOrder.Site = "SELL"
} else {
profitOrder.Site = "BUY"
}
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 = ext.MainOrderId
stopOrder.OrderType = 4
stopOrder.Status = 0
stopOrder.Rate = ext.ReducePriceRatio.String()
if ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 {
stopOrder.Num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit)).String()
} else {
stopOrder.Num = num.String()
}
if strings.ToUpper(preOrder.Site) == "BUY" {
stopOrder.Site = "SELL"
} else {
stopOrder.Site = "BUY"
}
setPrice(&stopOrder, preOrder, tradeSet)
orders = append(orders, stopOrder)
}
if len(orders) > 0 {
if err := db.Create(&orders).Error; err != nil {
logger.Error("主单回调,创建止盈、减仓单失败")
return orders, errors.New("主单回调,创建止盈、减仓单失败")
}
}
return orders, nil
}
// 根据下单百分比计算价格
func setPrice(order *models.LinePreOrder, preOrder *models.LinePreOrder, tradeSet models2.TradeSet) {
func SetPrice(order *models.LinePreOrder, preOrder *models.LinePreOrder, tradeSet models2.TradeSet) {
orderType := order.OrderType
itemSide := order.Site
rate := utility.StrToDecimal(order.Rate)