This commit is contained in:
zhangkun9038@dingtalk.com 2025-03-06 00:31:55 +08:00
commit 18f15b0534
11 changed files with 424 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor

49
cmd/main.go Normal file
View File

@ -0,0 +1,49 @@
package main
import (
"fmt"
"gitea.zjmud.xyz/phyer/tanya/okx" // 假设上述代码在 okx 包中
"strconv"
"time"
)
func main() {
service := okx.NewOkxPublicDataService()
// 获取交易对信息
instruments, err := service.GetInstruments(okx.InstrumentsRequest{
InstType: "SPOT",
})
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Instruments:", instruments)
// 获取K线数据
// // // 设置时间范围
layout := "2006-01-02 15:04:05"
startTime, _ := time.Parse(layout, "2023-01-01 00:00:00")
endTime, _ := time.Parse(layout, "2023-12-31 23:59:59")
candles, err := service.GetCandles(okx.CandlesRequest{
InstID: "BTC-USDT",
Bar: "1D",
Before: strconv.FormatInt(startTime.UnixMilli(), 10),
After: strconv.FormatInt(endTime.UnixMilli(), 10),
Limit: "50",
})
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Candles:", candles)
// 获取单个 ticker 数据
ticker, err := service.GetTicker(okx.TickerRequest{
InstID: "BTC-USDT",
})
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Ticker: %+v\n", ticker)
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module gitea.zjmud.xyz/phyer/tanya
go 1.24.0
require github.com/google/go-querystring v1.1.0

5
go.sum Normal file
View File

@ -0,0 +1,5 @@
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

28
okx/candleApi.go Normal file
View File

@ -0,0 +1,28 @@
// CandlesRequest 定义获取K线数据的请求参数
package okx
type CandlesRequest struct {
InstID string `url:"instId"` // 必填交易对ID
Bar string `url:"bar,omitempty"` // 可选K线周期默认1m
Before string `url:"before,omitempty"` // 可选:请求此时间戳之前
After string `url:"after,omitempty"` // 可选:请求此时间戳之后
Limit string `url:"limit,omitempty"` // 可选返回条数默认100
}
// Candle 定义K线数据的返回结构
type Candle struct {
Timestamp string `json:"timestamp"`
Open string `json:"open"`
High string `json:"high"`
Low string `json:"low"`
Close string `json:"close"`
Volume string `json:"volume"`
VolumeCcy string `json:"volumeCcy"`
}
// ApiResponse 定义 OKX API 的通用返回结构
type ApiResponse struct {
Code string `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}

21
okx/instrmentsApi.go Normal file
View File

@ -0,0 +1,21 @@
package okx
// InstrumentsRequest 定义获取交易对信息的请求参数
type InstrumentsRequest struct {
InstType string `url:"instType"` // 必填SPOT, SWAP, FUTURES, OPTION
Uly string `url:"uly,omitempty"` // 可选:标的指数
InstID string `url:"instId,omitempty"` // 可选具体交易对ID
}
// Instrument 定义交易对信息的返回结构
type Instrument struct {
InstType string `json:"instType"`
InstID string `json:"instId"`
Uly string `json:"uly,omitempty"`
BaseCcy string `json:"baseCcy"`
QuoteCcy string `json:"quoteCcy"`
TickSz string `json:"tickSz"`
LotSz string `json:"lotSz"`
State string `json:"state"`
// 可根据需要添加更多字段
}

142
okx/publicApiService.go Normal file
View File

@ -0,0 +1,142 @@
package okx
import (
"encoding/json"
"fmt"
"github.com/google/go-querystring/query" // 用于将 struct 转为 URL 参数需要安装go get github.com/google/go-querystring
"net/http"
"net/url"
)
type OkxPublicDataService struct {
BaseURL string
}
// NewOkxPublicDataService 创建服务实例
func NewOkxPublicDataService() *OkxPublicDataService {
return &OkxPublicDataService{
BaseURL: "https://www.okx.com/api/v5",
}
}
// 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 := http.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: %s", apiResp.Msg)
}
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()
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 [][]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
}

26
okx/tickerApi.go Normal file
View File

@ -0,0 +1,26 @@
package okx
// TickerRequest 定义获取单个 ticker 的请求参数
type TickerRequest struct {
InstID string `url:"instId"` // 必填交易对ID
}
// Ticker 定义 ticker 返回数据结构
type Ticker struct {
InstType string `json:"instType"` // 产品类型
InstID string `json:"instId"` // 交易对ID
Last string `json:"last"` // 最新成交价
LastSz string `json:"lastSz"` // 最新成交数量
AskPx string `json:"askPx"` // 卖一价
AskSz string `json:"askSz"` // 卖一量
BidPx string `json:"bidPx"` // 买一价
BidSz string `json:"bidSz"` // 买一量
Open24h string `json:"open24h"` // 24小时开盘价
High24h string `json:"high24h"` // 24小时最高价
Low24h string `json:"low24h"` // 24小时最低价
VolCcy24h string `json:"volCcy24h"` // 24小时成交量按币种计
Vol24h string `json:"vol24h"` // 24小时成交量按张数计
Ts string `json:"ts"` // 数据生成时间戳
SodUtc0 string `json:"sodUtc0"` // UTC 0点开盘价
SodUtc8 string `json:"sodUtc8"` // UTC 8点开盘价
}

View File

@ -0,0 +1,58 @@
package okx
import (
"fmt"
. "gitea.zjmud.xyz/phyer/tanya/okx"
"strconv"
"testing"
"time"
)
func TestGetCandles(t *testing.T) {
service := NewOkxPublicDataService()
params := CandlesRequest{
InstID: "BTC-USDT",
Bar: "1H",
Limit: "10",
}
candles, err := service.GetCandles(params)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(candles) == 0 {
t.Fatal("Expected at least one candle, got none")
}
// 检查返回数据的结构
for _, candle := range candles {
if candle.Timestamp == "" {
t.Error("Expected non-empty Timestamp")
}
if candle.Open == "" || candle.High == "" || candle.Low == "" || candle.Close == "" {
t.Error("Expected non-empty OHLC values")
}
if candle.Volume == "" || candle.VolumeCcy == "" {
t.Error("Expected non-empty Volume and VolumeCcy")
}
}
}
func TestGetCandles_InvalidInstID(t *testing.T) {
service := NewOkxPublicDataService()
layout := "2006-01-02 15:04:05"
startTime, _ := time.Parse(layout, "2023-01-01 00:00:00")
endTime, _ := time.Parse(layout, "2023-12-31 23:59:59")
candles, _ := service.GetCandles(CandlesRequest{
InstID: "BTC-USDT",
Bar: "1D",
Before: strconv.FormatInt(startTime.UnixMilli(), 10),
After: strconv.FormatInt(endTime.UnixMilli(), 10),
Limit: "50",
})
fmt.Println("Candles:", candles)
}

View File

@ -0,0 +1,44 @@
package okx
import (
. "gitea.zjmud.xyz/phyer/tanya/okx"
"testing"
)
func TestGetInstruments(t *testing.T) {
service := NewOkxPublicDataService() // 使用默认的正式环境 URL
params := InstrumentsRequest{InstType: "SPOT"}
instruments, err := service.GetInstruments(params)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(instruments) == 0 {
t.Fatal("Expected at least one instrument, got none")
}
// 检查返回数据的结构
for _, inst := range instruments {
if inst.InstType != "SPOT" {
t.Errorf("Expected InstType 'SPOT', got %s", inst.InstType)
}
if inst.InstID == "" {
t.Error("Expected non-empty InstID")
}
if inst.BaseCcy == "" || inst.QuoteCcy == "" {
t.Error("Expected non-empty BaseCcy and QuoteCcy")
}
}
}
func TestGetInstruments_InvalidInstType(t *testing.T) {
service := NewOkxPublicDataService()
params := InstrumentsRequest{InstType: "INVALID"}
_, err := service.GetInstruments(params)
if err == nil {
t.Fatal("Expected an error for invalid instType, got nil")
}
}

View File

@ -0,0 +1,45 @@
package okx
import (
"fmt"
. "gitea.zjmud.xyz/phyer/tanya/okx"
"testing"
)
func TestGetTicker(t *testing.T) {
service := NewOkxPublicDataService()
params := TickerRequest{InstID: "BTC-USDT"}
ticker, err := service.GetTicker(params)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// 检查返回数据的结构
if ticker.InstID != "BTC-USDT" {
t.Errorf("Expected InstID 'BTC-USDT', got %s", ticker.InstID)
}
if ticker.Last == "" {
t.Error("Expected non-empty Last price")
}
if ticker.AskPx == "" || ticker.BidPx == "" {
t.Error("Expected non-empty AskPx and BidPx")
}
if ticker.Open24h == "" || ticker.High24h == "" || ticker.Low24h == "" {
t.Error("Expected non-empty 24h OHLC values")
}
if ticker.Vol24h == "" || ticker.VolCcy24h == "" {
t.Error("Expected non-empty 24h volume values")
}
}
func TestGetTicker_InvalidInstID(t *testing.T) {
service := NewOkxPublicDataService()
params := TickerRequest{InstID: "BTC-USDT"}
ticker, _ := service.GetTicker(params)
// if err == nil {
// t.Fatal("Expected an error for invalid instId, got nil")
// }
fmt.Println("ticker: ", ticker)
}