Files
exchange_go/services/binanceservice/reverse_service_optimized.go
2025-08-27 14:54:03 +08:00

1121 lines
35 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package binanceservice
import (
"context"
"database/sql"
"errors"
"fmt"
DbModels "go-admin/app/admin/models"
"go-admin/common/global"
"go-admin/common/helper"
"go-admin/pkg/maphelper"
"go-admin/pkg/retryhelper"
"go-admin/pkg/utility/snowflakehelper"
"go-admin/services/cacheservice"
"strconv"
"sync"
"time"
"github.com/go-admin-team/go-admin-core/logger"
"github.com/go-admin-team/go-admin-core/sdk/service"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
// ReverseServiceOptimized 优化后的反向下单服务
type ReverseServiceOptimized struct {
service.Service
// 订单状态更新锁,防止并发状态覆盖
orderStatusMutex sync.RWMutex
// 持仓更新锁,防止持仓数据竞态
positionMutex sync.RWMutex
// 止盈止损订单取消锁
takeProfitMutex sync.RWMutex
}
// PositionSyncResult 持仓同步结果
type PositionSyncResult struct {
ExchangePosition decimal.Decimal // 交易所实际持仓
SystemPosition decimal.Decimal // 系统记录持仓
NeedSync bool // 是否需要同步
SyncError error // 同步错误
}
// OrderStatusUpdate 订单状态更新结构
type OrderStatusUpdate struct {
OrderSn string
Status int
UpdateData map[string]interface{}
RetryCount int
LastAttempt time.Time
}
// ReverseOrderOptimized 优化后的反向下单处理
// 增强并发安全性和错误处理
func (e *ReverseServiceOptimized) ReverseOrderOptimized(apiKey string, mapData map[string]interface{}) (bool, error) {
apiInfo := GetApiInfoByKey(apiKey)
if apiInfo.Id == 0 {
e.Log.Errorf("获取apiInfo失败 %s", apiKey)
return false, nil
}
status, _ := maphelper.GetString(mapData, "X")
orderSn, err := maphelper.GetString(mapData, "c")
if err != nil {
return true, err
}
// 使用Redis分布式锁确保同一订单号的处理原子性
lockKey := fmt.Sprintf("reverse_order_lock:%s", orderSn)
lock := helper.NewRedisLock(lockKey, 30, 5, 100*time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if acquired, err := lock.AcquireWait(ctx); err != nil || !acquired {
return false, fmt.Errorf("获取订单处理锁失败: %s, err: %v", orderSn, err)
}
defer lock.Release()
switch status {
case "NEW", "CANCELED", "EXPIRED", "EXPIRED_IN_MATCH":
return e.handleSimpleStatusChangeOptimized(status, orderSn, mapData)
case "FILLED":
return e.handleFilledOrderOptimized(apiInfo, mapData)
default:
return false, fmt.Errorf("不支持的订单状态 %s", status)
}
}
// handleSimpleStatusChangeOptimized 优化的简单状态变更处理
func (e *ReverseServiceOptimized) handleSimpleStatusChangeOptimized(status string, orderSn string, mapData map[string]interface{}) (bool, error) {
statusMap := map[string]int{
"NEW": 2,
"CANCELED": 6,
"EXPIRED": 7,
"EXPIRED_IN_MATCH": 7,
}
if newStatus, ok := statusMap[status]; ok {
// 使用重试机制确保状态更新成功
update := &OrderStatusUpdate{
OrderSn: orderSn,
Status: newStatus,
UpdateData: mapData,
}
return false, e.updateOrderStatusWithRetry(update)
}
return false, fmt.Errorf("不支持的订单状态 %s", status)
}
// handleFilledOrderOptimized 优化的成交订单处理
func (e *ReverseServiceOptimized) handleFilledOrderOptimized(apiInfo DbModels.LineApiUser, mapData map[string]interface{}) (bool, error) {
if apiInfo.ReverseStatus != 1 || apiInfo.OpenStatus != 1 || apiInfo.ReverseApiId <= 0 {
return false, nil
}
reverseApiInfo, err := GetApiInfo(apiInfo.ReverseApiId)
if err != nil {
e.Log.Errorf("获取反向api信息失败 reverseApiId:%d, err:%v", apiInfo.ReverseApiId, err)
return true, err
}
mainOrder, err := e.SaveMainOrderOptimized(mapData, apiInfo)
if err != nil {
return false, err
}
// 使用事务确保持仓和订单状态的一致性
return e.processMainOrderWithTransaction(&mainOrder, &apiInfo, &reverseApiInfo)
}
// SaveMainOrderOptimized 优化的主单保存
func (e *ReverseServiceOptimized) SaveMainOrderOptimized(mapData map[string]interface{}, apiInfo DbModels.LineApiUser) (DbModels.LineReverseOrder, error) {
now := time.Now()
symbol, err := maphelper.GetString(mapData, "s")
var reverseOrder DbModels.LineReverseOrder
if err != nil {
return reverseOrder, err
}
orderSn, err := maphelper.GetString(mapData, "c")
if err != nil {
return reverseOrder, err
}
// 检查订单是否已存在,防止重复处理
existingOrder := DbModels.LineReverseOrder{}
if err := e.Orm.Where("order_sn = ?", orderSn).First(&existingOrder).Error; err == nil {
e.Log.Warnf("订单已存在,跳过处理: %s", orderSn)
return existingOrder, nil
}
side, err := maphelper.GetString(mapData, "S")
if err != nil {
return reverseOrder, err
}
positionSide, err := maphelper.GetString(mapData, "ps")
if err != nil {
return reverseOrder, err
}
mainType, _ := maphelper.GetString(mapData, "ot")
reverseOrder = DbModels.LineReverseOrder{
ApiId: apiInfo.Id,
Category: 0,
OrderSn: orderSn,
TriggerTime: &now,
Status: 3,
Symbol: symbol,
FollowOrderSn: "",
Type: mainType,
Price: maphelper.GetDecimal(mapData, "p"),
FinalPrice: maphelper.GetDecimal(mapData, "ap"),
TotalNum: maphelper.GetDecimal(mapData, "z"),
Side: side,
PositionSide: positionSide,
}
reverseOrder.PriceU = reverseOrder.Price
if !reverseOrder.Price.IsZero() && !reverseOrder.TotalNum.IsZero() {
reverseOrder.BuyPrice = reverseOrder.Price.Mul(reverseOrder.TotalNum)
}
if id, err := maphelper.GetFloat64(mapData, "i"); err == nil {
reverseOrder.OrderId = strconv.FormatFloat(id, 'f', -1, 64)
}
orderType, _ := maphelper.GetString(mapData, "ot")
switch orderType {
case "LIMIT", "MARKET":
reverseOrder.OrderType = 0
case "TAKE_PROFIT_MARKET", "TAKE_PROFIT":
reverseOrder.OrderType = 1
case "STOP_MARKET", "STOP", "TRAILING_STOP_MARKET":
reverseOrder.OrderType = 2
default:
return reverseOrder, fmt.Errorf("不支持的订单类型: %s", orderType)
}
if reverseOrder.PositionSide == "BOTH" {
return reverseOrder, errors.New("不支持的持仓类型,必须为双向持仓")
}
// 使用事务保存订单
err = e.Orm.Transaction(func(tx *gorm.DB) error {
return tx.Create(&reverseOrder).Error
})
if err != nil {
e.Log.Errorf("保存主单失败:%v", err)
return reverseOrder, err
}
return reverseOrder, nil
}
// processMainOrderWithTransaction 在事务中处理主单
func (e *ReverseServiceOptimized) processMainOrderWithTransaction(mainOrder *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, reverseApiInfo *DbModels.LineApiUser) (bool, error) {
var needReverseOrder bool
var closePosition bool
var err error
err = e.Orm.Transaction(func(tx *gorm.DB) error {
// 在事务中处理持仓更新
switch {
case mainOrder.PositionSide == "LONG" && mainOrder.Side == "BUY",
mainOrder.PositionSide == "SHORT" && mainOrder.Side == "SELL":
if mainOrder.Category == 0 {
needReverseOrder, closePosition, err = e.savePositionOptimized(tx, mainOrder, apiInfo, true, false)
if err != nil {
return err
}
}
case mainOrder.PositionSide == "SHORT" && mainOrder.Side == "BUY",
mainOrder.PositionSide == "LONG" && mainOrder.Side == "SELL":
if mainOrder.Category == 0 {
needReverseOrder, closePosition, err = e.savePositionOptimized(tx, mainOrder, apiInfo, true, true)
if err != nil {
e.Log.Errorf("保存主订单失败: %v", err)
return err
}
}
default:
return errors.New("不支持的订单类型")
}
return nil
})
if err != nil {
return false, err
}
// 事务成功后,异步处理反向下单
if needReverseOrder {
go func() {
if err := e.DoAddReverseOrderOptimized(mainOrder, reverseApiInfo, apiInfo.OrderProportion, false, closePosition); err != nil {
e.Log.Errorf("异步反向下单失败: %v", err)
}
}()
}
return true, nil
}
// savePositionOptimized 优化的持仓保存,增强并发安全性
func (e *ReverseServiceOptimized) savePositionOptimized(tx *gorm.DB, order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, isMain, reducePosition bool) (bool, bool, error) {
e.positionMutex.Lock()
defer e.positionMutex.Unlock()
position := DbModels.LineReversePosition{}
positionSide := order.PositionSide
side := order.Side
totalNum := order.TotalNum
closePosition := false
needReverseOrder := false
symbol, err1 := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 1)
if err1 != nil {
e.Log.Errorf("获取交易对失败 symbol:%s err:%v", order.Symbol, err1)
}
var querySql string
sqlStr := ""
// 持仓同步检查
syncResult := e.syncPositionWithExchange(apiInfo, order.Symbol, order.PositionSide)
if syncResult.NeedSync {
e.Log.Warnf("检测到持仓不同步,交易所持仓: %s, 系统持仓: %s",
syncResult.ExchangePosition.String(), syncResult.SystemPosition.String())
}
// 如果是主单,存储仓位则是反单的持仓方向
if isMain {
if order.PositionSide == "LONG" {
positionSide = "SHORT"
} else {
positionSide = "LONG"
}
// 减仓判断是否为平仓
closePosition = e.getClosePositionOptimized(reducePosition, apiInfo, order, order.PositionSide, isMain)
if !reducePosition {
// 反单止盈止损方向相反
if side == "SELL" {
side = "BUY"
} else {
side = "SELL"
}
position.ReverseApiId = apiInfo.ReverseApiId
position.Side = side
position.ApiId = order.ApiId
position.Symbol = order.Symbol
position.Status = 1
position.ReverseStatus = 0
position.PositionSide = positionSide
position.AveragePrice = order.FinalPrice
position.PositionNo = snowflakehelper.GetOrderNo()
}
querySql = "api_id =? and position_side =? and symbol =? and status =1"
// 使用乐观锁防止并发更新冲突
if closePosition {
totalNum = decimal.Zero
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,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,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.getClosePositionOptimized(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,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,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,version=version+1 where id =@id and reverse_status !=2 and version=@version"
}
}
var averagePrice decimal.Decimal
var remainQuantity decimal.Decimal
var currentVersion int
// 获取当前持仓记录
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 err2 := tx.Create(&position).Error; err2 != nil {
return false, false, err2
}
averagePrice = position.AveragePrice
} else {
return false, false, err1
}
} else {
currentVersion = position.Version
// 计算平均价格逻辑保持不变
var totalAmount decimal.Decimal
var totalPrice decimal.Decimal
if !position.Amount.IsZero() && !position.AveragePrice.IsZero() {
if isMain {
totalPrice = position.Amount.Mul(position.AveragePrice)
totalAmount = position.Amount
} else {
totalPrice = position.ReverseAmount.Mul(position.ReverseAveragePrice)
totalAmount = position.ReverseAmount
}
if !reducePosition {
totalPrice = totalPrice.Add(order.Price.Mul(order.TotalNum))
totalAmount = totalAmount.Add(order.TotalNum)
} else if reducePosition && !closePosition {
totalPrice = totalPrice.Sub(order.Price.Mul(order.TotalNum))
totalAmount = totalAmount.Sub(order.TotalNum)
}
}
if totalAmount.IsZero() || totalPrice.IsZero() {
if isMain {
averagePrice = position.AveragePrice
} else {
if position.ReverseAveragePrice.IsZero() {
averagePrice = order.Price
} else {
averagePrice = position.ReverseAveragePrice
}
}
} else {
if closePosition {
if isMain {
averagePrice = position.AveragePrice
} else {
averagePrice = position.ReverseAveragePrice
}
} else {
averagePrice = totalPrice.Div(totalAmount).Truncate(int32(symbol.PriceDigit))
}
}
}
// 关联订单的仓位id
if err2 := tx.Exec("UPDATE line_reverse_order set position_id=@positionId where id=@orderId and position_id = 0",
sql.Named("positionId", position.Id), sql.Named("orderId", order.Id)).Error; err2 != nil {
return false, false, err2
}
// 使用版本号进行乐观锁更新
dbResult := tx.Exec(sqlStr,
sql.Named("totalNum", totalNum),
sql.Named("id", position.Id),
sql.Named("averagePrice", averagePrice),
sql.Named("version", currentVersion))
if dbResult.Error != nil {
return false, false, dbResult.Error
}
order.PositionId = position.Id
if dbResult.RowsAffected == 0 {
e.Log.Errorf("持仓更新失败,可能存在并发冲突 closePosition:%v order:%v", closePosition, order)
return false, false, errors.New("持仓更新失败,可能存在并发冲突")
}
if reducePosition && !isMain {
remainQuantity = position.ReverseAmount.Sub(totalNum)
} else if !isMain {
remainQuantity = position.ReverseAmount.Add(totalNum)
}
// 主单且对手单没有平仓
if isMain && position.ReverseStatus != 2 {
needReverseOrder = true
}
// 异步处理止盈止损
if !isMain && !closePosition {
go func() {
if err := e.doDefaultTakeStopOptimized(order, apiInfo, remainQuantity); err != nil {
e.Log.Errorf("设置默认止盈止损失败: %v", err)
}
}()
} else if !isMain && closePosition {
// 异步取消剩余委托
go func() {
if err := e.DoCancelTakeAndStopOptimized(order.Symbol, position.PositionSide, apiInfo); err != nil {
e.Log.Errorf("取消止盈止损订单失败: %v", err)
}
}()
}
return needReverseOrder, closePosition, nil
}
// syncPositionWithExchange 同步交易所持仓数据
func (e *ReverseServiceOptimized) syncPositionWithExchange(apiInfo *DbModels.LineApiUser, symbol, positionSide string) *PositionSyncResult {
result := &PositionSyncResult{}
// 获取交易所实际持仓
futApi := FutRestApi{}
holdData := HoldeData{}
err := futApi.GetPositionData(apiInfo, symbol, positionSide, &holdData)
if err != nil {
result.SyncError = err
return result
}
result.ExchangePosition = holdData.TotalQuantity
// 获取系统记录的持仓
position := DbModels.LineReversePosition{}
err = e.Orm.Where("api_id = ? AND position_side = ? AND symbol = ? AND status = 1",
apiInfo.Id, positionSide, symbol).First(&position).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
result.SystemPosition = decimal.Zero
} else {
result.SyncError = err
return result
}
} else {
result.SystemPosition = position.Amount
}
// 判断是否需要同步(允许小幅误差)
diff := result.ExchangePosition.Sub(result.SystemPosition).Abs()
threshold := decimal.NewFromFloat(0.001) // 0.001的误差阈值
result.NeedSync = diff.GreaterThan(threshold)
return result
}
// getClosePositionOptimized 优化的平仓判断
func (e *ReverseServiceOptimized) getClosePositionOptimized(reducePosition bool, apiInfo *DbModels.LineApiUser, order *DbModels.LineReverseOrder, positionSide string, isMain bool) bool {
closePosition := false
if reducePosition {
// 使用重试机制获取持仓数据
opts := retryhelper.DefaultRetryOptions()
opts.MaxRetries = 3
opts.InitialInterval = time.Second
holdData, err := retryhelper.RetryWithResult(func() (*HoldeData, error) {
futApi := FutRestApi{}
holdData := &HoldeData{}
err := futApi.GetPositionData(apiInfo, order.Symbol, positionSide, holdData)
return holdData, err
}, opts)
if err != nil {
e.Log.Errorf("获取剩余持仓信息失败 symbol:%s err:%v", order.Symbol, err)
// 降级到数据库查询
lastPosition := DbModels.LineReversePosition{}
if err2 := e.Orm.Model(&DbModels.LineReversePosition{}).Where("position_side =? and symbol =? and status =1", positionSide, order.Symbol).First(&lastPosition).Error; err2 != nil {
e.Log.Errorf("获取上一次持仓信息失败 symbol:%s err:%v", order.Symbol, err2)
} else if isMain && lastPosition.Amount.Cmp(order.TotalNum) <= 0 {
closePosition = true
} else if !isMain && lastPosition.ReverseAmount.Cmp(order.TotalNum) <= 0 {
closePosition = true
}
} else if holdData.TotalQuantity.IsZero() {
closePosition = true
}
}
return closePosition
}
// updateOrderStatusWithRetry 带重试的订单状态更新
func (e *ReverseServiceOptimized) updateOrderStatusWithRetry(update *OrderStatusUpdate) error {
e.orderStatusMutex.Lock()
defer e.orderStatusMutex.Unlock()
opts := retryhelper.DefaultRetryOptions()
opts.MaxRetries = 3
opts.InitialInterval = 500 * time.Millisecond
return retryhelper.Retry(func() error {
data := map[string]interface{}{
"status": update.Status,
"updated_at": time.Now(),
}
if update.Status == 3 {
now := time.Now()
if orderId, ok := update.UpdateData["i"].(float64); ok {
data["order_id"] = orderId
}
if ap, ok := update.UpdateData["ap"].(string); ok {
data["final_price"], _ = decimal.NewFromString(ap)
}
if num, ok := update.UpdateData["z"].(string); ok {
data["total_num"], _ = decimal.NewFromString(num)
}
data["trigger_time"] = &now
} else if update.Status == 2 {
if orderId, ok := update.UpdateData["i"].(float64); ok {
data["order_id"] = orderId
}
}
db := e.Orm.Model(&DbModels.LineReverseOrder{}).
Where("order_sn = ? and status != 3", update.OrderSn).
Updates(data)
if db.Error != nil {
e.Log.Errorf("修改订单状态失败 orderSn:%s, err:%v", update.OrderSn, db.Error)
return db.Error
}
if db.RowsAffected == 0 {
e.Log.Warnf("修改订单状态失败 orderSn:%s, 未找到订单或状态未变更", update.OrderSn)
}
return nil
}, opts)
}
// DoAddReverseOrderOptimized 优化的反向下单
func (e *ReverseServiceOptimized) DoAddReverseOrderOptimized(mainOrder *DbModels.LineReverseOrder, reverseApiInfo *DbModels.LineApiUser, orderProportion decimal.Decimal, reducePosition, closePosition bool) error {
// 使用分布式锁防止重复下单
lockKey := fmt.Sprintf("reverse_add_order:%s", mainOrder.OrderSn)
lock := helper.NewRedisLock(lockKey, 30, 5, 100*time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if acquired, err := lock.AcquireWait(ctx); err != nil || !acquired {
return fmt.Errorf("获取反向下单锁失败: %s, err: %v", mainOrder.OrderSn, err)
}
defer lock.Release()
// 检查是否已经存在反向订单
existingOrder := DbModels.LineReverseOrder{}
if err := e.Orm.Where("follow_order_sn = ? AND category = 1", mainOrder.OrderSn).First(&existingOrder).Error; err == nil {
e.Log.Warnf("反向订单已存在,跳过处理: %s", mainOrder.OrderSn)
return nil
}
order := DbModels.LineReverseOrder{}
order.ApiId = reverseApiInfo.Id
order.Category = 1
order.OrderSn = helper.GetOrderNo()
order.FollowOrderSn = mainOrder.OrderSn
order.Symbol = mainOrder.Symbol
order.OrderType = 0
// 设置持仓方向和交易方向
switch mainOrder.PositionSide {
case "LONG":
order.PositionSide = "SHORT"
case "SHORT":
order.PositionSide = "LONG"
}
switch mainOrder.Side {
case "SELL":
order.Side = "BUY"
case "BUY":
order.Side = "SELL"
default:
return fmt.Errorf("不支持的订单类型 side:%s", mainOrder.Side)
}
if reducePosition && closePosition {
order.OrderType = 4
} else if reducePosition {
order.OrderType = 3
}
symbol, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, mainOrder.Symbol, 1)
if err != nil {
e.Log.Errorf("获取交易对信息失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
return err
}
setting, err := cacheservice.GetReverseSetting(e.Orm)
if err != nil {
e.Log.Errorf("获取反单设置失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
return err
}
// 获取最新价格
signPrice, _ := decimal.NewFromString(symbol.LastPrice)
price := signPrice.Truncate(int32(symbol.PriceDigit))
if price.Cmp(decimal.Zero) <= 0 {
e.Log.Errorf("获取最新价格失败 symbol:%s custom:%s 单价小于0", mainOrder.Symbol, mainOrder.OrderSn)
return errors.New("获取最新价格失败")
}
// 计算溢价
var percent decimal.Decimal
switch {
case order.PositionSide == "LONG" && order.Side == "BUY", order.PositionSide == "SHORT" && order.Side == "BUY":
percent = decimal.NewFromInt(100).Add(setting.ReversePremiumRatio)
case order.PositionSide == "SHORT" && order.Side == "SELL", order.PositionSide == "LONG" && order.Side == "SELL":
percent = decimal.NewFromInt(100).Sub(setting.ReversePremiumRatio)
default:
return fmt.Errorf("不支持的订单类型 ps:%s, side:%s", order.PositionSide, order.Side)
}
percent = percent.Div(decimal.NewFromInt(100)).Truncate(4)
price = price.Mul(percent).Truncate(int32(symbol.PriceDigit))
var amount decimal.Decimal
// 计算下单数量
if closePosition {
var position DbModels.LineReversePosition
if err1 := e.Orm.Model(position).
Where("position_side = ? and reverse_api_id = ? and reverse_status = 1", order.PositionSide, order.ApiId).
Select("reverse_amount").
Find(&position).Error; err1 != nil {
e.Log.Errorf("获取剩余仓位失败 symbol:%s custom:%s :%v", order.Symbol, order.OrderSn, err1)
if len(err1.Error()) < 255 {
order.Remark = err1.Error()
} else {
order.Remark = err1.Error()[:254]
}
}
amount = position.ReverseAmount
if amount.IsZero() {
order.Remark = "没有持仓数量"
}
} else {
proportion := decimal.NewFromInt(100)
if orderProportion.Cmp(decimal.Zero) > 0 {
proportion = orderProportion
}
proportion = proportion.Div(decimal.NewFromInt(100)).Truncate(4)
amount = mainOrder.TotalNum.Mul(proportion).Truncate(int32(symbol.AmountDigit))
logger.Info("反向下单比例 %s ,原始数量:%s反向下单数量%s",
proportion.String(), mainOrder.TotalNum.String(), amount.String())
if amount.Cmp(decimal.Zero) <= 0 {
e.Log.Errorf("计算数量失败 symbol:%s custom:%s 数量小于0", mainOrder.Symbol, mainOrder.OrderSn)
return errors.New("计算数量失败")
}
}
order.TotalNum = amount
order.Price = price
order.PriceU = price
order.BuyPrice = amount.Mul(price).Truncate(int32(symbol.PriceDigit))
order.Status = 1
order.Type = setting.ReverseOrderType
order.SignPrice = signPrice
order.PositionId = mainOrder.PositionId
if order.Remark != "" {
order.Status = 8
}
// 保存反向订单
if err := e.Orm.Model(&order).Create(&order).Error; err != nil {
e.Log.Errorf("保存反单失败 symbol:%s custom:%s :%v", mainOrder.Symbol, mainOrder.OrderSn, err)
return err
}
// 如果状态正常,进行下单
if order.Status == 1 {
err = e.DoBianceOrderOptimized(&order, reverseApiInfo, &setting, reducePosition, closePosition)
// 下单成功且为平仓单时,取消止盈止损
if err == nil && closePosition {
go func() {
if err := e.DoCancelTakeProfitBatchOptimized(symbol.GetSymbol(), order.PositionSide, order.Side, 1, reverseApiInfo); err != nil {
e.Log.Errorf("取消止盈止损订单失败: %v", err)
}
}()
}
}
return err
}
// DoBianceOrderOptimized 优化的币安下单
func (e *ReverseServiceOptimized) DoBianceOrderOptimized(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, setting *DbModels.LineReverseSetting, reducePosition, closePosition bool) error {
// 使用重试机制进行下单
opts := retryhelper.DefaultRetryOptions()
opts.MaxRetries = 3
opts.InitialInterval = time.Second
opts.RetryableErrFn = func(err error) bool {
// 某些错误不需要重试
if err != nil {
errStr := err.Error()
// 余额不足、订单重复等错误不重试
if contains(errStr, []string{"余额不足", "订单重复", "API-key", "无效"}) {
return false
}
}
return true
}
return retryhelper.Retry(func() error {
futApiV2 := FutRestApi{}
params := FutOrderPlace{
ApiId: apiInfo.Id,
Symbol: order.Symbol,
Side: order.Side,
PositionSide: order.PositionSide,
OrderType: setting.ReverseOrderType,
Quantity: order.TotalNum,
Price: order.Price,
NewClientOrderId: order.OrderSn,
}
if err := futApiV2.OrderPlaceLoop(e.Orm, params, 3); err != nil {
e.Log.Errorf("币安下单失败 orderSn:%s, err:%v", order.OrderSn, err)
// 更新订单状态为失败
e.Orm.Model(&DbModels.LineReverseOrder{}).
Where("id = ?", order.Id).
Updates(map[string]interface{}{
"status": 8,
"remark": err.Error()[:min(len(err.Error()), 254)],
"updated_at": time.Now(),
})
return err
}
return nil
}, opts)
}
// DoCancelTakeProfitBatchOptimized 优化的批量取消止盈止损订单
func (e *ReverseServiceOptimized) DoCancelTakeProfitBatchOptimized(symbol, positionSide, side string, orderType int, apiInfo *DbModels.LineApiUser) error {
e.takeProfitMutex.Lock()
defer e.takeProfitMutex.Unlock()
// 获取需要取消的订单
orderSnList := e.GetTakeProfitBatchOptimized(symbol, positionSide, side, apiInfo.Id, orderType)
if len(orderSnList) == 0 {
return nil
}
// 分批取消每次最多10个
batchSize := 10
for i := 0; i < len(orderSnList); i += batchSize {
end := i + batchSize
if end > len(orderSnList) {
end = len(orderSnList)
}
batch := orderSnList[i:end]
// 使用重试机制取消订单
opts := retryhelper.DefaultRetryOptions()
opts.MaxRetries = 3
opts.InitialInterval = time.Second
err := retryhelper.Retry(func() error {
futApi := FutRestApi{}
return futApi.CancelBatchFutOrder(*apiInfo, symbol, batch)
}, opts)
if err != nil {
e.Log.Errorf("批量取消止盈止损订单失败 symbol:%s, batch:%v, err:%v", symbol, batch, err)
// 继续处理下一批,不因为一批失败而中断
continue
}
e.Log.Infof("成功取消止盈止损订单 symbol:%s, count:%d", symbol, len(batch))
}
return nil
}
// GetTakeProfitBatchOptimized 优化的获取止盈止损订单列表
func (e *ReverseServiceOptimized) GetTakeProfitBatchOptimized(symbol, positionSide, side string, apiId int, orderType int) []string {
var orders []DbModels.LineReverseOrder
query := e.Orm.Model(&DbModels.LineReverseOrder{}).
Where("symbol = ? AND position_side = ? AND side = ? AND api_id = ? AND order_type = ? AND status = 2",
symbol, positionSide, side, apiId, orderType).
Select("order_sn")
if err := query.Find(&orders).Error; err != nil {
e.Log.Errorf("查询止盈止损订单失败: %v", err)
return nil
}
orderSnList := make([]string, 0, len(orders))
for _, order := range orders {
orderSnList = append(orderSnList, order.OrderSn)
}
return orderSnList
}
// DoCancelTakeAndStopOptimized 优化的取消止盈止损订单
func (e *ReverseServiceOptimized) DoCancelTakeAndStopOptimized(symbol, positionSide string, apiInfo *DbModels.LineApiUser) error {
e.takeProfitMutex.Lock()
defer e.takeProfitMutex.Unlock()
var orders []DbModels.LineReverseOrder
if err := e.Orm.Model(&DbModels.LineReverseOrder{}).
Where("symbol = ? AND position_side = ? AND api_id = ? AND order_type IN (1,2) AND status = 2",
symbol, positionSide, apiInfo.Id).
Select("order_sn").
Find(&orders).Error; err != nil {
e.Log.Errorf("查询止盈止损订单失败: %v", err)
return err
}
if len(orders) == 0 {
return nil
}
orderSnList := make([]string, 0, len(orders))
for _, order := range orders {
orderSnList = append(orderSnList, order.OrderSn)
}
// 分批取消
batchSize := 10
for i := 0; i < len(orderSnList); i += batchSize {
end := i + batchSize
if end > len(orderSnList) {
end = len(orderSnList)
}
batch := orderSnList[i:end]
opts := retryhelper.DefaultRetryOptions()
opts.MaxRetries = 3
opts.InitialInterval = time.Second
err := retryhelper.Retry(func() error {
futApi := FutRestApi{}
return futApi.CancelBatchFutOrder(*apiInfo, symbol, batch)
}, opts)
if err != nil {
e.Log.Errorf("批量取消止盈止损订单失败 symbol:%s, batch:%v, err:%v", symbol, batch, err)
}
}
return nil
}
// doDefaultTakeStopOptimized 优化的默认止盈止损设置
func (e *ReverseServiceOptimized) doDefaultTakeStopOptimized(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, totalNum decimal.Decimal) error {
// 异步处理,避免阻塞主流程
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 使用重试机制
opts := retryhelper.DefaultRetryOptions()
opts.MaxRetries = 3
opts.InitialInterval = 2 * time.Second
err := retryhelper.Retry(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return e.processDefaultTakeStop(order, apiInfo, totalNum)
}
}, opts)
if err != nil {
e.Log.Errorf("设置默认止盈止损失败: %v", err)
}
}()
return nil
}
// processDefaultTakeStop 处理默认止盈止损逻辑
func (e *ReverseServiceOptimized) processDefaultTakeStop(order *DbModels.LineReverseOrder, apiInfo *DbModels.LineApiUser, totalNum decimal.Decimal) error {
// 获取反单设置
setting, err := cacheservice.GetReverseSetting(e.Orm)
if err != nil {
return fmt.Errorf("获取反单设置失败: %v", err)
}
// 获取交易对信息
symbol, err := cacheservice.GetTradeSet(global.EXCHANGE_BINANCE, order.Symbol, 1)
if err != nil {
return fmt.Errorf("获取交易对信息失败: %v", err)
}
// 获取当前价格
lastPrice, _ := decimal.NewFromString(symbol.LastPrice)
if lastPrice.IsZero() {
return errors.New("获取最新价格失败")
}
now := time.Now()
// 设置止盈订单
if setting.TakeProfitRatio.GreaterThan(decimal.Zero) {
takeProfitPrice := e.calculatePriceOptimized(order.PositionSide, lastPrice, setting.TakeProfitRatio, true)
takeProfitParams := CreateOrderParams{
ApiInfo: apiInfo,
Order: order,
Symbol: &symbol,
Side: e.getOppositeSideOptimized(order.Side),
OrderType: "TAKE_PROFIT_MARKET",
Price: takeProfitPrice,
TotalNum: totalNum,
Now: now,
LastPrice: lastPrice,
Close: false,
PositionId: order.PositionId,
OrderSn: helper.GetOrderNo(),
PositionSide: order.PositionSide,
}
if err := e.createReverseOrderOptimized(takeProfitParams); err != nil {
e.Log.Errorf("创建止盈订单失败: %v", err)
}
}
// 设置止损订单
if setting.StopLossRatio.GreaterThan(decimal.Zero) {
stopLossPrice := e.calculatePriceOptimized(order.PositionSide, lastPrice, setting.StopLossRatio, false)
stopLossParams := CreateOrderParams{
ApiInfo: apiInfo,
Order: order,
Symbol: &symbol,
Side: e.getOppositeSideOptimized(order.Side),
OrderType: "STOP_MARKET",
Price: stopLossPrice,
TotalNum: totalNum,
Now: now,
LastPrice: lastPrice,
Close: false,
PositionId: order.PositionId,
OrderSn: helper.GetOrderNo(),
PositionSide: order.PositionSide,
}
if err := e.createReverseOrderOptimized(stopLossParams); err != nil {
e.Log.Errorf("创建止损订单失败: %v", err)
}
}
return nil
}
// calculatePriceOptimized 优化的价格计算
func (e *ReverseServiceOptimized) calculatePriceOptimized(positionSide string, base decimal.Decimal, ratio decimal.Decimal, isTakeProfit bool) decimal.Decimal {
var multiplier decimal.Decimal
if isTakeProfit {
if positionSide == "LONG" {
multiplier = decimal.NewFromInt(1).Add(ratio.Div(decimal.NewFromInt(100)))
} else {
multiplier = decimal.NewFromInt(1).Sub(ratio.Div(decimal.NewFromInt(100)))
}
} else {
if positionSide == "LONG" {
multiplier = decimal.NewFromInt(1).Sub(ratio.Div(decimal.NewFromInt(100)))
} else {
multiplier = decimal.NewFromInt(1).Add(ratio.Div(decimal.NewFromInt(100)))
}
}
return base.Mul(multiplier)
}
// getOppositeSideOptimized 获取相反的交易方向
func (e *ReverseServiceOptimized) getOppositeSideOptimized(side string) string {
if side == "BUY" {
return "SELL"
}
return "BUY"
}
// CreateOrderParams 创建订单参数
// CreateOrderParams 已在 reverse_service.go 中定义
// createReverseOrderOptimized 优化的创建反向订单
func (e *ReverseServiceOptimized) createReverseOrderOptimized(params CreateOrderParams) error {
// 使用事务确保数据一致性
return e.Orm.Transaction(func(tx *gorm.DB) error {
reverseOrder := DbModels.LineReverseOrder{
ApiId: params.ApiInfo.Id,
Category: 1,
OrderSn: params.OrderSn,
FollowOrderSn: params.Order.OrderSn,
Symbol: params.Order.Symbol,
Side: params.Side,
PositionSide: params.PositionSide,
Price: params.Price,
PriceU: params.Price,
TotalNum: params.TotalNum,
BuyPrice: params.Price.Mul(params.TotalNum),
SignPrice: params.LastPrice,
Status: 1,
PositionId: params.PositionId,
TriggerTime: &params.Now,
}
// 设置订单类型
switch params.OrderType {
case "TAKE_PROFIT_MARKET", "TAKE_PROFIT":
reverseOrder.OrderType = 1
reverseOrder.Type = "TAKE_PROFIT_MARKET"
case "STOP_MARKET", "STOP":
reverseOrder.OrderType = 2
reverseOrder.Type = "STOP_MARKET"
default:
return fmt.Errorf("不支持的订单类型: %s", params.OrderType)
}
// 保存订单到数据库
if err := tx.Create(&reverseOrder).Error; err != nil {
e.Log.Errorf("保存反向订单失败: %v", err)
return err
}
// 下单到交易所
futApi := FutRestApi{}
orderParams := FutOrderPlace{
ApiId: params.ApiInfo.Id,
Symbol: reverseOrder.Symbol,
Side: reverseOrder.Side,
PositionSide: reverseOrder.PositionSide,
OrderType: "MARKET",
Quantity: reverseOrder.TotalNum,
StopPrice: reverseOrder.Price,
NewClientOrderId: reverseOrder.OrderSn,
}
if err := futApi.OrderPlaceLoop(tx, orderParams, 3); err != nil {
e.Log.Errorf("下单失败: %v", err)
// 更新订单状态为失败
tx.Model(&reverseOrder).Updates(map[string]interface{}{
"status": 8,
"remark": err.Error()[:min(len(err.Error()), 254)],
"updated_at": time.Now(),
})
return err
}
return nil
})
}
// contains 检查字符串是否包含任意一个子字符串
func contains(s string, substrs []string) bool {
for _, substr := range substrs {
if len(substr) > 0 && len(s) >= len(substr) {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
}
}
return false
}
// min 返回两个整数中的较小值
func min(a, b int) int {
if a < b {
return a
}
return b
}