2025-07-26 09:09:09 +08:00
package binanceservice
import (
"database/sql"
"errors"
"fmt"
DbModels "go-admin/app/admin/models"
"go-admin/common/global"
"go-admin/common/helper"
2025-08-01 10:30:43 +08:00
"go-admin/models"
2025-07-26 09:09:09 +08:00
"go-admin/pkg/maphelper"
2025-08-01 10:30:43 +08:00
"go-admin/pkg/utility/snowflakehelper"
2025-07-26 09:09:09 +08:00
"go-admin/services/cacheservice"
"strconv"
"time"
2025-08-11 09:27:32 +08:00
"github.com/go-admin-team/go-admin-core/logger"
2025-07-26 09:09:09 +08:00
"github.com/go-admin-team/go-admin-core/sdk/service"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
type ReverseService struct {
service . Service
}
// SaveMainOrder 保存订单信息
// mapData: 下单数据
// status: 订单状态
// orderSn: 自定义订单号
func ( e * ReverseService ) handleSimpleStatusChange ( 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 {
_ = e . changeOrderStatus ( newStatus , orderSn , mapData )
return false , nil
}
return false , fmt . Errorf ( "不支持的订单状态 %s" , status )
}
// ReverseOrder 反向下单
// apiKey: api key
// mapData: 下单数据
// return apiInfo, bool
// bool: true=需要反单, false=不需要反单
func ( e * ReverseService ) ReverseOrder ( 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
}
ot , err := maphelper . GetString ( mapData , "ot" )
if err != nil {
return true , err
}
switch status {
case "NEW" , "CANCELED" , "EXPIRED" , "EXPIRED_IN_MATCH" :
result , err := e . handleSimpleStatusChange ( status , orderSn , mapData )
//如果是 新开止盈止损 需要取消反单的止盈止损之后重下反单止盈止损
2025-08-01 10:30:43 +08:00
if status == "NEW" && ( ot == "TAKE_PROFIT_MARKET" || ot == "STOP_MARKET" || ot == "TAKE_PROFIT" || ot == "STOP" ) &&
2025-07-26 09:09:09 +08:00
apiInfo . ReverseStatus == 1 && apiInfo . OpenStatus == 1 && apiInfo . ReverseApiId > 0 {
2025-08-01 10:30:43 +08:00
symbolCode , err := maphelper . GetString ( mapData , "s" )
2025-07-26 09:09:09 +08:00
2025-08-01 10:30:43 +08:00
if err != nil {
e . Log . Errorf ( "获取symbolCode失败 symbol:%s err:%v" , symbolCode , err )
return true , err
}
symbol , err := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , symbolCode , 1 )
if err != nil {
e . Log . Errorf ( "获取symbol失败 symbol:%s err:%v" , symbolCode , err )
return true , err
}
if err := e . ReTakeOrStopOrder ( & mapData , orderSn , & apiInfo , & symbol ) ; err != nil {
2025-07-26 09:09:09 +08:00
return true , err
}
}
return result , err
case "FILLED" :
if apiInfo . ReverseStatus == 1 && apiInfo . OpenStatus == 1 && apiInfo . ReverseApiId > 0 {
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 . SaveMainOrder ( mapData , apiInfo )
if err != nil {
return false , err
}
switch {
case mainOrder . PositionSide == "LONG" && mainOrder . Side == "BUY" , mainOrder . PositionSide == "SHORT" && mainOrder . Side == "SELL" :
if mainOrder . Category == 0 {
2025-08-11 09:27:32 +08:00
needReverseOrder , closePosition , err1 := e . savePosition ( & mainOrder , & apiInfo , true , false )
if err1 != nil {
2025-07-26 09:09:09 +08:00
return true , err1
}
2025-08-11 09:27:32 +08:00
if needReverseOrder {
e . DoAddReverseOrder ( & mainOrder , & reverseApiInfo , apiInfo . OrderProportion , false , closePosition )
}
2025-07-26 09:09:09 +08:00
}
case mainOrder . PositionSide == "SHORT" && mainOrder . Side == "BUY" , mainOrder . PositionSide == "LONG" && mainOrder . Side == "SELL" :
if mainOrder . Category == 0 {
2025-08-11 09:27:32 +08:00
needReverseOrder , closePosition , err1 := e . savePosition ( & mainOrder , & apiInfo , true , true )
if err1 != nil {
2025-07-26 09:09:09 +08:00
e . Log . Errorf ( "保存主订单失败: %v" , err1 )
return true , err1
}
2025-08-11 09:27:32 +08:00
if needReverseOrder {
e . DoAddReverseOrder ( & mainOrder , & reverseApiInfo , apiInfo . OrderProportion , true , closePosition )
}
2025-07-26 09:09:09 +08:00
}
default :
return true , errors . New ( "不支持的订单类型" )
}
return true , nil
} else if apiInfo . Subordinate == "2" {
2025-08-11 09:27:32 +08:00
e . changeOrderStatus ( 3 , orderSn , mapData )
2025-07-26 09:09:09 +08:00
symbol , err := maphelper . GetString ( mapData , "s" )
if err != nil {
return true , err
}
positionSide , err := maphelper . GetString ( mapData , "ps" )
if err != nil {
return true , err
}
side , err := maphelper . GetString ( mapData , "S" )
if err != nil {
return true , err
}
2025-08-01 10:30:43 +08:00
price := maphelper . GetDecimal ( mapData , "ap" )
2025-07-26 09:09:09 +08:00
totalNum := maphelper . GetDecimal ( mapData , "z" )
mainOrder := DbModels . LineReverseOrder {
ApiId : apiInfo . Id ,
Symbol : symbol ,
PositionSide : positionSide ,
TotalNum : totalNum ,
Side : side ,
2025-08-01 10:30:43 +08:00
Price : price ,
2025-08-11 09:27:32 +08:00
FinalPrice : price ,
2025-07-26 09:09:09 +08:00
}
switch {
case mainOrder . PositionSide == "LONG" && mainOrder . Side == "BUY" , mainOrder . PositionSide == "SHORT" && mainOrder . Side == "SELL" :
if mainOrder . Category == 0 {
2025-08-11 09:27:32 +08:00
if _ , _ , err1 := e . savePosition ( & mainOrder , & apiInfo , false , false ) ; err1 != nil {
2025-07-26 09:09:09 +08:00
return true , err1
}
}
2025-08-11 09:27:32 +08:00
case mainOrder . PositionSide == "SHORT" && mainOrder . Side == "BUY" , mainOrder . PositionSide == "LONG" && mainOrder . Side == "SrgetELL" :
2025-07-26 09:09:09 +08:00
if mainOrder . Category == 0 {
2025-08-11 09:27:32 +08:00
if _ , _ , err1 := e . savePosition ( & mainOrder , & apiInfo , false , true ) ; err1 != nil {
2025-07-26 09:09:09 +08:00
return true , err1
}
}
default :
return true , errors . New ( "不支持的订单类型" )
}
}
default :
return false , fmt . Errorf ( "不支持的订单状态 %s" , status )
}
return false , nil
}
// 修改订单状态
// status: 订单状态 1-待下单 2-已下单 3-已成交 6-已取消 7-已过期
func ( e * ReverseService ) changeOrderStatus ( status int , orderSn string , mapData map [ string ] interface { } ) error {
data := map [ string ] interface { } { "status" : status , "updated_at" : time . Now ( ) }
if status == 3 {
now := time . Now ( )
if orderId , ok := mapData [ "i" ] . ( float64 ) ; ok {
data [ "order_id" ] = orderId
}
2025-08-01 10:30:43 +08:00
if ap , ok := mapData [ "ap" ] . ( string ) ; ok {
data [ "final_price" ] , _ = decimal . NewFromString ( ap )
2025-07-26 09:09:09 +08:00
}
if num , ok := mapData [ "z" ] . ( string ) ; ok {
data [ "total_num" ] , _ = decimal . NewFromString ( num )
}
data [ "trigger_time" ] = & now
2025-08-01 10:30:43 +08:00
} else if status == 2 {
if orderId , ok := mapData [ "i" ] . ( float64 ) ; ok {
data [ "order_id" ] = orderId
}
2025-07-26 09:09:09 +08:00
}
db := e . Orm . Model ( & DbModels . LineReverseOrder { } ) .
Where ( "order_sn =? and status != 3" , orderSn ) .
Updates ( data )
if db . Error != nil {
e . Log . Errorf ( "修改订单状态失败 orderSn:%s, err:%v" , orderSn , db . Error )
}
if db . RowsAffected == 0 {
e . Log . Errorf ( "修改订单状态失败 orderSn:%s, 未找到订单" , orderSn )
}
return db . Error
}
// 先保存主单,持仓信息 必须用双向持仓!
func ( e * ReverseService ) SaveMainOrder ( 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
}
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 ( "不支持的持仓类型,必须为双向持仓" )
}
if err := e . Orm . Create ( & reverseOrder ) . Error ; err != nil {
e . Log . Errorf ( "保存主单失败:%v" , err )
return reverseOrder , err
}
return reverseOrder , nil
}
// 更新仓位信息
2025-08-11 09:27:32 +08:00
// apiInfo: 当前下单api信息
// return
// neeReverseOrder: 是否需要反单
2025-07-26 09:09:09 +08:00
// closePosition: true=平仓, false=减仓
2025-08-11 09:27:32 +08:00
// err error 错误信息
func ( e * ReverseService ) savePosition ( order * DbModels . LineReverseOrder , apiInfo * DbModels . LineApiUser , isMain , reducePosition bool ) ( bool , bool , error ) {
2025-07-26 09:09:09 +08:00
position := DbModels . LineReversePosition { }
2025-08-11 09:27:32 +08:00
positionSide := order . PositionSide
side := order . Side
totalNum := order . TotalNum
closePosition := false
needReverseOrder := false
2025-07-26 09:09:09 +08:00
2025-08-11 09:27:32 +08:00
symbol , err1 := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , order . Symbol , 1 )
2025-08-01 10:30:43 +08:00
if err1 != nil {
2025-08-11 09:27:32 +08:00
e . Log . Errorf ( "获取交易对失败 symbol:%s err:%v" , order . Symbol , err1 )
2025-08-01 10:30:43 +08:00
}
2025-07-26 09:09:09 +08:00
var querySql string
sqlStr := ""
//如果是主单,存储仓位则是反单的持仓方向
if isMain {
2025-08-11 09:27:32 +08:00
if order . PositionSide == "LONG" {
2025-07-26 09:09:09 +08:00
positionSide = "SHORT"
} else {
positionSide = "LONG"
}
2025-08-11 09:27:32 +08:00
//减仓 判断是否为平仓
closePosition = e . getClosePosition ( reducePosition , apiInfo , order , order . PositionSide , isMain )
2025-07-26 09:09:09 +08:00
if ! reducePosition {
//反单止盈止损方向相反
if side == "SELL" {
side = "BUY"
} else {
side = "SELL"
}
2025-08-11 09:27:32 +08:00
position . ReverseApiId = apiInfo . ReverseApiId
2025-07-26 09:09:09 +08:00
position . Side = side
2025-08-11 09:27:32 +08:00
position . ApiId = order . ApiId
position . Symbol = order . Symbol
2025-07-26 09:09:09 +08:00
position . Status = 1
position . ReverseStatus = 0
position . PositionSide = positionSide
2025-08-11 09:27:32 +08:00
position . AveragePrice = order . FinalPrice
2025-08-01 10:30:43 +08:00
position . PositionNo = snowflakehelper . GetOrderNo ( )
2025-07-26 09:09:09 +08:00
}
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 where id =@id and status!=2 "
} else if reducePosition {
//只减仓
2025-08-01 10:30:43 +08:00
sqlStr = "UPDATE line_reverse_position set amount=amount - @totalNum,updated_at=now(),average_price=@averagePrice where id =@id and status!=2 "
2025-07-26 09:09:09 +08:00
} else {
2025-08-01 10:30:43 +08:00
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"
2025-07-26 09:09:09 +08:00
}
} else {
querySql = "reverse_api_id =? and position_side =? and symbol =? and reverse_status in (0,1)"
2025-08-11 09:27:32 +08:00
//减仓 判断是否为平仓
closePosition = e . getClosePosition ( reducePosition , apiInfo , order , order . PositionSide , isMain )
2025-07-26 09:09:09 +08:00
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"
} else if reducePosition {
2025-08-01 10:30:43 +08:00
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"
2025-07-26 09:09:09 +08:00
} else {
2025-08-01 10:30:43 +08:00
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"
2025-07-26 09:09:09 +08:00
}
}
2025-08-01 10:30:43 +08:00
var averagePrice decimal . Decimal
2025-08-11 09:27:32 +08:00
var remainQuantity decimal . Decimal
2025-08-01 10:30:43 +08:00
2025-07-26 09:09:09 +08:00
err := e . Orm . Transaction ( func ( tx * gorm . DB ) error {
err1 := tx . Model ( & position ) . Where ( querySql ,
2025-08-11 09:27:32 +08:00
order . ApiId , positionSide , order . Symbol ) . First ( & position ) . Error
2025-07-26 09:09:09 +08:00
if err1 != nil {
//主单仓位不存在,创建新仓位
if isMain && errors . Is ( err1 , gorm . ErrRecordNotFound ) && ! reducePosition {
if err2 := tx . Create ( & position ) . Error ; err2 != nil {
return err2
}
2025-08-01 10:30:43 +08:00
averagePrice = position . AveragePrice
2025-07-26 09:09:09 +08:00
} else {
return err1
}
2025-08-01 10:30:43 +08:00
} else {
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 {
2025-08-11 09:27:32 +08:00
totalPrice = totalPrice . Add ( order . Price . Mul ( order . TotalNum ) )
totalAmount = totalAmount . Add ( order . TotalNum )
2025-08-01 10:30:43 +08:00
} else if reducePosition && ! closePosition {
//只减仓
2025-08-11 09:27:32 +08:00
totalPrice = totalPrice . Sub ( order . Price . Mul ( order . TotalNum ) )
totalAmount = totalAmount . Sub ( order . TotalNum )
2025-08-01 10:30:43 +08:00
}
}
if totalAmount . IsZero ( ) || totalPrice . IsZero ( ) {
if isMain {
averagePrice = position . AveragePrice
} else {
if position . ReverseAveragePrice . IsZero ( ) {
2025-08-11 09:27:32 +08:00
averagePrice = order . Price
2025-08-01 10:30:43 +08:00
} 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
2025-08-11 09:27:32 +08:00
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 {
2025-08-01 10:30:43 +08:00
return err2
2025-07-26 09:09:09 +08:00
}
2025-08-01 10:30:43 +08:00
dbResult := tx . Exec ( sqlStr , sql . Named ( "totalNum" , totalNum ) , sql . Named ( "id" , position . Id ) , sql . Named ( "averagePrice" , averagePrice ) )
2025-07-26 09:09:09 +08:00
if dbResult . Error != nil {
return dbResult . Error
}
2025-08-11 09:27:32 +08:00
order . PositionId = position . Id
2025-07-26 09:09:09 +08:00
if dbResult . RowsAffected == 0 {
2025-08-11 09:27:32 +08:00
e . Log . Errorf ( "减仓数据 是否平仓单:%v :%v" , closePosition , order )
2025-07-26 09:09:09 +08:00
return errors . New ( "没有找到对应的持仓信息" )
}
2025-08-11 09:27:32 +08:00
if reducePosition && ! isMain {
remainQuantity = position . ReverseAmount . Sub ( totalNum )
} else if ! isMain {
remainQuantity = position . ReverseAmount . Add ( totalNum )
}
//主单且对手单没有平仓
if isMain && position . ReverseStatus != 2 {
needReverseOrder = true
}
2025-07-26 09:09:09 +08:00
return nil
} )
2025-08-11 09:27:32 +08:00
if ! isMain && ! closePosition {
e . doDefaultTakeStop ( order , apiInfo , remainQuantity )
} else if ! isMain && closePosition {
//取消剩余的委托
e . DoCancelTakeAndStop ( order . Symbol , position . PositionSide , apiInfo )
}
return needReverseOrder , closePosition , err
}
// 获取是否为平仓状态
func ( e * ReverseService ) getClosePosition ( reducePosition bool , apiInfo * DbModels . LineApiUser , order * DbModels . LineReverseOrder , positionSide string , isMain bool ) bool {
closePosition := false
if reducePosition {
futApi := FutRestApi { }
holdData := HoldeData { }
err := futApi . GetPositionData ( apiInfo , order . Symbol , positionSide , & holdData )
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
2025-07-26 09:09:09 +08:00
}
// 反向下单
// mainOrder: 主单信息
// reverseApiId: 反向apiId
// orderProportion: 反向下单比例
// reducePosition: 是否减仓
// closePosition: 是否平仓
func ( e * ReverseService ) DoAddReverseOrder ( mainOrder * DbModels . LineReverseOrder , reverseApiInfo * DbModels . LineApiUser , orderProportion decimal . Decimal , reducePosition , closePosition bool ) error {
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
}
//反向下单百分比
2025-08-01 10:30:43 +08:00
proportion = proportion . Div ( decimal . NewFromInt ( 100 ) ) . Truncate ( 4 )
2025-08-11 09:27:32 +08:00
2025-08-01 10:30:43 +08:00
amount = mainOrder . TotalNum . Mul ( proportion ) . Truncate ( int32 ( symbol . AmountDigit ) )
2025-07-26 09:09:09 +08:00
2025-08-11 09:27:32 +08:00
logger . Info ( "反向下单比例 %d ,原始数量:%d, 反向下单数量: %d" , proportion , mainOrder . TotalNum , amount )
2025-07-26 09:09:09 +08:00
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
2025-08-01 10:30:43 +08:00
order . PositionId = mainOrder . PositionId
2025-07-26 09:09:09 +08:00
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 {
2025-08-01 10:30:43 +08:00
err = e . DoBianceOrder ( & order , reverseApiInfo , & setting , reducePosition , closePosition )
//Biance下单成共 且为平仓单时 取消止盈止损
if err == nil && closePosition {
e . DoCancelTakeProfitBatch ( symbol . GetSymbol ( ) , order . PositionSide , order . Side , - 1 , reverseApiInfo )
}
}
return nil
}
// 取消止盈止损订单
// symbol: 交易对
// positionSide: 持仓方向
// side: 订单方向
// orderType: 订单类型 -1-全部 1-止盈 2-止损
// apiInfo: 下单api
func ( e * ReverseService ) DoCancelTakeProfitBatch ( symbol , positionSide , side string , orderType int , apiInfo * DbModels . LineApiUser ) error {
orderSns := e . GetTakeProfitBatch ( symbol , positionSide , side , apiInfo . Id , orderType )
if len ( orderSns ) == 0 {
return nil
}
if len ( orderSns ) > 0 {
futApi := FutRestApi { }
err := futApi . CancelBatchFutOrderLoop ( * apiInfo , symbol , orderSns )
if err != nil {
e . Log . Errorf ( "币安撤单失败 symbol:%s custom:%v :%v" , symbol , orderSns , err )
return err
}
2025-07-26 09:09:09 +08:00
}
return nil
}
2025-08-11 09:27:32 +08:00
// 取消止盈和止损单
func ( e * ReverseService ) DoCancelTakeAndStop ( symbol , positionSide string , apiInfo * DbModels . LineApiUser ) error {
var orderSns [ ] string
e . Orm . Model ( & DbModels . LineReverseOrder { } ) . Where ( "symbol =? and position_side =? and status =2" , symbol , positionSide ) . Pluck ( "order_sn" , & orderSns )
if len ( orderSns ) > 0 {
futApi := FutRestApi { }
if err := futApi . CancelBatchFutOrderLoop ( * apiInfo , symbol , orderSns ) ; err != nil {
e . Log . Errorf ( "币安撤单失败 symbol:%s custom:%v :%v" , symbol , orderSns , err )
return err
}
}
return nil
}
2025-08-01 10:30:43 +08:00
// 获取止盈止损订单
// symbol: 交易对
// positionSide: 持仓方向
// side: 订单方向
// apiId: 币安apiId
// orderType: 订单类型 -1-全部 1-止盈 2-止损
func ( e * ReverseService ) GetTakeProfitBatch ( symbol , positionSide , side string , apiId int , orderType int ) [ ] string {
var orderSns [ ] string
orderTypes := [ ] int { 1 , 2 }
if orderType == 1 {
orderTypes = [ ] int { 1 }
} else if orderType == 2 {
orderTypes = [ ] int { 2 }
}
e . Orm . Model ( & DbModels . LineReverseOrder { } ) . Where ( "symbol =? and position_side =? and side = ? and status =2 and order_type in ?" , symbol , positionSide , side , orderTypes ) . Pluck ( "order_sn" , & orderSns )
return orderSns
}
2025-07-26 09:09:09 +08:00
// 处理币安订单
// order: 反单信息
// apiInfo: 币安api信息
func ( e * ReverseService ) DoBianceOrder ( order * DbModels . LineReverseOrder , apiInfo * DbModels . LineApiUser , setting * DbModels . LineReverseSetting , reducePosition , closePosition bool ) error {
futApiV2 := FuturesResetV2 { Service : e . Service }
orderType := setting . ReverseOrderType
if orderType == "" {
orderType = "LIMIT"
}
params := FutOrderPlace {
ApiId : apiInfo . Id ,
Symbol : order . Symbol ,
PositionSide : order . PositionSide ,
Side : order . Side ,
OrderType : orderType ,
Quantity : order . TotalNum ,
Price : order . Price ,
NewClientOrderId : order . OrderSn ,
}
err := futApiV2 . OrderPlaceLoop ( apiInfo , params )
if err != nil {
e . Log . Errorf ( "币安下单失败 symbol:%s custom:%s :%v" , order . Symbol , order . OrderSn , err )
remark := err . Error ( )
if len ( remark ) > 255 {
remark = remark [ : 254 ]
}
if err1 := e . Orm . Model ( & order ) . Where ( "id =? and status !=3" , order . Id ) . Updates ( map [ string ] interface { } { "status" : 8 , "remark" : remark , "updated_at" : time . Now ( ) } ) . Error ; err1 != nil {
e . Log . Errorf ( "更新订单状态失败 symbol:%s custom:%s :%v" , err1 )
}
}
return nil
}
2025-08-11 09:27:32 +08:00
// 处理默认止盈止损
// order: 订单信息
// apiInfo: api信息
// Optimized Reverse Order Handling
// File: reverse_order_handler.go
2025-07-26 09:09:09 +08:00
2025-08-11 09:27:32 +08:00
func ( e * ReverseService ) doDefaultTakeStop ( order * DbModels . LineReverseOrder , apiInfo * DbModels . LineApiUser , totalNum decimal . Decimal ) error {
if totalNum . LessThanOrEqual ( decimal . Zero ) {
return nil
}
orders , err := e . getActiveReverseOrders ( order . PositionId )
2025-07-26 09:09:09 +08:00
if err != nil {
return err
}
2025-08-11 09:27:32 +08:00
if len ( orders ) == 2 {
return nil
}
2025-07-26 09:09:09 +08:00
2025-08-11 09:27:32 +08:00
setting , err := GetReverseSetting ( e . Orm )
if err != nil {
e . Log . Errorf ( "获取反单设置失败:%v" , err )
return err
}
2025-07-26 09:09:09 +08:00
2025-08-11 09:27:32 +08:00
symbol , err := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , order . Symbol , 1 )
2025-07-26 09:09:09 +08:00
if err != nil {
2025-08-11 09:27:32 +08:00
e . Log . Errorf ( "获取交易对信息失败:%v" , err )
2025-07-26 09:09:09 +08:00
return err
}
2025-08-11 09:27:32 +08:00
side := e . getOppositeSide ( order . Side )
lastPrice , _ := decimal . NewFromString ( symbol . LastPrice )
now := time . Now ( )
types := [ ] struct {
Enabled bool
CheckTypes [ ] string
OrderType string
Ratio decimal . Decimal
IsTakeProfit bool
} {
{ ! setting . TakeProfitRatio . IsZero ( ) , [ ] string { "TAKE_PROFIT_MARKET" , "TAKE_PROFIT" } , "TAKE_PROFIT_MARKET" , setting . TakeProfitRatio , true } ,
{ ! setting . StopLossRatio . IsZero ( ) , [ ] string { "STOP_MARKET" , "STOP" } , "STOP_MARKET" , setting . StopLossRatio , false } ,
2025-07-26 09:09:09 +08:00
}
2025-08-11 09:27:32 +08:00
for _ , t := range types {
if t . Enabled && ! e . hasOrderType ( orders , t . CheckTypes ... ) {
price := e . calculatePrice ( order . PositionSide , order . FinalPrice , t . Ratio , t . IsTakeProfit )
err := e . createReverseOrder ( CreateOrderParams {
Order : order ,
ApiInfo : apiInfo ,
Symbol : & symbol ,
Side : side ,
OrderType : t . OrderType ,
Price : price ,
TotalNum : totalNum ,
Now : now ,
LastPrice : lastPrice ,
Close : true ,
PositionId : order . PositionId ,
} )
if err != nil {
e . Log . Errorf ( "止盈止损下单失败:%v" , err )
}
}
}
return nil
}
2025-07-26 09:09:09 +08:00
2025-08-11 09:27:32 +08:00
// 重下止盈止损
// mapData:
func ( e * ReverseService ) ReTakeOrStopOrder ( mapData * map [ string ] interface { } , orderSn string , mainApiInfo * DbModels . LineApiUser , symbol * models . TradeSet ) error {
side , err := maphelper . GetString ( * mapData , "S" )
2025-07-26 09:09:09 +08:00
if err != nil {
return err
}
2025-08-11 09:27:32 +08:00
ot , err := maphelper . GetString ( * mapData , "ot" )
if err != nil {
return err
}
positionSide , err := maphelper . GetString ( * mapData , "ps" )
if err != nil {
return err
2025-07-26 09:09:09 +08:00
}
2025-08-01 10:30:43 +08:00
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
}
2025-07-26 09:09:09 +08:00
apiInfo , err := GetApiInfo ( mainApiInfo . ReverseApiId )
if err != nil {
e . Log . Errorf ( "根据主单api获取反单api失败 symbol:%s custom:%s :%v" , symbol , orderSn , err )
return err
}
2025-08-11 09:27:32 +08:00
side = e . getOppositeSide ( side )
if positionSide == "LONG" {
positionSide = "SHORT"
} else {
positionSide = "LONG"
}
var orderType int
2025-08-01 10:30:43 +08:00
switch ot {
case "STOP_MARKET" , "STOP" :
orderType = 2
case "TAKE_PROFIT_MARKET" , "TAKE_PROFIT" :
orderType = 1
default :
return fmt . Errorf ( "不支持的订单类型 ot:%s" , ot )
}
2025-07-26 09:09:09 +08:00
2025-08-01 10:30:43 +08:00
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
2025-07-26 09:09:09 +08:00
}
2025-08-11 09:27:32 +08:00
mainPercent := stopPrice . Div ( reversePosition . AveragePrice )
mainPercent = ( mainPercent . Sub ( decimal . NewFromInt ( 1 ) ) ) . Abs ( ) . Truncate ( 4 )
2025-08-01 10:30:43 +08:00
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 )
2025-08-11 09:27:32 +08:00
return e . createReverseOrder ( CreateOrderParams {
ApiInfo : & apiInfo ,
Symbol : symbol ,
Side : side ,
OrderType : ot ,
Price : price ,
TotalNum : reversePosition . TotalReverseAmount ,
Now : now ,
LastPrice : lastPrice ,
Close : close ,
PositionId : reversePosition . Id ,
OrderSn : orderSn ,
PositionSide : positionSide ,
} )
}
func ( e * ReverseService ) getActiveReverseOrders ( positionId int ) ( [ ] DbModels . LineReverseOrder , error ) {
var orders [ ] DbModels . LineReverseOrder
err := e . Orm . Model ( & DbModels . LineReverseOrder { } ) .
Where ( "position_id =? and status =2" , positionId ) .
Select ( "id,position_side,side,type" ) . Find ( & orders ) . Error
if err != nil {
e . Log . Errorf ( "获取订单信息失败:%v" , err )
}
return orders , err
}
func ( e * ReverseService ) getOppositeSide ( side string ) string {
if side == "SELL" {
return "BUY"
}
return "SELL"
}
func ( e * ReverseService ) hasOrderType ( orders [ ] DbModels . LineReverseOrder , types ... string ) bool {
typeSet := make ( map [ string ] bool )
for _ , t := range types {
typeSet [ t ] = true
}
for _ , o := range orders {
if typeSet [ o . Type ] {
return true
}
}
return false
}
func ( e * ReverseService ) calculatePrice ( positionSide string , base decimal . Decimal , ratio decimal . Decimal , isTakeProfit bool ) decimal . Decimal {
adjust := decimal . NewFromInt ( 100 )
if positionSide == "LONG" {
if isTakeProfit {
return base . Mul ( adjust . Add ( ratio ) . Div ( adjust ) ) . Truncate ( 4 )
}
return base . Mul ( adjust . Sub ( ratio ) . Div ( adjust ) ) . Truncate ( 4 )
}
if isTakeProfit {
return base . Mul ( adjust . Sub ( ratio ) . Div ( adjust ) ) . Truncate ( 4 )
}
return base . Mul ( adjust . Add ( ratio ) . Div ( adjust ) ) . Truncate ( 4 )
}
type CreateOrderParams struct {
ApiInfo * DbModels . LineApiUser
Order * DbModels . LineReverseOrder
Symbol * models . TradeSet
Side string
OrderType string
Price decimal . Decimal
TotalNum decimal . Decimal
Now time . Time
LastPrice decimal . Decimal
Close bool
PositionId int
OrderSn string
PositionSide string
}
func ( e * ReverseService ) createReverseOrder ( params CreateOrderParams ) error {
orderSn := params . OrderSn
if orderSn == "" {
orderSn = helper . GetOrderNo ( )
}
if params . PositionSide == "" && params . Order != nil {
params . PositionSide = params . Order . PositionSide
}
2025-08-01 10:30:43 +08:00
newOrder := DbModels . LineReverseOrder {
2025-08-11 09:27:32 +08:00
PositionId : params . PositionId ,
OrderSn : orderSn ,
OrderType : getOrderType ( params . OrderType ) ,
2025-08-01 10:30:43 +08:00
Status : 1 ,
2025-08-11 09:27:32 +08:00
Price : params . Price ,
TotalNum : params . TotalNum ,
Symbol : params . Symbol . GetSymbol ( ) ,
Side : params . Side ,
PositionSide : params . PositionSide ,
FollowOrderSn : params . OrderSn ,
Type : params . OrderType ,
SignPrice : params . LastPrice ,
2025-08-01 10:30:43 +08:00
Category : 1 ,
2025-08-11 09:27:32 +08:00
ApiId : params . ApiInfo . Id ,
2025-08-01 10:30:43 +08:00
IsAddPosition : 2 ,
2025-08-11 09:27:32 +08:00
TriggerTime : & params . Now ,
BuyPrice : params . TotalNum . Mul ( params . Price ) . Truncate ( int32 ( params . Symbol . PriceDigit ) ) ,
2025-08-01 10:30:43 +08:00
}
2025-08-11 09:27:32 +08:00
if err := e . Orm . Create ( & newOrder ) . Error ; err != nil {
e . Log . Errorf ( "保存反单止盈止损失败 symbol:%s custom:%s :%v" , params . Symbol . GetSymbol ( ) , newOrder . OrderSn , err )
return err
2025-08-01 10:30:43 +08:00
}
2025-08-11 09:27:32 +08:00
futApiV2 := FuturesResetV2 { Service : e . Service }
params2 := FutOrderPlace {
ApiId : params . ApiInfo . Id ,
Symbol : params . Symbol . GetSymbol ( ) ,
PositionSide : newOrder . PositionSide ,
Side : newOrder . Side ,
OrderType : newOrder . Type ,
Quantity : newOrder . TotalNum ,
Price : newOrder . Price ,
StopPrice : newOrder . Price ,
Profit : newOrder . Price ,
2025-08-01 10:30:43 +08:00
NewClientOrderId : newOrder . OrderSn ,
2025-08-11 09:27:32 +08:00
ClosePosition : params . Close ,
2025-07-26 09:09:09 +08:00
}
2025-08-11 09:27:32 +08:00
err := futApiV2 . OrderPlaceLoop ( params . ApiInfo , params2 )
2025-08-01 10:30:43 +08:00
if err != nil {
2025-08-11 09:27:32 +08:00
e . Log . Errorf ( "币安下单失败 symbol:%s custom:%s :%v" , params . Symbol . GetSymbol ( ) , newOrder . OrderSn , err )
e . Orm . Model ( & newOrder ) . Updates ( map [ string ] interface { } { "status" : 8 , "remark" : err . Error ( ) , "updated_at" : time . Now ( ) } )
2025-08-01 10:30:43 +08:00
return err
}
2025-08-11 09:27:32 +08:00
e . DoCancelTakeProfitBatch ( params . Symbol . GetSymbol ( ) , newOrder . PositionSide , newOrder . Side , newOrder . OrderType , params . ApiInfo )
2025-07-26 09:09:09 +08:00
return nil
}
2025-08-11 09:27:32 +08:00
func getOrderType ( t string ) int {
if t == "TAKE_PROFIT_MARKET" || t == "TAKE_PROFIT" {
return 1
}
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
// }