package okx import ( "bytes" "encoding/json" "fmt" "github.com/google/go-querystring/query" // 用于将 struct 转为 URL 参数,需要安装:go get github.com/google/go-querystring "io" "net/http" "net/url" "time" ) type OkxPublicDataService struct { BaseURL string client *http.Client } // NewOkxPublicDataService 创建服务实例 func NewOkxPublicDataService() *OkxPublicDataService { return &OkxPublicDataService{ BaseURL: "https://aws.okx.com/api/v5", client: &http.Client{ Timeout: 10 * time.Second, // 设置10秒超时 }, } } // GetInstruments 获取交易对信息 func (s *OkxPublicDataService) GetInstruments(params InstrumentsRequest) ([]Instrument, error) { u, err := url.Parse(s.BaseURL + "/public/instruments") if err != nil { return nil, err } // 将 struct 转为查询参数 q, err := query.Values(params) if err != nil { return nil, err } u.RawQuery = q.Encode() resp, err := s.client.Get(u.String()) if err != nil { return nil, err } defer resp.Body.Close() var apiResp ApiResponse apiResp.Data = new([]Instrument) // 预分配 Data 字段为 Instrument 切片 if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { return nil, err } if apiResp.Code != "0" { return nil, fmt.Errorf("API error: code=%s, msg=%s, request_url=%s", apiResp.Code, apiResp.Msg, u.String()) } // 进行类型断言并检查长度 switch data := apiResp.Data.(type) { case []Instrument: if len(data) == 0 { return nil, fmt.Errorf("no data returned from API") } case [][]string: if len(data) == 0 { return nil, fmt.Errorf("no data returned from API") } case []*Ticker: if len(data) == 0 { return nil, fmt.Errorf("no data returned from API") } default: return nil, fmt.Errorf("unexpected data type from API: %T", apiResp.Data) } return *apiResp.Data.(*[]Instrument), nil } // GetCandles 获取K线数据 func (s *OkxPublicDataService) GetCandles(params CandlesRequest) ([]*Candle, error) { u, err := url.Parse(s.BaseURL + "/market/candles") if err != nil { return nil, err } // 将 struct 转为查询参数 q, err := query.Values(params) if err != nil { return nil, err } u.RawQuery = q.Encode() // 添加调试日志 fmt.Printf("Making request to: %s\n", u.String()) resp, err := http.Get(u.String()) if err != nil { return nil, err } defer resp.Body.Close() // 记录响应状态 fmt.Printf("Response Status: %s\n", resp.Status) // 读取并记录响应体 body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } fmt.Printf("Response Body: %s\n", string(body)) // 重新设置resp.Body以便后续解码 resp.Body = io.NopCloser(bytes.NewBuffer(body)) var apiResp struct { Code string `json:"code"` Msg string `json:"msg"` Data [][]string `json:"data"` // K线数据是二维数组 } if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { return nil, err } if apiResp.Code != "0" { return nil, fmt.Errorf("API error: %s", apiResp.Msg) } // 将二维数组转为 Candle 切片 candles := make([]*Candle, len(apiResp.Data)) for i, item := range apiResp.Data { candles[i] = &Candle{ Timestamp: item[0], Open: item[1], High: item[2], Low: item[3], Close: item[4], Volume: item[5], VolumeCcy: item[6], } } return candles, nil } // GetTicker 获取单个交易对的 ticker 数据 func (s *OkxPublicDataService) GetTicker(params TickerRequest) (*Ticker, error) { u, err := url.Parse(s.BaseURL + "/market/ticker") if err != nil { return nil, err } // 将 struct 转为查询参数 q, err := query.Values(params) if err != nil { return nil, err } u.RawQuery = q.Encode() resp, err := http.Get(u.String()) if err != nil { return nil, err } defer resp.Body.Close() var apiResp struct { Code string `json:"code"` Msg string `json:"msg"` Data []*Ticker `json:"data"` // 返回的是指针数组 } if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { return nil, err } if apiResp.Code != "0" { return nil, fmt.Errorf("API error: %s", apiResp.Msg) } if len(apiResp.Data) == 0 { return nil, fmt.Errorf("no ticker data returned") } // 返回第一个 ticker(通常只返回一个) return apiResp.Data[0], nil }