1撤销限价后 市价分单

This commit is contained in:
2025-03-27 16:18:32 +08:00
parent 8cede57a70
commit ee148ed144
31 changed files with 1627 additions and 248 deletions

View File

@ -26,6 +26,7 @@ import (
"time"
"github.com/bytedance/sonic"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
"gorm.io/driver/mysql"
@ -158,7 +159,6 @@ func (t AutoPlaceOrder) Exec(arg interface{}) error {
}
func (t LimitOrderTimeoutDuration) Exec(arg interface{}) error {
str := time.Now().Format(timeFormat) + " [INFO] JobCore ClearLogJob exec success"
defer logger.Info(str)
var db *gorm.DB
@ -188,7 +188,10 @@ func (t LimitOrderTimeoutDuration) Exec(arg interface{}) error {
defer lock.Release()
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 ('4') 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 updated_at < ?", time.Now().Add(-time.Duration(limitOrderTimeoutDuration)*time.Second)).
Preload("Childs").
Find(&orders).Error
if err != nil {
return err
}
@ -228,6 +231,7 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
for i := 0; i < 3; i++ {
err = spotApi.CancelOpenOrderByOrderSn(apiUserinfo, order.Symbol, order.OrderSn)
if err == nil || strings.Contains(err.Error(), "该交易对没有订单") {
err = nil
break
}
}
@ -237,11 +241,12 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
} else {
var remainingQuantity decimal.Decimal
spotOrder, err := spotApi.GetOrderByOrderSnLoop(order.Symbol, order.OrderSn, apiUserinfo, 4)
tradeSet, _ := binanceservice.GetTradeSet(order.Symbol, 0)
if err == nil {
origQty := utility.StrToDecimal(spotOrder.OrigQuoteOrderQty)
origQty := utility.StrToDecimal(spotOrder.OrigQty)
excuteQty := utility.StrToDecimal(spotOrder.ExecutedQty)
remainingQuantity = origQty.Sub(excuteQty).Abs()
remainingQuantity = origQty.Sub(excuteQty).Abs().Truncate(int32(tradeSet.AmountDigit))
}
if remainingQuantity.Cmp(decimal.Zero) <= 0 {
@ -249,36 +254,62 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
return nil
}
tradeSet, _ := binanceservice.GetTradeSet(order.Symbol, 0)
newClientOrderId := snowflakehelper.GetOrderId()
maxMarketQty := decimal.NewFromFloat(tradeSet.MarketMaxQty).Truncate(int32(tradeSet.AmountDigit))
order.Num = remainingQuantity.Truncate(int32(tradeSet.AmountDigit)).String()
order.Desc = fmt.Sprintf("取消限价单,重下市价单源订单号:%s ", order.OrderSn)
order.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
order.MainOrderType = "MARKET"
err = db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn, "main_order_type": order.MainOrderType}).Error
// if config.ApplicationConfig.Mode == "dev" {
// maxMarketQty = decimal.NewFromFloat(10)
// }
if err != nil {
logger.Error(fmt.Sprintf("生成新市价单失败 err:%+v", err))
return err
}
params := binanceservice.OrderPlacementService{
ApiId: order.ApiId,
Symbol: order.Symbol,
Side: order.Site,
Type: "MARKET",
TimeInForce: "GTC",
Price: utility.StringToDecimal(order.Price),
StopPrice: utility.StrToDecimal(order.Price),
Quantity: remainingQuantity,
NewClientOrderId: utility.Int64ToString(newClientOrderId),
}
if err := spotApi.OrderPlace(db, params); err != nil {
logger.Error(fmt.Sprintf("重新下市价单失败 err:%+v", err))
err := db.Model(&order).Updates(map[string]interface{}{"status": "2", "desc": order.Desc + "err:" + err.Error()}).Error
if remainingQuantity.Cmp(maxMarketQty) > 0 && maxMarketQty.Cmp(decimal.Zero) > 0 {
multiple := remainingQuantity.Div(maxMarketQty).Abs().IntPart()
remainder := remainingQuantity.Mod(maxMarketQty)
saveOrders := make([]models.LinePreOrder, 0)
desc := fmt.Sprintf("取消限价单,重下市价单 源订单号:%s", order.OrderSn)
// 创建 multiple 个订单
for i := 0; i < int(multiple); i++ {
saveOrders = append(saveOrders, createNewOrder(order, maxMarketQty, desc))
}
// 处理余数
if remainder.Cmp(decimal.Zero) > 0 {
saveOrders = append(saveOrders, createNewOrder(order, remainder.Truncate(int32(tradeSet.AmountDigit)), desc))
}
if err := db.Create(&saveOrders).Error; err != nil {
logger.Errorf("市价订单拆分后保存失败,err:", err)
return err
}
for index := range saveOrders {
newNum := utility.StrToDecimal(saveOrders[index].Num)
if err := newSpotOrderClosePosition(saveOrders[index], newNum, spotApi, db); err != nil {
logger.Errorf("市价订单拆分后保存失败,err:", err)
}
if index == len(saveOrders)-1 {
orderCopy := saveOrders[index] // 复制数据
binanceservice.HandleSpotMarketSliceTakeProfit(db, orderCopy, order.Id, apiUserinfo, tradeSet)
}
}
} else {
newClientOrderId := snowflakehelper.GetOrderId()
order.Num = remainingQuantity.Truncate(int32(tradeSet.AmountDigit)).String()
order.Desc = fmt.Sprintf("取消限价单,重下市价单源订单号:%s ", order.OrderSn)
order.OrderSn = utility.Int64ToString(newClientOrderId)
order.MainOrderType = "MARKET"
err = db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn, "main_order_type": order.MainOrderType}).Error
if err != nil {
logger.Error("下单失败后修改订单失败")
logger.Error(fmt.Sprintf("生成新市价单失败 err:%+v", err))
return err
}
if err := newSpotOrderClosePosition(order, remainingQuantity, spotApi, db); err != nil {
return err
}
}
@ -287,12 +318,38 @@ func (t LimitOrderTimeoutDuration) ReSpotOrderPlace(db *gorm.DB, order models.Li
return nil
}
// 现货市价单平仓
func newSpotOrderClosePosition(order models.LinePreOrder, remainingQuantity decimal.Decimal, spotApi binanceservice.SpotRestApi, db *gorm.DB) error {
params := binanceservice.OrderPlacementService{
ApiId: order.ApiId,
Symbol: order.Symbol,
Side: order.Site,
Type: "MARKET",
TimeInForce: "GTC",
Price: utility.StringToDecimal(order.Price),
StopPrice: utility.StrToDecimal(order.Price),
Quantity: remainingQuantity,
NewClientOrderId: order.OrderSn,
}
if err := spotApi.OrderPlaceLoop(db, params, 3); err != nil {
logger.Error(fmt.Sprintf("重新下市价单失败 err:%+v", err))
err := db.Model(&order).Updates(map[string]interface{}{"status": "2", "desc": order.Desc + "err:" + err.Error()}).Error
if err != nil {
logger.Error("下单失败后修改订单失败")
return err
}
}
return nil
}
// ReFutOrderPlace 重下合约市价单
func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.LinePreOrder, apiUserinfo models.LineApiUser, futApi binanceservice.FutRestApi) error {
var err error
for i := 0; i < 3; i++ {
err := futApi.CancelFutOrder(apiUserinfo, order.Symbol, order.OrderSn)
if err == nil || strings.Contains(err.Error(), "该交易对没有订单") {
err = nil
break
}
}
@ -302,11 +359,12 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin
} else {
var remainingQuantity decimal.Decimal
spotOrder, err := futApi.GetOrderByOrderSnLoop(order.Symbol, order.OrderSn, apiUserinfo, 4)
tradeSet, _ := binanceservice.GetTradeSet(order.Symbol, 1)
if err == nil {
origQty := utility.StrToDecimal(spotOrder.OrigQty)
excuteQty := utility.StrToDecimal(spotOrder.ExecutedQty)
remainingQuantity = origQty.Sub(excuteQty).Abs()
remainingQuantity = origQty.Sub(excuteQty).Abs().Truncate(int32(tradeSet.AmountDigit))
}
if remainingQuantity.Cmp(decimal.Zero) <= 0 {
@ -314,51 +372,62 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin
return nil
}
tradeSet, _ := binanceservice.GetTradeSet(order.Symbol, 0)
qty := decimal.NewFromFloat(tradeSet.MarketMaxQty)
maxMarketQty := qty.Truncate(int32(tradeSet.AmountDigit))
newClientOrderId := snowflakehelper.GetOrderId()
order.Num = remainingQuantity.Truncate(int32(tradeSet.AmountDigit)).String()
order.Desc = fmt.Sprintf("取消限价单,重下市价单 源订单号:%s", order.OrderSn)
order.OrderSn = utility.Int64ToString(newClientOrderId)
// var newOrder models.LinePreOrder
// copier.Copy(&newOrder, order)
// newOrder.Id = 0
// newOrder.OrderSn = utility.Int64ToString(newClientOrderId)
// newOrder.CreatedAt = time.Now()
// newOrder.MainOrderType = "MARKET"
// err = db.Model(&models.LinePreOrder{}).Create(&newOrder).Error
var positionSide string
if order.Site == "BUY" {
positionSide = "SHORT"
} else {
positionSide = "LONG"
}
err = db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn}).Error
if err != nil {
logger.Error(fmt.Sprintf("生成合约新市价单失败 err:%+v", err))
return err
}
// params := binanceservice.FutOrderPlace{
// ApiId: order.ApiId,
// Symbol: order.Symbol,
// Side: order.Site,
// Quantity: remainingQuantity,
// Price: utility.StringToDecimal(order.Price),
// SideType: "MARKET",
// OpenOrder: 0,
// OrderType: "MARKET",
// NewClientOrderId: utility.Int64ToString(newClientOrderId),
// if config.ApplicationConfig.Mode == "dev" {
// maxMarketQty = decimal.NewFromFloat(10)
// }
if err := futApi.ClosePositionLoop(order.Symbol, order.OrderSn, remainingQuantity, order.Site, positionSide, apiUserinfo, "MARKET", "0", decimal.Zero, 3); err != nil {
logger.Error(fmt.Sprintf("重新下合约市价单失败 err:%+v", err))
err := db.Model(&order).Updates(map[string]interface{}{"status": "2", "desc": order.Desc + " err:" + err.Error()}).Error
//数量超过最大数量,则拆单
if remainingQuantity.Cmp(maxMarketQty) > 0 && maxMarketQty.Cmp(decimal.Zero) > 0 {
multiple := remainingQuantity.Div(maxMarketQty).Abs().IntPart()
remainder := remainingQuantity.Mod(maxMarketQty)
saveOrders := make([]models.LinePreOrder, 0)
desc := fmt.Sprintf("取消限价单,重下市价单 源订单号:%s", order.OrderSn)
// 创建 multiple 个订单
for i := 0; i < int(multiple); i++ {
saveOrders = append(saveOrders, createNewOrder(order, maxMarketQty, desc))
}
// 处理余数
if remainder.Cmp(decimal.Zero) > 0 {
saveOrders = append(saveOrders, createNewOrder(order, remainder.Truncate(int32(tradeSet.AmountDigit)), desc))
}
if err := db.Create(&saveOrders).Error; err != nil {
logger.Errorf("市价订单拆分后保存失败,err:", err)
return err
}
for index := range saveOrders {
newNum := utility.StrToDecimal(saveOrders[index].Num)
if err := newOrderClosePosition(saveOrders[index], futApi, newNum.Truncate(int32(tradeSet.AmountDigit)), apiUserinfo, db); err != nil {
logger.Errorf("市价单拆分后下单失败 orderSn:%s, err:%s", saveOrders[index].OrderSn, err)
}
if index == len(saveOrders)-1 {
orderCopy := saveOrders[index] // 复制数据
binanceservice.HandleMarketSliceTakeProfit(db, orderCopy, order.Id, apiUserinfo, tradeSet)
}
}
} else {
newClientOrderId := snowflakehelper.GetOrderId()
order.Num = remainingQuantity.Truncate(int32(tradeSet.AmountDigit)).String()
order.Desc = fmt.Sprintf("取消限价单,重下市价单 源订单号:%s", order.OrderSn)
order.OrderSn = utility.Int64ToString(newClientOrderId)
err = db.Model(&order).Updates(map[string]interface{}{"desc": order.Desc, "order_sn": order.OrderSn}).Error
if err != nil {
logger.Error(fmt.Sprintf("生成合约新市价单失败 err:%+v", err))
return err
}
err = newOrderClosePosition(order, futApi, remainingQuantity, apiUserinfo, db)
if err != nil {
logger.Error("下单失败后修改订单失败")
return err
}
}
@ -366,6 +435,54 @@ func (t LimitOrderTimeoutDuration) ReFutOrderPlace(db *gorm.DB, order models.Lin
return err
}
// 复制订单
func createNewOrder(order models.LinePreOrder, num decimal.Decimal, desc string) models.LinePreOrder {
newOrder := models.LinePreOrder{}
copier.Copy(&newOrder, order)
newOrder.Id = 0
newOrder.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
newOrder.Num = num.String()
newOrder.Desc = desc
newOrder.MainOrderType = "MARKET"
newOrder.Status = 0
// 重新创建 Childs并正确赋值 `Pid`
var newChilds []models.LinePreOrder
for _, child := range order.Childs {
newChild := child
newChild.Id = 0
newChild.Pid = 0
newChild.OrderSn = utility.Int64ToString(snowflakehelper.GetOrderId())
newChild.CreatedAt = time.Now()
newChilds = append(newChilds, newChild)
}
newOrder.Childs = newChilds // 重新赋值子订单,避免浅拷贝问题
return newOrder
}
// 新市价单
func newOrderClosePosition(order models.LinePreOrder, futApi binanceservice.FutRestApi, remainingQuantity decimal.Decimal, apiUserinfo models.LineApiUser, db *gorm.DB) error {
var positionSide string
if order.Site == "BUY" {
positionSide = "SHORT"
} else {
positionSide = "LONG"
}
if err := futApi.ClosePositionLoop(order.Symbol, order.OrderSn, remainingQuantity, order.Site, positionSide, apiUserinfo, "MARKET", "0", decimal.Zero, 3); err != nil {
logger.Error(fmt.Sprintf("重新下合约市价单失败 err:%+v", err))
err := db.Model(&order).Updates(map[string]interface{}{"status": "2", "desc": order.Desc + " err:" + err.Error()}).Error
if err != nil {
logger.Error("下单失败后修改订单失败")
return err
}
}
return nil
}
func (l ListenSymbol) Exec(arg interface{}) error {
str := time.Now().Format(timeFormat) + " [INFO] JobCore ClearLogJob exec success"
defer logger.Info(str)