mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 16:25:16 +00:00
okex: add market ticker api support
This commit is contained in:
parent
e678289577
commit
8842208441
|
@ -46,16 +46,53 @@ var rootCmd = &cobra.Command{
|
||||||
client := okexapi.NewClient()
|
client := okexapi.NewClient()
|
||||||
client.Auth(key, secret, passphrase)
|
client.Auth(key, secret, passphrase)
|
||||||
|
|
||||||
log.Infof("balances:")
|
log.Infof("ACCOUNT BALANCES:")
|
||||||
balanceSummaryList, err := client.Balances()
|
balanceSummaries, err := client.AccountBalances()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, balanceSummary := range balanceSummaryList {
|
for _, balanceSummary := range balanceSummaries {
|
||||||
log.Infof("%+v", balanceSummary)
|
log.Infof("%+v", balanceSummary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("ASSET BALANCES:")
|
||||||
|
assetBalances, err := client.AssetBalances()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, balance := range assetBalances {
|
||||||
|
log.Infof("%T%+v", balance, balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("ASSET CURRENCIES:")
|
||||||
|
currencies, err := client.AssetCurrencies()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, currency := range currencies {
|
||||||
|
log.Infof("%T%+v", currency, currency)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("MARKET TICKERS:")
|
||||||
|
tickers, err := client.MarketTickers("SPOT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ticker := range tickers {
|
||||||
|
log.Infof("%T%+v", ticker, ticker)
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker, err := client.MarketTicker("ETH-USDT")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof("TICKER:")
|
||||||
|
log.Infof("%T%+v", ticker, ticker)
|
||||||
|
|
||||||
_ = ctx
|
_ = ctx
|
||||||
// cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM)
|
// cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -186,7 +186,7 @@ outboundAccountInfo
|
||||||
"W": true, // Can withdraw?
|
"W": true, // Can withdraw?
|
||||||
"D": true, // Can deposit?
|
"D": true, // Can deposit?
|
||||||
"u": 1499405658848, // Time of last account update
|
"u": 1499405658848, // Time of last account update
|
||||||
"B": [ // Balances array
|
"B": [ // AccountBalances array
|
||||||
{
|
{
|
||||||
"a": "LTC", // Asset
|
"a": "LTC", // Asset
|
||||||
"f": "17366.18538083", // Free amount
|
"f": "17366.18538083", // Free amount
|
||||||
|
|
|
@ -5,14 +5,16 @@ import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
"github.com/c9s/bbgo/pkg/util"
|
"github.com/c9s/bbgo/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultHTTPTimeout = time.Second * 15
|
const defaultHTTPTimeout = time.Second * 15
|
||||||
|
@ -94,7 +96,6 @@ func (c *RestClient) newAuthenticatedRequest(method, refURL string, params url.V
|
||||||
// set location to UTC so that it outputs "2020-12-08T09:08:57.715Z"
|
// set location to UTC so that it outputs "2020-12-08T09:08:57.715Z"
|
||||||
t := time.Now().In(time.UTC)
|
t := time.Now().In(time.UTC)
|
||||||
timestamp := t.Format("2006-01-02T15:04:05.999Z07:00")
|
timestamp := t.Format("2006-01-02T15:04:05.999Z07:00")
|
||||||
log.Info(timestamp)
|
|
||||||
|
|
||||||
payload := timestamp + strings.ToUpper(method) + path
|
payload := timestamp + strings.ToUpper(method) + path
|
||||||
sign := signPayload(payload, c.Secret)
|
sign := signPayload(payload, c.Secret)
|
||||||
|
@ -135,7 +136,7 @@ type BalanceSummary struct {
|
||||||
|
|
||||||
type BalanceSummaryList []BalanceSummary
|
type BalanceSummaryList []BalanceSummary
|
||||||
|
|
||||||
func (c *RestClient) Balances() (BalanceSummaryList, error) {
|
func (c *RestClient) AccountBalances() (BalanceSummaryList, error) {
|
||||||
req, err := c.newAuthenticatedRequest("GET", "/api/v5/account/balance", nil)
|
req, err := c.newAuthenticatedRequest("GET", "/api/v5/account/balance", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -158,6 +159,158 @@ func (c *RestClient) Balances() (BalanceSummaryList, error) {
|
||||||
return balanceResponse.Data, nil
|
return balanceResponse.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AssetBalance struct {
|
||||||
|
Currency string `json:"ccy"`
|
||||||
|
Balance string `json:"bal"`
|
||||||
|
Frozen string `json:"frozenBal,omitempty"`
|
||||||
|
Available string `json:"availBal,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssetBalanceList []AssetBalance
|
||||||
|
|
||||||
|
func (c *RestClient) AssetBalances() (AssetBalanceList, error) {
|
||||||
|
req, err := c.newAuthenticatedRequest("GET", "/api/v5/asset/balances", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := c.sendRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var balanceResponse struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"msg"`
|
||||||
|
Data AssetBalanceList `json:"data"`
|
||||||
|
}
|
||||||
|
if err := response.DecodeJSON(&balanceResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return balanceResponse.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssetCurrency struct {
|
||||||
|
Currency string `json:"ccy"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Chain string `json:"chain"`
|
||||||
|
CanDeposit bool `json:"canDep"`
|
||||||
|
CanWithdraw bool `json:"canWd"`
|
||||||
|
CanInternal bool `json:"canInternal"`
|
||||||
|
MinWithdrawalFee string `json:"minFee"`
|
||||||
|
MaxWithdrawalFee string `json:"maxFee"`
|
||||||
|
MinWithdrawalThreshold string `json:"minWd"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RestClient) AssetCurrencies() ([]AssetCurrency, error) {
|
||||||
|
req, err := c.newAuthenticatedRequest("GET", "/api/v5/asset/currencies", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := c.sendRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var currencyResponse struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"msg"`
|
||||||
|
Data []AssetCurrency `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := response.DecodeJSON(¤cyResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return currencyResponse.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MarketTicker struct {
|
||||||
|
InstrumentType string `json:"instType"`
|
||||||
|
InstrumentID string `json:"instId"`
|
||||||
|
|
||||||
|
// last traded price
|
||||||
|
Last fixedpoint.Value `json:"last"`
|
||||||
|
|
||||||
|
// last traded size
|
||||||
|
LastSize fixedpoint.Value `json:"lastSz"`
|
||||||
|
|
||||||
|
AskPrice fixedpoint.Value `json:"askPx"`
|
||||||
|
AskSize fixedpoint.Value `json:"askSz"`
|
||||||
|
|
||||||
|
BidPrice fixedpoint.Value `json:"bidPx"`
|
||||||
|
BidSize fixedpoint.Value `json:"bidSz"`
|
||||||
|
|
||||||
|
Open24H fixedpoint.Value `json:"open24h"`
|
||||||
|
High24H fixedpoint.Value `json:"high24H"`
|
||||||
|
Low24H fixedpoint.Value `json:"low24H"`
|
||||||
|
Volume24H fixedpoint.Value `json:"vol24h"`
|
||||||
|
VolumeCurrency24H fixedpoint.Value `json:"volCcy24h"`
|
||||||
|
|
||||||
|
// Millisecond timestamp
|
||||||
|
Timestamp types.MillisecondTimestamp `json:"ts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RestClient) MarketTicker(instId string) (*MarketTicker, error) {
|
||||||
|
// SPOT, SWAP, FUTURES, OPTION
|
||||||
|
var params = url.Values{}
|
||||||
|
params.Add("instId", instId)
|
||||||
|
|
||||||
|
req, err := c.newAuthenticatedRequest("GET", "/api/v5/market/ticker", params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := c.sendRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var tickerResponse struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"msg"`
|
||||||
|
Data []MarketTicker `json:"data"`
|
||||||
|
}
|
||||||
|
if err := response.DecodeJSON(&tickerResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tickerResponse.Data) == 0 {
|
||||||
|
return nil, fmt.Errorf("ticker of %s not found", instId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tickerResponse.Data[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RestClient) MarketTickers(instType string) ([]MarketTicker, error) {
|
||||||
|
// SPOT, SWAP, FUTURES, OPTION
|
||||||
|
var params = url.Values{}
|
||||||
|
params.Add("instType", instType)
|
||||||
|
|
||||||
|
req, err := c.newAuthenticatedRequest("GET", "/api/v5/market/tickers", params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := c.sendRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var tickerResponse struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"msg"`
|
||||||
|
Data []MarketTicker `json:"data"`
|
||||||
|
}
|
||||||
|
if err := response.DecodeJSON(&tickerResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tickerResponse.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
// sendRequest sends the request to the API server and handle the response
|
// sendRequest sends the request to the API server and handle the response
|
||||||
func (c *RestClient) sendRequest(req *http.Request) (*util.Response, error) {
|
func (c *RestClient) sendRequest(req *http.Request) (*util.Response, error) {
|
||||||
resp, err := c.client.Do(req)
|
resp, err := c.client.Do(req)
|
||||||
|
|
|
@ -2,15 +2,71 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type MillisecondTimestamp time.Time
|
||||||
|
|
||||||
|
func (t *MillisecondTimestamp) UnmarshalJSON(data []byte) error {
|
||||||
|
var v interface{}
|
||||||
|
|
||||||
|
var err = json.Unmarshal(data, &v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch vt := v.(type) {
|
||||||
|
case string:
|
||||||
|
i, err := strconv.ParseInt(vt, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
*t = MillisecondTimestamp(time.Unix(0, i*int64(time.Millisecond)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := strconv.ParseFloat(vt, 64)
|
||||||
|
if err == nil {
|
||||||
|
*t = MillisecondTimestamp(time.Unix(0, int64(f*float64(time.Millisecond))))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tt, err := time.Parse(time.RFC3339Nano, vt)
|
||||||
|
if err == nil {
|
||||||
|
*t = MillisecondTimestamp(tt)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
*t = MillisecondTimestamp(time.Unix(0, vt*int64(time.Millisecond)))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case int:
|
||||||
|
*t = MillisecondTimestamp(time.Unix(0, int64(vt)*int64(time.Millisecond)))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
*t = MillisecondTimestamp(time.Unix(0, int64(vt)*int64(time.Millisecond)))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("can not parse %T %+v as millisecond timestamp", vt, vt)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback to RFC3339
|
||||||
|
return (*time.Time)(t).UnmarshalJSON(data)
|
||||||
|
}
|
||||||
|
|
||||||
type Time time.Time
|
type Time time.Time
|
||||||
|
|
||||||
var layout = "2006-01-02 15:04:05.999Z07:00"
|
var layout = "2006-01-02 15:04:05.999Z07:00"
|
||||||
|
|
||||||
func (t *Time) UnmarshalJSON(data []byte) error {
|
func (t *Time) UnmarshalJSON(data []byte) error {
|
||||||
|
// fallback to RFC3339
|
||||||
return (*time.Time)(t).UnmarshalJSON(data)
|
return (*time.Time)(t).UnmarshalJSON(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +82,7 @@ func (t Time) Time() time.Time {
|
||||||
return time.Time(t)
|
return time.Time(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// driver.Valuer interface
|
// Value implements the driver.Valuer interface
|
||||||
// see http://jmoiron.net/blog/built-in-interfaces/
|
// see http://jmoiron.net/blog/built-in-interfaces/
|
||||||
func (t Time) Value() (driver.Value, error) {
|
func (t Time) Value() (driver.Value, error) {
|
||||||
return time.Time(t), nil
|
return time.Time(t), nil
|
||||||
|
|
38
pkg/types/time_test.go
Normal file
38
pkg/types/time_test.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMillisecondTimestamp_UnmarshalJSON(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
t MillisecondTimestamp
|
||||||
|
args []byte
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "millisecond in string",
|
||||||
|
args: []byte("\"1620289117764\""),
|
||||||
|
t: MillisecondTimestamp(time.Unix(0, 1620289117764*int64(time.Millisecond))),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "millisecond in number",
|
||||||
|
args: []byte("1620289117764"),
|
||||||
|
t: MillisecondTimestamp(time.Unix(0, 1620289117764*int64(time.Millisecond))),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "millisecond in decimal",
|
||||||
|
args: []byte("1620289117.764"),
|
||||||
|
t: MillisecondTimestamp(time.Unix(0, 1620289117764*int64(time.Millisecond))),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := tt.t.UnmarshalJSON(tt.args); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user