2025-02-06 11:14:33 +08:00
package binanceservice
import (
"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"
"strings"
"time"
"github.com/bytedance/sonic"
"github.com/go-admin-team/go-admin-core/logger"
log "github.com/go-admin-team/go-admin-core/logger"
2025-02-06 18:03:09 +08:00
"github.com/go-admin-team/go-admin-core/sdk"
2025-02-06 11:14:33 +08:00
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
type AddPosition struct {
Db * gorm . DB
}
// 获取缓存交易对
// symbolType 0-现货 1-合约
func GetTradeSet ( symbol string , symbolType int ) ( models . TradeSet , error ) {
result := models . TradeSet { }
val := ""
switch symbolType {
case 0 :
key := fmt . Sprintf ( global . TICKER_SPOT , global . EXCHANGE_BINANCE , symbol )
val , _ = helper . DefaultRedis . GetString ( key )
case 1 :
key := fmt . Sprintf ( global . TICKER_FUTURES , global . EXCHANGE_BINANCE , symbol )
val , _ = helper . DefaultRedis . GetString ( key )
}
if val != "" {
if err := sonic . Unmarshal ( [ ] byte ( val ) , & result ) ; err != nil {
return result , err
}
} else {
return result , errors . New ( "未找到交易对信息" )
}
return result , nil
}
func GetRisk ( futApi * FutRestApi , apiInfo * DbModels . LineApiUser , symbol string ) [ ] PositionRisk {
for x := 0 ; x < 5 ; x ++ {
risks , _ := futApi . GetPositionV3 ( apiInfo , symbol )
if len ( risks ) > 0 {
return risks
}
time . Sleep ( time . Millisecond * 200 )
}
return [ ] PositionRisk { }
}
// CancelFutClosePosition 撤销交易对指定方向的平仓单
// apiUserInfo 账户信息
// symbol 交易对
// side 购买反向
// positionSide 仓位方向
// side=BUY positionSide=SHORT 就是撤销平空委托
// side=BUY&positionSide=LONG是开多,
// side=SELL&positionSide=LONG是平多,
// side=SELL&positionSide=SHORT是开空,
// side=BUY&positionSide=SHORT是平空。
func CancelFutClosePosition ( apiUserInfo DbModels . LineApiUser , symbol string , side string , positionSide string ) error {
//查询当前用户的委托订单
client := GetClient ( & apiUserInfo )
resp , _ , err := client . SendFuturesRequestAuth ( "/fapi/v1/openOrders" , "GET" , map [ string ] string {
"symbol" : symbol ,
"recvWindow" : "5000" ,
} )
if err != nil {
logger . Error ( "撤销平仓单时查询委托失败:" , err )
return err
}
var openOrders [ ] OpenOrders
sonic . Unmarshal ( resp , & openOrders )
orderIdList := make ( [ ] int , 0 )
for _ , order := range openOrders {
if order . Side == side && order . PositionSide == positionSide {
orderIdList = append ( orderIdList , order . OrderId )
}
}
// 每次取 10 个元素
batchSize := 10
for i := 0 ; i < len ( orderIdList ) ; i += batchSize {
end := i + batchSize
if end > len ( orderIdList ) {
end = len ( orderIdList ) // 避免越界
}
// 取出当前批次的元素
batch := orderIdList [ i : end ]
marshal , _ := sonic . Marshal ( & batch )
_ , _ , err = client . SendFuturesRequestAuth ( "/fapi/v1/batchOrders" , "DELETE" , map [ string ] string {
"symbol" : symbol ,
"orderIdList" : string ( marshal ) ,
"recvWindow" : "5000" ,
} )
if err != nil {
return err
}
}
return err
}
// CancelSpotClosePosition 取消现货平仓单(等同于撤销卖单)
// apiUserInfo: Api用户信息
// symbol: 交易对
func CancelSpotClosePosition ( apiUserInfo * DbModels . LineApiUser , symbol string ) error {
return CancelSpotOrder ( symbol , apiUserInfo , "SELL" )
}
// 取消现货订单
func CancelSpotOrder ( symbol string , apiUserInfo * DbModels . LineApiUser , side string ) error {
searchEndpoint := "/api/v3/openOrders"
cencelEndpoint := "/api/v3/order"
searchParams := map [ string ] interface { } {
"symbol" : symbol ,
}
client := GetClient ( apiUserInfo )
resp , _ , err := client . SendSpotAuth ( searchEndpoint , "GET" , searchParams )
db := GetDBConnection ( )
if err != nil {
if len ( resp ) > 0 {
} else {
logger . Error ( "查询现货当前下单失败:" , err )
}
}
var openOrders [ ] map [ string ] interface { }
err = sonic . Unmarshal ( resp , & openOrders )
if err != nil {
return err
}
for _ , order := range openOrders {
if orderSymbol , ok := order [ "symbol" ] . ( string ) ; ok && orderSymbol == symbol {
if orderSide , ok := order [ "side" ] . ( string ) ; ok && orderSide == side {
orderId , ok := order [ "orderId" ] . ( float64 )
if ! ok {
continue
}
orderSn , _ := order [ "clientOrderId" ] . ( string )
params := map [ string ] string {
"symbol" : orderSymbol ,
"orderId" : utility . Float64CutString ( orderId , 0 ) ,
// "cancelRestrictions": "ONLY_NEW",
}
_ , _ , err = client . SendSpotAuth ( cencelEndpoint , "DELETE" , params )
if err != nil {
logger . Error ( "撤销指定现货平仓单失败 ordersn:" , orderSn , " orderId:" , orderId , " err:" , err )
} else {
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "order_sn = ? and status !='9'" , orderSn ) . Update ( "status" , "4" ) . Error ; err != nil {
log . Error ( "修改止盈单撤销状态失败:" , err )
}
}
}
}
return nil
}
return nil
}
// GetTargetSymbol 获取目标交易对信息
func ( e * AddPosition ) GetTargetSymbol ( symbol string , symbolType int ) ( string , bool , DbModels . LineSymbol , error ) {
var targetSymbol string
var notUsdt bool
var symbolInfo DbModels . LineSymbol
// 处理非 USDT 交易对
if ! strings . HasSuffix ( symbol , "USDT" ) {
notUsdt = true
if err := e . Db . Model ( & DbModels . LineSymbol { } ) . Where ( "symbol = ? AND type = ?" , symbol , utility . IntToString ( symbolType ) ) . Find ( & symbolInfo ) . Error ; err != nil {
return "" , false , DbModels . LineSymbol { } , err
}
if symbolInfo . Id <= 0 {
return "" , false , DbModels . LineSymbol { } , fmt . Errorf ( "未找到交易对信息" )
}
targetSymbol = symbolInfo . BaseAsset + "USDT"
} else {
targetSymbol = symbol
}
return targetSymbol , notUsdt , symbolInfo , nil
}
func ( e * AddPosition ) GetOrderInfo ( req dto . ManuallyCover , symbol , orderType , site , status string ) ( DbModels . LinePreOrder , error ) {
var orderInfo DbModels . LinePreOrder
if err := e . Db . Model ( DbModels . LinePreOrder { } ) . Where ( "api_id = ? AND symbol = ? AND order_type = ? AND site = ? AND status = ?" , req . ApiId , symbol , orderType , site , status ) . Find ( & orderInfo ) . Error ; err != nil {
return DbModels . LinePreOrder { } , err
}
if orderInfo . Id <= 0 {
return DbModels . LinePreOrder { } , fmt . Errorf ( "未找到主仓信息" )
}
return orderInfo , nil
}
func ( e * AddPosition ) GetFutOrderInfo ( req dto . ManuallyCover , symbol , orderType , status string ) ( DbModels . LinePreOrder , error ) {
var orderInfo DbModels . LinePreOrder
if err := e . Db . Model ( DbModels . LinePreOrder { } ) . Where ( "api_id = ? AND symbol = ? AND order_type = ? AND status = ? AND cover_type = 2" , req . ApiId , symbol , orderType , status ) . Find ( & orderInfo ) . Error ; err != nil {
return DbModels . LinePreOrder { } , err
}
if orderInfo . Id <= 0 {
return DbModels . LinePreOrder { } , fmt . Errorf ( "未找到主仓信息" )
}
return orderInfo , nil
}
// GetFutSpotOrderInfo 获取合约对现货的订单信息
func ( e * AddPosition ) GetFutSpotOrderInfo ( req dto . ManuallyCover , symbol , orderType , status string ) ( DbModels . LinePreOrder , error ) {
var orderInfo DbModels . LinePreOrder
if err := e . Db . Model ( DbModels . LinePreOrder { } ) . Where ( "api_id = ? AND symbol = ? AND order_type = ? AND status = ? AND cover_type = 3" , req . ApiId , symbol , orderType , status ) . Find ( & orderInfo ) . Error ; err != nil {
return DbModels . LinePreOrder { } , err
}
if orderInfo . Id <= 0 {
return DbModels . LinePreOrder { } , fmt . Errorf ( "未找到主仓信息" )
}
return orderInfo , nil
}
// CalculateAmount 计算加仓数量
func ( e * AddPosition ) CalculateAmount ( req dto . ManuallyCover , totalNum , lastPrice decimal . Decimal , amountDigit int , notUsdt bool , symbolInfo DbModels . LineSymbol ) ( decimal . Decimal , error ) {
var amt decimal . Decimal
if req . CoverType == 1 {
decimalValue := utility . StringToDecimal ( req . Value ) . Div ( decimal . NewFromInt ( 100 ) )
amt = totalNum . Mul ( decimalValue )
} else {
decimalValue := utility . StringToDecimal ( req . Value )
if notUsdt {
tickerSymbolMaps := make ( [ ] dto . Ticker , 0 )
tickerSymbol := helper . DefaultRedis . Get ( rediskey . SpotSymbolTicker ) . Val ( )
if err := sonic . Unmarshal ( [ ] byte ( tickerSymbol ) , & tickerSymbolMaps ) ; err != nil {
return decimal . Zero , err
}
var tickerPrice decimal . Decimal
for _ , symbolMap := range tickerSymbolMaps {
if symbolMap . Symbol == strings . ToUpper ( symbolInfo . BaseAsset + "USDT" ) {
tickerPrice , _ = decimal . NewFromString ( symbolMap . Price )
break
}
}
for _ , symbolMap := range tickerSymbolMaps {
if symbolMap . Symbol == strings . ToUpper ( symbolInfo . QuoteAsset + "USDT" ) {
uTickerPrice , _ := decimal . NewFromString ( symbolMap . Price )
div := tickerPrice . Div ( decimal . NewFromInt ( 1 ) . Div ( uTickerPrice ) )
amt = decimalValue . Div ( div )
break
}
}
} else {
amt = decimalValue . Div ( lastPrice )
}
}
return amt . Truncate ( int32 ( amountDigit ) ) , nil
}
// 主单平仓删除缓存
// mainOrderId 主单id
// coverType 1现货->合约 2->合约->合约 3合约->现货
func MainClosePositionClearCache ( mainOrderId int , coverType int ) {
if coverType == 1 {
2025-02-10 18:21:44 +08:00
keySpotStop := fmt . Sprintf ( rediskey . SpotStopLossList , global . EXCHANGE_BINANCE )
keySpotAddposition := fmt . Sprintf ( rediskey . SpotAddPositionList , global . EXCHANGE_BINANCE )
spotStopArray , _ := helper . DefaultRedis . GetAllList ( keySpotStop )
spotAddpositionArray , _ := helper . DefaultRedis . GetAllList ( keySpotAddposition )
2025-02-06 11:14:33 +08:00
var position AddPositionList
var stop dto . StopLossRedisList
for _ , item := range spotAddpositionArray {
if err := sonic . Unmarshal ( [ ] byte ( item ) , & position ) ; err != nil {
log . Error ( "MainClosePositionClearCache Unmarshal err:" , err )
}
if position . Pid == mainOrderId {
2025-02-10 18:21:44 +08:00
helper . DefaultRedis . LRem ( keySpotAddposition , item )
2025-02-06 11:14:33 +08:00
}
}
for _ , item := range spotStopArray {
if err := sonic . Unmarshal ( [ ] byte ( item ) , & stop ) ; err != nil {
log . Error ( "MainClosePositionClearCache Unmarshal err:" , err )
}
if stop . PId == mainOrderId {
2025-02-10 18:21:44 +08:00
helper . DefaultRedis . LRem ( keySpotStop , item )
2025-02-06 11:14:33 +08:00
}
}
} else {
2025-02-10 18:21:44 +08:00
keyFutStop := fmt . Sprintf ( rediskey . FuturesAddPositionList , global . EXCHANGE_BINANCE )
keyFutAddposition := fmt . Sprintf ( rediskey . FuturesStopLossList , global . EXCHANGE_BINANCE )
futAddpositionArray , _ := helper . DefaultRedis . GetAllList ( keyFutStop )
futStopArray , _ := helper . DefaultRedis . GetAllList ( keyFutAddposition )
2025-02-06 11:14:33 +08:00
var position AddPositionList
var stop dto . StopLossRedisList
for _ , item := range futAddpositionArray {
if err := sonic . Unmarshal ( [ ] byte ( item ) , & position ) ; err != nil {
log . Error ( "MainClosePositionClearCache Unmarshal err:" , err )
}
if position . Pid == mainOrderId {
2025-02-10 18:21:44 +08:00
helper . DefaultRedis . LRem ( keyFutAddposition , item )
2025-02-06 11:14:33 +08:00
}
}
for _ , item := range futStopArray {
if err := sonic . Unmarshal ( [ ] byte ( item ) , & stop ) ; err != nil {
log . Error ( "MainClosePositionClearCache Unmarshal err:" , err )
}
if stop . PId == mainOrderId {
2025-02-10 18:21:44 +08:00
helper . DefaultRedis . LRem ( keyFutStop , item )
2025-02-06 11:14:33 +08:00
}
}
}
}
2025-02-06 18:03:09 +08:00
// 获取数据库连接
func GetDBConnection ( ) * gorm . DB {
dbs := sdk . Runtime . GetDb ( )
for _ , db := range dbs {
return db
}
return nil
}
// 查询订单
func getPreOrder ( db * gorm . DB , orderSn interface { } ) ( * DbModels . LinePreOrder , error ) {
preOrder := & DbModels . LinePreOrder { }
if err := db . Model ( preOrder ) . Where ( "order_sn = ?" , orderSn ) . First ( preOrder ) . Error ; err != nil {
return nil , err
}
return preOrder , nil
}