Files
exchange_go/services/binanceservice/binancerest.go

714 lines
21 KiB
Go
Raw Normal View History

2025-02-06 11:14:33 +08:00
package binanceservice
import (
"context"
"errors"
"fmt"
DbModels "go-admin/app/admin/models"
"go-admin/app/admin/service/dto"
"go-admin/common/const/rediskey"
commondto "go-admin/common/dto"
"go-admin/common/global"
"go-admin/common/helper"
"go-admin/models"
"go-admin/models/spot"
"go-admin/pkg/httputils"
"go-admin/pkg/utility"
"strings"
"time"
"github.com/shopspring/decimal"
"gorm.io/gorm"
"github.com/bytedance/sonic"
log "github.com/go-admin-team/go-admin-core/logger"
)
const (
binanceRestApi = "https://api.binance.com"
)
var ErrorMaps = map[float64]string{
-2021: "订单已拒绝。请调整触发价并重新下订单。 对于买入/做多,止盈订单触发价应低于市场价,止损订单的触发价应高于市场价。卖出/做空则与之相反",
-4164: "下单失败。少于最小下单金额",
-4061: "持仓方向需要设置为单向持仓。",
-2019: "保证金不足",
-1111: "金额设置错误。精度错误",
-1021: "请求的时间戳在recvWindow之外",
-2011: "该交易对没有订单",
-2010: "账户余额不足",
}
type SpotRestApi struct {
}
/*
获取 现货交易-规范信息
- return @info 规范信息
- return @err 错误信息
*/
func (e SpotRestApi) GetExchangeInfo() (symbols []spot.Symbol, err error) {
url := fmt.Sprintf("%s%s?permissions=SPOT", binanceRestApi, "/api/v3/exchangeInfo")
mapData, err := httputils.NewHttpRequestWithFasthttp("GET", url, "", map[string]string{})
if err != nil {
return
}
if len(mapData) == 0 {
err = errors.New("获取现货交易-规范信息数量为空")
return
}
var info spot.ExchangeInfo
err = sonic.Unmarshal(mapData, &info)
if err == nil {
return info.Symbols, nil
}
return
}
/*
获取现货24h行情变更
*/
func (e SpotRestApi) GetSpotTicker24h(tradeSet *map[string]models.TradeSet) (deleteSymbols []string, err error) {
tickerApi := fmt.Sprintf("%s%s", binanceRestApi, "/api/v3/ticker/24hr")
mapData, err := httputils.NewHttpRequestWithFasthttp("GET", tickerApi, "", map[string]string{})
if err != nil {
return []string{}, err
}
deleteSymbols = make([]string, 0)
if len(mapData) == 0 {
return deleteSymbols, errors.New("获取交易对失败,或数量为空")
}
tickers := make([]spot.SpotTicker24h, 0)
err = sonic.Unmarshal([]byte(mapData), &tickers)
if err != nil {
log.Error("反序列化json失败", err)
}
for _, item := range tickers {
2025-02-08 14:05:57 +08:00
key := fmt.Sprintf(global.TICKER_SPOT, global.EXCHANGE_BINANCE, item.Symbol)
2025-02-06 11:14:33 +08:00
symbol, exits := (*tradeSet)[item.Symbol]
if !exits {
helper.DefaultRedis.DeleteString(key)
continue
}
symbol.OpenPrice = utility.StringAsFloat(item.OpenPrice)
symbol.PriceChange = utility.StringAsFloat(item.PriceChangePercent)
symbol.LowPrice = item.LowPrice
symbol.HighPrice = item.HighPrice
symbol.Volume = item.Volume
symbol.QuoteVolume = item.QuoteVolume
symbol.LastPrice = item.LastPrice
val, err := sonic.Marshal(symbol)
if !strings.HasSuffix(item.Symbol, symbol.Currency) || item.Count <= 0 || utility.StringToFloat64(item.QuoteVolume) <= 0 {
helper.DefaultRedis.DeleteString(key)
deleteSymbols = append(deleteSymbols, item.Symbol)
continue
}
if err != nil {
log.Error("序列化失败", item.Symbol)
continue
}
err = helper.DefaultRedis.SetString(key, string(val))
if err != nil {
log.Error("缓存交易对失败|", item.Symbol, err)
}
helper.DefaultRedis.AddSortSet(global.COIN_PRICE_CHANGE, symbol.PriceChange, symbol.Coin)
}
return deleteSymbols, nil
}
/*
获取单个交易对24h行情
- @symbol 交易对
- @data 结果
*/
func (e SpotRestApi) GetSpotTicker24(symbol string, data *models.Ticker24, tradeSet *models.TradeSet) error {
2025-02-08 14:05:57 +08:00
key := fmt.Sprintf(global.TICKER_SPOT, global.EXCHANGE_BINANCE, symbol)
2025-02-06 11:14:33 +08:00
val, err := helper.DefaultRedis.GetString(key)
if err != nil {
return err
}
err = sonic.Unmarshal([]byte(val), tradeSet)
if err != nil {
return err
}
if tradeSet.Coin != "" {
data.HighPrice = tradeSet.HighPrice
data.ChangePercent = fmt.Sprintf("%g", tradeSet.PriceChange)
data.LastPrice = tradeSet.LastPrice
data.LowPrice = tradeSet.LowPrice
data.OpenPrice = fmt.Sprintf("%g", tradeSet.OpenPrice)
data.QuoteVolume = tradeSet.QuoteVolume
data.Volume = tradeSet.Volume
}
return nil
}
type Ticker struct {
Symbol string `json:"symbol"`
Price string `json:"price"`
}
func (e SpotRestApi) Ticker() {
tickerApi := fmt.Sprintf("%s%s", binanceRestApi, "/api/v3/ticker/price")
mapData, _ := httputils.NewHttpRequestWithFasthttp("GET", tickerApi, "", map[string]string{})
//sonic.Unmarshal(mapData, &tickerData)
helper.DefaultRedis.SetString(rediskey.SpotSymbolTicker, string(mapData))
}
// OrderPlace 现货下单
func (e SpotRestApi) OrderPlace(orm *gorm.DB, params OrderPlacementService) error {
if orm == nil {
return errors.New("数据库实例为空")
}
err2 := params.CheckParams()
if err2 != nil {
return err2
}
paramsMaps := map[string]string{
"symbol": params.Symbol,
"side": params.Side,
"quantity": params.Quantity.String(),
"type": params.Type,
"newClientOrderId": params.NewClientOrderId,
}
if strings.ToUpper(params.Type) != "MARKET" { //市价
paramsMaps["price"] = params.Price.String()
paramsMaps["timeInForce"] = "GTC"
if strings.ToUpper(params.Type) == "TAKE_PROFIT_LIMIT" || strings.ToUpper(params.Type) == "STOP_LOSS_LIMIT" {
paramsMaps["stopPrice"] = params.StopPrice.String()
}
}
var apiUserInfo DbModels.LineApiUser
err := orm.Model(&DbModels.LineApiUser{}).Where("id = ?", params.ApiId).Find(&apiUserInfo).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
log.Errorf("api用户出错 err: %+v", err)
return err
}
client := GetClient(&apiUserInfo)
resp, _, err := client.SendSpotAuth("/api/v3/order", "POST", paramsMaps)
if err != nil {
dataMap := make(map[string]interface{})
if err.Error() != "" {
if err := sonic.Unmarshal([]byte(err.Error()), &dataMap); err != nil {
return fmt.Errorf("api_id:%d 交易对:%s 下订单失败:%+v", apiUserInfo.Id, params.Symbol, err.Error())
}
}
code, ok := dataMap["code"]
if ok {
errContent := ErrorMaps[code.(float64)]
paramsVal, _ := sonic.MarshalString(paramsMaps)
log.Error("api_id:", utility.IntToString(apiUserInfo.Id), " 交易对:", params.Symbol, " 下单参数:", paramsVal)
if errContent == "" {
errContent, _ = dataMap["msg"].(string)
}
return fmt.Errorf("api_id:%d 交易对:%s 下订单失败:%s", apiUserInfo.Id, params.Symbol, errContent)
}
if strings.Contains(err.Error(), "Unknown order sent.") {
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%+v", apiUserInfo.Id, params.Symbol, ErrorMaps[-2011])
}
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%v", apiUserInfo.Id, params.Symbol, err)
}
var dataMap map[string]interface{}
if err := sonic.Unmarshal(resp, &dataMap); err != nil {
return fmt.Errorf("api_id:%d 交易对:%s 下单失败:%+v", apiUserInfo.Id, params.Symbol, err.Error())
}
return nil
}
// CancelOpenOrders 撤销单一交易对下所有挂单 包括了来自订单列表的挂单
func (e SpotRestApi) CancelOpenOrders(orm *gorm.DB, req CancelOpenOrdersReq) error {
if orm == nil {
return errors.New("数据库实例为空")
}
err := req.CheckParams()
if err != nil {
return err
}
params := map[string]string{
"symbol": req.Symbol,
}
var apiUserInfo DbModels.LineApiUser
err = orm.Model(&DbModels.LineApiUser{}).Where("id = ?", req.ApiId).Find(&apiUserInfo).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("api_id:%d 交易对:%s api用户出错:%+v", apiUserInfo.Id, req.Symbol, err)
}
var client *helper.BinanceClient
if apiUserInfo.UserPass == "" {
client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress)
} else {
client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress)
}
_, _, err = client.SendSpotAuth("/api/v3/openOrders", "DELETE", params)
if err != nil {
dataMap := make(map[string]interface{})
if err.Error() != "" {
if err := sonic.Unmarshal([]byte(err.Error()), &dataMap); err != nil {
return fmt.Errorf("api_id:%d 交易对:%s 撤销订单失败:%+v", apiUserInfo.Id, req.Symbol, err.Error())
}
}
code, ok := dataMap["code"]
if ok {
return fmt.Errorf("api_id:%d 交易对:%s 撤销订单失败:%s", apiUserInfo.Id, req.Symbol, ErrorMaps[code.(float64)])
}
if strings.Contains(err.Error(), "Unknown order sent.") {
return fmt.Errorf("api_id:%d 交易对:%s 撤销订单失败:%+v", apiUserInfo.Id, req.Symbol, ErrorMaps[-2011])
}
return fmt.Errorf("api_id:%d 交易对:%s 撤销订单失败:%+v", apiUserInfo.Id, req.Symbol, err.Error())
}
return nil
}
// CancelOpenOrderByOrderSn 通过单一订单号取消委托
func (e SpotRestApi) CancelOpenOrderByOrderSn(apiUserInfo DbModels.LineApiUser, symbol string, newClientOrderId string) error {
params := map[string]string{
"symbol": symbol,
"origClientOrderId": newClientOrderId,
"recvWindow": "10000",
}
var client *helper.BinanceClient
if apiUserInfo.UserPass == "" {
client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress)
} else {
client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress)
}
2025-02-08 14:05:57 +08:00
_, code, err := client.SendSpotAuth("/api/v3/order", "DELETE", params)
2025-02-06 11:14:33 +08:00
if err != nil || code != 200 {
log.Error("取消现货委托失败 参数:", params)
log.Error("取消现货委托失败 code:", code)
log.Error("取消现货委托失败 err:", err)
dataMap := make(map[string]interface{})
if err.Error() != "" {
if err := sonic.Unmarshal([]byte(err.Error()), &dataMap); err != nil {
return fmt.Errorf("api_id:%d 交易对:%s 撤销订单失败:%+v", apiUserInfo.Id, symbol, err.Error())
}
}
code, ok := dataMap["code"]
if ok {
return fmt.Errorf("api_id:%d 交易对:%s 撤销订单失败:%s", apiUserInfo.Id, symbol, ErrorMaps[code.(float64)])
}
if strings.Contains(err.Error(), "Unknown order sent.") {
return fmt.Errorf("api_id:%d 交易对:%s 撤销订单失败:%+v", apiUserInfo.Id, symbol, ErrorMaps[-2011])
}
return fmt.Errorf("api_id:%d 交易对:%s 撤销订单失败:%+v", apiUserInfo.Id, symbol, err.Error())
}
return nil
}
// ClosePosition 平仓
// symbol 交易对
// orderSn 平仓单号
// quantity 平仓数量
// side 原始仓位方向
// apiUserInfo 用户信息
// orderType 平仓类型 限价LIMIT 市价()
func (e SpotRestApi) ClosePosition(symbol string, orderSn string, quantity decimal.Decimal, side string,
apiUserInfo DbModels.LineApiUser, orderType string, rate string, price decimal.Decimal) error {
endpoint := "/api/v3/order "
params := map[string]string{
"symbol": symbol,
"type": orderType,
"quantity": quantity.String(),
"newClientOrderId": orderSn,
}
if side == "SELL" {
params["side"] = "BUY"
} else {
params["side"] = "SELL"
}
if orderType == "LIMIT" {
2025-02-08 14:05:57 +08:00
key := fmt.Sprintf(global.TICKER_SPOT, global.EXCHANGE_BINANCE, symbol)
2025-02-06 11:14:33 +08:00
tradeSet, _ := helper.GetObjString[models.TradeSet](helper.DefaultRedis, key)
rateFloat, _ := decimal.NewFromString(rate)
if rateFloat.GreaterThan(decimal.Zero) {
if side == "SELL" { //仓位是空 平空的话
price = price.Mul(decimal.NewFromInt(1).Add(rateFloat)).Truncate(int32(tradeSet.PriceDigit))
} else {
price = price.Mul(decimal.NewFromInt(1).Sub(rateFloat)).Truncate(int32(tradeSet.PriceDigit))
}
params["price"] = price.String()
}
}
params["timeInForce"] = "GTC"
client := GetClient(&apiUserInfo)
resp, _, err := client.SendFuturesRequestAuth(endpoint, "POST", params)
if err != nil {
var dataMap map[string]interface{}
if err2 := sonic.Unmarshal([]byte(err.Error()), &dataMap); err2 != nil {
return fmt.Errorf("api_id:%d 交易对:%s 平仓出错:%s", apiUserInfo.Id, symbol, err.Error())
}
code, ok := dataMap["code"]
if ok {
errContent := FutErrorMaps[code.(float64)]
if errContent == "" {
errContent = err.Error()
}
return fmt.Errorf("api_id:%d 交易对:%s 平仓出错:%s", apiUserInfo.Id, symbol, errContent)
}
}
var orderResp FutOrderResp
err = sonic.Unmarshal(resp, &orderResp)
if err != nil {
return fmt.Errorf("api_id:%d 交易对:%s 平仓出错:%s", apiUserInfo.Id, symbol, err.Error())
}
if orderResp.Symbol == "" {
return fmt.Errorf("api_id:%d 交易对:%s 平仓出错:未找到订单信息", apiUserInfo.Id, symbol)
}
return nil
}
func GetClient(apiUserInfo *DbModels.LineApiUser) *helper.BinanceClient {
var client *helper.BinanceClient
if apiUserInfo.UserPass == "" {
client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "", apiUserInfo.IpAddress)
} else {
client, _ = helper.NewBinanceClient(apiUserInfo.ApiKey, apiUserInfo.ApiSecret, "socks5", apiUserInfo.UserPass+"@"+apiUserInfo.IpAddress)
}
return client
}
/*
判断是否触发
*/
func JudgeSpotPrice(trade models.TradeSet) {
2025-02-06 18:03:09 +08:00
key := fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE)
preOrderVal, _ := helper.DefaultRedis.GetAllList(key)
2025-02-06 11:14:33 +08:00
db := GetDBConnection()
if len(preOrderVal) == 0 {
return
}
spotApi := SpotRestApi{}
for _, item := range preOrderVal {
preOrder := dto.PreOrderRedisList{}
if err := sonic.Unmarshal([]byte(item), &preOrder); err != nil {
log.Error("反序列化失败")
continue
}
if preOrder.Symbol == trade.Coin+trade.Currency {
orderPrice, _ := decimal.NewFromString(preOrder.Price)
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
//买入
if strings.ToUpper(preOrder.Site) == "BUY" && orderPrice.Cmp(tradePrice) >= 0 && orderPrice.Cmp(decimal.Zero) > 0 && tradePrice.Cmp(decimal.Zero) > 0 {
SpotOrderLock(db, &preOrder, item, spotApi)
}
}
}
}
// 分布式锁下单
// v 预下单信息
// item 预下单源文本
func SpotOrderLock(db *gorm.DB, v *dto.PreOrderRedisList, item string, spotApi SpotRestApi) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, v.ApiId, v.Symbol), 20, 5, 100*time.Millisecond)
if ok, err := lock.AcquireWait(context.Background()); err != nil {
log.Error("获取锁失败", err)
return
} else if ok {
defer lock.Release()
2025-02-06 18:03:09 +08:00
key := fmt.Sprintf(rediskey.PreSpotOrderList, global.EXCHANGE_BINANCE)
2025-02-06 11:14:33 +08:00
preOrder := DbModels.LinePreOrder{}
if err := db.Where("id = ?", v.Id).First(&preOrder).Error; err != nil {
log.Error("获取预下单失败", err)
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Error("不存在待触发主单", item)
2025-02-06 18:03:09 +08:00
helper.DefaultRedis.LRem(key, item)
2025-02-06 11:14:33 +08:00
}
return
}
2025-02-06 18:03:09 +08:00
hasrecord, _ := helper.DefaultRedis.IsElementInList(key, item)
2025-02-06 11:14:33 +08:00
if !hasrecord {
log.Error("不存在待触发主单", item)
return
}
price, _ := decimal.NewFromString(v.Price)
num, _ := decimal.NewFromString(preOrder.Num)
params := OrderPlacementService{
ApiId: v.ApiId,
Symbol: v.Symbol,
Side: v.Site,
Type: preOrder.MainOrderType,
TimeInForce: "GTC",
Price: price,
Quantity: num,
NewClientOrderId: v.OrderSn,
}
preOrderVal, _ := sonic.MarshalString(&v)
if err := spotApi.OrderPlace(db, params); err != nil {
log.Error("下单失败", v.Symbol, " err:", err)
2025-02-06 18:03:09 +08:00
err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status =0", preOrder.Id).Updates(map[string]interface{}{"status": "2", "desc": err.Error()}).Error
2025-02-06 11:14:33 +08:00
if err != nil {
log.Error("下单失败后修改订单失败")
}
if preOrderVal != "" {
2025-02-06 18:03:09 +08:00
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
2025-02-06 11:14:33 +08:00
log.Error("删除redis 预下单失败:", err)
}
}
return
}
if preOrderVal != "" {
2025-02-06 18:03:09 +08:00
if _, err := helper.DefaultRedis.LRem(key, preOrderVal); err != nil {
2025-02-06 11:14:33 +08:00
log.Error("删除redis 预下单失败:", err)
}
}
if err := db.Model(&DbModels.LinePreOrder{}).Where("id =? AND status ='0'", preOrder.Id).Update("status", "1").Error; err != nil {
log.Error("更新预下单状态失败 ordersn:", v.OrderSn, " status:1")
}
return
} else {
log.Error("获取锁失败")
return
}
}
2025-02-08 14:05:57 +08:00
// 判断是否触发止损
func JudgeSpotStopLoss(trade models.TradeSet) {
key := fmt.Sprintf(rediskey.SpotStopLossList, global.EXCHANGE_BINANCE)
stopLossVal, _ := helper.DefaultRedis.GetAllList(key)
if len(stopLossVal) == 0 {
return
}
db := GetDBConnection()
spotApi := SpotRestApi{}
setting, err := GetSystemSetting(db)
if err != nil {
log.Error("获取系统设置失败")
return
}
tradeSet, err := GetTradeSet(trade.Coin+trade.Currency, 0)
if err != nil {
log.Error("获取交易设置失败")
return
}
for _, item := range stopLossVal {
stopOrder := dto.StopLossRedisList{}
if err := sonic.Unmarshal([]byte(item), &stopOrder); err != nil {
log.Error("反序列化失败")
continue
}
if stopOrder.Symbol == trade.Coin+trade.Currency {
orderPrice := stopOrder.Price
tradePrice, _ := decimal.NewFromString(trade.LastPrice)
//买入
if strings.ToUpper(stopOrder.Site) == "SELL" &&
orderPrice.Cmp(tradePrice) >= 0 &&
orderPrice.Cmp(decimal.Zero) > 0 &&
tradePrice.Cmp(decimal.Zero) > 0 {
SpotStopLossTrigger(db, stopOrder, spotApi, setting, tradeSet, key, item)
}
}
}
}
// 触发现货止损
func SpotStopLossTrigger(db *gorm.DB, stopOrder dto.StopLossRedisList, spotApi SpotRestApi, setting DbModels.LineSystemSetting, tradeSet models.TradeSet, key string, item string) {
lock := helper.NewRedisLock(fmt.Sprintf(rediskey.SpotTrigger, stopOrder.ApiId, stopOrder.Symbol), 20, 5, 100*time.Millisecond)
if ok, err := lock.AcquireWait(context.Background()); err != nil {
log.Error("获取锁失败", err)
return
} else if ok {
defer lock.Release()
takeOrder := DbModels.LinePreOrder{}
if err := db.Model(&DbModels.LinePreOrder{}).Where("pid =? AND order_type =1", stopOrder.PId).Find(&takeOrder).Error; err != nil {
log.Error("查询止盈单失败")
return
}
apiInfo, _ := GetApiInfo(takeOrder.ApiId)
if apiInfo.Id == 0 {
log.Error("现货止损 查询api用户不存在")
return
}
var err error
for x := 1; x <= 4; x++ {
err = spotApi.CancelOpenOrderByOrderSn(apiInfo, takeOrder.Symbol, takeOrder.OrderSn)
if err == nil {
break
}
}
if err != nil {
log.Error("现货止损撤单失败", err)
return
}
stopPreOrder, _ := GetOrderById(db, stopOrder.Id)
price := stopOrder.Price.Mul(decimal.NewFromInt(1).Sub(setting.StopLossPremium.Div(decimal.NewFromInt(100)))).Truncate(int32(tradeSet.PriceDigit))
num := utility.StrToDecimal(takeOrder.Num).Truncate(int32(tradeSet.AmountDigit))
params := OrderPlacementService{
ApiId: takeOrder.ApiId,
Side: takeOrder.Site,
Type: "LIMIT",
TimeInForce: "GTC",
Symbol: takeOrder.Symbol,
Price: price,
Quantity: num,
NewClientOrderId: stopPreOrder.OrderSn,
}
if err := spotApi.OrderPlace(db, params); err != nil {
log.Errorf("现货止损挂单失败 id%s err:%v", stopOrder.Id, err)
}
if _, err := helper.DefaultRedis.LRem(key, item); err != nil {
log.Errorf("现货止损 删除缓存失败 id:%v err:%v", stopOrder.Id, err)
}
} else {
log.Error("获取锁失败")
}
}
2025-02-06 11:14:33 +08:00
/*
获取api用户信息
*/
func GetApiInfo(apiId int) (DbModels.LineApiUser, error) {
api := DbModels.LineApiUser{}
key := fmt.Sprintf(rediskey.API_USER, apiId)
val, _ := helper.DefaultRedis.GetString(key)
if val != "" {
if err := sonic.UnmarshalString(val, &api); err == nil {
return api, nil
}
}
db := GetDBConnection()
if err := db.Model(&api).Where("id =?", apiId).First(&api).Error; err != nil {
return api, err
}
val, _ = sonic.MarshalString(&api)
if val != "" {
helper.DefaultRedis.SetString(key, val)
}
return api, nil
}
/*
根据A账户获取B账号信息
*/
func GetChildApiInfo(apiId int) (DbModels.LineApiUser, error) {
var api DbModels.LineApiUser
childApiId := 0
groups := GetApiGroups()
for _, item := range groups {
if item.ApiUserId == apiId {
childApiId = item.ChildApiUserId
break
}
}
if childApiId > 0 {
return GetApiInfo(childApiId)
}
return api, nil
}
func GetApiGroups() []commondto.ApiGroupDto {
apiGroups := make([]commondto.ApiGroupDto, 0)
apiGroupStr, _ := helper.DefaultRedis.GetAllKeysAndValues(rediskey.ApiGroupAll)
if len(apiGroupStr) == 0 {
return apiGroups
}
for _, item := range apiGroupStr {
apiGroup := commondto.ApiGroupDto{}
if err := sonic.UnmarshalString(item, &apiGroup); err != nil {
log.Error("groups 序列化失败", err)
continue
}
apiGroups = append(apiGroups, apiGroup)
}
return apiGroups
}
// GetSpotSymbolLastPrice 获取现货交易对最新价格
func (e SpotRestApi) GetSpotSymbolLastPrice(targetSymbol string) (lastPrice decimal.Decimal) {
tickerSymbol := helper.DefaultRedis.Get(rediskey.SpotSymbolTicker).Val()
tickerSymbolMaps := make([]dto.Ticker, 0)
sonic.Unmarshal([]byte(tickerSymbol), &tickerSymbolMaps)
//key := fmt.Sprintf("%s:%s", global.TICKER_SPOT, targetSymbol)
//tradeSet, _ := helper.GetObjString[models.TradeSet](helper.DefaultRedis, key)
for _, symbolMap := range tickerSymbolMaps {
if symbolMap.Symbol == strings.ToUpper(targetSymbol) {
lastPrice = utility.StringToDecimal(symbolMap.Price)
}
}
return lastPrice
}