up
This commit is contained in:
commit
18f15b0534
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
vendor
|
49
cmd/main.go
Normal file
49
cmd/main.go
Normal 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
5
go.mod
Normal 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
5
go.sum
Normal 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
28
okx/candleApi.go
Normal 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
21
okx/instrmentsApi.go
Normal 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
142
okx/publicApiService.go
Normal 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
26
okx/tickerApi.go
Normal 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点开盘价
|
||||
}
|
58
test/okxAli/get_candles_test.go
Normal file
58
test/okxAli/get_candles_test.go
Normal 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)
|
||||
|
||||
}
|
44
test/okxAli/get_instruments_test.go
Normal file
44
test/okxAli/get_instruments_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
|
45
test/okxAli/get_ticker_test.go
Normal file
45
test/okxAli/get_ticker_test.go
Normal 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)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user