From 3c32acc3ede21adec6cdb17720e4db93962127c8 Mon Sep 17 00:00:00 2001 From: Edwin Date: Mon, 24 Jul 2023 17:01:34 +0800 Subject: [PATCH] pkg/exchange: add query market to bybit --- .../bybitapi/get_instruments_info_request.go | 49 ++++++++------- pkg/exchange/bybit/convert.go | 53 ++++++++++++++++ pkg/exchange/bybit/convert_test.go | 62 +++++++++++++++++++ pkg/exchange/bybit/exchange.go | 16 +++++ 4 files changed, 158 insertions(+), 22 deletions(-) create mode 100644 pkg/exchange/bybit/convert.go create mode 100644 pkg/exchange/bybit/convert_test.go diff --git a/pkg/exchange/bybit/bybitapi/get_instruments_info_request.go b/pkg/exchange/bybit/bybitapi/get_instruments_info_request.go index 524eb4bcb..288223101 100644 --- a/pkg/exchange/bybit/bybitapi/get_instruments_info_request.go +++ b/pkg/exchange/bybit/bybitapi/get_instruments_info_request.go @@ -10,27 +10,29 @@ import ( //go:generate -command PostRequest requestgen -method POST -responseType .APIResponse -responseDataField Result type InstrumentsInfo struct { - Category Category `json:"category"` - List []struct { - Symbol string `json:"symbol"` - BaseCoin string `json:"baseCoin"` - QuoteCoin string `json:"quoteCoin"` - Innovation string `json:"innovation"` - Status Status `json:"status"` - MarginTrading string `json:"marginTrading"` - LotSizeFilter struct { - BasePrecision fixedpoint.Value `json:"basePrecision"` - QuotePrecision fixedpoint.Value `json:"quotePrecision"` - MinOrderQty fixedpoint.Value `json:"minOrderQty"` - MaxOrderQty fixedpoint.Value `json:"maxOrderQty"` - MinOrderAmt fixedpoint.Value `json:"minOrderAmt"` - MaxOrderAmt fixedpoint.Value `json:"maxOrderAmt"` - } `json:"lotSizeFilter"` + Category Category `json:"category"` + List []Instrument `json:"list"` +} - PriceFilter struct { - TickSize fixedpoint.Value `json:"tickSize"` - } `json:"priceFilter"` - } `json:"list"` +type Instrument struct { + Symbol string `json:"symbol"` + BaseCoin string `json:"baseCoin"` + QuoteCoin string `json:"quoteCoin"` + Innovation string `json:"innovation"` + Status Status `json:"status"` + MarginTrading string `json:"marginTrading"` + LotSizeFilter struct { + BasePrecision fixedpoint.Value `json:"basePrecision"` + QuotePrecision fixedpoint.Value `json:"quotePrecision"` + MinOrderQty fixedpoint.Value `json:"minOrderQty"` + MaxOrderQty fixedpoint.Value `json:"maxOrderQty"` + MinOrderAmt fixedpoint.Value `json:"minOrderAmt"` + MaxOrderAmt fixedpoint.Value `json:"maxOrderAmt"` + } `json:"lotSizeFilter"` + + PriceFilter struct { + TickSize fixedpoint.Value `json:"tickSize"` + } `json:"priceFilter"` } //go:generate GetRequest -url "/v5/market/instruments-info" -type GetInstrumentsInfoRequest -responseDataType .InstrumentsInfo @@ -39,8 +41,11 @@ type GetInstrumentsInfoRequest struct { category Category `param:"category,query" validValues:"spot"` symbol *string `param:"symbol,query"` - limit *uint64 `param:"limit,query"` - cursor *string `param:"cursor,query"` + + // limit is invalid if category spot. + limit *uint64 `param:"limit,query"` + // cursor is invalid if category spot. + cursor *string `param:"cursor,query"` } func (c *RestClient) NewGetInstrumentsInfoRequest() *GetInstrumentsInfoRequest { diff --git a/pkg/exchange/bybit/convert.go b/pkg/exchange/bybit/convert.go new file mode 100644 index 000000000..0b37d7adc --- /dev/null +++ b/pkg/exchange/bybit/convert.go @@ -0,0 +1,53 @@ +package bybit + +import ( + "math" + + "github.com/c9s/bbgo/pkg/exchange/bybit/bybitapi" + "github.com/c9s/bbgo/pkg/types" +) + +func toGlobalMarket(m bybitapi.Instrument) types.Market { + // sample: + //Symbol: BTCUSDT + //BaseCoin: BTC + //QuoteCoin: USDT + //Innovation: 0 + //Status: Trading + //MarginTrading: both + // + //LotSizeFilter: + //{ + // BasePrecision: 0.000001 + // QuotePrecision: 0.00000001 + // MinOrderQty: 0.000048 + // MaxOrderQty: 71.73956243 + // MinOrderAmt: 1 + // MaxOrderAmt: 2000000 + //} + // + //PriceFilter: + //{ + // TickSize: 0.01 + //} + return types.Market{ + Symbol: m.Symbol, + LocalSymbol: m.Symbol, + PricePrecision: int(math.Log10(m.LotSizeFilter.QuotePrecision.Float64())), + VolumePrecision: int(math.Log10(m.LotSizeFilter.BasePrecision.Float64())), + QuoteCurrency: m.QuoteCoin, + BaseCurrency: m.BaseCoin, + MinNotional: m.LotSizeFilter.MinOrderAmt, + MinAmount: m.LotSizeFilter.MinOrderAmt, + + // quantity + MinQuantity: m.LotSizeFilter.MinOrderQty, + MaxQuantity: m.LotSizeFilter.MaxOrderQty, + StepSize: m.LotSizeFilter.BasePrecision, + + // price + MinPrice: m.LotSizeFilter.MinOrderAmt, + MaxPrice: m.LotSizeFilter.MaxOrderAmt, + TickSize: m.PriceFilter.TickSize, + } +} diff --git a/pkg/exchange/bybit/convert_test.go b/pkg/exchange/bybit/convert_test.go new file mode 100644 index 000000000..22325b953 --- /dev/null +++ b/pkg/exchange/bybit/convert_test.go @@ -0,0 +1,62 @@ +package bybit + +import ( + "math" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/exchange/bybit/bybitapi" + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +func TestToGlobalMarket(t *testing.T) { + inst := bybitapi.Instrument{ + Symbol: "BTCUSDT", + BaseCoin: "BTC", + QuoteCoin: "USDT", + Innovation: "0", + Status: bybitapi.StatusTrading, + MarginTrading: "both", + LotSizeFilter: struct { + BasePrecision fixedpoint.Value `json:"basePrecision"` + QuotePrecision fixedpoint.Value `json:"quotePrecision"` + MinOrderQty fixedpoint.Value `json:"minOrderQty"` + MaxOrderQty fixedpoint.Value `json:"maxOrderQty"` + MinOrderAmt fixedpoint.Value `json:"minOrderAmt"` + MaxOrderAmt fixedpoint.Value `json:"maxOrderAmt"` + }{ + BasePrecision: fixedpoint.NewFromFloat(0.000001), + QuotePrecision: fixedpoint.NewFromFloat(0.00000001), + MinOrderQty: fixedpoint.NewFromFloat(0.000048), + MaxOrderQty: fixedpoint.NewFromFloat(71.73956243), + MinOrderAmt: fixedpoint.NewFromInt(1), + MaxOrderAmt: fixedpoint.NewFromInt(2000000), + }, + PriceFilter: struct { + TickSize fixedpoint.Value `json:"tickSize"` + }{ + TickSize: fixedpoint.NewFromFloat(0.01), + }, + } + + exp := types.Market{ + Symbol: inst.Symbol, + LocalSymbol: inst.Symbol, + PricePrecision: int(math.Log10(inst.LotSizeFilter.QuotePrecision.Float64())), + VolumePrecision: int(math.Log10(inst.LotSizeFilter.BasePrecision.Float64())), + QuoteCurrency: inst.QuoteCoin, + BaseCurrency: inst.BaseCoin, + MinNotional: inst.LotSizeFilter.MinOrderAmt, + MinAmount: inst.LotSizeFilter.MinOrderAmt, + MinQuantity: inst.LotSizeFilter.MinOrderQty, + MaxQuantity: inst.LotSizeFilter.MaxOrderQty, + StepSize: inst.LotSizeFilter.BasePrecision, + MinPrice: inst.LotSizeFilter.MinOrderAmt, + MaxPrice: inst.LotSizeFilter.MaxOrderAmt, + TickSize: inst.PriceFilter.TickSize, + } + + assert.Equal(t, toGlobalMarket(inst), exp) +} diff --git a/pkg/exchange/bybit/exchange.go b/pkg/exchange/bybit/exchange.go index b5e125ac8..5abd5c645 100644 --- a/pkg/exchange/bybit/exchange.go +++ b/pkg/exchange/bybit/exchange.go @@ -1,6 +1,8 @@ package bybit import ( + "context" + "github.com/sirupsen/logrus" "github.com/c9s/bbgo/pkg/exchange/bybit/bybitapi" @@ -43,3 +45,17 @@ func (e *Exchange) Name() types.ExchangeName { func (e *Exchange) PlatformFeeCurrency() string { return "" } + +func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) { + instruments, err := e.client.NewGetInstrumentsInfoRequest().Do(ctx) + if err != nil { + return nil, err + } + + marketMap := types.MarketMap{} + for _, s := range instruments.List { + marketMap.Add(toGlobalMarket(s)) + } + + return marketMap, nil +}