1
This commit is contained in:
331
services/binanceservice/commonservice.go
Normal file
331
services/binanceservice/commonservice.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user