Files
exchange_go/services/bitgetservice/bitget_client.go
2025-10-14 19:58:59 +08:00

448 lines
14 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 bitgetservice
import (
"errors"
"fmt"
"go-admin/common/global"
"go-admin/common/helper"
"go-admin/common/interfaces"
"go-admin/models"
"go-admin/pkg/utility"
"strconv"
"strings"
"github.com/bytedance/sonic"
)
// BitgetClient Bitget交易所客户端实现
type BitgetClient struct {
httpClient *helper.BitgetClient
marketClient *BitgetMarketClient // 公开行情客户端
}
// NewBitgetClient 创建新的Bitget客户端实例
func NewBitgetClient(apiKey, apiSecret, passphrase, proxyUrl string) (*BitgetClient, error) {
httpClient, err := helper.NewBitgetClient(apiKey, apiSecret, passphrase, proxyUrl)
if err != nil {
return nil, err
}
client := &BitgetClient{
httpClient: httpClient,
}
// 初始化公开行情客户端
marketConfig := DefaultMarketConfig()
client.marketClient = NewBitgetMarketClient(marketConfig)
return client, nil
}
// GetExchangeName 获取交易所名称
func (c *BitgetClient) GetExchangeName() string {
return global.EXCHANGE_BITGET
}
// GetExchangeInfo 获取交易所信息
func (c *BitgetClient) GetExchangeInfo() (interfaces.ExchangeInfoResponse, error) {
restApi := NewBitgetRestApi()
datas, err := restApi.GetSpotSymbols()
if err != nil {
return interfaces.ExchangeInfoResponse{}, err
}
symbols := make([]interfaces.SymbolInfo, 0, len(datas))
for _, item := range datas {
symbols = append(symbols, interfaces.SymbolInfo{
Symbol: item.Symbol,
BaseAsset: item.BaseCoin,
QuoteAsset: item.QuoteCoin,
Status: item.Status,
MinQty: item.MinQty,
MaxQty: item.MaxQty,
StepSize: strconv.Itoa(item.QuantityDigit),
TickSize: strconv.Itoa(item.PriceDigit),
})
}
return interfaces.ExchangeInfoResponse{Symbols: symbols}, nil
}
// GetSpotTickers 获取所有现货行情
func (c *BitgetClient) GetSpotTickers() ([]models.TradeSet, error) {
params := map[string]interface{}{}
resp, _, err := c.httpClient.SendSpotRequestAuth("/api/v2/spot/market/tickers", "GET", params)
if err != nil {
return nil, fmt.Errorf("获取Bitget现货行情列表失败: %v", err)
}
var response 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:"close"`
Open string `json:"open"`
Volume24h string `json:"volume24h"`
QuoteVol string `json:"quoteVol"`
Change24h string `json:"change24h"`
Change string `json:"change"`
} `json:"data"`
}
if err := sonic.Unmarshal(resp, &response); err != nil {
return nil, fmt.Errorf("解析Bitget行情列表失败: %v", err)
}
if response.Code != "00000" {
return nil, fmt.Errorf("bitget API错误: %s", response.Msg)
}
tradeSets := make([]models.TradeSet, 0, len(response.Data))
for _, item := range response.Data {
// 解析交易对,获取基础币种和计价币种
var coin, currency string
if strings.HasSuffix(item.Symbol, "USDT") {
coin = strings.TrimSuffix(item.Symbol, "USDT")
currency = "USDT"
} else if strings.HasSuffix(item.Symbol, "BTC") {
coin = strings.TrimSuffix(item.Symbol, "BTC")
currency = "BTC"
} else if strings.HasSuffix(item.Symbol, "ETH") {
coin = strings.TrimSuffix(item.Symbol, "ETH")
currency = "ETH"
} else {
continue // 跳过不支持的交易对
}
tradeSet := models.TradeSet{
Coin: coin,
Currency: currency,
LastPrice: item.Close,
HighPrice: item.High24h,
LowPrice: item.Low24h,
Volume: item.Volume24h,
QuoteVolume: item.QuoteVol,
OpenPrice: utility.StringAsFloat(item.Open),
PriceChange: utility.StringAsFloat(item.Change),
}
tradeSets = append(tradeSets, tradeSet)
}
return tradeSets, nil
}
// GetSpotTicker24h 获取现货24小时行情
func (c *BitgetClient) GetSpotTicker24h(symbol string) (models.Ticker24, error) {
params := map[string]interface{}{
"symbol": symbol,
}
resp, _, err := c.httpClient.SendSpotRequestAuth("/api/v2/spot/market/ticker", "GET", params)
if err != nil {
return models.Ticker24{}, fmt.Errorf("获取Bitget现货24h行情失败: %v", err)
}
var response 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:"close"`
Open string `json:"open"`
Volume24h string `json:"volume24h"`
QuoteVol string `json:"quoteVol"`
Change24h string `json:"change24h"`
Change string `json:"change"`
} `json:"data"`
}
if err := sonic.Unmarshal(resp, &response); err != nil {
return models.Ticker24{}, fmt.Errorf("解析Bitget 24h行情失败: %v", err)
}
if response.Code != "00000" {
return models.Ticker24{}, fmt.Errorf("Bitget API错误: %s", response.Msg)
}
ticker := models.Ticker24{
HighPrice: response.Data.High24h,
LowPrice: response.Data.Low24h,
LastPrice: response.Data.Close,
OpenPrice: response.Data.Open,
Volume: response.Data.Volume24h,
QuoteVolume: response.Data.QuoteVol,
ChangePercent: response.Data.Change,
}
return ticker, nil
}
// PlaceSpotOrder 下现货订单
func (c *BitgetClient) PlaceSpotOrder(params interfaces.SpotOrderParams) (interfaces.SpotOrderResponse, error) {
orderParams := map[string]interface{}{
"symbol": params.Symbol,
"side": strings.ToLower(params.Side),
"orderType": strings.ToLower(params.Type),
"quantity": params.Quantity.String(),
}
if params.Type == "LIMIT" {
orderParams["price"] = params.Price.String()
}
if params.TimeInForce != "" {
orderParams["timeInForce"] = params.TimeInForce
}
if params.NewClientOrderId != "" {
orderParams["clientOrderId"] = params.NewClientOrderId
}
resp, _, err := c.httpClient.SendSpotRequestAuth("/api/v2/spot/trade/orders", "POST", orderParams)
if err != nil {
return interfaces.SpotOrderResponse{}, fmt.Errorf("Bitget现货下单失败: %v", err)
}
var response struct {
Code string `json:"code"`
Msg string `json:"msg"`
Data struct {
OrderId string `json:"orderId"`
ClientOrderId string `json:"clientOrderId"`
} `json:"data"`
}
if err := sonic.Unmarshal(resp, &response); err != nil {
return interfaces.SpotOrderResponse{}, fmt.Errorf("解析Bitget下单响应失败: %v", err)
}
if response.Code != "00000" {
return interfaces.SpotOrderResponse{}, fmt.Errorf("Bitget下单API错误: %s", response.Msg)
}
orderId, _ := strconv.ParseInt(response.Data.OrderId, 10, 64)
return interfaces.SpotOrderResponse{
Symbol: params.Symbol,
OrderId: orderId,
ClientOrderId: response.Data.ClientOrderId,
Price: params.Price.String(),
OrigQty: params.Quantity.String(),
Side: params.Side,
Type: params.Type,
Status: "NEW",
}, nil
}
// CancelSpotOrder 取消现货订单
func (c *BitgetClient) CancelSpotOrder(symbol, orderID string) error {
params := map[string]interface{}{
"symbol": symbol,
"orderId": orderID,
}
resp, _, err := c.httpClient.SendSpotRequestAuth("/api/v2/spot/trade/cancel-order", "POST", params)
if err != nil {
return fmt.Errorf("Bitget取消现货订单失败: %v", err)
}
var response struct {
Code string `json:"code"`
Msg string `json:"msg"`
}
if err := sonic.Unmarshal(resp, &response); err != nil {
return fmt.Errorf("解析Bitget取消订单响应失败: %v", err)
}
if response.Code != "00000" {
return fmt.Errorf("Bitget取消订单API错误: %s", response.Msg)
}
return nil
}
// GetSpotOrder 获取现货订单详情
func (c *BitgetClient) GetSpotOrder(symbol, orderID string) (interfaces.SpotOrderResponse, error) {
// 实现订单查询逻辑
return interfaces.SpotOrderResponse{}, fmt.Errorf("GetSpotOrder not implemented yet")
}
// GetSpotOrders 获取现货订单列表
func (c *BitgetClient) GetSpotOrders(symbol string) ([]interfaces.SpotOrderResponse, error) {
params := map[string]interface{}{
"symbol": symbol,
}
_, _, err := c.httpClient.SendSpotRequestAuth("/api/v2/spot/trade/open-orders", "GET", params)
if err != nil {
return nil, fmt.Errorf("获取Bitget现货订单列表失败: %v", err)
}
// 这里需要根据实际的Bitget API响应格式来解析
return []interfaces.SpotOrderResponse{}, nil
}
// 合约相关接口实现(暂时返回错误)
func (c *BitgetClient) GetFuturesTicker24h(symbol string) (models.Ticker24, error) {
return models.Ticker24{}, fmt.Errorf("futures not implemented yet")
}
func (c *BitgetClient) GetFuturesTickers() ([]models.TradeSet, error) {
return nil, fmt.Errorf("futures not implemented yet")
}
func (c *BitgetClient) PlaceFuturesOrder(params interfaces.FuturesOrderParams) (interfaces.FuturesOrderResponse, error) {
return interfaces.FuturesOrderResponse{}, fmt.Errorf("futures not implemented yet")
}
func (c *BitgetClient) CancelFuturesOrder(symbol, orderID string) error {
return fmt.Errorf("futures not implemented yet")
}
func (c *BitgetClient) GetFuturesOrder(symbol, orderID string) (interfaces.FuturesOrderResponse, error) {
return interfaces.FuturesOrderResponse{}, fmt.Errorf("futures not implemented yet")
}
// WebSocket相关接口实现已重构为使用公开行情客户端
// 注意:这些方法已被重构,现在使用新的公开行情客户端
// 如需私有数据订阅(如订单、持仓),请使用 BitgetWebSocketManager
// 旧的订阅方法(保留兼容性,但建议使用新方法)
func (c *BitgetClient) SubscribeSpotTickerLegacy(symbols []string, callback func(models.Ticker24, string, string)) error {
return fmt.Errorf("已废弃,请使用 SubscribeSpotTicker(symbol, listener) 方法")
}
func (c *BitgetClient) SubscribeFuturesTickerLegacy(symbols []string, callback func(models.Ticker24, string, string)) error {
return fmt.Errorf("已废弃,请使用 SubscribeFuturesTicker(symbol, listener) 方法")
}
func (c *BitgetClient) SubscribeSpotDepthLegacy(symbols []string, callback func(models.DepthBin, string, string)) error {
return fmt.Errorf("已废弃,请使用 SubscribeSpotDepth(symbol, listener) 方法")
}
func (c *BitgetClient) SubscribeSpotTradesLegacy(symbols []string, callback func(models.NewDealPush, string, string)) error {
return fmt.Errorf("已废弃,请使用 SubscribeSpotTrades(symbol, listener) 方法")
}
func (c *BitgetClient) SubscribeKlineLegacy(symbols []string, interval string, callback func(models.Kline, int, string, string)) error {
return fmt.Errorf("已废弃,请使用 SubscribeKline(symbol, interval, listener) 方法")
}
func (c *BitgetClient) Close() error {
// 关闭HTTP客户端等清理工作
return nil
}
// SetMarketProxy 设置市场数据代理
func (c *BitgetClient) SetMarketProxy(proxyType, proxyAddress string) {
if c.marketClient != nil {
c.marketClient.SetProxy(proxyType, proxyAddress)
}
}
// SetMarketListeners 设置市场数据监听器
func (c *BitgetClient) SetMarketListeners(msgListener, errorListener OnReceive) {
if c.marketClient != nil {
c.marketClient.SetListeners(msgListener, errorListener)
}
}
// ConnectMarketData 连接市场数据WebSocket
func (c *BitgetClient) ConnectMarketData() error {
if c.marketClient == nil {
return errors.New("市场数据客户端未初始化")
}
return c.marketClient.Connect()
}
// SubscribeMarketData 订阅市场数据(公开行情)
func (c *BitgetClient) SubscribeMarketData(subscribeReqs []SubscribeReq, listener OnReceive) error {
if c.marketClient == nil {
return errors.New("市场数据客户端未初始化")
}
return c.marketClient.Subscribe(subscribeReqs, listener)
}
// UnsubscribeMarketData 取消订阅市场数据
func (c *BitgetClient) UnsubscribeMarketData(subscribeReqs []SubscribeReq) error {
if c.marketClient == nil {
return errors.New("市场数据客户端未初始化")
}
return c.marketClient.Unsubscribe(subscribeReqs)
}
// IsMarketDataConnected 检查市场数据连接状态
func (c *BitgetClient) IsMarketDataConnected() bool {
if c.marketClient == nil {
return false
}
return c.marketClient.IsConnected()
}
// CloseMarketData 关闭市场数据连接
func (c *BitgetClient) CloseMarketData() error {
if c.marketClient == nil {
return nil
}
return c.marketClient.Close()
}
// SubscribeSpotTicker 订阅现货行情(使用公开行情客户端)
func (c *BitgetClient) SubscribeSpotTicker(symbol string, listener OnReceive) error {
subscribeReq := SubscribeReq{
InstType: "SPOT",
Channel: "ticker",
InstId: symbol,
}
return c.SubscribeMarketData([]SubscribeReq{subscribeReq}, listener)
}
// SubscribeFuturesTicker 订阅合约行情(使用公开行情客户端)
func (c *BitgetClient) SubscribeFuturesTicker(symbol string, listener OnReceive) error {
subscribeReq := SubscribeReq{
InstType: "UMCBL",
Channel: "ticker",
InstId: symbol,
}
return c.SubscribeMarketData([]SubscribeReq{subscribeReq}, listener)
}
// SubscribeSpotDepth 订阅现货深度(使用公开行情客户端)
func (c *BitgetClient) SubscribeSpotDepth(symbol string, listener OnReceive) error {
subscribeReq := SubscribeReq{
InstType: "SPOT",
Channel: "books",
InstId: symbol,
}
return c.SubscribeMarketData([]SubscribeReq{subscribeReq}, listener)
}
// SubscribeSpotTrades 订阅现货成交(使用公开行情客户端)
func (c *BitgetClient) SubscribeSpotTrades(symbol string, listener OnReceive) error {
subscribeReq := SubscribeReq{
InstType: "SPOT",
Channel: "trade",
InstId: symbol,
}
return c.SubscribeMarketData([]SubscribeReq{subscribeReq}, listener)
}
// SubscribeKline 订阅K线数据使用公开行情客户端
func (c *BitgetClient) SubscribeKline(symbol, interval string, listener OnReceive) error {
subscribeReq := SubscribeReq{
InstType: "SPOT",
Channel: "candle" + interval,
InstId: symbol,
}
return c.SubscribeMarketData([]SubscribeReq{subscribeReq}, listener)
}