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) }