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"
"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 {
if err1 := e . savePosition ( & mainOrder , reverseApiInfo . Id , true , false , false ) ; err1 != nil {
return true , err1
}
e . DoAddReverseOrder ( & mainOrder , & reverseApiInfo , apiInfo . OrderProportion , false , false )
}
case mainOrder . PositionSide == "SHORT" && mainOrder . Side == "BUY" , mainOrder . PositionSide == "LONG" && mainOrder . Side == "SELL" :
if mainOrder . Category == 0 {
closePosition := maphelper . GetBool ( mapData , "R" )
if err1 := e . savePosition ( & mainOrder , reverseApiInfo . Id , true , true , closePosition ) ; err1 != nil {
e . Log . Errorf ( "保存主订单失败: %v" , err1 )
return true , err1
}
e . DoAddReverseOrder ( & mainOrder , & reverseApiInfo , apiInfo . OrderProportion , true , closePosition )
}
default :
return true , errors . New ( "不支持的订单类型" )
}
return true , nil
} else if apiInfo . Subordinate == "2" {
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-07-26 09:09:09 +08:00
}
e . changeOrderStatus ( 3 , orderSn , mapData )
switch {
case mainOrder . PositionSide == "LONG" && mainOrder . Side == "BUY" , mainOrder . PositionSide == "SHORT" && mainOrder . Side == "SELL" :
if mainOrder . Category == 0 {
if err1 := e . savePosition ( & mainOrder , 0 , false , false , false ) ; err1 != nil {
return true , err1
}
}
case mainOrder . PositionSide == "SHORT" && mainOrder . Side == "BUY" , mainOrder . PositionSide == "LONG" && mainOrder . Side == "SELL" :
if mainOrder . Category == 0 {
closePosition := maphelper . GetBool ( mapData , "R" )
if err1 := e . savePosition ( & mainOrder , 0 , false , true , closePosition ) ; err1 != nil {
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
}
// 更新仓位信息
// closePosition: true=平仓, false=减仓
func ( e * ReverseService ) savePosition ( reverseOrder * DbModels . LineReverseOrder , reverseApiId int , isMain , reducePosition , closePosition bool ) error {
position := DbModels . LineReversePosition { }
positionSide := reverseOrder . PositionSide
side := reverseOrder . Side
totalNum := reverseOrder . TotalNum
2025-08-01 10:30:43 +08:00
symbol , err1 := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , reverseOrder . Symbol , 1 )
if err1 != nil {
e . Log . Errorf ( "获取交易对失败 symbol:%s err:%v" , reverseOrder . Symbol , err1 )
}
2025-07-26 09:09:09 +08:00
var querySql string
sqlStr := ""
//如果是主单,存储仓位则是反单的持仓方向
if isMain {
if reverseOrder . PositionSide == "LONG" {
positionSide = "SHORT"
} else {
positionSide = "LONG"
}
if ! reducePosition {
//反单止盈止损方向相反
if side == "SELL" {
side = "BUY"
} else {
side = "SELL"
}
position . ReverseApiId = reverseApiId
position . Side = side
position . ApiId = reverseOrder . ApiId
position . Symbol = reverseOrder . Symbol
position . Status = 1
position . ReverseStatus = 0
position . PositionSide = positionSide
2025-08-01 10:30:43 +08:00
position . AveragePrice = reverseOrder . FinalPrice
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)"
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-07-26 09:09:09 +08:00
err := e . Orm . Transaction ( func ( tx * gorm . DB ) error {
err1 := tx . Model ( & position ) . Where ( querySql ,
reverseOrder . ApiId , positionSide , reverseOrder . Symbol ) . First ( & position ) . Error
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 {
totalPrice = totalPrice . Add ( reverseOrder . Price . Mul ( reverseOrder . TotalNum ) )
totalAmount = totalAmount . Add ( reverseOrder . TotalNum )
} else if reducePosition && ! closePosition {
//只减仓
totalPrice = totalPrice . Sub ( reverseOrder . Price . Mul ( reverseOrder . TotalNum ) )
totalAmount = totalAmount . Sub ( reverseOrder . TotalNum )
}
}
if totalAmount . IsZero ( ) || totalPrice . IsZero ( ) {
if isMain {
averagePrice = position . AveragePrice
} else {
if position . ReverseAveragePrice . IsZero ( ) {
averagePrice = reverseOrder . 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" , reverseOrder . Id ) ) . Error ; err2 != nil {
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-01 10:30:43 +08:00
reverseOrder . PositionId = position . Id
2025-07-26 09:09:09 +08:00
if dbResult . RowsAffected == 0 {
e . Log . Errorf ( "减仓数据 是否平仓单:%v :%v" , closePosition , reverseOrder )
return errors . New ( "没有找到对应的持仓信息" )
}
return nil
} )
return err
}
// 反向下单
// 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 )
amount = mainOrder . TotalNum . Mul ( proportion ) . Truncate ( int32 ( symbol . AmountDigit ) )
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-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
}
// 重下止盈止损
// mapData: 主单止盈止损回调
2025-08-01 10:30:43 +08:00
func ( e * ReverseService ) ReTakeOrStopOrder ( mapData * map [ string ] interface { } , orderSn string , mainApiInfo * DbModels . LineApiUser , symbol * models . TradeSet ) error {
orderType := 0 //订单类型 1-止盈 2-止损
2025-07-26 09:09:09 +08:00
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"
}
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-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
2025-07-26 09:09:09 +08:00
2025-08-01 10:30:43 +08:00
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-01 10:30:43 +08:00
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
}
2025-07-26 09:09:09 +08:00
params := FutOrderPlace {
ApiId : apiInfo . Id ,
2025-08-01 10:30:43 +08:00
Symbol : symbol . GetSymbol ( ) ,
2025-07-26 09:09:09 +08:00
PositionSide : positionSide ,
Side : side ,
OrderType : ot ,
2025-08-01 10:30:43 +08:00
Quantity : reversePosition . TotalReverseAmount ,
Price : price ,
StopPrice : price ,
Profit : price ,
NewClientOrderId : newOrder . OrderSn ,
ClosePosition : close ,
2025-07-26 09:09:09 +08:00
}
futApiV2 := FuturesResetV2 { Service : e . Service }
2025-08-01 10:30:43 +08:00
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 ( ) , positionSide , side , orderType , & apiInfo )
2025-07-26 09:09:09 +08:00
return nil
}