1、状态更新错误处理
This commit is contained in:
@ -170,13 +170,15 @@ func (e *ReverseService) ReverseOrder(apiKey string, mapData map[string]interfac
|
||||
switch {
|
||||
case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY", mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL":
|
||||
if mainOrder.Category == 0 {
|
||||
if _, _, err1 := e.savePosition(&mainOrder, &apiInfo, false, false); err1 != nil {
|
||||
if _, _, err1 := e.savePositionWithRetry(&mainOrder, &apiInfo, false, false); err1 != nil {
|
||||
e.Log.Errorf("反向订单持仓保存失败: %v", err1)
|
||||
return true, err1
|
||||
}
|
||||
}
|
||||
case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SrgetELL":
|
||||
case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY", mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
|
||||
if mainOrder.Category == 0 {
|
||||
if _, _, err1 := e.savePosition(&mainOrder, &apiInfo, false, true); err1 != nil {
|
||||
if _, _, err1 := e.savePositionWithRetry(&mainOrder, &apiInfo, false, true); err1 != nil {
|
||||
e.Log.Errorf("反向订单持仓保存失败: %v", err1)
|
||||
return true, err1
|
||||
}
|
||||
}
|
||||
@ -307,6 +309,20 @@ func (e *ReverseService) SaveMainOrder(mapData map[string]interface{}, apiInfo D
|
||||
return reverseOrder, nil
|
||||
}
|
||||
|
||||
// savePositionWithRetry 带重试机制的持仓保存
|
||||
func (e *ReverseService) savePositionWithRetry(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, isMain, reducePosition bool) (bool, bool, error) {
|
||||
const maxRetries = 3
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
needReverseOrder, closePosition, err := e.savePosition(order, apiInfo, isMain, reducePosition)
|
||||
if err == nil {
|
||||
return needReverseOrder, closePosition, nil
|
||||
}
|
||||
e.Log.Warnf("持仓保存失败,重试 %d/%d: %v", i+1, maxRetries, err)
|
||||
time.Sleep(time.Millisecond * 100 * time.Duration(i+1))
|
||||
}
|
||||
return false, false, fmt.Errorf("持仓保存失败,已重试 %d 次", maxRetries)
|
||||
}
|
||||
|
||||
// 更新仓位信息
|
||||
// apiInfo: 当前下单api信息
|
||||
// return
|
||||
@ -364,25 +380,27 @@ func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo
|
||||
//平仓
|
||||
if closePosition {
|
||||
totalNum = decimal.Zero
|
||||
sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2 where id =@id and status!=2 "
|
||||
sqlStr = "UPDATE line_reverse_position set amount=@totalNum,updated_at=now(),status=2,version=version+1 where id =@id and status!=2 and version=@version"
|
||||
} else if reducePosition {
|
||||
//只减仓
|
||||
sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now(),average_price=@averagePrice where id =@id and status!=2 "
|
||||
sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now(),average_price=@averagePrice,version=version+1 where id =@id and status!=2 and version=@version"
|
||||
} else {
|
||||
sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now(),average_price=@averagePrice where id =@id and status!=2"
|
||||
sqlStr = "UPDATE line_reverse_position set total_amount=total_amount + @totalNum,amount=amount + @totalNum,updated_at=now(),average_price=@averagePrice,version=version+1 where id =@id and status!=2 and version=@version"
|
||||
}
|
||||
} else {
|
||||
querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)"
|
||||
//减仓 判断是否为平仓
|
||||
closePosition = e.getClosePosition(reducePosition, apiInfo, order, order.PositionSide, isMain)
|
||||
|
||||
// 关键修复:确保反向持仓状态正确更新
|
||||
if closePosition {
|
||||
totalNum = decimal.Zero
|
||||
sqlStr = "UPDATE line_reverse_position set reverse_amount=@totalNum,updated_at=now(),reverse_status=2 where id =@id and reverse_status !=2"
|
||||
// 增强的平仓更新逻辑,确保状态正确设置为2
|
||||
sqlStr = "UPDATE line_reverse_position set reverse_amount=@totalNum,updated_at=now(),reverse_status=2,version=version+1 where id =@id and reverse_status !=2 and version=@version"
|
||||
} else if reducePosition {
|
||||
sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now(),reverse_average_price=@averagePrice where id =@id and reverse_status !=2"
|
||||
sqlStr = "UPDATE line_reverse_position set reverse_amount=reverse_amount - @totalNum,updated_at=now(),reverse_average_price=@averagePrice,version=version+1 where id =@id and reverse_status !=2 and version=@version"
|
||||
} else {
|
||||
sqlStr = "UPDATE line_reverse_position set total_reverse_amount=total_reverse_amount + @totalNum,reverse_amount=reverse_amount + @totalNum,updated_at=now(),reverse_status =1,reverse_average_price=@averagePrice where id =@id and reverse_status !=2"
|
||||
sqlStr = "UPDATE line_reverse_position set total_reverse_amount=total_reverse_amount + @totalNum,reverse_amount=reverse_amount + @totalNum,updated_at=now(),reverse_status =1,reverse_average_price=@averagePrice,version=version+1 where id =@id and reverse_status !=2 and version=@version"
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,22 +408,23 @@ func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo
|
||||
var remainQuantity decimal.Decimal
|
||||
|
||||
err := e.Orm.Transaction(func(tx *gorm.DB) error {
|
||||
err1 := tx.Model(&position).Where(querySql,
|
||||
order.ApiId, positionSide, order.Symbol).First(&position).Error
|
||||
err1 := tx.Model(&position).Where(querySql,
|
||||
order.ApiId, positionSide, order.Symbol).First(&position).Error
|
||||
|
||||
if err1 != nil {
|
||||
//主单仓位不存在,创建新仓位
|
||||
if isMain && errors.Is(err1, gorm.ErrRecordNotFound) && !reducePosition {
|
||||
if err1 != nil {
|
||||
//主单仓位不存在,创建新仓位
|
||||
if isMain && errors.Is(err1, gorm.ErrRecordNotFound) && !reducePosition {
|
||||
// 初始化版本号
|
||||
position.Version = 0
|
||||
if err2 := tx.Create(&position).Error; err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
if err2 := tx.Create(&position).Error; err2 != nil {
|
||||
return err2
|
||||
averagePrice = position.AveragePrice
|
||||
} else {
|
||||
return err1
|
||||
}
|
||||
|
||||
averagePrice = position.AveragePrice
|
||||
} else {
|
||||
return err1
|
||||
}
|
||||
} else {
|
||||
var totalAmount decimal.Decimal
|
||||
var totalPrice decimal.Decimal
|
||||
if !position.Amount.IsZero() && !position.AveragePrice.IsZero() {
|
||||
@ -457,16 +476,17 @@ func (e *ReverseService) savePosition(order *DbModels.LineReverseOrder, apiInfo
|
||||
return err2
|
||||
}
|
||||
|
||||
dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id), sql.Named("averagePrice", averagePrice))
|
||||
if dbResult.Error != nil {
|
||||
return dbResult.Error
|
||||
}
|
||||
// 使用乐观锁执行更新
|
||||
dbResult := tx.Exec(sqlStr, sql.Named("totalNum", totalNum), sql.Named("id", position.Id), sql.Named("averagePrice", averagePrice), sql.Named("version", position.Version))
|
||||
if dbResult.Error != nil {
|
||||
return dbResult.Error
|
||||
}
|
||||
|
||||
order.PositionId = position.Id
|
||||
if dbResult.RowsAffected == 0 {
|
||||
e.Log.Errorf("减仓数据 是否平仓单:%v :%v", closePosition, order)
|
||||
return errors.New("没有找到对应的持仓信息")
|
||||
}
|
||||
order.PositionId = position.Id
|
||||
if dbResult.RowsAffected == 0 {
|
||||
e.Log.Errorf("减仓数据 是否平仓单:%v :%v, 可能存在并发冲突", closePosition, order)
|
||||
return fmt.Errorf("没有找到对应的持仓信息或版本冲突,position_id:%d, version:%d", position.Id, position.Version)
|
||||
}
|
||||
|
||||
if reducePosition && !isMain {
|
||||
remainQuantity = position.ReverseAmount.Sub(totalNum)
|
||||
@ -1052,145 +1072,3 @@ func getOrderType(t string) int {
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
// // 重下止盈止损
|
||||
// // mapData: 主单止盈止损回调
|
||||
// func (e *ReverseService) ReTakeOrStopOrder(mapData *map[string]interface{}, orderSn string, mainApiInfo *DbModels.LineApiUser, symbol *models.TradeSet) error {
|
||||
// orderType := 0 //订单类型 1-止盈 2-止损
|
||||
// side, err := maphelper.GetString(*mapData, "S")
|
||||
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// ot, err := maphelper.GetString(*mapData, "ot")
|
||||
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// //反单止盈止损方向相反
|
||||
// if side == "SELL" {
|
||||
// side = "BUY"
|
||||
// } else {
|
||||
// side = "SELL"
|
||||
// }
|
||||
|
||||
// positionSide, err := maphelper.GetString(*mapData, "ps")
|
||||
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if positionSide == "LONG" {
|
||||
// positionSide = "SHORT"
|
||||
// } else {
|
||||
// positionSide = "LONG"
|
||||
// }
|
||||
|
||||
// close := maphelper.GetBool(*mapData, "cp")
|
||||
// stopPrice := maphelper.GetDecimal(*mapData, "sp")
|
||||
// if stopPrice.IsZero() {
|
||||
// e.Log.Errorf("获取止盈止损单触发价失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||
// return err
|
||||
// }
|
||||
// apiInfo, err := GetApiInfo(mainApiInfo.ReverseApiId)
|
||||
|
||||
// if err != nil {
|
||||
// e.Log.Errorf("根据主单api获取反单api失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||
// return err
|
||||
// }
|
||||
|
||||
// switch ot {
|
||||
// case "STOP_MARKET", "STOP":
|
||||
// orderType = 2
|
||||
// case "TAKE_PROFIT_MARKET", "TAKE_PROFIT":
|
||||
// orderType = 1
|
||||
// default:
|
||||
// return fmt.Errorf("不支持的订单类型 ot:%s", ot)
|
||||
// }
|
||||
|
||||
// var reversePosition DbModels.LineReversePosition
|
||||
|
||||
// e.Orm.Model(&reversePosition).
|
||||
// Where("symbol =? and reverse_api_id =? and position_side =? and reverse_status =1", symbol.GetSymbol(), apiInfo.Id, positionSide).
|
||||
// First(&reversePosition)
|
||||
|
||||
// if reversePosition.Id == 0 {
|
||||
// e.Log.Errorf("获取反单持仓失败 symbol:%s custom:%s :%v", symbol, orderSn, err)
|
||||
// return err
|
||||
// }
|
||||
|
||||
// mainPercent := decimal.NewFromInt(1)
|
||||
|
||||
// if !stopPrice.IsZero() && !reversePosition.AveragePrice.IsZero() {
|
||||
// mainPercent = stopPrice.Div(reversePosition.AveragePrice)
|
||||
// mainPercent = (mainPercent.Sub(decimal.NewFromInt(1))).Abs().Truncate(4)
|
||||
// }
|
||||
|
||||
// var percent decimal.Decimal
|
||||
// switch {
|
||||
// //做多止损
|
||||
// case orderType == 2 && positionSide == "LONG", orderType == 1 && positionSide == "SHORT":
|
||||
// percent = decimal.NewFromInt(1).Sub(mainPercent)
|
||||
// case orderType == 2 && positionSide == "SHORT", orderType == 1 && positionSide == "LONG":
|
||||
// percent = decimal.NewFromInt(1).Add(mainPercent)
|
||||
// default:
|
||||
// return fmt.Errorf("不支持的订单类型 ot:%s, ps:%s", ot, positionSide)
|
||||
// }
|
||||
|
||||
// now := time.Now()
|
||||
// price := reversePosition.AveragePrice.Mul(percent).Truncate(int32(symbol.PriceDigit))
|
||||
// lastPrice, _ := decimal.NewFromString(symbol.LastPrice)
|
||||
// newOrder := DbModels.LineReverseOrder{
|
||||
// PositionId: reversePosition.Id,
|
||||
// OrderSn: helper.GetOrderNo(),
|
||||
// OrderType: orderType,
|
||||
// Status: 1,
|
||||
// Price: price,
|
||||
// TotalNum: reversePosition.TotalReverseAmount,
|
||||
// Symbol: symbol.GetSymbol(),
|
||||
// Side: side,
|
||||
// PositionSide: positionSide,
|
||||
// FollowOrderSn: orderSn,
|
||||
// Type: ot,
|
||||
// SignPrice: lastPrice,
|
||||
// Category: 1,
|
||||
// ApiId: apiInfo.Id,
|
||||
// IsAddPosition: 2,
|
||||
// TriggerTime: &now,
|
||||
// BuyPrice: reversePosition.TotalReverseAmount.Mul(price).Truncate(int32(symbol.PriceDigit)),
|
||||
// }
|
||||
|
||||
// if err1 := e.Orm.Create(&newOrder).Error; err1 != nil {
|
||||
// e.Log.Errorf("保存反单止盈止损失败 symbol:%s custom:%s :%v", symbol, orderSn, err1)
|
||||
// return err1
|
||||
// }
|
||||
// params := FutOrderPlace{
|
||||
// ApiId: apiInfo.Id,
|
||||
// Symbol: symbol.GetSymbol(),
|
||||
// PositionSide: newOrder.PositionSide,
|
||||
// Side: newOrder.Side,
|
||||
// OrderType: ot,
|
||||
// Quantity: newOrder.TotalNum,
|
||||
// Price: price,
|
||||
// StopPrice: price,
|
||||
// Profit: price,
|
||||
// NewClientOrderId: newOrder.OrderSn,
|
||||
// ClosePosition: close,
|
||||
// }
|
||||
// futApiV2 := FuturesResetV2{Service: e.Service}
|
||||
// err = futApiV2.OrderPlaceLoop(&apiInfo, params)
|
||||
|
||||
// if err != nil {
|
||||
// e.Log.Errorf("币安下单失败 symbol:%s custom:%s :%v", symbol.GetSymbol(), orderSn, err)
|
||||
|
||||
// if err1 := e.Orm.Model(&newOrder).Updates(map[string]interface{}{"status": 8, "remark": err.Error(), "updated_at": time.Now()}).Error; err1 != nil {
|
||||
// e.Log.Errorf("更新订单状态失败 symbol:%s custom:%s :%v", newOrder.Symbol, newOrder.OrderSn, err1)
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
|
||||
// e.DoCancelTakeProfitBatch(symbol.GetSymbol(), newOrder.PositionSide, newOrder.Side, newOrder.OrderType, &apiInfo)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user