2025-02-06 11:14:33 +08:00
package binanceservice
import (
"context"
"fmt"
2025-02-06 18:03:09 +08:00
"go-admin/app/admin/models"
DbModels "go-admin/app/admin/models"
2025-02-06 11:14:33 +08:00
"go-admin/common/const/rediskey"
2025-02-06 18:03:09 +08:00
"go-admin/common/global"
2025-02-06 11:14:33 +08:00
"go-admin/common/helper"
2025-02-06 18:03:09 +08:00
models2 "go-admin/models"
"go-admin/pkg/utility"
"go-admin/pkg/utility/snowflakehelper"
"strings"
2025-02-06 11:14:33 +08:00
"time"
"github.com/bytedance/sonic"
"github.com/go-admin-team/go-admin-core/logger"
2025-02-06 18:03:09 +08:00
"github.com/shopspring/decimal"
"gorm.io/gorm"
2025-02-06 11:14:33 +08:00
)
/ *
修改订单信息
* /
func ChangeFutureOrder ( mapData map [ string ] interface { } ) {
// 检查订单号是否存在
orderSn , ok := mapData [ "c" ]
2025-02-08 14:05:57 +08:00
originOrderSn := mapData [ "C" ] //取消操作 代表原始订单号
2025-02-06 11:14:33 +08:00
if ! ok {
logger . Error ( "合约订单回调失败,没有订单号" )
return
}
2025-02-08 14:05:57 +08:00
if originOrderSn != "" {
orderSn = originOrderSn
}
2025-02-06 11:14:33 +08:00
// 获取数据库连接
db := GetDBConnection ( )
if db == nil {
logger . Error ( "合约订单回调失败,无法获取数据库连接" )
return
}
// 获取 Redis 锁
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotCallBack , orderSn ) , 10 , 5 , 500 * time . Millisecond )
acquired , err := lock . AcquireWait ( context . Background ( ) )
if err != nil {
logger . Error ( "合约订单回调失败,获取锁失败:" , orderSn , " err:" , err )
return
}
if ! acquired {
logger . Error ( "合约订单回调失败,获取锁失败:" , orderSn )
return
}
defer lock . Release ( )
// 查询订单
preOrder , err := getPreOrder ( db , orderSn )
if err != nil {
logger . Error ( "合约订单回调失败,查询订单失败:" , orderSn , " err:" , err )
return
}
// 解析订单状态
status , ok := mapData [ "X" ] . ( string )
if ! ok {
mapStr , _ := sonic . Marshal ( & mapData )
logger . Error ( "订单回调失败,没有状态:" , string ( mapStr ) )
return
}
2025-02-06 18:03:09 +08:00
// 更新订单状态
orderStatus , reason := parseOrderStatus ( status , mapData )
2025-02-06 11:14:33 +08:00
2025-02-06 18:03:09 +08:00
if orderStatus == 0 {
logger . Error ( "订单回调失败,状态错误:" , orderSn , " status:" , status , " reason:" , reason )
return
}
if err := updateOrderStatus ( db , preOrder , orderStatus , reason , true , mapData ) ; err != nil {
logger . Error ( "修改订单状态失败:" , orderSn , " err:" , err )
return
}
handleFutOrderByType ( db , preOrder , orderStatus )
}
// 合约回调
func handleFutOrderByType ( db * gorm . DB , preOrder * DbModels . LinePreOrder , orderStatus int ) {
switch {
//主单成交
case preOrder . OrderType == 0 && orderStatus == 6 :
handleFutMainOrderFilled ( db , preOrder )
//止盈成交
case preOrder . OrderType == 1 && orderStatus == 6 :
handleTakeProfit ( db , preOrder )
//止损成交
case preOrder . OrderType == 2 && orderStatus == 6 :
handleStopLoss ( db , preOrder )
//平仓成交
case preOrder . OrderType == 3 && orderStatus == 6 :
handleClosePosition ( db , preOrder )
}
}
// 平仓单成交
func handleClosePosition ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
panic ( "unimplemented" )
}
// 止损单成交
func handleStopLoss ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
// 获取主订单
order , err := GetOrderById ( db , preOrder . Pid )
if err != nil {
logger . Errorf ( "止损单成交 获取主单失败 订单号:%s err:%v" , preOrder . OrderSn , err )
return
}
// 检查订单类型
if order . OrderCategory != 1 {
if err := db . Model ( & order ) . Where ( "pid =? AND order_status <9 AND order_type =0" , preOrder . Pid ) . Error ; err != nil {
logger . Errorf ( "对冲止损单成交 修改对冲主单状态失败 订单号:%s err:%v" , order . OrderSn , err )
}
return
}
// 修改主单状态
if err := db . Model ( & order ) . Where ( "id =? AND order_status < 9" , preOrder . Pid ) . Error ; err != nil {
logger . Errorf ( "止损单成交 修改主单状态失败 订单号:%s err:%v" , order . OrderSn , err )
return
}
// 检查是否有对冲
if order . CoverType <= 0 {
return
}
2025-02-08 14:05:57 +08:00
triggerHedgeOrder ( order , db , preOrder )
}
// 下合约对冲单
func triggerHedgeOrder ( order DbModels . LinePreOrder , db * gorm . DB , preOrder * DbModels . LinePreOrder ) bool {
2025-02-06 18:03:09 +08:00
symbolName := utility . ReplaceSuffix ( order . Symbol , order . QuoteSymbol , "USDT" )
2025-02-08 14:05:57 +08:00
// 获取交易对配置
2025-02-06 18:03:09 +08:00
tradeSet , err := GetTradeSet ( symbolName , 1 )
if tradeSet . Coin == "" || err != nil {
logger . Errorf ( "止损单成交 获取交易对配置失败 交易对:%s 订单号:%s err:%v" , symbolName , order . OrderSn , err )
2025-02-08 14:05:57 +08:00
return true
2025-02-06 18:03:09 +08:00
}
// 检查对冲单购买金额
2025-02-08 18:01:53 +08:00
// if order.HedgeBuyTotal.Cmp(decimal.Zero) <= 0 {
// logger.Errorf("止损单成交 对冲单购买金额为0 订单号:%s", order.OrderSn)
// return true
// }
2025-02-06 18:03:09 +08:00
// 获取系统设置
setting , err := GetSystemSetting ( db )
if err != nil {
logger . Errorf ( "止损单成交 获取系统设置失败 订单号:%s err:%v" , order . OrderSn , err )
2025-02-08 14:05:57 +08:00
return true
2025-02-06 18:03:09 +08:00
}
// 获取API信息
apiInfo , err := GetChildApiInfo ( order . ApiId )
if err != nil {
logger . Errorf ( "止损单成交 获取api信息失败 订单号:%s err:%v" , order . OrderSn , err )
2025-02-08 14:05:57 +08:00
return true
2025-02-06 18:03:09 +08:00
}
// 计算价格
price := utility . StrToDecimal ( preOrder . Price ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
2025-02-08 14:05:57 +08:00
if order . CoverType == 1 {
2025-02-06 18:03:09 +08:00
price = utility . StrToDecimal ( tradeSet . LastPrice ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
}
if price . Cmp ( decimal . Zero ) <= 0 {
logger . Errorf ( "单价异常 price:%v 止损单编号:%s 行情:%v" , price , order . OrderSn , tradeSet )
2025-02-08 14:05:57 +08:00
return true
2025-02-06 18:03:09 +08:00
}
// 创建对冲订单
hedgeOrder , hedgeStoplossOrder , hedgeTakeProfitOrder := createHedgeOrders ( order , price , & tradeSet , & apiInfo , & setting )
orders := [ ] DbModels . LinePreOrder { hedgeOrder , hedgeStoplossOrder , hedgeTakeProfitOrder }
// 批量插入订单
if err := db . Model ( & DbModels . LinePreOrder { } ) . CreateInBatches ( orders , 1 ) . Error ; err != nil {
logger . Errorf ( "主单止损 重下对冲失败, 止损单id: %s err:%v" , order . OrderSn , err )
2025-02-08 14:05:57 +08:00
return true
2025-02-06 18:03:09 +08:00
}
// 调用API下单
if err := placeFutOrder ( db , hedgeOrder , price , & apiInfo ) ; err != nil {
logger . Errorf ( "主单止损 重下对冲失败,止损单号:%s err:%v" , order . OrderSn , err )
}
2025-02-08 14:05:57 +08:00
return false
2025-02-06 18:03:09 +08:00
}
// 创建对冲订单
func createHedgeOrders ( order DbModels . LinePreOrder , price decimal . Decimal , tradeSet * models2 . TradeSet , apiInfo * DbModels . LineApiUser , setting * DbModels . LineSystemSetting ) ( DbModels . LinePreOrder , DbModels . LinePreOrder , DbModels . LinePreOrder ) {
2025-02-08 18:01:53 +08:00
// buyPrice := order.HedgeBuyTotal.String()
2025-02-08 14:05:57 +08:00
2025-02-08 18:01:53 +08:00
// if order.HedgeBuyType == 1 {
// mainTotal := utility.StrToDecimal(order.BuyPrice)
// buyPrice = mainTotal.Mul(order.HedgeBuyTotal).Truncate(int32(tradeSet.PriceDigit)).String()
// }
2025-02-08 14:05:57 +08:00
2025-02-06 18:03:09 +08:00
hedgeOrder := DbModels . LinePreOrder {
ApiId : apiInfo . Id ,
ExchangeType : order . ExchangeType ,
Pid : order . Pid ,
Symbol : order . Symbol ,
SymbolType : global . SYMBOL_FUTURES ,
SignPrice : order . Price ,
SignPriceType : "new" ,
Rate : "0" ,
2025-02-08 18:01:53 +08:00
// BuyPrice: buyPrice,
2025-02-06 18:03:09 +08:00
OrderCategory : 2 ,
OrderSn : utility . Int64ToString ( snowflakehelper . GetOrderId ( ) ) ,
OrderType : 0 ,
CoverType : 0 ,
2025-02-08 18:01:53 +08:00
// MainOrderType: order.HedgeOrderType,
2025-02-06 18:03:09 +08:00
}
hedgeStoplossOrder := order
hedgeTakeProfitOrder := order
if order . Site == "BUY" {
hedgeOrder . Site = "SELL"
hedgeStoplossOrder . Site = "BUY"
hedgeTakeProfitOrder . Site = "BUY"
} else {
hedgeOrder . Site = "BUY"
hedgeStoplossOrder . Site = "SELL"
hedgeTakeProfitOrder . Site = "SELL"
}
// 计算止盈止损价格
2025-02-08 18:01:53 +08:00
// if hedgeOrder.Site == "BUY" {
// hedgeTakeProfitOrder.Price = price.Mul(decimal.NewFromInt(1).Add(order.HedgeTakeProfit.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
// hedgeStoplossOrder.Price = price.Mul(decimal.NewFromInt(1).Sub(order.HedgeStopLoss.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
// } else {
// hedgeTakeProfitOrder.Price = price.Mul(decimal.NewFromInt(1).Sub(order.HedgeTakeProfit.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
// hedgeStoplossOrder.Price = price.Mul(decimal.NewFromInt(1).Add(order.HedgeStopLoss.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit)).String()
// }
2025-02-06 18:03:09 +08:00
if hedgeOrder . MainOrderType == "LIMIT" {
coverRate := utility . StrToDecimal ( setting . CoverOrderTypeBRate )
price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( coverRate ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
}
2025-02-08 18:01:53 +08:00
// num := order.HedgeBuyTotal.Div(price).Truncate(int32(tradeSet.AmountDigit))
2025-02-06 18:03:09 +08:00
hedgeOrder . Price = price . String ( )
2025-02-08 18:01:53 +08:00
// hedgeOrder.Num = num.String()
2025-02-06 18:03:09 +08:00
hedgeStoplossOrder . OrderSn = utility . Int64ToString ( snowflakehelper . GetOrderId ( ) )
2025-02-08 18:01:53 +08:00
// hedgeStoplossOrder.Rate = order.HedgeStopLoss.String()
// hedgeStoplossOrder.Num = num.String()
2025-02-06 18:03:09 +08:00
hedgeTakeProfitOrder . OrderSn = utility . Int64ToString ( snowflakehelper . GetOrderId ( ) )
2025-02-08 18:01:53 +08:00
// hedgeTakeProfitOrder.Rate = order.HedgeTakeProfit.String()
// hedgeTakeProfitOrder.Num = num.String()
2025-02-06 18:03:09 +08:00
return hedgeOrder , hedgeStoplossOrder , hedgeTakeProfitOrder
}
// 调用合约API下单
func placeFutOrder ( db * gorm . DB , hedgeOrder DbModels . LinePreOrder , price decimal . Decimal , apiInfo * DbModels . LineApiUser ) error {
futApi := FutRestApi { }
params := FutOrderPlace {
ApiId : apiInfo . Id ,
Symbol : hedgeOrder . Symbol ,
Side : hedgeOrder . Site ,
Quantity : utility . StrToDecimal ( hedgeOrder . Num ) ,
Price : price ,
NewClientOrderId : hedgeOrder . OrderSn ,
SideType : hedgeOrder . MainOrderType ,
OrderType : hedgeOrder . MainOrderType ,
}
return futApi . OrderPlace ( db , params )
}
// 止盈单成交
func handleTakeProfit ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
if preOrder . OrderCategory == 1 {
//主单止盈成交
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id = ?" , preOrder . Pid ) . Update ( "order_type" , 3 ) . Error ; err != nil {
logger . Errorf ( "主单止盈成功修改主单状态失败 订单号:%s:" , err )
}
} else {
//对冲单止盈成交
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "pid = ? AND order_category = 2 AND order_type =0 AND status < 9" , preOrder . Pid ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Errorf ( "对冲止盈成功修改主单状态失败 订单号:%s:" , err )
}
}
}
// 主单成交 处理止盈止损订单
// preOrder 主单
func handleFutMainOrderFilled ( db * gorm . DB , preOrder * models . LinePreOrder ) {
orders := [ ] models . LinePreOrder { }
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "pid = ? AND order_category = 1 AND order_type > 0 AND status = '0' " , preOrder . Id ) . Find ( & orders ) . Error ; err != nil {
logger . Error ( "订单回调查询止盈止损单失败:" , err )
return
}
futApi := FutRestApi { }
num , _ := decimal . NewFromString ( preOrder . Num )
tradeSet , _ := GetTradeSet ( preOrder . Symbol , 0 )
if tradeSet . Coin == "" {
logger . Error ( "获取交易对失败" )
return
}
for i , order := range orders {
if i >= 2 { // 最多处理 2 个订单
break
}
2025-02-08 14:05:57 +08:00
price := utility . StrToDecimal ( order . Price ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
2025-02-06 18:03:09 +08:00
num = num . Truncate ( int32 ( tradeSet . AmountDigit ) )
2025-02-08 14:05:57 +08:00
order . Price = price . String ( )
2025-02-06 18:03:09 +08:00
if err := db . Model ( & order ) . Update ( "num" , num ) . Error ; err != nil {
logger . Errorf ( "修改止盈止损数量失败 订单号:%s err:%v" , order . OrderSn , err )
}
switch order . OrderType {
case 1 : // 止盈
processFutTakeProfitOrder ( db , futApi , order , num )
case 2 : // 止损
2025-02-08 14:05:57 +08:00
processFutStopLossOrder ( db , order , price , num )
2025-02-06 18:03:09 +08:00
}
}
}
// 处理止盈订单
func processFutTakeProfitOrder ( db * gorm . DB , futApi FutRestApi , order models . LinePreOrder , num decimal . Decimal ) {
price , _ := decimal . NewFromString ( order . Price )
params := FutOrderPlace {
ApiId : order . ApiId ,
Symbol : order . Symbol ,
Side : order . Site ,
Price : price ,
Quantity : num ,
OrderType : "TAKE_PROFIT_LIMIT" ,
StopPrice : price ,
NewClientOrderId : order . OrderSn ,
}
err := futApi . OrderPlace ( db , params )
if err != nil {
for x := 0 ; x < 5 ; x ++ {
if strings . Contains ( err . Error ( ) , "LOT_SIZE" ) {
break
}
err = futApi . OrderPlace ( db , params )
if err == nil {
break
}
2025-02-08 14:05:57 +08:00
time . Sleep ( time . Millisecond * 250 )
2025-02-06 18:03:09 +08:00
}
}
if err != nil {
logger . Error ( "合约止盈下单失败:" , order . OrderSn , " err:" , err )
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id = ?" , order . Id ) .
Updates ( map [ string ] interface { } { "status" : "2" , "desc" : err . Error ( ) , "num" : params . Quantity } ) . Error ; err != nil {
logger . Error ( "合约止盈下单失败,更新状态失败:" , order . OrderSn , " err:" , err )
}
} else {
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id = ? and status =0" , order . Id ) .
Updates ( map [ string ] interface { } { "status" : "1" , "num" : num . String ( ) } ) . Error ; err != nil {
logger . Error ( "合约止盈下单成功,更新状态失败:" , order . OrderSn , " err:" , err )
}
}
}
// 处理止损订单
// order 止损单
2025-02-08 14:05:57 +08:00
func processFutStopLossOrder ( db * gorm . DB , order models . LinePreOrder , price , num decimal . Decimal ) error {
params := FutOrderPlace {
ApiId : order . ApiId ,
Symbol : order . Symbol ,
Side : order . Site ,
Price : price ,
Quantity : num ,
OrderType : "STOP_MARKET" ,
StopPrice : price ,
NewClientOrderId : order . OrderSn ,
2025-02-06 18:03:09 +08:00
}
2025-02-08 14:05:57 +08:00
futApi := FutRestApi { }
var err error
for x := 1 ; x < 4 ; x ++ {
err = futApi . OrderPlace ( db , params )
2025-02-06 18:03:09 +08:00
2025-02-08 14:05:57 +08:00
if err == nil {
break
}
2025-02-06 18:03:09 +08:00
2025-02-08 14:05:57 +08:00
time . Sleep ( time . Millisecond * 200 )
2025-02-06 18:03:09 +08:00
}
2025-02-08 14:05:57 +08:00
if err != nil {
if err2 := db . Model ( & order ) . Updates ( map [ string ] interface { } { "status" : 2 , "desc" : err . Error ( ) } ) . Error ; err2 != nil {
logger . Error ( "合约止损下单失败,更新状态失败:" , order . OrderSn , " err:" , err2 )
}
} else {
if err := db . Model ( & order ) . Where ( "status =0" ) .
Updates ( map [ string ] interface { } { "status" : "1" } ) . Error ; err != nil {
logger . Error ( "合约止损下单成功,更新状态失败:" , order . OrderSn , " err:" , err )
}
2025-02-06 18:03:09 +08:00
}
2025-02-06 11:14:33 +08:00
2025-02-06 18:03:09 +08:00
return nil
2025-02-06 11:14:33 +08:00
}