Merge pull request #1413 from c9s/edwin/bitget/use-v2-symbols

FEATURE: [bitget] use v2 symbols
This commit is contained in:
bailantaotao 2023-11-14 20:19:27 +08:00 committed by GitHub
commit beb20a8e57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 250 additions and 45 deletions

View File

@ -87,4 +87,10 @@ func TestClient(t *testing.T) {
assert.NoError(t, err)
t.Logf("resp: %+v", resp)
})
t.Run("GetSymbolsRequest", func(t *testing.T) {
resp, err := client.NewGetSymbolsRequest().Do(ctx)
assert.NoError(t, err)
t.Logf("resp: %+v", resp)
})
}

View File

@ -0,0 +1,47 @@
package bitgetapi
import (
"github.com/c9s/requestgen"
"github.com/c9s/bbgo/pkg/fixedpoint"
)
//go:generate -command GetRequest requestgen -method GET -responseType .APIResponse -responseDataField Data
//go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Data
type SymbolStatus string
const (
// SymbolOffline represent market is suspended, users cannot trade.
SymbolOffline SymbolStatus = "offline"
// SymbolGray represents market is online, but user trading is not available.
SymbolGray SymbolStatus = "gray"
// SymbolOnline trading begins, users can trade.
SymbolOnline SymbolStatus = "online"
)
type Symbol struct {
Symbol string `json:"symbol"`
BaseCoin string `json:"baseCoin"`
QuoteCoin string `json:"quoteCoin"`
MinTradeAmount fixedpoint.Value `json:"minTradeAmount"`
MaxTradeAmount fixedpoint.Value `json:"maxTradeAmount"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate"`
MakerFeeRate fixedpoint.Value `json:"makerFeeRate"`
PricePrecision fixedpoint.Value `json:"pricePrecision"`
QuantityPrecision fixedpoint.Value `json:"quantityPrecision"`
QuotePrecision fixedpoint.Value `json:"quotePrecision"`
MinTradeUSDT fixedpoint.Value `json:"minTradeUSDT"`
Status SymbolStatus `json:"status"`
BuyLimitPriceRatio fixedpoint.Value `json:"buyLimitPriceRatio"`
SellLimitPriceRatio fixedpoint.Value `json:"sellLimitPriceRatio"`
}
//go:generate GetRequest -url "/api/v2/spot/public/symbols" -type GetSymbolsRequest -responseDataType []Symbol
type GetSymbolsRequest struct {
client requestgen.APIClient
}
func (c *Client) NewGetSymbolsRequest() *GetSymbolsRequest {
return &GetSymbolsRequest{client: c.Client}
}

View File

@ -0,0 +1,158 @@
// Code generated by "requestgen -method GET -responseType .APIResponse -responseDataField Data -url /api/v2/spot/public/symbols -type GetSymbolsRequest -responseDataType []Symbol"; DO NOT EDIT.
package bitgetapi
import (
"context"
"encoding/json"
"fmt"
"github.com/c9s/bbgo/pkg/exchange/bitget/bitgetapi"
"net/url"
"reflect"
"regexp"
)
// GetQueryParameters builds and checks the query parameters and returns url.Values
func (g *GetSymbolsRequest) GetQueryParameters() (url.Values, error) {
var params = map[string]interface{}{}
query := url.Values{}
for _k, _v := range params {
query.Add(_k, fmt.Sprintf("%v", _v))
}
return query, nil
}
// GetParameters builds and checks the parameters and return the result in a map object
func (g *GetSymbolsRequest) GetParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
// GetParametersQuery converts the parameters from GetParameters into the url.Values format
func (g *GetSymbolsRequest) GetParametersQuery() (url.Values, error) {
query := url.Values{}
params, err := g.GetParameters()
if err != nil {
return query, err
}
for _k, _v := range params {
if g.isVarSlice(_v) {
g.iterateSlice(_v, func(it interface{}) {
query.Add(_k+"[]", fmt.Sprintf("%v", it))
})
} else {
query.Add(_k, fmt.Sprintf("%v", _v))
}
}
return query, nil
}
// GetParametersJSON converts the parameters from GetParameters into the JSON format
func (g *GetSymbolsRequest) GetParametersJSON() ([]byte, error) {
params, err := g.GetParameters()
if err != nil {
return nil, err
}
return json.Marshal(params)
}
// GetSlugParameters builds and checks the slug parameters and return the result in a map object
func (g *GetSymbolsRequest) GetSlugParameters() (map[string]interface{}, error) {
var params = map[string]interface{}{}
return params, nil
}
func (g *GetSymbolsRequest) applySlugsToUrl(url string, slugs map[string]string) string {
for _k, _v := range slugs {
needleRE := regexp.MustCompile(":" + _k + "\\b")
url = needleRE.ReplaceAllString(url, _v)
}
return url
}
func (g *GetSymbolsRequest) iterateSlice(slice interface{}, _f func(it interface{})) {
sliceValue := reflect.ValueOf(slice)
for _i := 0; _i < sliceValue.Len(); _i++ {
it := sliceValue.Index(_i).Interface()
_f(it)
}
}
func (g *GetSymbolsRequest) isVarSlice(_v interface{}) bool {
rt := reflect.TypeOf(_v)
switch rt.Kind() {
case reflect.Slice:
return true
}
return false
}
func (g *GetSymbolsRequest) GetSlugsMap() (map[string]string, error) {
slugs := map[string]string{}
params, err := g.GetSlugParameters()
if err != nil {
return slugs, nil
}
for _k, _v := range params {
slugs[_k] = fmt.Sprintf("%v", _v)
}
return slugs, nil
}
// GetPath returns the request path of the API
func (g *GetSymbolsRequest) GetPath() string {
return "/api/v2/spot/public/symbols"
}
// Do generates the request object and send the request object to the API endpoint
func (g *GetSymbolsRequest) Do(ctx context.Context) ([]Symbol, error) {
// no body params
var params interface{}
query := url.Values{}
var apiURL string
apiURL = g.GetPath()
req, err := g.client.NewRequest(ctx, "GET", apiURL, query, params)
if err != nil {
return nil, err
}
response, err := g.client.SendRequest(req)
if err != nil {
return nil, err
}
var apiResponse bitgetapi.APIResponse
if err := response.DecodeJSON(&apiResponse); err != nil {
return nil, err
}
type responseValidator interface {
Validate() error
}
validator, ok := interface{}(apiResponse).(responseValidator)
if ok {
if err := validator.Validate(); err != nil {
return nil, err
}
}
var data []Symbol
if err := json.Unmarshal(apiResponse.Data, &data); err != nil {
return nil, err
}
return data, nil
}

View File

@ -5,7 +5,6 @@ import (
"fmt"
"math"
"strconv"
"strings"
"time"
"github.com/c9s/bbgo/pkg/exchange/bitget/bitgetapi"
@ -14,10 +13,6 @@ import (
"github.com/c9s/bbgo/pkg/types"
)
func toGlobalSymbol(s string) string {
return strings.ToUpper(s)
}
func toGlobalBalance(asset bitgetapi.AccountAsset) types.Balance {
return types.Balance{
Currency: asset.CoinName,
@ -30,23 +25,23 @@ func toGlobalBalance(asset bitgetapi.AccountAsset) types.Balance {
}
}
func toGlobalMarket(s bitgetapi.Symbol) types.Market {
if s.Status != bitgetapi.SymbolOnline {
func toGlobalMarket(s v2.Symbol) types.Market {
if s.Status != v2.SymbolOnline {
log.Warnf("The symbol %s is not online", s.Symbol)
}
return types.Market{
Symbol: s.SymbolName,
Symbol: s.Symbol,
LocalSymbol: s.Symbol,
PricePrecision: s.PriceScale.Int(),
VolumePrecision: s.QuantityScale.Int(),
PricePrecision: s.PricePrecision.Int(),
VolumePrecision: s.QuantityPrecision.Int(),
QuoteCurrency: s.QuoteCoin,
BaseCurrency: s.BaseCoin,
MinNotional: s.MinTradeUSDT,
MinAmount: s.MinTradeUSDT,
MinQuantity: s.MinTradeAmount,
MaxQuantity: s.MaxTradeAmount,
StepSize: fixedpoint.NewFromFloat(1.0 / math.Pow10(s.QuantityScale.Int())),
TickSize: fixedpoint.NewFromFloat(1.0 / math.Pow10(s.PriceScale.Int())),
StepSize: fixedpoint.NewFromFloat(1.0 / math.Pow10(s.QuantityPrecision.Int())),
TickSize: fixedpoint.NewFromFloat(1.0 / math.Pow10(s.PricePrecision.Int())),
MinPrice: fixedpoint.Zero,
MaxPrice: fixedpoint.Zero,
}

View File

@ -46,53 +46,53 @@ func Test_toGlobalBalance(t *testing.T) {
func Test_toGlobalMarket(t *testing.T) {
// sample:
//{
// "symbol":"BTCUSDT_SPBL",
// "symbolName":"BTCUSDT",
// "baseCoin":"BTC",
// "quoteCoin":"USDT",
// "minTradeAmount":"0.0001",
// "maxTradeAmount":"10000",
// "takerFeeRate":"0.001",
// "makerFeeRate":"0.001",
// "priceScale":"4",
// "quantityScale":"8",
// "minTradeUSDT":"5",
// "status":"online",
// "buyLimitPriceRatio": "0.05",
// "sellLimitPriceRatio": "0.05"
// }
inst := bitgetapi.Symbol{
Symbol: "BTCUSDT_SPBL",
SymbolName: "BTCUSDT",
// "symbol":"BTCUSDT",
// "baseCoin":"BTC",
// "quoteCoin":"USDT",
// "minTradeAmount":"0",
// "maxTradeAmount":"10000000000",
// "takerFeeRate":"0.002",
// "makerFeeRate":"0.002",
// "pricePrecision":"2",
// "quantityPrecision":"4",
// "quotePrecision":"6",
// "status":"online",
// "minTradeUSDT":"5",
// "buyLimitPriceRatio":"0.05",
// "sellLimitPriceRatio":"0.05"
//}
inst := v2.Symbol{
Symbol: "BTCUSDT",
BaseCoin: "BTC",
QuoteCoin: "USDT",
MinTradeAmount: fixedpoint.NewFromFloat(0.0001),
MaxTradeAmount: fixedpoint.NewFromFloat(10000),
TakerFeeRate: fixedpoint.NewFromFloat(0.001),
MakerFeeRate: fixedpoint.NewFromFloat(0.001),
PriceScale: fixedpoint.NewFromFloat(4),
QuantityScale: fixedpoint.NewFromFloat(8),
MinTradeAmount: fixedpoint.NewFromFloat(0),
MaxTradeAmount: fixedpoint.NewFromFloat(10000000000),
TakerFeeRate: fixedpoint.NewFromFloat(0.002),
MakerFeeRate: fixedpoint.NewFromFloat(0.002),
PricePrecision: fixedpoint.NewFromFloat(2),
QuantityPrecision: fixedpoint.NewFromFloat(4),
QuotePrecision: fixedpoint.NewFromFloat(6),
MinTradeUSDT: fixedpoint.NewFromFloat(5),
Status: bitgetapi.SymbolOnline,
Status: v2.SymbolOnline,
BuyLimitPriceRatio: fixedpoint.NewFromFloat(0.05),
SellLimitPriceRatio: fixedpoint.NewFromFloat(0.05),
}
exp := types.Market{
Symbol: inst.SymbolName,
Symbol: inst.Symbol,
LocalSymbol: inst.Symbol,
PricePrecision: 4,
VolumePrecision: 8,
PricePrecision: 2,
VolumePrecision: 4,
QuoteCurrency: inst.QuoteCoin,
BaseCurrency: inst.BaseCoin,
MinNotional: inst.MinTradeUSDT,
MinAmount: inst.MinTradeUSDT,
MinQuantity: inst.MinTradeAmount,
MaxQuantity: inst.MaxTradeAmount,
StepSize: fixedpoint.NewFromFloat(0.00000001),
StepSize: fixedpoint.NewFromFloat(0.0001),
MinPrice: fixedpoint.Zero,
MaxPrice: fixedpoint.Zero,
TickSize: fixedpoint.NewFromFloat(0.0001),
TickSize: fixedpoint.NewFromFloat(0.01),
}
assert.Equal(t, toGlobalMarket(inst), exp)

View File

@ -94,7 +94,7 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
return nil, fmt.Errorf("markets rate limiter wait error: %w", err)
}
req := e.client.NewGetSymbolsRequest()
req := e.v2Client.NewGetSymbolsRequest()
symbols, err := req.Do(ctx)
if err != nil {
return nil, err
@ -102,8 +102,7 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
markets := types.MarketMap{}
for _, s := range symbols {
symbol := toGlobalSymbol(s.SymbolName)
markets[symbol] = toGlobalMarket(s)
markets[s.Symbol] = toGlobalMarket(s)
}
return markets, nil