bitget: minimize api client code

This commit is contained in:
c9s 2023-05-17 14:26:25 +08:00
parent f942f7afd8
commit e23f4b5114
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
2 changed files with 50 additions and 129 deletions

View File

@ -1,16 +1,15 @@
//go:build exchangetest
// +build exchangetest
package cmd
import (
"context"
"fmt"
"syscall"
"time"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/cmd/cmdutil"
"github.com/c9s/bbgo/pkg/exchange"
"github.com/c9s/bbgo/pkg/types"
)
@ -18,90 +17,47 @@ import (
var exchangeTestCmd = &cobra.Command{
Use: "exchange-test",
Short: "test the exchange",
PreRunE: cobraInitRequired([]string{
"session",
"symbol",
"interval",
}),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
environ := bbgo.NewEnvironment()
if err := environ.ConfigureExchangeSessions(userConfig); err != nil {
return err
}
sessionName, err := cmd.Flags().GetString("session")
exchangeNameStr, err := cmd.Flags().GetString("exchange")
if err != nil {
return err
}
session, ok := environ.Session(sessionName)
if !ok {
return fmt.Errorf("session %s not found", sessionName)
}
symbol, err := cmd.Flags().GetString("symbol")
if err != nil {
return fmt.Errorf("can not get the symbol from flags: %w", err)
}
if symbol == "" {
return fmt.Errorf("--symbol option is required")
}
interval, err := cmd.Flags().GetString("interval")
exchangeName, err := types.ValidExchangeName(exchangeNameStr)
if err != nil {
return err
}
now := time.Now()
kLines, err := session.Exchange.QueryKLines(ctx, symbol, types.Interval(interval), types.KLineQueryOptions{
Limit: 50,
EndTime: &now,
})
exMinimal, err := exchange.NewWithEnvVarPrefix(exchangeName, "")
if err != nil {
return err
}
log.Infof("kLines from RESTful API")
for _, k := range kLines {
log.Info(k.String())
log.Infof("types.ExchangeMinimal: ✅")
if service, ok := exMinimal.(types.ExchangeAccountService); ok {
log.Infof("types.ExchangeAccountService: ✅ (%T)", service)
}
s := session.Exchange.NewStream()
s.SetPublicOnly()
s.Subscribe(types.KLineChannel, symbol, types.SubscribeOptions{Interval: types.Interval(interval)})
s.OnKLineClosed(func(kline types.KLine) {
log.Infof("kline closed: %s", kline.String())
})
s.OnKLine(func(kline types.KLine) {
log.Infof("kline: %s", kline.String())
})
log.Infof("connecting...")
if err := s.Connect(ctx); err != nil {
return err
if service, ok := exMinimal.(types.ExchangeMarketDataService); ok {
log.Infof("types.ExchangeMarketDataService: ✅ (%T)", service)
}
log.Infof("connected")
defer func() {
log.Infof("closing connection...")
if err := s.Close(); err != nil {
log.WithError(err).Errorf("connection close error")
}
}()
if ex, ok := exMinimal.(types.Exchange); ok {
log.Infof("types.Exchange: ✅ (%T)", ex)
}
cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM)
_ = ctx
// cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM)
return nil
},
}
func init() {
// since the public data does not require trading authentication, we use --exchange option here.
exchangeTestCmd.Flags().String("session", "", "session name")
exchangeTestCmd.Flags().String("symbol", "", "the trading pair. e.g, BTCUSDT, LTCUSDT...")
exchangeTestCmd.Flags().String("interval", "1m", "interval of the kline (candle), .e.g, 1m, 3m, 15m")
exchangeTestCmd.Flags().String("exchange", "", "session name")
exchangeTestCmd.MarkFlagRequired("exchange")
RootCmd.AddCommand(exchangeTestCmd)
}

View File

@ -12,9 +12,8 @@ import (
"strings"
"time"
"github.com/c9s/requestgen"
"github.com/pkg/errors"
"github.com/c9s/bbgo/pkg/util"
)
const defaultHTTPTimeout = time.Second * 15
@ -23,8 +22,7 @@ const PublicWebSocketURL = "wss://ws.bitget.com/spot/v1/stream"
const PrivateWebSocketURL = "wss://ws.bitget.com/spot/v1/stream"
type RestClient struct {
BaseURL *url.URL
client *http.Client
requestgen.BaseAPIClient
Key, Secret, Passphrase string
}
@ -36,56 +34,21 @@ func NewClient() *RestClient {
}
return &RestClient{
BaseURL: u,
client: &http.Client{
Timeout: defaultHTTPTimeout,
BaseAPIClient: requestgen.BaseAPIClient{
BaseURL: u,
HttpClient: &http.Client{
Timeout: defaultHTTPTimeout,
},
},
}
}
func (c *RestClient) Auth(key, secret, passphrase string) {
c.Key = key
// pragma: allowlist nextline secret
c.Secret = secret
c.Passphrase = passphrase
}
// NewRequest create new API request. Relative url can be provided in refURL.
func (c *RestClient) newRequest(ctx context.Context, method, refURL string, params url.Values, body []byte) (*http.Request, error) {
rel, err := url.Parse(refURL)
if err != nil {
return nil, err
}
if params != nil {
rel.RawQuery = params.Encode()
}
pathURL := c.BaseURL.ResolveReference(rel)
return http.NewRequestWithContext(ctx, method, pathURL.String(), bytes.NewReader(body))
}
// 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
}
// newAuthenticatedRequest creates new http request for authenticated routes.
func (c *RestClient) NewAuthenticatedRequest(ctx context.Context, method, refURL string, params url.Values, payload interface{}) (*http.Request, error) {
if len(c.Key) == 0 {
@ -115,26 +78,13 @@ func (c *RestClient) NewAuthenticatedRequest(ctx context.Context, method, refURL
t := time.Now().In(time.UTC)
timestamp := t.Format("2006-01-02T15:04:05.999Z07:00")
var body []byte
if payload != nil {
switch v := payload.(type) {
case string:
body = []byte(v)
case []byte:
body = v
default:
body, err = json.Marshal(v)
if err != nil {
return nil, err
}
}
body, err := castPayload(payload)
if err != nil {
return nil, err
}
signKey := timestamp + strings.ToUpper(method) + path + string(body)
signature := Sign(signKey, c.Secret)
signature := sign(signKey, c.Secret)
req, err := http.NewRequestWithContext(ctx, method, pathURL.String(), bytes.NewReader(body))
if err != nil {
@ -150,7 +100,7 @@ func (c *RestClient) NewAuthenticatedRequest(ctx context.Context, method, refURL
return req, nil
}
func Sign(payload string, secret string) string {
func sign(payload string, secret string) string {
var sig = hmac.New(sha256.New, []byte(secret))
_, err := sig.Write([]byte(payload))
if err != nil {
@ -158,5 +108,20 @@ func Sign(payload string, secret string) string {
}
return base64.StdEncoding.EncodeToString(sig.Sum(nil))
// return hex.EncodeToString(sig.Sum(nil))
}
func castPayload(payload interface{}) ([]byte, error) {
if payload == nil {
return nil, nil
}
switch v := payload.(type) {
case string:
return []byte(v), nil
case []byte:
return v, nil
}
return json.Marshal(payload)
}