diff --git a/config/config.go b/config/config.go index 993e277..1bf6841 100644 --- a/config/config.go +++ b/config/config.go @@ -50,6 +50,14 @@ var defaultExchangeSymbolsMap = map[string]map[asset.Pair]types.Symbol{ "uusdt:uusd": "USDT-USDC", "uatom:uusd": "ATOM-USDT", }, + // https://api.bybit.com/v5/market/tickers?category=spot + sources.Bybit: { + "ubtc:uusd": "BTCUSDT", + "ueth:uusd": "ETHUSDT", + "uusdc:uusd": "USDCUSDT", + "uatom:uusd": "ATOMUSDT", + "unibi:uusd": "NIBIUSDT", + }, } func MustGet() *Config { diff --git a/feeder/priceprovider/priceprovider.go b/feeder/priceprovider/priceprovider.go index f4e13eb..aad7d04 100644 --- a/feeder/priceprovider/priceprovider.go +++ b/feeder/priceprovider/priceprovider.go @@ -50,6 +50,8 @@ func NewPriceProvider( source = sources.NewTickSource(mapValues(pairToSymbolMap), sources.GateIoPriceUpdate, logger) case sources.CoinMarketCap: source = sources.NewTickSource(mapValues(pairToSymbolMap), sources.CoinmarketcapPriceUpdate(config), logger) + case sources.Bybit: + source = sources.NewTickSource(mapValues(pairToSymbolMap), sources.BybitPriceUpdate, logger) default: panic("unknown price provider: " + sourceName) } diff --git a/feeder/priceprovider/sources/bybit.go b/feeder/priceprovider/sources/bybit.go new file mode 100644 index 0000000..a09aaba --- /dev/null +++ b/feeder/priceprovider/sources/bybit.go @@ -0,0 +1,67 @@ +package sources + +import ( + "encoding/json" + "io" + "net/http" + "strconv" + + "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/pricefeeder/types" + "github.com/rs/zerolog" +) + +const ( + Bybit = "bybit" +) + +var _ types.FetchPricesFunc = BybitPriceUpdate + +type BybitResponse struct { + Data struct { + List []struct { + Symbol string `json:"symbol"` + Price string `json:"lastPrice"` + } `json:"list"` + } `json:"result"` +} + +// BybitPriceUpdate returns the prices for given symbols or an error. +// Uses BYBIT API at https://bybit-exchange.github.io/docs/v5/market/tickers. +func BybitPriceUpdate(symbols set.Set[types.Symbol], logger zerolog.Logger) (rawPrices map[types.Symbol]float64, err error) { + url := "https://api.bybit.com/v5/market/tickers?category=spot" + + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + b, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var response BybitResponse + err = json.Unmarshal(b, &response) + if err != nil { + return nil, err + } + + rawPrices = make(map[types.Symbol]float64) + + for _, ticker := range response.Data.List { + symbol := types.Symbol(ticker.Symbol) + price, err := strconv.ParseFloat(ticker.Price, 64) + if err != nil { + logger.Err(err).Msgf("failed to parse price for %s on data source %s", symbol, Bybit) + continue + } + + if _, ok := symbols[symbol]; ok { + rawPrices[symbol] = price + } + } + logger.Debug().Msgf("fetched prices for %s on data source %s: %v", symbols, Bybit, rawPrices) + return rawPrices, nil +} diff --git a/feeder/priceprovider/sources/bybit_test.go b/feeder/priceprovider/sources/bybit_test.go new file mode 100644 index 0000000..b9d58f2 --- /dev/null +++ b/feeder/priceprovider/sources/bybit_test.go @@ -0,0 +1,21 @@ +package sources + +import ( + "io" + "testing" + + "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/pricefeeder/types" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" +) + +func TestBybitPriceUpdate(t *testing.T) { + t.Run("success", func(t *testing.T) { + rawPrices, err := BybitPriceUpdate(set.New[types.Symbol]("BTCUSDT", "ETHUSDT"), zerolog.New(io.Discard)) + require.NoError(t, err) + require.Equal(t, 2, len(rawPrices)) + require.NotZero(t, rawPrices["BTCUSDT"]) + require.NotZero(t, rawPrices["ETHUSDT"]) + }) +} diff --git a/feeder/priceprovider/sources/okex.go b/feeder/priceprovider/sources/okex.go index 3b9a40e..7435bbf 100644 --- a/feeder/priceprovider/sources/okex.go +++ b/feeder/priceprovider/sources/okex.go @@ -23,7 +23,7 @@ type OkexTicker struct { Price string `json:"last"` } -type Response struct { +type OkexResponse struct { Data []OkexTicker `json:"data"` } @@ -43,7 +43,7 @@ func OkexPriceUpdate(symbols set.Set[types.Symbol], logger zerolog.Logger) (rawP return nil, err } - var response Response + var response OkexResponse err = json.Unmarshal(b, &response) if err != nil { return nil, err