448 lines
14 KiB
Go
448 lines
14 KiB
Go
|
|
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)
|
|||
|
|
}
|