mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-17 01:14:40 +08:00
feat: init coinank openapi http request framework (#1240)
This commit is contained in:
62
provider/coinank/base_coin.go
Normal file
62
provider/coinank/base_coin.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package coinank
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"nofx/provider/coinank/coinank_enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListCoin list all support coin from coinank, response is list of coin symbol
|
||||||
|
func (c *CoinankClient) ListCoin(ctx context.Context, productType coinank_enum.ProductType) (*[]string, error) {
|
||||||
|
paramsMap := make(map[string]string, 1)
|
||||||
|
paramsMap["productType"] = string(productType)
|
||||||
|
resp, err := c.Get(ctx, "/api/baseCoin/list", paramsMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result CoinankResponse[[]string]
|
||||||
|
err = json.Unmarshal([]byte(resp), &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(resp), &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !result.Success {
|
||||||
|
return nil, HttpError
|
||||||
|
}
|
||||||
|
return &result.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListSymbols list all support symbols from coinank
|
||||||
|
func (c *CoinankClient) ListSymbols(ctx context.Context, exchange coinank_enum.Exchange, productType coinank_enum.ProductType) (*[]SymbolResp, error) {
|
||||||
|
paramsMap := make(map[string]string, 2)
|
||||||
|
paramsMap["exchange"] = string(exchange)
|
||||||
|
paramsMap["productType"] = string(productType)
|
||||||
|
resp, err := c.Get(ctx, "/api/baseCoin/symbols", paramsMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result CoinankResponse[[]SymbolResp]
|
||||||
|
err = json.Unmarshal([]byte(resp), &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(resp), &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !result.Success {
|
||||||
|
return nil, HttpError
|
||||||
|
}
|
||||||
|
return &result.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SymbolResp struct {
|
||||||
|
Symbol string `json:"symbol"` // symbol,such as:`BTCUSDT`
|
||||||
|
BaseCoin string `json:"baseCoin"` // baseCoin from symbol,such as `BTC`
|
||||||
|
ExchangeName string `json:"exchangeName"` // symbol source ,such as:`Binance`
|
||||||
|
ExpireAt int `json:"expireAt"`
|
||||||
|
UpdateAt int `json:"updateAt"`
|
||||||
|
}
|
||||||
34
provider/coinank/base_coin_test.go
Normal file
34
provider/coinank/base_coin_test.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package coinank
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"nofx/provider/coinank/coinank_enum"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListCoin(t *testing.T) {
|
||||||
|
client := NewCoinankClient(coinank_enum.MainUrl, TestApikey)
|
||||||
|
resp, err := client.ListCoin(context.TODO(), "SPOT")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
res, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Logf("%s", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListSymbols(t *testing.T) {
|
||||||
|
client := NewCoinankClient(coinank_enum.MainUrl, TestApikey)
|
||||||
|
resp, err := client.ListSymbols(context.TODO(), "Binance", "SWAP")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
res, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Logf("%s", res)
|
||||||
|
}
|
||||||
25
provider/coinank/coinank_enum/exchange.go
Normal file
25
provider/coinank/coinank_enum/exchange.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package coinank_enum
|
||||||
|
|
||||||
|
type Exchange string
|
||||||
|
|
||||||
|
const ( // all maybe support exchange
|
||||||
|
Binance Exchange = "Binance"
|
||||||
|
Huobi Exchange = "Huobi"
|
||||||
|
Okex Exchange = "Okex"
|
||||||
|
Bitmex Exchange = "Bitmex"
|
||||||
|
FTX Exchange = "FTX"
|
||||||
|
Bybit Exchange = "Bybit"
|
||||||
|
Gate Exchange = "Gate"
|
||||||
|
Bitget Exchange = "Bitget"
|
||||||
|
dYdX Exchange = "dYdX"
|
||||||
|
Deribit Exchange = "Deribit"
|
||||||
|
Kraken Exchange = "Kraken"
|
||||||
|
Bitfinex Exchange = "Bitfinex"
|
||||||
|
AAX Exchange = "AAX"
|
||||||
|
CME Exchange = "CME"
|
||||||
|
Upbit Exchange = "Upbit"
|
||||||
|
Coinbase Exchange = "Coinbase"
|
||||||
|
Hyperliquid Exchange = "Hyperliquid"
|
||||||
|
Bitunix Exchange = "Bitunix"
|
||||||
|
Aster Exchange = "Aster"
|
||||||
|
)
|
||||||
6
provider/coinank/coinank_enum/product_type.go
Normal file
6
provider/coinank/coinank_enum/product_type.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package coinank_enum
|
||||||
|
|
||||||
|
type ProductType string
|
||||||
|
|
||||||
|
const SWAP ProductType = "SWAP" //Contract
|
||||||
|
const SPOT ProductType = "SPOT" //SPOT
|
||||||
5
provider/coinank/coinank_enum/url.go
Normal file
5
provider/coinank/coinank_enum/url.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package coinank_enum
|
||||||
|
|
||||||
|
var MainUrl = "https://open-api.coinank.com" //coinank openapi main url
|
||||||
|
|
||||||
|
var MainCnUrl = "https://open-api-cn.coinank.com" //coinank openapi url for chinese mainland
|
||||||
86
provider/coinank/coinank_http.go
Normal file
86
provider/coinank/coinank_http.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package coinank
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CoinankClient coinank openapi url and apikey
|
||||||
|
type CoinankClient struct {
|
||||||
|
Url string
|
||||||
|
Apikey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CoinankResponse coinank openapi common respones
|
||||||
|
type CoinankResponse[T any] struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Data T `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var HttpError error = errors.New("http client error")
|
||||||
|
|
||||||
|
// NewCoinankClient new coinank http client for coinank openapi
|
||||||
|
func NewCoinankClient(url, apikey string) *CoinankClient {
|
||||||
|
return &CoinankClient{url, apikey}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get coinank openapi get request
|
||||||
|
func (c *CoinankClient) Get(ctx context.Context, path string, paramsMap map[string]string) (string, error) {
|
||||||
|
data := url.Values{}
|
||||||
|
for key, value := range paramsMap {
|
||||||
|
data.Add(key, value)
|
||||||
|
}
|
||||||
|
fullURL := fmt.Sprintf("%s%s?%s", c.Url, path, data.Encode())
|
||||||
|
request, err := http.NewRequestWithContext(ctx, "GET", fullURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
request.Header.Add("apikey", c.Apikey)
|
||||||
|
resp, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post coinank openapi post request
|
||||||
|
func (c *CoinankClient) Post(ctx context.Context, path string, data any) (string, error) {
|
||||||
|
fullURL := fmt.Sprintf("%s%s", c.Url, path)
|
||||||
|
postData, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
request, err := http.NewRequestWithContext(ctx, "POST", fullURL, bytes.NewBuffer(postData))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
request.Header.Set("Content-Type", "application/json")
|
||||||
|
request.Header.Add("apikey", c.Apikey)
|
||||||
|
resp, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
3
provider/coinank/coinank_http_test.go
Normal file
3
provider/coinank/coinank_http_test.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package coinank
|
||||||
|
|
||||||
|
var TestApikey = "" //need fill the apikey before test
|
||||||
89
provider/coinank/instruments.go
Normal file
89
provider/coinank/instruments.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package coinank
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"nofx/provider/coinank/coinank_enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetLastPrice get symbol latest information, param example -> symbol:`BTCUSDT`,exchange:`Binance`,productType:`SWAP`
|
||||||
|
func (c *CoinankClient) GetLastPrice(ctx context.Context,
|
||||||
|
symbol string, exchange coinank_enum.Exchange, productType coinank_enum.ProductType) (*GetLastPriceResponse, error) {
|
||||||
|
paramsMap := make(map[string]string, 3)
|
||||||
|
paramsMap["symbol"] = symbol
|
||||||
|
paramsMap["exchange"] = string(exchange)
|
||||||
|
paramsMap["productType"] = string(productType)
|
||||||
|
resp, err := c.Get(ctx, "/api/instruments/getLastPrice", paramsMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result CoinankResponse[GetLastPriceResponse]
|
||||||
|
err = json.Unmarshal([]byte(resp), &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !result.Success {
|
||||||
|
return nil, HttpError
|
||||||
|
}
|
||||||
|
return &result.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCoinMarketCap get market cap info for coin ,example -> baseCoin:`BTC`
|
||||||
|
func (c *CoinankClient) GetCoinMarketCap(ctx context.Context,
|
||||||
|
baseCoin string) (*GetCoinMarketResponse, error) {
|
||||||
|
paramsMap := make(map[string]string, 1)
|
||||||
|
paramsMap["baseCoin"] = baseCoin
|
||||||
|
resp, err := c.Get(ctx, "/api/instruments/getCoinMarketCap", paramsMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result CoinankResponse[GetCoinMarketResponse]
|
||||||
|
err = json.Unmarshal([]byte(resp), &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !result.Success {
|
||||||
|
return nil, HttpError
|
||||||
|
}
|
||||||
|
return &result.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetLastPriceResponse struct {
|
||||||
|
BaseCoin string `json:"baseCoin"` //symbol base_coin
|
||||||
|
QuoteCoin string `json:"quoteCoin"` //symbol quote_coin
|
||||||
|
Symbol string `json:"symbol"` //symbol name
|
||||||
|
ExchangeName string `json:"exchangeName"` //symbol from exchange
|
||||||
|
ContractType string `json:"contractType"` //`SWAP`:Perpetual Contracts,`FUTURES`:Delivery Contracts
|
||||||
|
LastPrice float64 `json:"lastPrice"` //Latest transaction price
|
||||||
|
Open24H float64 `json:"open24h"` //24-hour opening price
|
||||||
|
High24H float64 `json:"high24h"` //24-hour highest price
|
||||||
|
Low24H float64 `json:"low24h"` //24-hour lowest price
|
||||||
|
PriceChange24H float64 `json:"priceChange24h"` //24-hour price changes
|
||||||
|
VolCcy24H float64 `json:"volCcy24h"` //24-hour trading volume
|
||||||
|
Turnover24H float64 `json:"turnover24h"` //24-hour transaction volume
|
||||||
|
TradeTimes int `json:"tradeTimes"` //Number of transactions
|
||||||
|
OiUSD float64 `json:"oiUSD"` //Open interest(USD)
|
||||||
|
OiCcy float64 `json:"oiCcy"` //Open interest(ccy)
|
||||||
|
OiVol float64 `json:"oiVol"` //Open interest(vol)
|
||||||
|
FundingRate float64 `json:"fundingRate"` //Real-time funding rates
|
||||||
|
MarkPrice float64 `json:"markPrice"` //mark price
|
||||||
|
LiqLong24H float64 `json:"liqLong24h"` //24-hour margin call on long positions
|
||||||
|
LiqShort24H float64 `json:"liqShort24h"` //24-hour margin call on short positions
|
||||||
|
Liq24H float64 `json:"liq24h"` //24-hour margin call
|
||||||
|
OiChg24H float64 `json:"oiChg24h"` //24-hour position changes
|
||||||
|
BuyTurnover float64 `json:"buyTurnover"` //buy turnover
|
||||||
|
SellTurnover float64 `json:"sellTurnover"` //sell turnover
|
||||||
|
Basis float64 `json:"basis"`
|
||||||
|
BasisRate float64 `json:"basisRate"`
|
||||||
|
ExpireAt int64 `json:"expireAt"` //expire time
|
||||||
|
Ts int `json:"ts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetCoinMarketResponse struct {
|
||||||
|
BaseCoin string `json:"baseCoin"` //coin symbol such as `BTC`
|
||||||
|
Price float64 `json:"price"` // now price
|
||||||
|
MarketCap float64 `json:"marketCap"` // now market cap
|
||||||
|
CirculatingSupply float64 `json:"circulatingSupply"`
|
||||||
|
TotalSupply float64 `json:"totalSupply"`
|
||||||
|
SupportContract bool `json:"supportContract"`
|
||||||
|
}
|
||||||
34
provider/coinank/instruments_test.go
Normal file
34
provider/coinank/instruments_test.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package coinank
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"nofx/provider/coinank/coinank_enum"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetLastPrice(t *testing.T) {
|
||||||
|
client := NewCoinankClient(coinank_enum.MainUrl, TestApikey)
|
||||||
|
resp, err := client.GetLastPrice(context.TODO(), "BTCUSDT", "Binance", "SWAP")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
res, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Logf("%s", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCoinMarketCap(t *testing.T) {
|
||||||
|
client := NewCoinankClient(coinank_enum.MainUrl, TestApikey)
|
||||||
|
resp, err := client.GetCoinMarketCap(context.TODO(), "BTC")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
res, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Logf("%s", res)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user