mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
implement okex balances endpoint
This commit is contained in:
parent
fe269fd93d
commit
e678289577
82
examples/okex-book/main.go
Normal file
82
examples/okex-book/main.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/exchange/okex/okexapi"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().String("okex-api-key", "", "okex api key")
|
||||
rootCmd.PersistentFlags().String("okex-api-secret", "", "okex api secret")
|
||||
rootCmd.PersistentFlags().String("okex-api-passphrase", "", "okex api secret")
|
||||
rootCmd.PersistentFlags().String("symbol", "BNBUSDT", "symbol")
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "okex-book",
|
||||
Short: "okex book",
|
||||
|
||||
// SilenceUsage is an option to silence usage when an error occurs.
|
||||
SilenceUsage: true,
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
symbol := viper.GetString("symbol")
|
||||
if len(symbol) == 0 {
|
||||
return errors.New("empty symbol")
|
||||
}
|
||||
|
||||
key, secret, passphrase := viper.GetString("okex-api-key"),
|
||||
viper.GetString("okex-api-secret"),
|
||||
viper.GetString("okex-api-passphrase")
|
||||
if len(key) == 0 || len(secret) == 0 {
|
||||
return errors.New("empty key, secret or passphrase")
|
||||
}
|
||||
|
||||
client := okexapi.NewClient()
|
||||
client.Auth(key, secret, passphrase)
|
||||
|
||||
log.Infof("balances:")
|
||||
balanceSummaryList, err := client.Balances()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, balanceSummary := range balanceSummaryList {
|
||||
log.Infof("%+v", balanceSummary)
|
||||
}
|
||||
|
||||
_ = ctx
|
||||
// cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
if _, err := os.Stat(".env.local"); err == nil {
|
||||
if err := godotenv.Load(".env.local"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
viper.AutomaticEnv()
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
|
||||
if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil {
|
||||
log.WithError(err).Error("bind pflags error")
|
||||
}
|
||||
|
||||
if err := rootCmd.ExecuteContext(context.Background()); err != nil {
|
||||
log.WithError(err).Error("cmd error")
|
||||
}
|
||||
}
|
|
@ -293,7 +293,7 @@ func (c *RestClient) sendRequest(req *http.Request) (*util.Response, error) {
|
|||
|
||||
// Check error, if there is an error, return the ErrorResponse struct type
|
||||
if response.IsError() {
|
||||
errorResponse, err := toErrorResponse(response)
|
||||
errorResponse, err := ToErrorResponse(response)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
@ -377,8 +377,8 @@ func (r *ErrorResponse) Error() string {
|
|||
)
|
||||
}
|
||||
|
||||
// toErrorResponse tries to convert/parse the server response to the standard Error interface object
|
||||
func toErrorResponse(response *util.Response) (errorResponse *ErrorResponse, err error) {
|
||||
// ToErrorResponse tries to convert/parse the server response to the standard Error interface object
|
||||
func ToErrorResponse(response *util.Response) (errorResponse *ErrorResponse, err error) {
|
||||
errorResponse = &ErrorResponse{Response: response}
|
||||
|
||||
contentType := response.Header.Get("content-type")
|
||||
|
|
|
@ -10,9 +10,12 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const defaultHTTPTimeout = time.Second * 15
|
||||
const RestBaseURL = "https://www.okex.com/"
|
||||
const PublicWebSocketURL = "wss://ws.okex.com:8443/ws/v5/public"
|
||||
const PrivateWebSocketURL = "wss://ws.okex.com:8443/ws/v5/private"
|
||||
|
@ -20,6 +23,8 @@ const PrivateWebSocketURL = "wss://ws.okex.com:8443/ws/v5/private"
|
|||
type RestClient struct {
|
||||
BaseURL *url.URL
|
||||
|
||||
client *http.Client
|
||||
|
||||
Key, Secret, Passphrase string
|
||||
}
|
||||
|
||||
|
@ -31,6 +36,9 @@ func NewClient() *RestClient {
|
|||
|
||||
return &RestClient{
|
||||
BaseURL: u,
|
||||
client: &http.Client{
|
||||
Timeout: defaultHTTPTimeout,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,9 +91,11 @@ func (c *RestClient) newAuthenticatedRequest(method, refURL string, params url.V
|
|||
pathURL := c.BaseURL.ResolveReference(rel)
|
||||
path := pathURL.Path
|
||||
|
||||
// 2020-12-08T09:08:57.715Z
|
||||
t := time.Now()
|
||||
// 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)
|
||||
|
||||
|
@ -105,6 +115,70 @@ func (c *RestClient) newAuthenticatedRequest(method, refURL string, params url.V
|
|||
return req, nil
|
||||
}
|
||||
|
||||
type BalanceDetail struct {
|
||||
Currency string `json:"ccy"`
|
||||
Available string `json:"availEq"`
|
||||
CashBalance string `json:"cashBal"`
|
||||
OrderFrozen string `json:"ordFrozen"`
|
||||
Frozen string `json:"frozenBal"`
|
||||
Equity string `json:"eq"`
|
||||
EquityInUSD string `json:"eqUsd"`
|
||||
UpdateTime string `json:"uTime"`
|
||||
UnrealizedProfitAndLoss string `json:"upl"`
|
||||
}
|
||||
|
||||
type BalanceSummary struct {
|
||||
TotalEquityInUSD string `json:"totalEq"`
|
||||
UpdateTime string `json:"uTime"`
|
||||
Details []BalanceDetail `json:"details"`
|
||||
}
|
||||
|
||||
type BalanceSummaryList []BalanceSummary
|
||||
|
||||
func (c *RestClient) Balances() (BalanceSummaryList, error) {
|
||||
req, err := c.newAuthenticatedRequest("GET", "/api/v5/account/balance", 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 []BalanceSummary `json:"data"`
|
||||
}
|
||||
if err := response.DecodeJSON(&balanceResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return balanceResponse.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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// newResponse reads the response body and return a new Response object
|
||||
response, err := util.NewResponse(resp)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
// Check error, if there is an error, return the ErrorResponse struct type
|
||||
if response.IsError() {
|
||||
return response, errors.New(string(response.Body))
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func signPayload(payload string, secret string) string {
|
||||
var sig = hmac.New(sha256.New, []byte(secret))
|
||||
_, err := sig.Write([]byte(payload))
|
||||
|
|
|
@ -15,7 +15,7 @@ type Response struct {
|
|||
Body []byte
|
||||
}
|
||||
|
||||
// newResponse is a wrapper of the http.Response instance, it reads the response body and close the file.
|
||||
// NewResponse is a wrapper of the http.Response instance, it reads the response body and close the file.
|
||||
func NewResponse(r *http.Response) (response *Response, err error) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue
Block a user