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"
"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 ) )
db := GetDBConnection ( )
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 )
if len ( reduceVal ) == 0 {
return
}
db := GetDBConnection ( )
2025-02-11 18:03:30 +08:00
futApi := FutRestApi { }
2025-02-11 14:49:16 +08:00
setting , err := GetSystemSetting ( db )
if err != nil {
log . Error ( "获取系统设置失败" )
return
}
for _ , item := range reduceVal {
reduceOrder := ReduceListItem { }
if err := sonic . Unmarshal ( [ ] byte ( item ) , & reduceOrder ) ; err != nil {
log . Error ( "反序列化失败" )
continue
}
if reduceOrder . Symbol == trade . Coin + trade . Currency {
orderPrice := reduceOrder . Price
tradePrice , _ := decimal . NewFromString ( trade . LastPrice )
//买入
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-02-11 18:03:30 +08:00
FuturesReduceTrigger ( db , reduceOrder , futApi , setting , key , item )
2025-02-11 14:49:16 +08:00
}
}
}
}
// 触发合约减仓
2025-02-11 18:03:30 +08:00
func FuturesReduceTrigger ( db * gorm . DB , reduceOrder ReduceListItem , futApi FutRestApi , setting DbModels . LineSystemSetting , key , item string ) {
tradeSet , _ := GetTradeSet ( reduceOrder . Symbol , 1 )
if tradeSet . LastPrice == "" {
return
}
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 )
return
} else if ok {
defer lock . Release ( )
2025-03-10 11:13:08 +08:00
takeOrders := make ( [ ] DbModels . LinePreOrder , 0 )
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "main_id =? AND order_type =1 AND status IN (1,5)" , reduceOrder . MainId ) . Find ( & takeOrders ) . Error ; err != nil {
2025-02-11 14:49:16 +08:00
log . Error ( "查询止盈单失败" )
return
}
2025-02-11 18:03:30 +08:00
hasrecord , _ := helper . DefaultRedis . IsElementInList ( key , item )
if ! hasrecord {
log . Debug ( "减仓缓存中不存在" , item )
return
}
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用户不存在" )
return
}
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 {
log . Error ( "合约止盈撤单失败" , err )
return
}
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
// params := FutOrderPlace{
// ApiId: reduceOrder.ApiId,
// Side: reduceOrder.Side,
// OrderType: "STOP",
// Symbol: reduceOrder.Symbol,
// Price: price,
// StopPrice: price,
// Quantity: num,
// NewClientOrderId: reduceOrder.OrderSn,
// }
if err := futApi . ClosePositionLoop ( reduceOrder . Symbol , reduceOrder . OrderSn , num , reduceOrder . Side , positionSide , apiInfo , "LIMIT" , "0" , price , 3 ) ; err != nil {
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-02-11 14:49:16 +08:00
}
if _ , err := helper . DefaultRedis . LRem ( key , item ) ; err != nil {
log . Errorf ( "合约减仓 删除缓存失败 id:%v err:%v" , reduceOrder . Id , err )
}
} else {
log . Error ( "获取锁失败" )
}
}
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 )
db := GetDBConnection ( )
if len ( preOrderVal ) == 0 {
return
}
futApi := FutRestApi { }
for _ , item := range preOrderVal {
preOrder := AddPositionList { }
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 )
}
}
}
}
// 合约加仓触发
func FutAddPositionTrigger ( db * gorm . DB , v * AddPositionList , item string , futApi FutRestApi ) {
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 ( )
setting , _ := GetSystemSetting ( db )
2025-02-20 12:00:04 +08:00
tradeSet , _ := GetTradeSet ( v . Symbol , 1 )
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
}
}