2025-02-11 14:49:16 +08:00
package binanceservice
import (
"context"
"errors"
"fmt"
DbModels "go-admin/app/admin/models"
"go-admin/app/admin/service/dto"
"go-admin/common/const/rediskey"
"go-admin/common/global"
"go-admin/common/helper"
"go-admin/models"
2025-10-14 19:58:59 +08:00
"go-admin/models/positiondto"
2025-04-07 18:36:36 +08:00
"go-admin/pkg/utility"
2025-04-03 18:32:23 +08:00
"go-admin/services/cacheservice"
2025-10-14 19:58:59 +08:00
"go-admin/services/commonservice"
"go-admin/services/orderservice"
2025-02-11 14:49:16 +08:00
"strings"
"time"
"github.com/bytedance/sonic"
log "github.com/go-admin-team/go-admin-core/logger"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
/ *
判断合约触发
* /
func JudgeFuturesPrice ( tradeSet models . TradeSet ) {
preOrderVal , _ := helper . DefaultRedis . GetAllList ( fmt . Sprintf ( rediskey . PreFutOrderList , global . EXCHANGE_BINANCE ) )
2025-10-14 19:58:59 +08:00
db := commonservice . GetDBConnection ( )
2025-02-11 14:49:16 +08:00
if len ( preOrderVal ) == 0 {
return
}
futApi := FutRestApi { }
for _ , item := range preOrderVal {
preOrder := dto . PreOrderRedisList { }
if err := sonic . Unmarshal ( [ ] byte ( item ) , & preOrder ) ; err != nil {
log . Error ( "反序列化失败" )
continue
}
if preOrder . Symbol != tradeSet . Coin + tradeSet . Currency {
continue
}
orderPrice , _ := decimal . NewFromString ( preOrder . Price )
tradePrice , _ := decimal . NewFromString ( tradeSet . LastPrice )
if orderPrice . Cmp ( decimal . Zero ) == 0 || tradePrice . Cmp ( decimal . Zero ) == 0 {
continue
}
//多
if ( strings . ToUpper ( preOrder . Site ) == "BUY" && orderPrice . Cmp ( tradePrice ) >= 0 ) ||
( strings . ToUpper ( preOrder . Site ) == "SELL" && orderPrice . Cmp ( tradePrice ) <= 0 ) {
futTriggerOrder ( db , & preOrder , item , futApi )
}
}
}
// 分布式锁下单
func futTriggerOrder ( db * gorm . DB , v * dto . PreOrderRedisList , item string , futApi FutRestApi ) {
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotTrigger , v . ApiId , v . Symbol ) , 200 , 5 , 100 * time . Millisecond )
if ok , err := lock . AcquireWait ( context . Background ( ) ) ; err != nil {
log . Debug ( "获取锁失败" , err )
return
} else if ok {
defer lock . Release ( )
key := fmt . Sprintf ( rediskey . PreFutOrderList , global . EXCHANGE_BINANCE )
preOrder := DbModels . LinePreOrder { }
if err := db . Where ( "id = ?" , v . Id ) . First ( & preOrder ) . Error ; err != nil {
log . Error ( "获取预下单失败" , err )
if errors . Is ( err , gorm . ErrRecordNotFound ) {
log . Error ( "不存在待触发主单" , item )
helper . DefaultRedis . LRem ( key , item )
}
return
}
hasrecord , _ := helper . DefaultRedis . IsElementInList ( key , item )
if ! hasrecord {
log . Debug ( "预下单缓存中不存在" , item )
return
}
price , _ := decimal . NewFromString ( v . Price )
num , _ := decimal . NewFromString ( preOrder . Num )
if price . Cmp ( decimal . Zero ) == 0 {
log . Error ( "价格不能为0" )
return
}
params := FutOrderPlace {
ApiId : v . ApiId ,
Symbol : v . Symbol ,
Side : v . Site ,
OrderType : preOrder . MainOrderType ,
SideType : preOrder . MainOrderType ,
Price : price ,
Quantity : num ,
NewClientOrderId : v . OrderSn ,
}
preOrderVal , _ := sonic . MarshalString ( & v )
2025-02-14 09:43:49 +08:00
if err := futApi . OrderPlaceLoop ( db , params , 3 ) ; err != nil {
2025-02-11 14:49:16 +08:00
log . Error ( "下单失败" , v . Symbol , " err:" , err )
err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? and status='0'" , preOrder . Id ) . Updates ( map [ string ] interface { } { "status" : "2" , "desc" : err . Error ( ) } ) . Error
if err != nil {
log . Error ( "更新预下单状态失败" )
}
if preOrderVal != "" {
if _ , err := helper . DefaultRedis . LRem ( key , preOrderVal ) ; err != nil {
log . Error ( "删除redis 预下单失败:" , err )
}
}
return
}
if preOrderVal != "" {
if _ , err := helper . DefaultRedis . LRem ( key , preOrderVal ) ; err != nil {
log . Error ( "删除redis 预下单失败:" , err )
}
}
2025-02-22 11:24:08 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? " , preOrder . Id ) . Updates ( map [ string ] interface { } { "trigger_time" : time . Now ( ) } ) . Error ; err != nil {
log . Error ( "更新预下单状态失败 ordersn:" , preOrder . OrderSn , " status:1" )
}
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? AND status ='0'" , preOrder . Id ) . Updates ( map [ string ] interface { } { "status" : "1" } ) . Error ; err != nil {
2025-02-11 14:49:16 +08:00
log . Error ( "更新预下单状态失败 ordersn:" , v . OrderSn , " status:1" )
}
return
} else {
log . Error ( "获取锁失败" )
return
}
}
// 判断是否触发合约减仓
func JudgeFuturesReduce ( trade models . TradeSet ) {
key := fmt . Sprintf ( rediskey . FuturesReduceList , global . EXCHANGE_BINANCE )
reduceVal , _ := helper . DefaultRedis . GetAllList ( key )
2025-10-14 19:58:59 +08:00
db := commonservice . GetDBConnection ( )
2025-02-11 18:03:30 +08:00
futApi := FutRestApi { }
2025-04-03 18:32:23 +08:00
setting , err := cacheservice . GetSystemSetting ( db )
2025-02-11 14:49:16 +08:00
if err != nil {
log . Error ( "获取系统设置失败" )
return
}
2025-10-14 19:58:59 +08:00
reduceOrder := positiondto . ReduceListItem { }
2025-04-03 18:32:23 +08:00
tradePrice , _ := decimal . NewFromString ( trade . LastPrice )
//减仓单减仓策略
2025-04-07 18:36:36 +08:00
reduceReduceListKey := fmt . Sprintf ( rediskey . FutOrderReduceStrategyList , global . EXCHANGE_BINANCE )
orderReduceVal , _ := helper . DefaultRedis . HGetAllFields ( reduceReduceListKey )
2025-04-03 18:32:23 +08:00
reduceOrderStrategy := dto . LineOrderReduceStrategyResp { }
2025-10-14 19:58:59 +08:00
orderService := orderservice . OrderService { }
orderService . Orm = db
2025-04-03 18:32:23 +08:00
for _ , item := range orderReduceVal {
sonic . Unmarshal ( [ ] byte ( item ) , & reduceOrderStrategy )
2025-04-07 18:36:36 +08:00
for index , item2 := range reduceOrderStrategy . Items {
if reduceOrderStrategy . Symbol == trade . Coin + trade . Currency && ! item2 . Actived {
2025-04-03 18:32:23 +08:00
//买入
if item2 . TriggerPrice . Cmp ( decimal . Zero ) > 0 &&
tradePrice . Cmp ( decimal . Zero ) > 0 &&
( ( strings . ToUpper ( reduceOrderStrategy . Side ) == "SELL" && item2 . TriggerPrice . Cmp ( tradePrice ) >= 0 ) ||
( strings . ToUpper ( reduceOrderStrategy . Side ) == "BUY" && item2 . TriggerPrice . Cmp ( tradePrice ) <= 0 ) ) {
2025-04-07 18:36:36 +08:00
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . ReduceStrategyFutTriggerLock , reduceOrder . ApiId , reduceOrder . Symbol ) , 50 , 15 , 100 * time . Millisecond )
if ok , err := lock . AcquireWait ( context . Background ( ) ) ; err != nil {
log . Error ( "获取锁失败" , err )
return
} else if ok {
defer lock . Release ( )
2025-04-09 09:09:25 +08:00
hasrecord , _ := helper . DefaultRedis . HExists ( reduceReduceListKey , utility . IntToString ( reduceOrderStrategy . OrderId ) , item )
2025-04-07 18:36:36 +08:00
if ! hasrecord {
log . Debug ( "减仓缓存中不存在" , item )
return
}
2025-10-14 19:58:59 +08:00
order , err := orderService . CreateReduceReduceOrder ( db , reduceOrderStrategy . OrderId , item2 . Price , item2 . Num , trade . PriceDigit )
2025-04-07 18:36:36 +08:00
if err != nil {
log . Errorf ( "%d 生成订单失败" , reduceOrderStrategy . OrderId )
}
reduceOrder . ApiId = order . ApiId
reduceOrder . Id = order . Id
reduceOrder . Pid = order . Pid
reduceOrder . MainId = order . MainId
reduceOrder . Symbol = order . Symbol
reduceOrder . Side = reduceOrderStrategy . Side
reduceOrder . OrderSn = order . OrderSn
reduceOrder . Price = item2 . Price
reduceOrder . Num = item2 . Num
//下单成功修改策略节点状态
2025-04-09 09:09:25 +08:00
if FuturesReduceTrigger ( db , reduceOrder , futApi , setting , reduceReduceListKey , item , true , reduceOrderStrategy . OrderId ) {
2025-04-07 18:36:36 +08:00
reduceOrderStrategy . Items [ index ] . Actived = true
allActive := true
orderId := utility . IntToString ( reduceOrderStrategy . OrderId )
for _ , item3 := range reduceOrderStrategy . Items {
if ! item3 . Actived {
allActive = false
break
}
}
if allActive {
if err := helper . DefaultRedis . HDelField ( reduceReduceListKey , orderId ) ; err != nil {
log . Errorf ( "删除redis reduceReduceListKey失败 %s" , err . Error ( ) )
}
} else {
str , _ := sonic . MarshalString ( reduceOrderStrategy )
if err := helper . DefaultRedis . HSetField ( reduceReduceListKey , orderId , str ) ; err != nil {
log . Errorf ( "更新redis reduceReduceListKey失败 %s" , err . Error ( ) )
}
}
}
}
2025-04-03 18:32:23 +08:00
}
}
}
}
2025-02-11 14:49:16 +08:00
for _ , item := range reduceVal {
if err := sonic . Unmarshal ( [ ] byte ( item ) , & reduceOrder ) ; err != nil {
log . Error ( "反序列化失败" )
continue
}
if reduceOrder . Symbol == trade . Coin + trade . Currency {
orderPrice := reduceOrder . Price
//买入
if orderPrice . Cmp ( decimal . Zero ) > 0 &&
tradePrice . Cmp ( decimal . Zero ) > 0 &&
( ( strings . ToUpper ( reduceOrder . Side ) == "SELL" && orderPrice . Cmp ( tradePrice ) >= 0 ) ||
( strings . ToUpper ( reduceOrder . Side ) == "BUY" && orderPrice . Cmp ( tradePrice ) <= 0 ) ) {
2025-04-09 09:09:25 +08:00
FuturesReduceTrigger ( db , reduceOrder , futApi , setting , key , item , false , 0 )
2025-02-11 14:49:16 +08:00
}
}
}
}
// 触发合约减仓
2025-04-07 18:36:36 +08:00
// isStrategy 是否是策略减仓
2025-04-09 09:09:25 +08:00
// reduceId 父减仓单id
2025-10-14 19:58:59 +08:00
func FuturesReduceTrigger ( db * gorm . DB , reduceOrder positiondto . ReduceListItem , futApi FutRestApi , setting DbModels . LineSystemSetting , key , item string , isStrategy bool , reduceId int ) bool {
tradeSet , _ := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , reduceOrder . Symbol , 2 )
2025-04-07 18:36:36 +08:00
result := true
2025-02-11 18:03:30 +08:00
if tradeSet . LastPrice == "" {
2025-04-07 18:36:36 +08:00
return false
2025-02-11 18:03:30 +08:00
}
2025-02-11 14:49:16 +08:00
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . FutTrigger , reduceOrder . ApiId , reduceOrder . Symbol ) , 20 , 5 , 100 * time . Millisecond )
if ok , err := lock . AcquireWait ( context . Background ( ) ) ; err != nil {
log . Error ( "获取锁失败" , err )
2025-04-07 18:36:36 +08:00
return false
2025-02-11 14:49:16 +08:00
} else if ok {
defer lock . Release ( )
2025-03-10 11:13:08 +08:00
takeOrders := make ( [ ] DbModels . LinePreOrder , 0 )
2025-04-07 18:36:36 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "main_id =? AND order_type IN (1,2,4) AND status IN (1,5)" , reduceOrder . MainId ) . Find ( & takeOrders ) . Error ; err != nil {
2025-02-11 14:49:16 +08:00
log . Error ( "查询止盈单失败" )
2025-04-07 18:36:36 +08:00
return false
2025-02-11 14:49:16 +08:00
}
2025-04-09 09:09:25 +08:00
var hasrecord bool
if isStrategy {
hasrecord , _ = helper . DefaultRedis . HExists ( key , utility . IntToString ( reduceId ) , item )
} else {
hasrecord , _ = helper . DefaultRedis . IsElementInList ( key , item )
}
2025-02-11 18:03:30 +08:00
if ! hasrecord {
log . Debug ( "减仓缓存中不存在" , item )
2025-04-07 18:36:36 +08:00
return false
2025-02-11 18:03:30 +08:00
}
2025-03-10 11:13:08 +08:00
apiInfo , _ := GetApiInfo ( reduceOrder . ApiId )
2025-02-11 14:49:16 +08:00
if apiInfo . Id == 0 {
log . Error ( "现货减仓 查询api用户不存在" )
2025-04-07 18:36:36 +08:00
return false
2025-02-11 14:49:16 +08:00
}
2025-03-10 11:13:08 +08:00
for _ , takeOrder := range takeOrders {
err := CancelFutOrderByOrderSnLoop ( apiInfo , takeOrder . Symbol , takeOrder . OrderSn )
2025-02-11 14:49:16 +08:00
2025-03-10 11:13:08 +08:00
if err != nil {
2025-04-07 18:36:36 +08:00
log . Error ( "撤单失败" , err )
return false
2025-03-10 11:13:08 +08:00
}
2025-02-11 14:49:16 +08:00
}
2025-02-14 09:43:49 +08:00
2025-02-11 14:49:16 +08:00
price := reduceOrder . Price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( setting . ReducePremium . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
2025-02-15 18:38:58 +08:00
num := reduceOrder . Num . Truncate ( int32 ( tradeSet . AmountDigit ) )
2025-02-14 09:43:49 +08:00
var positionSide string
2025-02-11 14:49:16 +08:00
2025-02-14 09:43:49 +08:00
if reduceOrder . Side == "BUY" {
positionSide = "SHORT"
} else {
positionSide = "LONG"
2025-02-11 14:49:16 +08:00
}
2025-02-14 09:43:49 +08:00
if err := futApi . ClosePositionLoop ( reduceOrder . Symbol , reduceOrder . OrderSn , num , reduceOrder . Side , positionSide , apiInfo , "LIMIT" , "0" , price , 3 ) ; err != nil {
2025-04-07 18:36:36 +08:00
result = false
2025-02-11 14:49:16 +08:00
log . Errorf ( "合约减仓挂单失败 id: %s err:%v" , reduceOrder . Id , err )
2025-02-18 15:40:45 +08:00
if err2 := db . Model ( & DbModels . LinePreOrder { } ) .
Where ( "id = ? AND status =0" , reduceOrder . Id ) .
Updates ( map [ string ] interface { } { "status" : 2 , "desc" : err . Error ( ) } ) . Error ; err2 != nil {
log . Errorf ( "合约减仓更新状态失败 id: %s err:%v" , reduceOrder . Id , err2 )
}
2025-02-22 09:51:45 +08:00
} else {
2025-02-22 11:24:08 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? " , reduceOrder . Id ) . Updates ( map [ string ] interface { } { "trigger_time" : time . Now ( ) } ) . Error ; err != nil {
log . Error ( "更新减仓单状态失败 ordersn:" , reduceOrder . OrderSn , " status:1" )
}
2025-02-22 09:51:45 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) .
2025-02-22 11:24:08 +08:00
Where ( "id = ? AND status =0" , reduceOrder . Id ) . Updates ( map [ string ] interface { } { "status" : 1 } ) . Error ; err != nil {
2025-02-22 09:51:45 +08:00
log . Errorf ( "合约减仓更新状态失败 id: %s err:%v" , reduceOrder . Id , err )
}
2025-04-03 18:32:23 +08:00
//处理减仓单减仓策略
2025-04-07 18:36:36 +08:00
if err := CacheOrderStrategyAndReCreate ( db , reduceOrder , 2 , tradeSet , setting ) ; err != nil {
log . Errorf ( "合约减仓策略处理失败 id: %s err:%v" , reduceOrder . Id , err )
}
2025-02-11 14:49:16 +08:00
}
2025-04-07 18:36:36 +08:00
if ! isStrategy {
if _ , err := helper . DefaultRedis . LRem ( key , item ) ; err != nil {
log . Errorf ( "合约减仓 删除缓存失败 id:%v err:%v" , reduceOrder . Id , err )
}
2025-02-11 14:49:16 +08:00
}
} else {
log . Error ( "获取锁失败" )
2025-04-07 18:36:36 +08:00
result = false
2025-02-11 14:49:16 +08:00
}
2025-04-07 18:36:36 +08:00
return result
2025-02-11 14:49:16 +08:00
}
2025-02-22 09:51:45 +08:00
// 判断合约加仓
2025-02-11 14:49:16 +08:00
func JudgeFutAddPosition ( trade models . TradeSet ) {
key := fmt . Sprintf ( rediskey . FuturesAddPositionList , global . EXCHANGE_BINANCE )
preOrderVal , _ := helper . DefaultRedis . GetAllList ( key )
2025-10-14 19:58:59 +08:00
db := commonservice . GetDBConnection ( )
2025-02-11 14:49:16 +08:00
if len ( preOrderVal ) == 0 {
return
}
futApi := FutRestApi { }
for _ , item := range preOrderVal {
2025-10-14 19:58:59 +08:00
preOrder := positiondto . AddPositionList { }
2025-02-11 14:49:16 +08:00
if err := sonic . Unmarshal ( [ ] byte ( item ) , & preOrder ) ; err != nil {
log . Error ( "反序列化失败" )
continue
}
if preOrder . Symbol == trade . Coin + trade . Currency {
orderPrice := preOrder . Price
tradePrice , _ := decimal . NewFromString ( trade . LastPrice )
if orderPrice . Cmp ( decimal . Zero ) == 0 || tradePrice . Cmp ( decimal . Zero ) == 0 {
continue
}
//多
if ( strings . ToUpper ( preOrder . Side ) == "BUY" && orderPrice . Cmp ( tradePrice ) >= 0 ) ||
( strings . ToUpper ( preOrder . Side ) == "SELL" && orderPrice . Cmp ( tradePrice ) <= 0 ) {
FutAddPositionTrigger ( db , & preOrder , item , futApi )
}
}
}
}
// 合约加仓触发
2025-10-14 19:58:59 +08:00
func FutAddPositionTrigger ( db * gorm . DB , v * positiondto . AddPositionList , item string , futApi FutRestApi ) {
2025-02-11 14:49:16 +08:00
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . FutTrigger , v . ApiId , v . Symbol ) , 20 , 5 , 100 * time . Millisecond )
if ok , err := lock . AcquireWait ( context . Background ( ) ) ; err != nil {
log . Error ( "获取锁失败" , err )
return
} else if ok {
defer lock . Release ( )
2025-04-03 18:32:23 +08:00
setting , _ := cacheservice . GetSystemSetting ( db )
2025-10-14 19:58:59 +08:00
tradeSet , _ := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , v . Symbol , 2 )
2025-02-11 14:49:16 +08:00
if tradeSet . LastPrice == "" {
log . Errorf ( "合约加仓触发 查询交易对失败 交易对:%s ordersn:%s" , v . Symbol , v . OrderSn )
return
}
key := fmt . Sprintf ( rediskey . FuturesAddPositionList , global . EXCHANGE_BINANCE )
preOrder := DbModels . LinePreOrder { }
if err := db . Where ( "id = ?" , v . Id ) . First ( & preOrder ) . Error ; err != nil {
log . Error ( "获取预下单失败" , err )
if errors . Is ( err , gorm . ErrRecordNotFound ) {
log . Error ( "不存在待触发加仓主单" , item )
helper . DefaultRedis . LRem ( key , item )
}
return
}
hasrecord , _ := helper . DefaultRedis . IsElementInList ( key , item )
if ! hasrecord {
log . Error ( "不存在待触发加仓主单" , item )
return
}
2025-02-15 18:38:58 +08:00
price := v . Price . Truncate ( int32 ( tradeSet . PriceDigit ) )
2025-02-11 14:49:16 +08:00
num , _ := decimal . NewFromString ( preOrder . Num )
if setting . AddPositionPremium . Cmp ( decimal . Zero ) > 0 {
price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( setting . AddPositionPremium . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
}
params := FutOrderPlace {
ApiId : v . ApiId ,
Symbol : v . Symbol ,
Side : v . Side ,
OrderType : "LIMIT" ,
Price : price ,
2025-02-15 18:38:58 +08:00
Quantity : num . Truncate ( int32 ( tradeSet . AmountDigit ) ) ,
2025-02-11 14:49:16 +08:00
NewClientOrderId : v . OrderSn ,
}
preOrderVal , _ := sonic . MarshalString ( & v )
2025-02-14 09:43:49 +08:00
if err := futApi . OrderPlaceLoop ( db , params , 3 ) ; err != nil {
2025-02-11 14:49:16 +08:00
log . Error ( "下单失败" , v . Symbol , " err:" , err )
err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? AND status =0" , preOrder . Id ) . Updates ( map [ string ] interface { } { "status" : "2" , "desc" : err . Error ( ) } ) . Error
if err != nil {
log . Error ( "下单失败后修改订单失败" )
}
if preOrderVal != "" {
if _ , err := helper . DefaultRedis . LRem ( key , preOrderVal ) ; err != nil {
log . Error ( "删除redis 预下单失败:" , err )
}
}
return
}
if preOrderVal != "" {
if _ , err := helper . DefaultRedis . LRem ( key , preOrderVal ) ; err != nil {
log . Error ( "删除redis 预下单失败:" , err )
}
}
2025-02-22 11:24:08 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? " , preOrder . Id ) . Updates ( map [ string ] interface { } { "trigger_time" : time . Now ( ) } ) . Error ; err != nil {
log . Error ( "更新预下单状态失败 ordersn:" , v . OrderSn , " status:1" )
}
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? AND status ='0'" , preOrder . Id ) . Updates ( map [ string ] interface { } { "status" : "1" } ) . Error ; err != nil {
2025-02-11 14:49:16 +08:00
log . Error ( "更新预下单状态失败 ordersn:" , v . OrderSn , " status:1" )
}
return
} else {
log . Error ( "获取锁失败" )
return
}
}