2025-02-06 11:14:33 +08:00
package binanceservice
import (
"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"
2025-02-28 18:27:52 +08:00
"go-admin/models/positiondto"
2025-02-06 11:14:33 +08:00
"go-admin/pkg/utility"
2025-10-14 19:58:59 +08:00
"go-admin/services/commonservice"
2025-02-28 18:27:52 +08:00
"go-admin/services/positionservice"
2025-02-06 11:14:33 +08:00
"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"
"github.com/shopspring/decimal"
"gorm.io/gorm"
)
type AddPosition struct {
Db * gorm . DB
}
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 )
2025-10-14 19:58:59 +08:00
db := commonservice . GetDBConnection ( )
2025-02-06 11:14:33 +08:00
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
}
// 主单平仓删除缓存
// mainOrderId 主单id
2025-02-14 09:43:49 +08:00
// symbolType 1现货 2合约
func MainClosePositionClearCache ( mainId int , symbolType int ) {
if symbolType == 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-10-14 19:58:59 +08:00
var position positiondto . AddPositionList
2025-02-06 11:14:33 +08:00
var stop dto . StopLossRedisList
for _ , item := range spotAddpositionArray {
if err := sonic . Unmarshal ( [ ] byte ( item ) , & position ) ; err != nil {
log . Error ( "MainClosePositionClearCache Unmarshal err:" , err )
}
2025-02-14 09:43:49 +08:00
if position . MainId == mainId {
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 )
}
2025-02-14 09:43:49 +08:00
if stop . MainId == mainId {
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-10-14 19:58:59 +08:00
var position positiondto . AddPositionList
2025-02-06 11:14:33 +08:00
var stop dto . StopLossRedisList
for _ , item := range futAddpositionArray {
if err := sonic . Unmarshal ( [ ] byte ( item ) , & position ) ; err != nil {
log . Error ( "MainClosePositionClearCache Unmarshal err:" , err )
}
2025-02-14 09:43:49 +08:00
if position . MainId == mainId {
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 )
}
2025-02-14 09:43:49 +08:00
if stop . MainId == mainId {
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 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
}
2025-02-14 09:43:49 +08:00
// 取消主单下的止盈止损
// mainId 主单id
// symbolType 1现货 2合约
func cancelSymbolTakeAndStop ( db * gorm . DB , mainId int , symbolType int ) error {
orders , err := GetSymbolTakeAndStop ( db , mainId , symbolType )
errStr := ""
if err != nil {
return err
}
for _ , order := range orders {
apiUserInfo , _ := GetApiInfo ( order . ApiId )
if apiUserInfo . Id == 0 {
logger . Errorf ( "cancelSymbolTakeAndStop 查询api用户失败 apiid:%v ordersn:%s" , order . ApiId , order . OrderSn )
}
switch {
case order . OrderType == 1 && symbolType == 1 :
err = CancelOpenOrderByOrderSnLoop ( apiUserInfo , order . Symbol , order . OrderSn )
if err != nil {
errStr += fmt . Sprintf ( "取消止盈失败,订单号:%s,错误:%v " , order . OrderSn , err )
}
case order . OrderType == 2 && symbolType == 1 :
key := fmt . Sprintf ( rediskey . SpotStopLossList , global . EXCHANGE_BINANCE )
stoplosss , _ := helper . DefaultRedis . GetAllList ( key )
stop := dto . StopLossRedisList { }
for _ , item := range stoplosss {
sonic . Unmarshal ( [ ] byte ( item ) , & stop )
if stop . MainId == order . MainId {
_ , err2 := helper . DefaultRedis . LRem ( key , item )
if err2 != nil {
logger . Errorf ( "移除止损缓存失败 err:%v" , err2 )
}
}
}
case order . OrderType == 1 && symbolType == 2 :
err = CancelFutOrderByOrderSnLoop ( apiUserInfo , order . Symbol , order . OrderSn )
if err != nil {
errStr += fmt . Sprintf ( "取消止盈失败,订单号:%s,错误:%v " , order . OrderSn , err )
}
case order . OrderType == 2 && symbolType == 2 :
err = CancelFutOrderByOrderSnLoop ( apiUserInfo , order . Symbol , order . OrderSn )
if err != nil {
errStr += fmt . Sprintf ( "取消止损失败,订单号:%s,错误:%v " , order . OrderSn , err )
}
}
}
return nil
}
2025-02-28 18:27:52 +08:00
// 保存仓位信息
func savePosition ( db * gorm . DB , preOrder * DbModels . LinePreOrder ) positiondto . PositionDto {
positionManage := positionservice . BinancePositionManagement { }
positionManage . Orm = db
positionReq := positiondto . PositionAddReq {
ApiId : preOrder . ApiId ,
SymbolType : preOrder . SymbolType ,
Symbol : preOrder . Symbol ,
Price : utility . StrToDecimal ( preOrder . Price ) ,
2025-03-03 17:35:41 +08:00
// Side: preOrder.Site,
Quantity : utility . StrToDecimal ( preOrder . Num ) ,
2025-02-28 18:27:52 +08:00
}
switch {
case preOrder . OrderType == 0 && preOrder . Site == "BUY" ,
preOrder . OrderType != 0 && preOrder . Site == "SELL" :
positionReq . PositionSide = "LONG"
case preOrder . OrderType == 0 && preOrder . Site == "SELL" ,
preOrder . OrderType != 0 && preOrder . Site == "BUY" :
positionReq . PositionSide = "SHORT"
}
//减仓单 数量为负
2025-03-03 17:35:41 +08:00
if preOrder . OrderType != 0 {
if preOrder . Site == "BUY" {
positionReq . Side = "SELL"
} else {
positionReq . Side = "BUY"
}
2025-02-28 18:27:52 +08:00
positionReq . Quantity = positionReq . Quantity . Mul ( decimal . NewFromInt ( - 1 ) )
2025-03-03 17:35:41 +08:00
} else {
positionReq . Side = preOrder . Site
2025-02-28 18:27:52 +08:00
}
positionData , err := positionManage . SavePosition ( & positionReq , global . EXCHANGE_BINANCE )
if err != nil {
logger . Error ( "保存持仓信息失败, 主单号:%s, 错误信息:%v" , preOrder . OrderSn , err )
}
return positionData
}
// 获取已开仓的主单id
func getOpenPositionMainOrderId ( db * gorm . DB , newId , apiId , symbolType int , exchangeType , symbol , side string ) ( [ ] DbModels . LinePreOrder , error ) {
mainOrders := make ( [ ] DbModels . LinePreOrder , 0 )
if err := db . Model ( & DbModels . LinePreOrder { } ) .
2025-03-06 18:16:35 +08:00
Where ( "api_id =? AND status>4 AND order_type =0 AND status<7 AND symbol=? AND symbol_type =? AND site= ? AND exchange_type=? AND id!=?" ,
2025-02-28 18:27:52 +08:00
apiId , symbol , symbolType , side , exchangeType , newId ) .
2025-03-03 17:35:41 +08:00
Select ( "id" , "main_id" , "order_sn" ) . Find ( & mainOrders ) . Error ; err != nil {
2025-02-28 18:27:52 +08:00
return nil , err
}
return mainOrders , nil
}
// 获取需要取消的订单号
2025-03-10 11:13:08 +08:00
func GetOpenOrderSns ( db * gorm . DB , mainIds [ ] int ) ( [ ] string , error ) {
2025-02-28 18:27:52 +08:00
result := [ ] string { }
//委托中的订单
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "main_id IN ? AND status=5" , mainIds ) . Select ( "order_sn" ) . Find ( & result ) . Error ; err != nil {
return nil , err
}
return result , nil
}