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" "go-admin/models/positiondto" "go-admin/pkg/utility" "go-admin/services/commonservice" "go-admin/services/positionservice" "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) db := commonservice.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 } // 主单平仓删除缓存 // mainOrderId 主单id // symbolType 1现货 2合约 func MainClosePositionClearCache(mainId int, symbolType int) { if symbolType == 1 { 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) var position positiondto.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.MainId == mainId { helper.DefaultRedis.LRem(keySpotAddposition, item) } } for _, item := range spotStopArray { if err := sonic.Unmarshal([]byte(item), &stop); err != nil { log.Error("MainClosePositionClearCache Unmarshal err:", err) } if stop.MainId == mainId { helper.DefaultRedis.LRem(keySpotStop, item) } } } else { 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) var position positiondto.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.MainId == mainId { helper.DefaultRedis.LRem(keyFutAddposition, item) } } for _, item := range futStopArray { if err := sonic.Unmarshal([]byte(item), &stop); err != nil { log.Error("MainClosePositionClearCache Unmarshal err:", err) } if stop.MainId == mainId { helper.DefaultRedis.LRem(keyFutStop, item) } } } } // 查询订单 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 } // 取消主单下的止盈止损 // 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 } // 保存仓位信息 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), // Side: preOrder.Site, Quantity: utility.StrToDecimal(preOrder.Num), } 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" } //减仓单 数量为负 if preOrder.OrderType != 0 { if preOrder.Site == "BUY" { positionReq.Side = "SELL" } else { positionReq.Side = "BUY" } positionReq.Quantity = positionReq.Quantity.Mul(decimal.NewFromInt(-1)) } else { positionReq.Side = preOrder.Site } 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!=?", apiId, symbol, symbolType, side, exchangeType, newId). Select("id", "main_id", "order_sn").Find(&mainOrders).Error; err != nil { return nil, err } return mainOrders, nil } // 获取需要取消的订单号 func GetOpenOrderSns(db *gorm.DB, mainIds []int) ([]string, error) { 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 }