mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-21 22:43:52 +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.Auth(key, secret, passphrase)
|
||||
|
||||
log.Infof("balances:")
|
||||
balanceSummaryList, err := client.Balances()
|
||||
log.Infof("ACCOUNT BALANCES:")
|
||||
balanceSummaries, err := client.AccountBalances()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, balanceSummary := range balanceSummaryList {
|
||||
for _, balanceSummary := range balanceSummaries {
|
||||
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
|
||||
// cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||
return nil
|
||||
|
|
|
@ -186,7 +186,7 @@ outboundAccountInfo
|
|||
"W": true, // Can withdraw?
|
||||
"D": true, // Can deposit?
|
||||
"u": 1499405658848, // Time of last account update
|
||||
"B": [ // Balances array
|
||||
"B": [ // AccountBalances array
|
||||
{
|
||||
"a": "LTC", // Asset
|
||||
"f": "17366.18538083", // Free amount
|
||||
|
|
|
@ -5,14 +5,16 @@ import (
|
|||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
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"
|
||||
t := time.Now().In(time.UTC)
|
||||
timestamp := t.Format("2006-01-02T15:04:05.999Z07:00")
|
||||
log.Info(timestamp)
|
||||
|
||||
payload := timestamp + strings.ToUpper(method) + path
|
||||
sign := signPayload(payload, c.Secret)
|
||||
|
@ -135,7 +136,7 @@ type BalanceSummary struct {
|
|||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -158,6 +159,158 @@ func (c *RestClient) Balances() (BalanceSummaryList, error) {
|
|||
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
|
||||
func (c *RestClient) sendRequest(req *http.Request) (*util.Response, error) {
|
||||
resp, err := c.client.Do(req)
|
||||
|
|
|
@ -2,15 +2,71 @@ package types
|
|||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"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
|
||||
|
||||
var layout = "2006-01-02 15:04:05.999Z07:00"
|
||||
|
||||
func (t *Time) UnmarshalJSON(data []byte) error {
|
||||
// fallback to RFC3339
|
||||
return (*time.Time)(t).UnmarshalJSON(data)
|
||||
}
|
||||
|
||||
|
@ -26,7 +82,7 @@ func (t Time) Time() time.Time {
|
|||
return time.Time(t)
|
||||
}
|
||||
|
||||
// driver.Valuer interface
|
||||
// Value implements the driver.Valuer interface
|
||||
// see http://jmoiron.net/blog/built-in-interfaces/
|
||||
func (t Time) Value() (driver.Value, error) {
|
||||
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