This commit is contained in:
2025-02-06 11:14:33 +08:00
commit 07847a2d9e
535 changed files with 65131 additions and 0 deletions

View File

@ -0,0 +1,331 @@
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/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)
}
}
}
}