From 4bc177f21bd0be289fd3d75753683447e26c1f55 Mon Sep 17 00:00:00 2001 From: Edwin Date: Tue, 31 Oct 2023 11:56:15 +0800 Subject: [PATCH] pkg/exchange: refactor get symbol api --- .../bitget/bitgetapi/get_symbols_request.go | 17 ++++- pkg/exchange/bitget/convert.go | 23 +++++++ pkg/exchange/bitget/convert_test.go | 66 +++++++++++++++++++ pkg/exchange/bitget/exchange.go | 32 ++++----- 4 files changed, 116 insertions(+), 22 deletions(-) create mode 100644 pkg/exchange/bitget/convert_test.go diff --git a/pkg/exchange/bitget/bitgetapi/get_symbols_request.go b/pkg/exchange/bitget/bitgetapi/get_symbols_request.go index c3c4e64a9..48a202a2e 100644 --- a/pkg/exchange/bitget/bitgetapi/get_symbols_request.go +++ b/pkg/exchange/bitget/bitgetapi/get_symbols_request.go @@ -9,6 +9,17 @@ import ( "github.com/c9s/bbgo/pkg/fixedpoint" ) +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"` SymbolName string `json:"symbolName"` @@ -18,10 +29,10 @@ type Symbol struct { MaxTradeAmount fixedpoint.Value `json:"maxTradeAmount"` TakerFeeRate fixedpoint.Value `json:"takerFeeRate"` MakerFeeRate fixedpoint.Value `json:"makerFeeRate"` - PriceScale int `json:"priceScale"` - QuantityScale int `json:"quantityScale"` + PriceScale fixedpoint.Value `json:"priceScale"` + QuantityScale fixedpoint.Value `json:"quantityScale"` MinTradeUSDT fixedpoint.Value `json:"minTradeUSDT"` - Status string `json:"status"` + Status SymbolStatus `json:"status"` BuyLimitPriceRatio fixedpoint.Value `json:"buyLimitPriceRatio"` SellLimitPriceRatio fixedpoint.Value `json:"sellLimitPriceRatio"` } diff --git a/pkg/exchange/bitget/convert.go b/pkg/exchange/bitget/convert.go index 0836b4dd0..f40d1d58a 100644 --- a/pkg/exchange/bitget/convert.go +++ b/pkg/exchange/bitget/convert.go @@ -1,6 +1,7 @@ package bitget import ( + "math" "strings" "github.com/c9s/bbgo/pkg/exchange/bitget/bitgetapi" @@ -23,3 +24,25 @@ func toGlobalBalance(asset bitgetapi.AccountAsset) types.Balance { MaxWithdrawAmount: fixedpoint.Zero, } } + +func toGlobalMarket(s bitgetapi.Symbol) types.Market { + if s.Status != bitgetapi.SymbolOnline { + log.Warnf("The symbol %s is not online", s.Symbol) + } + return types.Market{ + Symbol: s.SymbolName, + LocalSymbol: s.Symbol, + PricePrecision: s.PriceScale.Int(), + VolumePrecision: s.QuantityScale.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())), + MinPrice: fixedpoint.Zero, + MaxPrice: fixedpoint.Zero, + } +} diff --git a/pkg/exchange/bitget/convert_test.go b/pkg/exchange/bitget/convert_test.go new file mode 100644 index 000000000..6ab315eaa --- /dev/null +++ b/pkg/exchange/bitget/convert_test.go @@ -0,0 +1,66 @@ +package bitget + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/exchange/bitget/bitgetapi" + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +func TestToGlobalMarket(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", + 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), + MinTradeUSDT: fixedpoint.NewFromFloat(5), + Status: bitgetapi.SymbolOnline, + BuyLimitPriceRatio: fixedpoint.NewFromFloat(0.05), + SellLimitPriceRatio: fixedpoint.NewFromFloat(0.05), + } + + exp := types.Market{ + Symbol: inst.SymbolName, + LocalSymbol: inst.Symbol, + PricePrecision: 4, + VolumePrecision: 8, + QuoteCurrency: inst.QuoteCoin, + BaseCurrency: inst.BaseCoin, + MinNotional: inst.MinTradeUSDT, + MinAmount: inst.MinTradeUSDT, + MinQuantity: inst.MinTradeAmount, + MaxQuantity: inst.MaxTradeAmount, + StepSize: fixedpoint.NewFromFloat(0.00000001), + MinPrice: fixedpoint.Zero, + MaxPrice: fixedpoint.Zero, + TickSize: fixedpoint.NewFromFloat(0.0001), + } + + assert.Equal(t, toGlobalMarket(inst), exp) +} diff --git a/pkg/exchange/bitget/exchange.go b/pkg/exchange/bitget/exchange.go index 0d17c1b00..b7af3828b 100644 --- a/pkg/exchange/bitget/exchange.go +++ b/pkg/exchange/bitget/exchange.go @@ -2,12 +2,13 @@ package bitget import ( "context" - "math" + "fmt" + "time" "github.com/sirupsen/logrus" + "golang.org/x/time/rate" "github.com/c9s/bbgo/pkg/exchange/bitget/bitgetapi" - "github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/types" ) @@ -19,6 +20,11 @@ var log = logrus.WithFields(logrus.Fields{ "exchange": ID, }) +var ( + // queryMarketRateLimiter has its own rate limit. https://bitgetlimited.github.io/apidoc/en/spot/#get-symbols + queryMarketRateLimiter = rate.NewLimiter(rate.Every(time.Second/10), 5) +) + type Exchange struct { key, secret, passphrase string @@ -54,7 +60,10 @@ func (e *Exchange) NewStream() types.Stream { } func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) { - // TODO implement me + if err := queryMarketRateLimiter.Wait(ctx); err != nil { + return nil, fmt.Errorf("markets rate limiter wait error: %w", err) + } + req := e.client.NewGetSymbolsRequest() symbols, err := req.Do(ctx) if err != nil { @@ -64,22 +73,7 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) { markets := types.MarketMap{} for _, s := range symbols { symbol := toGlobalSymbol(s.SymbolName) - markets[symbol] = types.Market{ - Symbol: s.SymbolName, - LocalSymbol: s.Symbol, - PricePrecision: s.PriceScale, - VolumePrecision: s.QuantityScale, - QuoteCurrency: s.QuoteCoin, - BaseCurrency: s.BaseCoin, - MinNotional: s.MinTradeUSDT, - MinAmount: s.MinTradeUSDT, - MinQuantity: s.MinTradeAmount, - MaxQuantity: s.MaxTradeAmount, - StepSize: fixedpoint.NewFromFloat(math.Pow10(-s.QuantityScale)), - TickSize: fixedpoint.NewFromFloat(math.Pow10(-s.PriceScale)), - MinPrice: fixedpoint.Zero, - MaxPrice: fixedpoint.Zero, - } + markets[symbol] = toGlobalMarket(s) } return markets, nil