Files
exchange_go/services/binanceservice/commonservice.go
2025-02-06 18:03:09 +08:00

351 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"
"github.com/go-admin-team/go-admin-core/sdk"
"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 {
spotStopArray, _ := helper.DefaultRedis.GetAllList(rediskey.SpotStopLossList)
spotAddpositionArray, _ := helper.DefaultRedis.GetAllList(rediskey.SpotAddPositionList)
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 {
helper.DefaultRedis.LRem(rediskey.SpotAddPositionList, item)
}
}
for _, item := range spotStopArray {
if err := sonic.Unmarshal([]byte(item), &stop); err != nil {
log.Error("MainClosePositionClearCache Unmarshal err:", err)
}
if stop.PId == mainOrderId {
helper.DefaultRedis.LRem(rediskey.SpotStopLossList, item)
}
}
} else {
futAddpositionArray, _ := helper.DefaultRedis.GetAllList(rediskey.FuturesAddPositionList)
futStopArray, _ := helper.DefaultRedis.GetAllList(rediskey.FuturesStopLossList)
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 {
helper.DefaultRedis.LRem(rediskey.FuturesAddPositionList, item)
}
}
for _, item := range futStopArray {
if err := sonic.Unmarshal([]byte(item), &stop); err != nil {
log.Error("MainClosePositionClearCache Unmarshal err:", err)
}
if stop.PId == mainOrderId {
helper.DefaultRedis.LRem(rediskey.FuturesStopLossList, item)
}
}
}
}
// 获取数据库连接
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
}