2025-02-06 11:14:33 +08:00
package binanceservice
import (
"context"
2025-02-11 14:49:16 +08:00
"errors"
2025-02-06 11:14:33 +08:00
"fmt"
2025-02-06 18:03:09 +08:00
"go-admin/app/admin/models"
DbModels "go-admin/app/admin/models"
2025-02-11 14:49:16 +08:00
"go-admin/app/admin/service/dto"
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"
"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-11 18:03:30 +08:00
if originOrderSn != nil && originOrderSn != "" {
2025-02-08 14:05:57 +08:00
orderSn = originOrderSn
}
2025-02-06 11:14:33 +08:00
// 获取数据库连接
db := GetDBConnection ( )
if db == nil {
logger . Error ( "合约订单回调失败,无法获取数据库连接" )
return
}
// 获取 Redis 锁
2025-02-08 18:32:41 +08:00
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . FutCallBack , orderSn ) , 10 , 5 , 500 * time . Millisecond )
2025-02-06 11:14:33 +08:00
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 :
2025-03-06 18:16:35 +08:00
handleFutMainOrderFilled ( db , preOrder , preOrder . Id , true )
2025-02-06 18:03:09 +08:00
//止盈成交
case preOrder . OrderType == 1 && orderStatus == 6 :
handleTakeProfit ( db , preOrder )
2025-02-10 18:21:44 +08:00
//减仓回调
case preOrder . OrderType == 4 && orderStatus == 6 :
handleReduceFilled ( db , preOrder )
2025-03-07 16:48:55 +08:00
//主单取消
case preOrder . OrderType == 0 && preOrder . Pid == 0 && orderStatus == 4 :
handleMainOrderCancel ( db , preOrder , 2 )
2025-02-06 18:03:09 +08:00
//止损成交
case preOrder . OrderType == 2 && orderStatus == 6 :
handleStopLoss ( db , preOrder )
//平仓成交
case preOrder . OrderType == 3 && orderStatus == 6 :
handleClosePosition ( db , preOrder )
}
}
2025-02-10 18:21:44 +08:00
// 减仓回调
func handleReduceFilled ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
2025-02-20 12:00:04 +08:00
if err := db . Model ( & DbModels . LinePreOrderStatus { } ) . Where ( "order_id =? " , preOrder . MainId ) . Update ( "reduce_status" , 1 ) . Error ; err != nil {
logger . Errorf ( "handleReduceFilled 更新主单减仓状态失败,订单号:%s" , preOrder . OrderSn )
}
2025-02-10 18:21:44 +08:00
apiUserInfo , _ := GetApiInfo ( preOrder . ApiId )
if apiUserInfo . Id == 0 {
2025-02-20 12:00:04 +08:00
logger . Errorf ( "handleReduceFilled 获取api信息失败,订单号:%s" , preOrder . OrderSn )
2025-02-10 18:21:44 +08:00
return
}
tradeSet , err := GetTradeSet ( preOrder . Symbol , 1 )
if err != nil {
2025-02-20 12:00:04 +08:00
logger . Errorf ( "handleReduceFilled 获取交易对设置失败,订单号:%s" , preOrder . OrderSn )
2025-02-10 18:21:44 +08:00
return
}
2025-02-28 18:27:52 +08:00
rate := utility . StringAsFloat ( preOrder . Rate )
2025-02-10 18:21:44 +08:00
2025-02-28 18:27:52 +08:00
// 100%减仓 终止流程
if rate >= 100 {
//缓存
removeFutLossAndAddPosition ( preOrder . MainId , preOrder . OrderSn )
removePosition ( db , preOrder )
2025-02-10 18:21:44 +08:00
2025-02-28 18:27:52 +08:00
ids := [ ] int { preOrder . MainId , preOrder . Pid }
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id IN ? AND status =6" , ids ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Info ( "100%减仓完毕,终结流程" )
2025-02-10 18:21:44 +08:00
}
return
}
2025-02-28 18:27:52 +08:00
positionData := savePosition ( db , preOrder )
2025-02-10 18:21:44 +08:00
orders := make ( [ ] models . LinePreOrder , 0 )
2025-02-18 09:59:51 +08:00
if err := db . Model ( & models . LinePreOrder { } ) . Where ( "pid =? AND order_type IN (1,2) AND status = 0" , preOrder . Id ) . Find ( & orders ) . Error ; err != nil {
logger . Errorf ( "handleMainReduceFilled 获取待触发订单失败,订单号:%s" , preOrder . OrderSn )
2025-02-14 09:43:49 +08:00
return
}
2025-02-10 18:21:44 +08:00
2025-02-28 18:27:52 +08:00
orderExt := models . LinePreOrderExt { }
2025-02-14 09:43:49 +08:00
totalNum := getFuturesPositionAvailableQuantity ( db , apiUserInfo , preOrder , tradeSet )
2025-02-28 18:27:52 +08:00
price := utility . StrToDecimal ( preOrder . Price ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
2025-02-14 09:43:49 +08:00
futApi := FutRestApi { }
2025-03-07 16:48:55 +08:00
mainId := preOrder . Id
if preOrder . MainId > 0 {
mainId = preOrder . MainId
}
2025-02-28 18:27:52 +08:00
db . Model ( & orderExt ) . Where ( "order_id =?" , preOrder . Pid ) . First ( & orderExt )
2025-02-14 09:43:49 +08:00
for _ , v := range orders {
if v . OrderType == 1 {
2025-02-28 18:27:52 +08:00
//亏损大于0 重新计算比例
if positionData . TotalLoss . Cmp ( decimal . Zero ) > 0 && orderExt . Id > 0 {
2025-03-03 17:35:41 +08:00
percentag := positionData . TotalLoss . Div ( totalNum ) . Div ( price ) . Mul ( decimal . NewFromInt ( 100 ) )
2025-02-28 18:27:52 +08:00
percentag = percentag . Add ( orderExt . TakeProfitRatio ) . Truncate ( 2 )
v . Rate = percentag . String ( )
percentag = percentag . Div ( decimal . NewFromInt ( 100 ) )
if positionData . PositionSide == "LONG" {
v . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
} else {
v . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
}
2025-02-14 09:43:49 +08:00
processFutTakeProfitOrder ( db , futApi , v , totalNum )
} else if v . OrderType == 2 {
processFutStopLossOrder ( db , v , utility . StrToDecimal ( v . Price ) , totalNum )
2025-02-10 18:21:44 +08:00
}
}
2025-03-07 16:48:55 +08:00
nextFuturesReduceTrigger ( db , mainId , totalNum , tradeSet )
}
2025-02-10 18:21:44 +08:00
2025-03-07 16:48:55 +08:00
// 下一个合约待触发
func nextFuturesReduceTrigger ( db * gorm . DB , mainId int , totalNum decimal . Decimal , tradeSet models2 . TradeSet ) {
nextOrder := DbModels . LinePreOrder { }
nextExt := DbModels . LinePreOrderExt { }
2025-02-10 18:21:44 +08:00
2025-03-07 16:48:55 +08:00
if err := db . Model ( & models . LinePreOrder { } ) . Where ( "main_id =? AND order_type =4 AND status=0" , mainId ) . Order ( "rate asc" ) . First ( & nextOrder ) . Error ; err != nil {
logger . Errorf ( "获取下一个单失败 err:%v" , err )
2025-02-10 18:21:44 +08:00
return
}
2025-03-10 11:13:08 +08:00
if err := db . Model ( & models . LinePreOrderExt { } ) . Where ( "order_id =? and add_type =2" , nextOrder . Id ) . First ( & nextExt ) . Error ; err != nil {
2025-03-07 16:48:55 +08:00
logger . Errorf ( "获取下一个单失败 err:%v" , err )
2025-02-10 18:21:44 +08:00
}
2025-03-07 16:48:55 +08:00
num := totalNum
//移除缓存
key := fmt . Sprintf ( rediskey . FuturesReduceList , global . EXCHANGE_BINANCE )
vals , _ := helper . DefaultRedis . GetAllList ( key )
item := ReduceListItem { }
2025-02-10 18:21:44 +08:00
2025-03-07 16:48:55 +08:00
for _ , val := range vals {
sonic . Unmarshal ( [ ] byte ( val ) , & item )
if item . MainId == mainId {
if _ , err := helper . DefaultRedis . LRem ( key , val ) ; err != nil {
logger . Errorf ( "减仓单 redis删除失败 main_id:%v err:%v" , mainId , err )
}
}
2025-02-10 18:21:44 +08:00
}
2025-03-07 16:48:55 +08:00
//
if nextExt . AddPositionVal . Cmp ( decimal . Zero ) > 0 && nextExt . AddPositionVal . Cmp ( decimal . Zero ) < 100 {
// 计算减仓数量
num = totalNum . Mul ( nextExt . AddPositionVal . Div ( decimal . NewFromInt ( 100 ) ) ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
nextOrder . Num = num . String ( )
2025-03-10 11:13:08 +08:00
if err := db . Model ( & nextOrder ) . Update ( "num" , nextOrder . Num ) . Error ; err != nil {
logger . Errorf ( "更新减仓单数量失败 err:%v" , err )
}
2025-02-10 18:21:44 +08:00
}
2025-03-07 16:48:55 +08:00
processFutReduceOrder ( nextOrder , utility . StrToDecimal ( nextOrder . Price ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) , num )
2025-02-10 18:21:44 +08:00
}
2025-02-14 09:43:49 +08:00
// 获取合约可用数量
func getFuturesPositionAvailableQuantity ( db * gorm . DB , apiUserInfo DbModels . LineApiUser , preOrder * DbModels . LinePreOrder , tradeSet models2 . TradeSet ) decimal . Decimal {
var totalNum decimal . Decimal
var err error
for x := 1 ; x < 4 ; x ++ {
totalNum , err = getFuturesPositionNum ( apiUserInfo , preOrder , tradeSet )
if err == nil {
break
}
time . Sleep ( time . Millisecond * 150 )
}
if totalNum . Cmp ( decimal . Zero ) <= 0 {
var mainId int
if preOrder . MainId > 0 {
mainId = preOrder . MainId
} else {
mainId = preOrder . Id
}
totalNum = getInternalNum ( db , mainId )
}
if totalNum . Cmp ( decimal . Zero ) <= 0 {
totalNum = utility . StrToDecimal ( preOrder . Num )
}
2025-02-15 18:38:58 +08:00
return totalNum . Truncate ( int32 ( tradeSet . AmountDigit ) )
2025-02-14 09:43:49 +08:00
}
2025-02-11 14:49:16 +08:00
// 获取币安合约持仓
func getFuturesPositionNum ( apiUserInfo DbModels . LineApiUser , preOrder * DbModels . LinePreOrder , tradeSet models2 . TradeSet ) ( decimal . Decimal , error ) {
futApi := FutRestApi { }
positions , err := futApi . GetPositionV3 ( & apiUserInfo , preOrder . Symbol )
var num decimal . Decimal
2025-02-06 18:03:09 +08:00
if err != nil {
2025-02-11 14:49:16 +08:00
logger . Errorf ( "handleMainReduceFilled 获取持仓信息失败,交易对:%s ,订单号:%s" , preOrder . Symbol , preOrder . OrderSn )
return num , err
}
for _ , item := range positions {
if item . Symbol == preOrder . Symbol {
positionAmt := utility . StrToDecimal ( item . PositionAmt )
//多
2025-03-04 15:34:32 +08:00
if positionAmt . Cmp ( decimal . Zero ) > 0 && ( ( preOrder . OrderType == 0 && preOrder . Site == "BUY" ) || ( preOrder . OrderType != 0 && preOrder . Site == "SELL" ) ) {
2025-02-11 14:49:16 +08:00
num = positionAmt . Abs ( ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
break
2025-03-04 15:34:32 +08:00
} else if positionAmt . Cmp ( decimal . Zero ) < 0 && ( ( preOrder . OrderType != 0 && preOrder . Site == "BUY" ) || ( preOrder . OrderType == 0 && preOrder . Site == "SELL" ) ) {
2025-02-11 14:49:16 +08:00
//空
num = positionAmt . Abs ( ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
break
}
2025-02-06 18:03:09 +08:00
}
}
2025-02-11 14:49:16 +08:00
if num . Cmp ( decimal . Zero ) <= 0 {
logger . Errorf ( "handleMainReduceFilled 获取持仓数量为0,交易对:%s ,订单号:%s" , preOrder . Symbol , preOrder . OrderSn )
return num , errors . New ( "获取持仓数量为0" )
2025-02-06 18:03:09 +08:00
}
2025-02-11 14:49:16 +08:00
return num , nil
}
2025-02-06 18:03:09 +08:00
2025-02-11 14:49:16 +08:00
// 平仓单成交
func handleClosePosition ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
2025-02-28 18:27:52 +08:00
removeFutLossAndAddPosition ( preOrder . MainId , preOrder . OrderSn )
removePosition ( db , preOrder )
2025-02-20 15:13:30 +08:00
futApi := FutRestApi { }
apiUserInfo , _ := GetApiInfo ( preOrder . ApiId )
if apiUserInfo . Id > 0 {
if err := futApi . CancelAllFutOrder ( apiUserInfo , preOrder . Symbol ) ; err != nil {
logger . Errorf ( "止盈单成功 取消其它订单失败 订单号:%s:" , err )
}
}
2025-02-11 14:49:16 +08:00
ids := [ ] int { preOrder . Pid , preOrder . MainId }
//主单止盈成交
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id IN ?" , ids ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Errorf ( "平仓单成功 修改主单状态失败 订单号:%s:" , err )
2025-02-06 18:03:09 +08:00
}
2025-02-08 14:05:57 +08:00
}
2025-02-11 14:49:16 +08:00
// 止损单成交
func handleStopLoss ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
2025-02-28 18:27:52 +08:00
removeFutLossAndAddPosition ( preOrder . MainId , preOrder . OrderSn )
removePosition ( db , preOrder )
2025-02-20 15:13:30 +08:00
apiUserInfo , _ := GetApiInfo ( preOrder . ApiId )
if apiUserInfo . Id > 0 {
2025-03-10 11:13:08 +08:00
mainIds := [ ] int { preOrder . MainId }
if err := cancelMainOrders ( mainIds , db , apiUserInfo , preOrder . Symbol , false ) ; err != nil {
logger . Errorf ( "止损单成功 取消其它订单失败 订单号:%s:" , err )
2025-02-20 15:13:30 +08:00
}
}
2025-02-11 14:49:16 +08:00
ids := [ ] int { preOrder . Pid , preOrder . MainId }
//主单止损成交
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id IN ?" , ids ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Errorf ( "止损单成功 修改主单状态失败 订单号:%s:" , err )
2025-02-06 18:03:09 +08:00
}
2025-02-11 14:49:16 +08:00
}
2025-02-06 18:03:09 +08:00
2025-02-11 14:49:16 +08:00
// 止盈单成交
func handleTakeProfit ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) {
2025-03-06 18:16:35 +08:00
childCount , _ := GetChildTpOrder ( db , preOrder . Id )
2025-02-06 18:03:09 +08:00
2025-03-06 18:16:35 +08:00
if childCount > 0 {
extOrderId := preOrder . Pid //ext主单id
2025-02-20 15:13:30 +08:00
2025-03-06 18:16:35 +08:00
handleFutMainOrderFilled ( db , preOrder , extOrderId , false )
} else {
removeFutLossAndAddPosition ( preOrder . MainId , preOrder . OrderSn )
removePosition ( db , preOrder )
apiUserInfo , _ := GetApiInfo ( preOrder . ApiId )
if apiUserInfo . Id > 0 {
2025-03-10 11:13:08 +08:00
mainOrder , _ := GetOrderById ( db , preOrder . MainId )
if err := cancelPositionOtherOrders ( apiUserInfo , db , & mainOrder , false ) ; err != nil {
logger . Errorf ( "止损单成功 取消其它订单失败 订单号:%s:" , err )
2025-03-06 18:16:35 +08:00
}
2025-02-20 15:13:30 +08:00
}
2025-03-06 18:16:35 +08:00
ids := [ ] int { preOrder . Pid , preOrder . MainId }
//主单止盈成交
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id IN ? AND order_type=0" , ids ) . Update ( "status" , 9 ) . Error ; err != nil {
logger . Errorf ( "主单止盈成功修改主单状态失败 订单号:%s:" , err )
}
2025-02-06 18:03:09 +08:00
}
2025-02-11 14:49:16 +08:00
}
2025-02-06 18:03:09 +08:00
2025-02-11 14:49:16 +08:00
// 清除合约缓存
2025-02-28 18:27:52 +08:00
func removeFutLossAndAddPosition ( mainId int , orderSn string ) {
2025-02-11 14:49:16 +08:00
stoplossKey := fmt . Sprintf ( rediskey . FuturesStopLossList , global . EXCHANGE_BINANCE )
stoplossVal , _ := helper . DefaultRedis . GetAllList ( stoplossKey )
addPositionKey := fmt . Sprintf ( rediskey . FuturesAddPositionList , global . EXCHANGE_BINANCE )
addPositionVal , _ := helper . DefaultRedis . GetAllList ( addPositionKey )
reduceKey := fmt . Sprintf ( rediskey . FuturesReduceList , global . EXCHANGE_BINANCE )
reduceVal , _ := helper . DefaultRedis . GetAllList ( reduceKey )
stoploss := dto . StopLossRedisList { }
addPosition := AddPositionList { }
reduce := ReduceListItem { }
//止损缓存
for _ , v := range stoplossVal {
sonic . Unmarshal ( [ ] byte ( v ) , & stoploss )
2025-02-28 18:27:52 +08:00
if stoploss . MainId == mainId {
2025-02-11 14:49:16 +08:00
_ , err := helper . DefaultRedis . LRem ( stoplossKey , v )
if err != nil {
2025-02-28 18:27:52 +08:00
logger . Errorf ( "订单回调失败, 回调订单号:%s 删除止损缓存失败:%v" , orderSn , err )
2025-02-11 14:49:16 +08:00
}
}
2025-02-06 18:03:09 +08:00
}
2025-02-11 14:49:16 +08:00
//加仓缓存
for _ , v := range addPositionVal {
sonic . Unmarshal ( [ ] byte ( v ) , & addPosition )
2025-02-28 18:27:52 +08:00
if addPosition . MainId == mainId {
2025-02-11 14:49:16 +08:00
_ , err := helper . DefaultRedis . LRem ( addPositionKey , v )
2025-02-06 18:03:09 +08:00
2025-02-11 14:49:16 +08:00
if err != nil {
2025-02-28 18:27:52 +08:00
logger . Errorf ( "订单回调失败, 回调订单号:%s 删除加仓缓存失败:%v" , orderSn , err )
2025-02-11 14:49:16 +08:00
}
}
2025-02-06 18:03:09 +08:00
}
2025-02-11 14:49:16 +08:00
//减仓缓存
for _ , v := range reduceVal {
sonic . Unmarshal ( [ ] byte ( v ) , & reduce )
2025-02-28 18:27:52 +08:00
if reduce . MainId == mainId {
2025-02-11 14:49:16 +08:00
_ , err := helper . DefaultRedis . LRem ( reduceKey , v )
2025-02-06 18:03:09 +08:00
2025-02-11 14:49:16 +08:00
if err != nil {
2025-02-28 18:27:52 +08:00
logger . Errorf ( "订单回调失败, 回调订单号:%s 删除减仓缓存失败:%v" , orderSn , err )
2025-02-11 14:49:16 +08:00
}
2025-02-06 18:03:09 +08:00
}
}
}
2025-02-28 18:27:52 +08:00
// 处理主单成交,处理止盈、止损、减仓订单
2025-03-06 18:16:35 +08:00
func handleFutMainOrderFilled ( db * gorm . DB , preOrder * models . LinePreOrder , extOrderId int , first bool ) {
2025-02-28 18:27:52 +08:00
// 获取交易对配置和API信息
tradeSet , err := GetTradeSet ( preOrder . Symbol , 1 )
2025-03-07 16:48:55 +08:00
mainId := preOrder . Id
2025-02-28 18:27:52 +08:00
if err != nil || tradeSet . Coin == "" {
logger . Errorf ( "获取交易对配置失败, 回调订单号:%s, 错误信息: %v" , preOrder . OrderSn , err )
2025-02-06 18:03:09 +08:00
return
}
2025-02-28 18:27:52 +08:00
apiInfo , err := GetApiInfo ( preOrder . ApiId )
if err != nil || apiInfo . Id == 0 {
logger . Errorf ( "订单回调查询apiuserinfo失败, 订单号:%s, 错误信息: %v" , preOrder . OrderSn , err )
2025-02-11 18:03:30 +08:00
return
}
2025-03-07 16:48:55 +08:00
if preOrder . MainId > 0 {
mainId = preOrder . MainId
}
2025-02-28 18:27:52 +08:00
// 处理主单加仓
2025-02-14 09:43:49 +08:00
if preOrder . OrderCategory == 3 {
2025-02-28 18:27:52 +08:00
if err := handleMainOrderAddPosition ( db , preOrder ) ; err != nil {
logger . Errorf ( "处理主单加仓失败, 主单号:%s, 错误信息: %v" , preOrder . MainId , err )
return
2025-02-20 12:00:04 +08:00
}
2025-03-10 11:13:08 +08:00
} else if first {
2025-02-28 18:27:52 +08:00
// 处理其他主单逻辑
2025-03-10 11:13:08 +08:00
if err := cancelPositionOtherOrders ( apiInfo , db , preOrder , true ) ; err != nil {
2025-02-28 18:27:52 +08:00
logger . Errorf ( "取消主单相关订单失败, 订单号:%s, 错误信息: %v" , preOrder . OrderSn , err )
return
2025-02-14 09:43:49 +08:00
}
2025-03-07 16:48:55 +08:00
//加仓待触发
addPositionOrders := make ( [ ] DbModels . LinePreOrder , 0 )
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "main_id =? AND order_category=3 AND order_type=0 AND status=0" , preOrder . Id ) . Order ( "rate asc" ) . Find ( & addPositionOrders ) . Error ; err != nil {
logger . Errorf ( "handleMainReduceFilled 获取加仓单失败,订单号:%s err:%v" , preOrder . OrderSn , err )
}
keyFutAddpositionKey := fmt . Sprintf ( rediskey . FuturesAddPositionList , global . EXCHANGE_BINANCE )
for _ , addPositionOrder := range addPositionOrders {
addPositionData := AddPositionList {
Id : addPositionOrder . Id ,
OrderSn : addPositionOrder . OrderSn ,
MainId : addPositionOrder . MainId ,
Pid : addPositionOrder . Pid ,
Price : utility . StrToDecimal ( addPositionOrder . Price ) ,
ApiId : addPositionOrder . ApiId ,
Symbol : addPositionOrder . Symbol ,
Side : addPositionOrder . Site ,
SymbolType : addPositionOrder . SymbolType ,
}
addVal , err := sonic . MarshalString ( addPositionData )
if err != nil {
logger . Errorf ( "handleMainReduceFilled 序列化加仓单失败,订单号:%s err:%v" , preOrder . OrderSn , err )
return
}
if err := helper . DefaultRedis . RPushList ( keyFutAddpositionKey , addVal ) ; err != nil {
logger . Errorf ( "handleMainReduceFilled 添加加仓单失败,订单号:%s err:%v" , preOrder . OrderSn , err )
}
}
2025-02-14 09:43:49 +08:00
}
2025-02-28 18:27:52 +08:00
// 获取止盈止损订单
orders , err := getStopOrders ( db , preOrder )
if err != nil {
logger . Errorf ( "查询止盈止损订单失败, 订单号:%s, 错误信息: %v" , preOrder . OrderSn , err )
2025-02-11 14:49:16 +08:00
return
}
2025-02-28 18:27:52 +08:00
// 获取和保存持仓数据
positionData := savePosition ( db , preOrder )
orderExt := models . LinePreOrderExt { }
2025-03-06 18:16:35 +08:00
db . Model ( & orderExt ) . Where ( "order_id =?" , extOrderId ) . First ( & orderExt )
2025-03-10 11:13:08 +08:00
totalNum := getFuturesPositionAvailableQuantity ( db , apiInfo , preOrder , tradeSet ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
2025-02-11 14:49:16 +08:00
2025-02-28 18:27:52 +08:00
// 更新订单数量并处理止盈、止损、减仓
2025-02-18 09:59:51 +08:00
for _ , order := range orders {
2025-02-08 14:05:57 +08:00
price := utility . StrToDecimal ( order . Price ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
order . Price = price . String ( )
2025-02-06 18:03:09 +08:00
2025-02-28 18:27:52 +08:00
// 更新止盈止损订单数量
2025-03-10 11:13:08 +08:00
num := updateOrderQuantity ( db , order , preOrder , & orderExt , totalNum , first , tradeSet )
2025-02-06 18:03:09 +08:00
2025-02-28 18:27:52 +08:00
// 根据订单类型处理
2025-02-06 18:03:09 +08:00
switch order . OrderType {
case 1 : // 止盈
2025-02-28 18:27:52 +08:00
//亏损大于0 重新计算比例
2025-03-06 18:16:35 +08:00
if first && positionData . TotalLoss . Cmp ( decimal . Zero ) > 0 && orderExt . Id > 0 {
2025-03-03 17:35:41 +08:00
percentag := positionData . TotalLoss . Div ( num ) . Div ( price ) . Mul ( decimal . NewFromInt ( 100 ) )
2025-02-28 18:27:52 +08:00
percentag = percentag . Add ( orderExt . TakeProfitRatio ) . Truncate ( 2 )
order . Rate = percentag . String ( )
percentag = percentag . Div ( decimal . NewFromInt ( 100 ) )
if positionData . PositionSide == "LONG" {
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
} else {
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( percentag ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
2025-03-06 18:16:35 +08:00
} else if ! first && orderExt . TpTpPriceRatio . Cmp ( decimal . Zero ) > 0 {
//止盈后止盈
order . Rate = orderExt . TpTpPriceRatio . String ( )
if positionData . PositionSide == "LONG" {
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( orderExt . TpTpPriceRatio . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
} else {
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( orderExt . TpTpPriceRatio . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
2025-02-28 18:27:52 +08:00
}
processFutTakeProfitOrder ( db , FutRestApi { } , order , num )
2025-02-06 18:03:09 +08:00
case 2 : // 止损
2025-03-06 18:16:35 +08:00
if ! first && orderExt . TpSlPriceRatio . Cmp ( decimal . Zero ) > 0 {
//止盈后止损
order . Rate = orderExt . TpSlPriceRatio . String ( )
if positionData . PositionSide == "LONG" {
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( orderExt . TpSlPriceRatio . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
} else {
order . Price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( orderExt . TpSlPriceRatio . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) ) . String ( )
}
}
2025-02-08 14:05:57 +08:00
processFutStopLossOrder ( db , order , price , num )
2025-03-07 16:48:55 +08:00
// case 4: // 减仓
// processFutReduceOrder(order, price, num)
2025-02-06 18:03:09 +08:00
}
}
2025-03-07 16:48:55 +08:00
2025-03-10 11:13:08 +08:00
nextFuturesReduceTrigger ( db , mainId , totalNum , tradeSet )
2025-02-06 18:03:09 +08:00
}
2025-02-28 18:27:52 +08:00
// 处理主单加仓
func handleMainOrderAddPosition ( db * gorm . DB , preOrder * models . LinePreOrder ) error {
// 更新加仓状态
if err := db . Model ( & DbModels . LinePreOrderStatus { } ) .
Where ( "order_id = ?" , preOrder . MainId ) .
Update ( "add_position_status" , 1 ) . Error ; err != nil {
return err
}
// 取消止盈止损订单
return cancelSymbolTakeAndStop ( db , preOrder . MainId , preOrder . SymbolType )
}
// 取消主单相关订单
2025-03-10 11:13:08 +08:00
// changeMainOrderStatus 是否修改主单状态
func cancelPositionOtherOrders ( apiUserInfo DbModels . LineApiUser , db * gorm . DB , preOrder * models . LinePreOrder , changeMainOrderStatus bool ) error {
2025-02-28 18:27:52 +08:00
mainOrders , err := getOpenPositionMainOrderId ( db , preOrder . Id , preOrder . ApiId , preOrder . SymbolType , preOrder . ExchangeType , preOrder . Symbol , preOrder . Site )
if err != nil {
return err
}
mainIds := [ ] int { }
for _ , mainOrder := range mainOrders {
2025-03-03 17:35:41 +08:00
mainId := mainOrder . Id
if mainOrder . MainId > 0 {
mainId = mainOrder . MainId
}
removeFutLossAndAddPosition ( mainId , mainOrder . OrderSn )
2025-03-04 15:34:32 +08:00
if ! utility . ContainsInt ( mainIds , mainId ) {
mainIds = append ( mainIds , mainId )
}
2025-02-28 18:27:52 +08:00
}
2025-03-10 11:13:08 +08:00
// 批量取消订单
err = cancelMainOrders ( mainIds , db , apiUserInfo , preOrder . Symbol , changeMainOrderStatus )
return err
}
// 根据mainid 取消订单
// @changeMainOrderStatus 是否更新主单状态
func cancelMainOrders ( mainIds [ ] int , db * gorm . DB , apiUserInfo DbModels . LineApiUser , symbol string , changeMainOrderStatus bool ) error {
2025-02-28 18:27:52 +08:00
if len ( mainIds ) > 0 {
2025-03-10 11:13:08 +08:00
orderSns , err := GetOpenOrderSns ( db , mainIds )
2025-02-28 18:27:52 +08:00
if err != nil {
return err
}
orderArray := utility . SplitSlice ( orderSns , 10 )
futApi := FutRestApi { }
for _ , item := range orderArray {
2025-03-10 11:13:08 +08:00
err := futApi . CancelBatchFutOrder ( apiUserInfo , symbol , item )
2025-02-28 18:27:52 +08:00
if err != nil {
logger . Errorf ( "批量取消订单失败 orderSns:%v" , item )
}
}
2025-03-03 17:35:41 +08:00
2025-03-10 11:13:08 +08:00
//需要修改主单状态
if changeMainOrderStatus {
if err := db . Exec ( "UPDATE line_pre_order SET `status`=4, `desc`=CONCAT(`desc`, ' 新单触发取消') WHERE id IN ? AND `status` =6" , mainIds ) . Error ; err != nil {
logger . Errorf ( "合约 新下单成功后更新主单取消状态失败, 交易对:%s, 错误信息:%v" , symbol , err )
}
2025-03-03 17:35:41 +08:00
}
2025-02-28 18:27:52 +08:00
}
return nil
}
// 获取止盈止损订单
func getStopOrders ( db * gorm . DB , preOrder * models . LinePreOrder ) ( [ ] models . LinePreOrder , error ) {
var orders [ ] models . LinePreOrder
if err := db . Model ( & DbModels . LinePreOrder { } ) .
Where ( "pid = ? AND order_type > 0 AND status = '0' " , preOrder . Id ) .
Find ( & orders ) . Error ; err != nil && ! errors . Is ( err , gorm . ErrRecordNotFound ) {
return nil , err
}
return orders , nil
}
// 更新订单数量
2025-03-06 18:16:35 +08:00
func updateOrderQuantity ( db * gorm . DB , order models . LinePreOrder , preOrder * models . LinePreOrder , ext * models . LinePreOrderExt , num decimal . Decimal , first bool , tradeSet models2 . TradeSet ) decimal . Decimal {
2025-02-28 18:27:52 +08:00
// 处理减仓比例
2025-03-06 18:16:35 +08:00
// if order.OrderType == 4 && ext.ReduceNumRatio.Cmp(decimal.Zero) > 0 {
// // 计算减仓数量
// num = num.Mul(ext.ReduceNumRatio.Div(decimal.NewFromInt(100))).Truncate(int32(tradeSet.AmountDigit))
// order.Num = num.String()
// } else
2025-02-28 18:27:52 +08:00
2025-03-10 11:13:08 +08:00
if first && ( order . OrderCategory == 1 || order . OrderCategory == 3 ) && order . OrderType == 1 && ext . TakeProfitNumRatio . Cmp ( decimal . Zero ) > 0 && ext . TakeProfitNumRatio . Cmp ( decimal . NewFromInt ( 100 ) ) != 0 {
2025-03-06 18:16:35 +08:00
// 计算止盈数量
num = num . Mul ( ext . TakeProfitNumRatio . Div ( decimal . NewFromInt ( 100 ) ) ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
order . Num = num . String ( )
2025-02-28 18:27:52 +08:00
}
// 更新订单数量
if err := db . Model ( & order ) . Update ( "num" , num ) . Error ; err != nil {
logger . Errorf ( "修改止盈止损数量失败 订单号:%s err:%v" , order . OrderSn , err )
}
return num
}
2025-02-10 18:21:44 +08:00
// 减仓单
2025-02-11 14:49:16 +08:00
func processFutReduceOrder ( order DbModels . LinePreOrder , price , num decimal . Decimal ) {
key := fmt . Sprintf ( rediskey . FuturesReduceList , global . EXCHANGE_BINANCE )
item := ReduceListItem {
Id : order . Id ,
ApiId : order . ApiId ,
Pid : order . Pid ,
MainId : order . MainId ,
Price : price ,
Num : num ,
Side : order . Site ,
Symbol : order . Symbol ,
OrderSn : order . OrderSn ,
}
itemVal , err := sonic . MarshalString ( & item )
if err != nil {
logger . Errorf ( "序列化失败 err:%v" , err )
return
}
if err := helper . DefaultRedis . RPushList ( key , itemVal ) ; err != nil {
logger . Errorf ( "减仓单写入缓存失败 err:%v" , err )
return
}
2025-02-10 18:21:44 +08:00
}
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 )
2025-02-19 09:45:38 +08:00
tradeSet , _ := GetTradeSet ( order . Symbol , 1 )
2025-02-06 18:03:09 +08:00
params := FutOrderPlace {
ApiId : order . ApiId ,
Symbol : order . Symbol ,
Side : order . Site ,
2025-02-19 09:45:38 +08:00
Price : price . Truncate ( int32 ( tradeSet . PriceDigit ) ) ,
Quantity : num . Truncate ( int32 ( tradeSet . AmountDigit ) ) ,
2025-02-10 18:21:44 +08:00
OrderType : "TAKE_PROFIT" ,
2025-02-14 09:43:49 +08:00
Profit : price ,
2025-02-06 18:03:09 +08:00
NewClientOrderId : order . OrderSn ,
}
2025-02-14 09:43:49 +08:00
err := futApi . OrderPlaceLoop ( db , params , 3 )
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 ) .
2025-03-06 18:16:35 +08:00
Updates ( map [ string ] interface { } { "status" : "2" , "desc" : err . Error ( ) , "num" : params . Quantity , "rate" : order . Rate , "price" : order . Price } ) . Error ; err != nil {
2025-02-06 18:03:09 +08:00
logger . Error ( "合约止盈下单失败,更新状态失败:" , order . OrderSn , " err:" , err )
}
} else {
2025-02-28 18:27:52 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? " , order . Id ) .
2025-03-06 18:16:35 +08:00
Updates ( map [ string ] interface { } { "trigger_time" : time . Now ( ) , "rate" : order . Rate , "num" : num . String ( ) , "price" : order . Price } ) . Error ; err != nil {
2025-02-22 11:24:08 +08:00
logger . Error ( "更新合约止盈单触发事件 ordersn:" , order . OrderSn )
}
2025-02-06 18:03:09 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id = ? and status =0" , order . Id ) .
2025-03-06 18:16:35 +08:00
Updates ( map [ string ] interface { } { "status" : "1" } ) . Error ; err != nil {
2025-02-06 18:03:09 +08:00
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 {
2025-02-19 09:45:38 +08:00
tradeSet , _ := GetTradeSet ( order . Symbol , 1 )
2025-02-08 14:05:57 +08:00
params := FutOrderPlace {
ApiId : order . ApiId ,
Symbol : order . Symbol ,
Side : order . Site ,
2025-02-19 09:45:38 +08:00
Price : price . Truncate ( int32 ( tradeSet . PriceDigit ) ) ,
Quantity : num . Truncate ( int32 ( tradeSet . AmountDigit ) ) ,
2025-02-10 18:21:44 +08:00
OrderType : "STOP" ,
2025-02-08 14:05:57 +08:00
StopPrice : price ,
NewClientOrderId : order . OrderSn ,
2025-02-06 18:03:09 +08:00
}
2025-02-08 14:05:57 +08:00
futApi := FutRestApi { }
2025-02-14 09:43:49 +08:00
err := futApi . OrderPlaceLoop ( db , params , 3 )
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 {
2025-02-22 11:24:08 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? " , order . Id ) . Updates ( map [ string ] interface { } { "trigger_time" : time . Now ( ) } ) . Error ; err != nil {
logger . Error ( "更新合约止损单触发事件 ordersn:" , order . OrderSn )
}
2025-02-08 14:05:57 +08:00
if err := db . Model ( & order ) . Where ( "status =0" ) .
2025-02-22 11:24:08 +08:00
Updates ( map [ string ] interface { } { "status" : "1" } ) . Error ; err != nil {
2025-02-08 14:05:57 +08:00
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
}
2025-02-14 09:43:49 +08:00
// 循环取消合约订单
func CancelFutOrderByOrderSnLoop ( apiInfo DbModels . LineApiUser , symbol , orderSn string ) error {
futApi := FutRestApi { }
var err error
for x := 1 ; x <= 4 ; x ++ {
err = futApi . CancelFutOrder ( apiInfo , symbol , orderSn )
if err == nil || strings . Contains ( err . Error ( ) , "取消订单被拒绝" ) {
err = nil
break
}
}
return err
}