拆分加仓、减仓

This commit is contained in:
2025-03-07 16:48:55 +08:00
parent 126193df36
commit 0aa2ab7355
4 changed files with 303 additions and 89 deletions

View File

@ -277,6 +277,10 @@ func (req LineAddPreOrderReq) Valid() error {
return errors.New("主单减仓数量百分比不能为空")
}
if req.ReducePriceRatio.IsZero() || req.ReducePriceRatio.Cmp(decimal.NewFromInt(100)) >= 0 {
return errors.New("主单减仓价格百分比错误")
}
for _, v := range req.Ext {
name := "加仓"
@ -299,13 +303,17 @@ func (req LineAddPreOrderReq) Valid() error {
return errors.New("止盈价格不正确")
}
if v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 {
if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 &&
v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 &&
(v.TpTpPriceRatio.IsZero() || v.TpTpPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) {
return errors.New("止盈后止盈价格不正确")
}
if v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0 {
return errors.New("止盈后止损价格不正确")
}
// if v.AddType == 1 && v.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 &&
// v.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 &&
// (v.TpSlPriceRatio.Cmp(decimal.Zero) <= 0 || v.TpSlPriceRatio.Cmp(decimal.NewFromInt(100)) > 0) {
// return errors.New("止盈后止损价格不正确")
// }
}
return nil

View File

@ -481,10 +481,12 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
defultExt.TotalAfter = utility.StrToDecimal(AddOrder.Num).Truncate(int32(tradeSet.AmountDigit))
defultExt2.TotalBefore = defultExt.TotalAfter
defultExt2.TotalAfter = mainAmount.Mul(decimal.NewFromInt(100).Sub(req.ReduceNumRatio)).Div(decimal.NewFromInt(100)).Truncate(int32(tradeSet.AmountDigit))
defultExt2.ReTakeRatio = req.ReducePriceRatio.Div(decimal.NewFromInt(100).Sub(req.ReduceNumRatio).Div(decimal.NewFromInt(100))).Truncate(2)
preOrderExts = append(preOrderExts, defultExt)
calculateResp := dto.CalculateBreakEvenRatioResp{}
mainParam := dto.CalculateBreakEevenRatioReq{
AddType: 2,
Price: mainPrice,
ExchangeType: req.ExchangeType,
Symbol: req.Symbol,
@ -492,8 +494,8 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
BuyPrice: buyPrice,
LossBeginPercent: decimal.Zero,
LossEndPercent: req.ReducePriceRatio,
AddPositionType: 2,
AddPositionVal: decimal.Zero,
AddPositionType: 1,
AddPositionVal: req.ReduceNumRatio,
}
//计算减仓后
@ -510,14 +512,17 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
ext := models.LinePreOrderExt{
AddType: addPosition.AddType,
OrderType: addPosition.OrderType,
PriceRatio: addPosition.PriceRatio,
TakeProfitRatio: addPosition.TakeProfitRatio,
TakeProfitNumRatio: addPosition.TakeProfitNumRatio,
StopLossRatio: addPosition.StopLossRatio,
TpTpPriceRatio: addPosition.TpTpPriceRatio,
TpSlPriceRatio: addPosition.TpSlPriceRatio,
AddPositionType: addPosition.AddPositionType,
AddPositionVal: addPosition.AddPositionVal,
}
mainParam.AddType = addPosition.AddType
mainParam.LossEndPercent = req.Ext[index].PriceRatio
mainParam.AddPositionType = req.Ext[index].AddPositionType
mainParam.AddPositionVal = req.Ext[index].AddPositionVal
@ -623,7 +628,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
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 {
if newOrders, err := makeReduceTakeAndStoploss(&stopOrder, defultExt2, tradeSet, false); err != nil {
logger.Errorf("主单减仓生成止盈、减仓失败 err:%v", err)
return err
} else if len(newOrders) > 0 {
@ -638,30 +643,30 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
//添加止盈单
for index, v := range preOrderExts {
preOrderExts[index].MainOrderId = AddOrder.Id
if index == 0 {
preOrderExts[index].OrderId = AddOrder.Id
continue
}
var AddOrder models.LinePreOrder
// if index == 0 {
// preOrderExts[index].OrderId = AddOrder.Id
// continue
// }
var newOrder models.LinePreOrder
if v.AddType == 1 {
AddOrder = createPreAddPosition(&AddOrder, v, tradeSet)
newOrder = createPreAddPosition(&AddOrder, v, tradeSet)
} else if v.AddType == 2 {
AddOrder = createPreReduceOrder(&AddOrder, v, tradeSet)
newOrder = createPreReduceOrder(&AddOrder, v, tradeSet)
}
if AddOrder.OrderSn == "" {
if newOrder.OrderSn == "" {
continue
}
if err := e.Orm.Create(&AddOrder).Error; err != nil {
if err := e.Orm.Create(&newOrder).Error; err != nil {
logger.Error("保存加仓单失败")
return err
}
preOrderExts[index].OrderId = AddOrder.Id
preOrderExts[index].OrderId = newOrder.Id
//止盈、减仓
orders, err := makeFuturesTakeAndReduce(&AddOrder, v, tradeSet)
orders, err := makeFuturesTakeAndReduce(&newOrder, v, tradeSet)
if err != nil {
logger.Error("构造止盈、止损失败")
@ -676,7 +681,7 @@ func (e *LinePreOrder) AddPreOrder(req *dto.LineAddPreOrderReq, p *actions.DataP
for index := range orders {
//减仓单且 减仓比例大于0 小于100 就冲下止盈止损
if orders[index].OrderType == 1 && v.TakeProfitRatio.Cmp(decimal.Zero) > 0 && v.TakeProfitRatio.Cmp(decimal.NewFromInt(100)) < 0 {
reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet)
reduceChildOrders, err := makeReduceTakeAndStoploss(&(orders[index]), v, tradeSet, true)
if err != nil {
logger.Error("生产止盈后止盈、减仓失败")
@ -722,6 +727,11 @@ func createPreAddPosition(preOrder *models.LinePreOrder, v models.LinePreOrderEx
data.Pid = preOrder.Id
data.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
data.MainId = preOrder.Id
if preOrder.MainId > 0 {
data.MainId = preOrder.MainId
}
data.CreatedAt = time.Now()
data.MainOrderType = v.OrderType
data.Status = 0
@ -761,12 +771,18 @@ func createPreReduceOrder(preOrder *models.LinePreOrder, ext models.LinePreOrder
stopOrder.Id = 0
stopOrder.OrderSn = strconv.FormatInt(snowflakehelper.GetOrderId(), 10)
stopOrder.Pid = preOrder.Id
stopOrder.MainId = preOrder.MainId
stopOrder.MainId = preOrder.Id
if preOrder.MainId > 0 {
stopOrder.MainId = preOrder.MainId
}
stopOrder.OrderType = 4
stopOrder.Status = 0
stopOrder.Rate = ext.PriceRatio.String()
stopOrder.Num = ext.TotalAfter.Sub(ext.TotalBefore).Abs().Truncate(int32(tradeSet.AmountDigit)).String()
stopOrder.BuyPrice = "0"
stopOrder.Rate = ext.PriceRatio.String()
if strings.ToUpper(preOrder.Site) == "BUY" {
stopOrder.Site = "SELL"
@ -801,7 +817,11 @@ func makeFuturesTakeAndReduce(preOrder *models.LinePreOrder, ext models.LinePreO
profitOrder.Pid = preOrder.Id
profitOrder.OrderType = 1
profitOrder.Status = 0
profitOrder.MainId = preOrder.MainId
profitOrder.MainId = preOrder.Id
if preOrder.MainId > 0 {
profitOrder.MainId = preOrder.MainId
}
profitOrder.BuyPrice = "0"
profitOrder.Site = side
@ -877,16 +897,25 @@ func makeTpOrder(parentOrder *models.LinePreOrder, reminQuantity decimal.Decimal
}
// 构建减仓后止盈止损
func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet) ([]models.LinePreOrder, error) {
// isTpTp 是否止盈后止盈止损
func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.LinePreOrderExt, tradeSet models2.TradeSet, isTpTp bool) ([]models.LinePreOrder, error) {
orders := make([]models.LinePreOrder, 0)
var num decimal.Decimal
num := ext.TotalAfter
var takeProfitRatio, slPriceRatio decimal.Decimal
if isTpTp {
takeProfitRatio = ext.TpTpPriceRatio
slPriceRatio = ext.TpSlPriceRatio
} else {
takeProfitRatio = ext.TakeProfitRatio
slPriceRatio = ext.StopLossRatio
}
if ext.TakeProfitNumRatio.Cmp(decimal.Zero) > 0 && ext.TakeProfitNumRatio.Cmp(decimal.NewFromInt(100)) < 0 {
percent := decimal.NewFromInt(1).Sub(ext.TakeProfitNumRatio.Div(decimal.NewFromInt(100)))
num = ext.TotalAfter.Mul(percent).Truncate(int32(tradeSet.AmountDigit))
}
if ext.TpTpPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 {
if takeProfitRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 {
takeProfitOrder := models.LinePreOrder{}
copier.Copy(&takeProfitOrder, parentOrder)
takeProfitOrder.Id = 0
@ -894,21 +923,25 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line
takeProfitOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
takeProfitOrder.Status = 0
takeProfitOrder.OrderType = 1
takeProfitOrder.Rate = ext.TpTpPriceRatio.String()
takeProfitOrder.SignPrice = parentOrder.Price
takeProfitOrder.CreatedAt = time.Now()
takeProfitOrder.BuyPrice = "0"
takeProfitOrder.MainOrderType = "LIMIT"
takeProfitOrder.Num = num.String()
//止盈需要累加之前的亏损
takeProfitOrder.Rate = ext.TpTpPriceRatio.Truncate(2).String()
if isTpTp {
takeProfitOrder.Rate = takeProfitRatio.Truncate(2).String()
} else {
takeProfitOrder.Rate = takeProfitRatio.Add(ext.ReTakeRatio).Truncate(2).String()
}
takeProfitOrder.BuyPrice = "0"
binanceservice.SetPrice(&takeProfitOrder, parentOrder, tradeSet)
orders = append(orders, takeProfitOrder)
}
//有止损单
if ext.TpSlPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 {
if slPriceRatio.Cmp(decimal.Zero) > 0 && num.Cmp(decimal.Zero) > 0 {
var stoploss models.LinePreOrder
copier.Copy(&stoploss, parentOrder)
@ -920,7 +953,7 @@ func makeReduceTakeAndStoploss(parentOrder *models.LinePreOrder, ext models.Line
stoploss.OrderType = 2
stoploss.SignPrice = parentOrder.Price
stoploss.BuyPrice = "0"
stoploss.Rate = ext.TpSlPriceRatio.String()
stoploss.Rate = slPriceRatio.String()
stoploss.MainOrderType = "LIMIT"
stoploss.Num = num.String()
stoploss.BuyPrice = "0"
@ -1658,6 +1691,7 @@ func (e *LinePreOrder) FutClosePosition(position *dto.ClosePosition, errs *[]err
// ClearUnTriggered 清除待触发的交易对
func (e *LinePreOrder) ClearUnTriggered() error {
var orderLists []models.LinePreOrder
positions := map[string]positiondto.LinePreOrderPositioinDelReq{}
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 {
@ -1680,8 +1714,43 @@ func (e *LinePreOrder) ClearUnTriggered() error {
key := fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType)
helper.DefaultRedis.LRem(key, string(marshal))
}
//会影响持仓的
removeSymbolKey := fmt.Sprintf("%v_%s_%s_%s_%v", order.ApiId, order.ExchangeType, order.Symbol, order.Site, order.SymbolType)
if _, ok := positions[removeSymbolKey]; !ok {
positions[removeSymbolKey] = positiondto.LinePreOrderPositioinDelReq{
ApiId: order.ApiId,
Symbol: order.Symbol,
ExchangeType: order.ExchangeType,
Side: order.Site,
SymbolType: order.SymbolType,
}
}
e.Orm.Model(&models.LinePreOrder{}).Where("main_id = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{})
}
//清理仓位缓存
for _, v := range positions {
var count int64
e.Orm.Model(&models.LinePreOrder{}).
Where("api_id =? AND site=? AND symbol=? AND symbol_type =? AND exchange_type =? AND status =6",
v.ApiId, v.Side, v.Symbol, v.SymbolType, v.ExchangeType).Count(&count)
//没有已开仓的订单 直接清理仓位
if count == 0 {
var key string
if v.SymbolType == 1 {
key = fmt.Sprintf(rediskey.SpotPosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side)
} else {
key = fmt.Sprintf(rediskey.FuturePosition, v.ExchangeType, v.ApiId, v.Symbol, v.Side)
}
helper.DefaultRedis.DeleteString(key)
}
}
return nil
}
@ -1774,8 +1843,8 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error {
BuyPrice: buyPrice,
LossBeginPercent: lossBeginPercent,
LossEndPercent: req.ReducePriceRatio,
AddPositionType: 2,
AddPositionVal: decimal.Zero,
AddPositionType: 1,
AddPositionVal: req.ReduceNumRatio,
}
//计算减仓后
@ -1794,6 +1863,7 @@ func (e *LinePreOrder) GenerateOrder(req *dto.LineAddPreOrderReq) error {
})
for index := range req.Ext {
mainParam.AddType = req.Ext[index].AddType
mainParam.LossBeginPercent = lossBeginPercent
mainParam.LossEndPercent = req.Ext[index].PriceRatio
mainParam.AddPositionType = req.Ext[index].AddPositionType
@ -1819,9 +1889,9 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio
var addPositionBuyPrice decimal.Decimal
if req.AddPositionType == 1 {
if req.AddType == 1 && req.AddPositionType == 1 {
addPositionBuyPrice = req.BuyPrice.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(2)
} else {
} else if req.AddType == 1 {
addPositionBuyPrice = req.AddPositionVal.Truncate(2)
}
@ -1839,7 +1909,7 @@ func (e *LinePreOrder) CalculateBreakEvenRatio(req *dto.CalculateBreakEevenRatio
lossAmountU := req.Price.Mul(percentDiff.Div(decimal.NewFromInt(100).Truncate(4))).Mul(req.RemainingQuantity).Truncate(int32(tradeSet.PriceDigit))
//计算减仓数量
if req.AddPositionType == 2 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 {
if req.AddType == 2 && req.AddPositionType == 1 && req.AddPositionVal.Cmp(decimal.NewFromInt(0)) > 0 {
reduceAmount = totalAmount.Mul(req.AddPositionVal.Div(decimal.NewFromInt(100).Truncate(4))).Truncate(int32(tradeSet.AmountDigit))
}