Files
exchange_go/services/binanceservice/commonservice.go

409 lines
13 KiB
Go
Raw Normal View History

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{}).
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
}