1、暂存
This commit is contained in:
417
services/bitgetservice/bitget_rest.go
Normal file
417
services/bitgetservice/bitget_rest.go
Normal file
@ -0,0 +1,417 @@
|
||||
package bitgetservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go-admin/common/global"
|
||||
"go-admin/common/helper"
|
||||
"go-admin/config"
|
||||
"go-admin/models"
|
||||
"go-admin/pkg/utility"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
log "github.com/go-admin-team/go-admin-core/logger"
|
||||
)
|
||||
|
||||
// BitgetRestApi Bitget REST API服务
|
||||
type BitgetRestApi struct {
|
||||
httpClient *helper.BitgetClient
|
||||
}
|
||||
|
||||
// NewBitgetRestApi 创建新的Bitget REST API实例
|
||||
func NewBitgetRestApi() *BitgetRestApi {
|
||||
// 从配置中获取代理设置
|
||||
proxyUrl := config.ExtConfig.ProxyUrl
|
||||
|
||||
// 使用代理配置创建HTTP客户端
|
||||
httpClient, err := helper.NewBitgetClient("", "", "", proxyUrl)
|
||||
if err != nil {
|
||||
log.Error("创建Bitget HTTP客户端失败", err)
|
||||
// 如果代理创建失败,使用无代理的客户端
|
||||
httpClient, _ = helper.NewBitgetClient("", "", "", "")
|
||||
}
|
||||
|
||||
return &BitgetRestApi{
|
||||
httpClient: httpClient,
|
||||
}
|
||||
}
|
||||
|
||||
// NewBitgetSimpleClient 创建简单的Bitget客户端(兼容性函数)
|
||||
func NewBitgetSimpleClient() *BitgetClient {
|
||||
// 从配置中获取代理设置
|
||||
proxyUrl := config.ExtConfig.ProxyUrl
|
||||
|
||||
client, err := NewBitgetClient("", "", "", proxyUrl)
|
||||
if err != nil {
|
||||
log.Error("创建Bitget简单客户端失败", err)
|
||||
// 如果代理创建失败,使用无代理的客户端
|
||||
client, _ = NewBitgetClient("", "", "", "")
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
// bitgetSymbolResponse 通用的符号响应结构体
|
||||
type bitgetSymbolResponse struct {
|
||||
Code string `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data []struct {
|
||||
Symbol string `json:"symbol"`
|
||||
BaseCoin string `json:"baseCoin"`
|
||||
QuoteCoin string `json:"quoteCoin"`
|
||||
Status string `json:"status"`
|
||||
MinTradeAmount string `json:"minTradeAmount"`
|
||||
MaxTradeAmount string `json:"maxTradeAmount"`
|
||||
TakerFeeRate string `json:"takerFeeRate"`
|
||||
MakerFeeRate string `json:"makerFeeRate"`
|
||||
PricePrecision string `json:"pricePrecision"`
|
||||
QuantityPrecision string `json:"quantityPrecision"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// getSymbolsFromAPI 通用的符号获取方法
|
||||
// symbolType: "spot" 现货, "futures" 合约
|
||||
func (api *BitgetRestApi) getSymbolsFromAPI(symbolType string) ([]BitgetSymbol, error) {
|
||||
var url string
|
||||
var errorPrefix string
|
||||
|
||||
switch symbolType {
|
||||
case "spot":
|
||||
url = "/api/v2/spot/public/symbols"
|
||||
errorPrefix = "现货"
|
||||
case "futures":
|
||||
url = "/api/v2/mix/market/contracts?productType=USDT-FUTURES"
|
||||
errorPrefix = "合约"
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的符号类型: %s", symbolType)
|
||||
}
|
||||
|
||||
// 发送API请求
|
||||
resp, _, err := api.httpClient.SendSpotRequestAuth(url, "GET", map[string]interface{}{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取bitget %s交易所信息失败: %v", errorPrefix, err)
|
||||
}
|
||||
|
||||
// 解析响应数据
|
||||
var response bitgetSymbolResponse
|
||||
if err := sonic.Unmarshal(resp, &response); err != nil {
|
||||
return nil, fmt.Errorf("解析bitget %s交易所信息失败: %v", errorPrefix, err)
|
||||
}
|
||||
|
||||
// 检查API响应状态
|
||||
if response.Code != "00000" {
|
||||
return nil, fmt.Errorf("bitget API错误: %s", response.Msg)
|
||||
}
|
||||
|
||||
// 转换数据格式
|
||||
return api.convertToSymbols(response.Data), nil
|
||||
}
|
||||
|
||||
// convertToSymbols 将API响应数据转换为BitgetSymbol切片
|
||||
func (api *BitgetRestApi) convertToSymbols(data []struct {
|
||||
Symbol string `json:"symbol"`
|
||||
BaseCoin string `json:"baseCoin"`
|
||||
QuoteCoin string `json:"quoteCoin"`
|
||||
Status string `json:"status"`
|
||||
MinTradeAmount string `json:"minTradeAmount"`
|
||||
MaxTradeAmount string `json:"maxTradeAmount"`
|
||||
TakerFeeRate string `json:"takerFeeRate"`
|
||||
MakerFeeRate string `json:"makerFeeRate"`
|
||||
PricePrecision string `json:"pricePrecision"`
|
||||
QuantityPrecision string `json:"quantityPrecision"`
|
||||
}) []BitgetSymbol {
|
||||
symbols := make([]BitgetSymbol, 0, len(data))
|
||||
|
||||
for _, item := range data {
|
||||
if item.Status == "online" {
|
||||
priceDigit, _ := strconv.Atoi(item.PricePrecision)
|
||||
quantileDigit, _ := strconv.Atoi(item.QuantityPrecision)
|
||||
|
||||
symbol := BitgetSymbol{
|
||||
Symbol: item.Symbol,
|
||||
BaseAsset: item.BaseCoin,
|
||||
QuoteAsset: item.QuoteCoin,
|
||||
Status: item.Status,
|
||||
MinQty: item.MinTradeAmount,
|
||||
MaxQty: item.MaxTradeAmount,
|
||||
PriceDigit: priceDigit,
|
||||
QuantityDigit: quantileDigit,
|
||||
}
|
||||
symbols = append(symbols, symbol)
|
||||
}
|
||||
}
|
||||
|
||||
return symbols
|
||||
}
|
||||
|
||||
// GetSpotSymbols 获取现货交易对信息
|
||||
func (api *BitgetRestApi) GetSpotSymbols() ([]BitgetSymbol, error) {
|
||||
return api.getSymbolsFromAPI("spot")
|
||||
}
|
||||
|
||||
// GetFutSymbols 获取合约交易对信息
|
||||
func (api *BitgetRestApi) GetFutSymbols() ([]BitgetSymbol, error) {
|
||||
return api.getSymbolsFromAPI("futures")
|
||||
}
|
||||
|
||||
// symbolConfig 符号配置结构体
|
||||
type symbolConfig struct {
|
||||
symbolType string
|
||||
quoteAssetSymbols []string
|
||||
}
|
||||
|
||||
// buildTradeSetFromSymbols 通用的TradeSet构建方法
|
||||
func buildTradeSetFromSymbols(symbols []BitgetSymbol, config symbolConfig) (map[string]models.TradeSet, []string) {
|
||||
tradeSets := make(map[string]models.TradeSet, len(symbols))
|
||||
deleteSymbols := make([]string, 0)
|
||||
|
||||
for _, item := range symbols {
|
||||
if utility.ContainsStr(config.quoteAssetSymbols, item.QuoteAsset) && item.Status == "online" {
|
||||
tradeSet := models.TradeSet{
|
||||
Coin: item.BaseAsset,
|
||||
Currency: item.QuoteAsset,
|
||||
PriceDigit: item.PriceDigit,
|
||||
AmountDigit: item.QuantityDigit,
|
||||
MinQty: utility.StringAsFloat(item.MinQty),
|
||||
}
|
||||
tradeSets[item.Symbol] = tradeSet
|
||||
}
|
||||
}
|
||||
|
||||
return tradeSets, deleteSymbols
|
||||
}
|
||||
|
||||
// GetSpotSymbols 获取现货交易对并构建TradeSet映射
|
||||
func GetSpotSymbols() (map[string]models.TradeSet, []string, error) {
|
||||
api := NewBitgetRestApi()
|
||||
symbols, err := api.GetSpotSymbols()
|
||||
if err != nil {
|
||||
log.Error("获取bitget规范信息失败", err)
|
||||
return make(map[string]models.TradeSet, 0), []string{}, err
|
||||
}
|
||||
|
||||
config := symbolConfig{
|
||||
symbolType: "spot",
|
||||
quoteAssetSymbols: []string{"USDT", "ETH", "BTC", "SOL", "BNB", "DOGE"},
|
||||
}
|
||||
|
||||
tradeSets, deleteSymbols := buildTradeSetFromSymbols(symbols, config)
|
||||
return tradeSets, deleteSymbols, nil
|
||||
}
|
||||
|
||||
// GetFutSymbols 获取合约交易对并构建TradeSet映射
|
||||
func GetFutSymbols() (map[string]models.TradeSet, []string, error) {
|
||||
api := NewBitgetRestApi()
|
||||
symbols, err := api.GetFutSymbols()
|
||||
if err != nil {
|
||||
log.Error("获取bitget合约规范信息失败", err)
|
||||
return make(map[string]models.TradeSet, 0), []string{}, err
|
||||
}
|
||||
|
||||
config := symbolConfig{
|
||||
symbolType: "futures",
|
||||
quoteAssetSymbols: []string{"USDT"},
|
||||
}
|
||||
|
||||
tradeSets, deleteSymbols := buildTradeSetFromSymbols(symbols, config)
|
||||
return tradeSets, deleteSymbols, nil
|
||||
}
|
||||
|
||||
// getTicker24hData 获取24小时行情数据的通用方法
|
||||
func (api *BitgetRestApi) getTicker24hData(url, tickerType, description string, tradeSet *map[string]models.TradeSet) ([]string, error) {
|
||||
// 定义响应数据结构
|
||||
type tickerResponse struct {
|
||||
Code string `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data []struct {
|
||||
Symbol string `json:"symbol"`
|
||||
High24h string `json:"high24h"`
|
||||
Low24h string `json:"low24h"`
|
||||
Close string `json:"lastPr"`
|
||||
Open string `json:"open"`
|
||||
Volume24h string `json:"baseVolume"` // 基础币成交量
|
||||
QuoteVol string `json:"usdtVolume"` // USDT成交量
|
||||
Change24h string `json:"changeUtc24h"` // UTC 24h涨跌幅
|
||||
Change string `json:"change24h"` // 24h涨跌幅
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// 发送API请求
|
||||
resp, _, err := api.httpClient.SendSpotRequestAuth(url, "GET", map[string]interface{}{})
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("获取bitget %s 24h行情失败: %v", description, err)
|
||||
}
|
||||
|
||||
// 解析响应数据
|
||||
var response tickerResponse
|
||||
if err := sonic.Unmarshal(resp, &response); err != nil {
|
||||
return []string{}, fmt.Errorf("解析bitget %s 24h行情失败: %v", description, err)
|
||||
}
|
||||
|
||||
// 检查API响应状态
|
||||
if response.Code != "00000" {
|
||||
return []string{}, fmt.Errorf("bitget %s API错误: %s", description, response.Msg)
|
||||
}
|
||||
|
||||
// 处理行情数据
|
||||
deleteSymbols := make([]string, 0)
|
||||
processedCount := 0
|
||||
errorCount := 0
|
||||
|
||||
for _, item := range response.Data {
|
||||
// 构建缓存键
|
||||
key := fmt.Sprintf(tickerType, global.EXCHANGE_BITGET, item.Symbol)
|
||||
|
||||
// 检查交易对是否存在
|
||||
symbol, exists := (*tradeSet)[item.Symbol]
|
||||
if !exists {
|
||||
// 清理不存在的交易对缓存
|
||||
if err := helper.DefaultRedis.DeleteString(key); err != nil {
|
||||
log.Warn("删除无效缓存失败: %s, error: %v", key, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析交易对获取coin和currency
|
||||
var coin, currency string
|
||||
if strings.HasSuffix(item.Symbol, symbol.Currency) {
|
||||
coin = strings.TrimSuffix(item.Symbol, symbol.Currency)
|
||||
currency = symbol.Currency
|
||||
} else {
|
||||
deleteSymbols = append(deleteSymbols, item.Symbol)
|
||||
continue
|
||||
}
|
||||
|
||||
// 构建交易对数据
|
||||
symbol.Coin = coin
|
||||
symbol.Currency = currency
|
||||
symbol.OpenPrice = utility.StringAsFloat(item.Open)
|
||||
symbol.PriceChange = utility.StringAsFloat(item.Change)
|
||||
symbol.LowPrice = item.Low24h
|
||||
symbol.HighPrice = item.High24h
|
||||
symbol.Volume = item.Volume24h
|
||||
symbol.QuoteVolume = item.QuoteVol
|
||||
symbol.LastPrice = item.Close
|
||||
|
||||
// 验证数据有效性
|
||||
if symbol.Coin == "" || symbol.Currency == "" ||
|
||||
utility.StringToFloat64(symbol.QuoteVolume) <= 0 ||
|
||||
symbol.LastPrice == "" || utility.StringToFloat64(symbol.LastPrice) <= 0 {
|
||||
if err := helper.DefaultRedis.DeleteString(key); err != nil {
|
||||
log.Warn("删除无效数据缓存失败: %s, error: %v", key, err)
|
||||
}
|
||||
deleteSymbols = append(deleteSymbols, item.Symbol)
|
||||
continue
|
||||
}
|
||||
|
||||
// 缓存数据
|
||||
val, err := sonic.Marshal(symbol)
|
||||
if err != nil {
|
||||
log.Error("序列化交易对数据失败: %s, error: %v", item.Symbol, err)
|
||||
errorCount++
|
||||
continue
|
||||
}
|
||||
|
||||
if err := helper.DefaultRedis.SetString(key, string(val)); err != nil {
|
||||
log.Error("缓存交易对失败: %s, error: %v", item.Symbol, err)
|
||||
errorCount++
|
||||
continue
|
||||
}
|
||||
|
||||
processedCount++
|
||||
}
|
||||
|
||||
log.Info("处理%s行情数据完成: 成功=%d, 错误=%d, 删除=%d",
|
||||
description, processedCount, errorCount, len(deleteSymbols))
|
||||
|
||||
return deleteSymbols, nil
|
||||
}
|
||||
|
||||
// GetSpotTicker24h 获取现货24小时行情变更并缓存
|
||||
func (api *BitgetRestApi) GetSpotTicker24h(tradeSet *map[string]models.TradeSet) ([]string, error) {
|
||||
return api.getTicker24hData("/api/v2/spot/market/tickers", global.TICKER_SPOT, "现货", tradeSet)
|
||||
}
|
||||
|
||||
// GetFutTicker24h 获取U本位合约24小时行情变更并缓存
|
||||
func (api *BitgetRestApi) GetFutTicker24h(tradeSet *map[string]models.TradeSet) ([]string, error) {
|
||||
return api.getTicker24hData("/api/v2/mix/market/tickers?productType=USDT-FUTURES", global.TICKER_FUTURES, "U本位合约", tradeSet)
|
||||
}
|
||||
|
||||
// BitgetSymbol Bitget交易对信息
|
||||
type BitgetSymbol struct {
|
||||
Symbol string `json:"symbol"`
|
||||
BaseCoin string `json:"baseCoin"`
|
||||
QuoteCoin string `json:"quoteCoin"`
|
||||
BaseAsset string `json:"baseAsset"`
|
||||
QuoteAsset string `json:"quoteAsset"`
|
||||
Status string `json:"status"`
|
||||
MinQty string `json:"minQty"`
|
||||
MaxQty string `json:"maxQty"`
|
||||
PriceDigit int `json:"priceDigit"`
|
||||
QuantityDigit int `json:"quantityDigit"`
|
||||
}
|
||||
|
||||
// GetAndReloadSymbols 获取并重新加载交易对信息
|
||||
// symbolType 1-现货 2-合约
|
||||
func GetAndReloadSymbols(data *map[string]models.TradeSet, symbolType int) error {
|
||||
var tradeSets map[string]models.TradeSet
|
||||
var err error
|
||||
|
||||
switch symbolType {
|
||||
case 1:
|
||||
tradeSets, _, err = GetSpotSymbols()
|
||||
case 2:
|
||||
tradeSets, _, err = GetFutSymbols()
|
||||
default:
|
||||
return fmt.Errorf("无效的symbolType: %d", symbolType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*data = tradeSets
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitSymbolsTicker24h 初始化24小时行情数据
|
||||
// symbolType 1-现货 2-合约
|
||||
func InitSymbolsTicker24h(data *map[string]models.TradeSet, symbolType int) ([]string, error) {
|
||||
api := NewBitgetRestApi()
|
||||
|
||||
switch symbolType {
|
||||
case 1:
|
||||
return api.GetSpotTicker24h(data)
|
||||
case 2:
|
||||
return api.GetFutTicker24h(data)
|
||||
default:
|
||||
return []string{}, fmt.Errorf("无效的symbolType: %d", symbolType)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSpotTicker24 获取单个交易对24h行情
|
||||
func (api *BitgetRestApi) GetSpotTicker24(symbol string, data *models.Ticker24, tradeSet *models.TradeSet) error {
|
||||
key := fmt.Sprintf(global.TICKER_SPOT, global.EXCHANGE_BITGET, symbol)
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user