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"
"go-admin/pkg/utility"
2025-04-03 18:32:23 +08:00
"go-admin/services/cacheservice"
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 JudgeSpotPrice ( trade models . TradeSet ) {
key := fmt . Sprintf ( rediskey . PreSpotOrderList , global . EXCHANGE_BINANCE )
preOrderVal , _ := helper . DefaultRedis . GetAllList ( key )
db := GetDBConnection ( )
if len ( preOrderVal ) == 0 {
return
}
spotApi := SpotRestApi { }
for _ , item := range preOrderVal {
preOrder := dto . PreOrderRedisList { }
if err := sonic . Unmarshal ( [ ] byte ( item ) , & preOrder ) ; err != nil {
log . Error ( "反序列化失败" )
continue
}
if preOrder . Symbol == trade . Coin + trade . Currency {
orderPrice , _ := decimal . NewFromString ( preOrder . Price )
tradePrice , _ := decimal . NewFromString ( trade . LastPrice )
//买入
if strings . ToUpper ( preOrder . Site ) == "BUY" && orderPrice . Cmp ( tradePrice ) >= 0 && orderPrice . Cmp ( decimal . Zero ) > 0 && tradePrice . Cmp ( decimal . Zero ) > 0 {
SpotOrderLock ( db , & preOrder , item , spotApi )
}
}
}
}
// 分布式锁下单
// v 预下单信息
// item 预下单源文本
func SpotOrderLock ( db * gorm . DB , v * dto . PreOrderRedisList , item string , spotApi SpotRestApi ) {
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotTrigger , 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 ( )
key := fmt . Sprintf ( rediskey . PreSpotOrderList , 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
}
price , _ := decimal . NewFromString ( v . Price )
num , _ := decimal . NewFromString ( preOrder . Num )
params := OrderPlacementService {
ApiId : v . ApiId ,
Symbol : v . Symbol ,
Side : v . Site ,
Type : preOrder . MainOrderType ,
TimeInForce : "GTC" ,
Price : price ,
Quantity : num ,
NewClientOrderId : v . OrderSn ,
}
preOrderVal , _ := sonic . MarshalString ( & v )
2025-02-14 09:43:49 +08:00
if err := spotApi . 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 {
2025-02-11 14:49:16 +08:00
log . Error ( "更新预下单状态失败 ordersn:" , v . OrderSn , " status:1" )
}
return
} else {
log . Error ( "获取锁失败" )
return
}
}
// 判断是否触发止损
func JudgeSpotStopLoss ( trade models . TradeSet ) {
key := fmt . Sprintf ( rediskey . SpotStopLossList , global . EXCHANGE_BINANCE )
stopLossVal , _ := helper . DefaultRedis . GetAllList ( key )
if len ( stopLossVal ) == 0 {
return
}
db := GetDBConnection ( )
spotApi := SpotRestApi { }
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-04-03 18:32:23 +08:00
tradeSet , err := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , trade . Coin + trade . Currency , 0 )
2025-02-11 14:49:16 +08:00
if err != nil {
log . Error ( "获取交易设置失败" )
return
}
for _ , item := range stopLossVal {
stopOrder := dto . StopLossRedisList { }
if err := sonic . Unmarshal ( [ ] byte ( item ) , & stopOrder ) ; err != nil {
log . Error ( "反序列化失败" )
continue
}
if stopOrder . Symbol == trade . Coin + trade . Currency {
orderPrice := stopOrder . Price
tradePrice , _ := decimal . NewFromString ( trade . LastPrice )
//买入
if strings . ToUpper ( stopOrder . Site ) == "SELL" &&
orderPrice . Cmp ( tradePrice ) >= 0 &&
orderPrice . Cmp ( decimal . Zero ) > 0 &&
tradePrice . Cmp ( decimal . Zero ) > 0 {
SpotStopLossTrigger ( db , stopOrder , spotApi , setting , tradeSet , key , item )
}
}
}
}
// 触发现货止损
func SpotStopLossTrigger ( db * gorm . DB , stopOrder dto . StopLossRedisList , spotApi SpotRestApi , setting DbModels . LineSystemSetting , tradeSet models . TradeSet , key string , item string ) {
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotTrigger , stopOrder . ApiId , stopOrder . 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 ( )
takeOrder := DbModels . LinePreOrder { }
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "pid =? AND order_type =1" , stopOrder . PId ) . Find ( & takeOrder ) . Error ; err != nil {
log . Error ( "查询止盈单失败" )
return
}
2025-02-11 18:03:30 +08:00
hasrecord , _ := helper . DefaultRedis . IsElementInList ( key , item )
if ! hasrecord {
log . Debug ( "减仓缓存中不存在" , item )
return
}
2025-02-11 14:49:16 +08:00
apiInfo , _ := GetApiInfo ( takeOrder . ApiId )
if apiInfo . Id == 0 {
log . Error ( "现货止损 查询api用户不存在" )
return
}
var err error
for x := 1 ; x <= 4 ; x ++ {
err = spotApi . CancelOpenOrderByOrderSn ( apiInfo , takeOrder . Symbol , takeOrder . OrderSn )
2025-02-18 15:40:45 +08:00
if err == nil || strings . Contains ( err . Error ( ) , "该交易对没有订单" ) {
err = nil
2025-02-11 14:49:16 +08:00
break
}
}
if err != nil {
log . Error ( "现货止损撤单失败" , err )
return
}
stopPreOrder , _ := GetOrderById ( db , stopOrder . Id )
price := stopOrder . Price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( setting . StopLossPremium . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
num := utility . StrToDecimal ( takeOrder . Num ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
params := OrderPlacementService {
ApiId : takeOrder . ApiId ,
Side : takeOrder . Site ,
Type : "LIMIT" ,
TimeInForce : "GTC" ,
Symbol : takeOrder . Symbol ,
Price : price ,
Quantity : num ,
NewClientOrderId : stopPreOrder . OrderSn ,
}
2025-02-14 09:43:49 +08:00
if err := spotApi . OrderPlaceLoop ( db , params , 3 ) ; err != nil {
2025-02-11 14:49:16 +08:00
log . Errorf ( "现货止损挂单失败 id: %s err:%v" , stopOrder . Id , err )
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 = ? " , stopOrder . Id ) . Updates ( map [ string ] interface { } { "trigger_time" : time . Now ( ) } ) . Error ; err != nil {
2025-02-22 09:51:45 +08:00
log . Errorf ( "现货止损更新状态失败 id: %s err:%v" , stopOrder . 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" , stopOrder . Id , err )
}
2025-02-22 09:51:45 +08:00
2025-02-11 14:49:16 +08:00
} else {
log . Error ( "获取锁失败" )
}
}
// 判断是否触发现货减仓
func JudgeSpotReduce ( trade models . TradeSet ) {
key := fmt . Sprintf ( rediskey . SpotReduceList , global . EXCHANGE_BINANCE )
db := GetDBConnection ( )
spotApi := SpotRestApi { }
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-04-03 18:32:23 +08:00
tradePrice , _ := decimal . NewFromString ( trade . LastPrice )
//减仓单减仓策略
orderReduceVal , _ := helper . DefaultRedis . GetAllList ( fmt . Sprintf ( rediskey . SpotOrderReduceStrategyList , global . EXCHANGE_BINANCE ) )
reduceOrderStrategy := dto . LineOrderReduceStrategyResp { }
for _ , item := range orderReduceVal {
sonic . Unmarshal ( [ ] byte ( item ) , & reduceOrderStrategy )
for _ , item2 := range reduceOrderStrategy . Items {
if reduceOrderStrategy . Symbol == trade . Coin + trade . Currency {
//买入
if strings . ToUpper ( reduceOrderStrategy . Side ) == "SELL" &&
item2 . TriggerPrice . Cmp ( tradePrice ) >= 0 &&
item2 . TriggerPrice . Cmp ( decimal . Zero ) > 0 &&
tradePrice . Cmp ( decimal . Zero ) > 0 {
//todo 生成订单并触发
// SpotReduceTrigger(db, reduceOrder, spotApi, setting, key, item)
}
}
}
}
//减仓单
reduceVal , _ := helper . DefaultRedis . GetAllList ( key )
2025-02-11 14:49:16 +08:00
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
//买入
if strings . ToUpper ( reduceOrder . Side ) == "SELL" &&
orderPrice . Cmp ( tradePrice ) >= 0 &&
orderPrice . Cmp ( decimal . Zero ) > 0 &&
tradePrice . Cmp ( decimal . Zero ) > 0 {
2025-02-11 18:03:30 +08:00
SpotReduceTrigger ( db , reduceOrder , spotApi , setting , key , item )
2025-02-11 14:49:16 +08:00
}
}
}
}
// 触发现货减仓
2025-02-11 18:03:30 +08:00
func SpotReduceTrigger ( db * gorm . DB , reduceOrder ReduceListItem , spotApi SpotRestApi , setting DbModels . LineSystemSetting , key , item string ) {
2025-04-03 18:32:23 +08:00
tradeSet , err := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , reduceOrder . Symbol , 0 )
2025-02-11 18:03:30 +08:00
if err != nil {
log . Error ( "获取交易设置失败" )
return
}
2025-02-11 14:49:16 +08:00
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotTrigger , 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-02-11 14:49:16 +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用户不存在" )
return
}
2025-03-10 11:13:08 +08:00
for _ , takeOrder := range takeOrders {
err := CancelOpenOrderByOrderSnLoop ( 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
}
price := reduceOrder . Price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( setting . ReducePremium . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
params := OrderPlacementService {
ApiId : reduceOrder . ApiId ,
Side : reduceOrder . Side ,
Type : "LIMIT" ,
TimeInForce : "GTC" ,
Symbol : reduceOrder . Symbol ,
Price : price ,
2025-02-11 18:03:30 +08:00
Quantity : reduceOrder . Num ,
2025-02-11 14:49:16 +08:00
NewClientOrderId : reduceOrder . OrderSn ,
}
2025-02-14 09:43:49 +08:00
if err := spotApi . OrderPlaceLoop ( db , params , 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 )
}
2025-02-22 09:51:45 +08:00
if err := db . Model ( & DbModels . LinePreOrder { } ) .
Where ( "id =? AND status =0" , reduceOrder . Id ) .
2025-02-22 11:24:08 +08:00
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
//处理减仓单减仓策略
CacheOrderStrategyAndReCreate ( db , reduceOrder , 1 , tradeSet , setting )
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 ( "获取锁失败" )
}
}
// 判断现货加仓
func JudgeSpotAddPosition ( trade models . TradeSet ) {
key := fmt . Sprintf ( rediskey . SpotAddPositionList , global . EXCHANGE_BINANCE )
preOrderVal , _ := helper . DefaultRedis . GetAllList ( key )
db := GetDBConnection ( )
if len ( preOrderVal ) == 0 {
return
}
spotApi := SpotRestApi { }
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 strings . ToUpper ( preOrder . Side ) == "BUY" && orderPrice . Cmp ( tradePrice ) >= 0 && orderPrice . Cmp ( decimal . Zero ) > 0 && tradePrice . Cmp ( decimal . Zero ) > 0 {
SpotAddPositionTrigger ( db , & preOrder , item , spotApi )
}
}
}
}
func SpotAddPositionTrigger ( db * gorm . DB , v * AddPositionList , item string , spotApi SpotRestApi ) {
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotTrigger , 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 )
tradeSet , _ := cacheservice . GetTradeSet ( global . EXCHANGE_BINANCE , v . Symbol , 0 )
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 . SpotAddPositionList , 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
}
price := v . Price
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 := OrderPlacementService {
ApiId : v . ApiId ,
Symbol : v . Symbol ,
Side : v . Side ,
Type : preOrder . MainOrderType ,
TimeInForce : "GTC" ,
Price : price ,
2025-02-14 09:43:49 +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 := spotApi . 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 09:51:45 +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 )
}
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
}
}