// package bybit provides rates of cryptocurrencies from bybit exchange orderbook. package bybit import ( "context" "fmt" "log/slog" "net/http" "time" "gitea.computernetthings.ru/yash/crypto_alert_bot/internal/config" "gitea.computernetthings.ru/yash/crypto_alert_bot/internal/entities" "gitea.computernetthings.ru/yash/crypto_alert_bot/internal/provider" "github.com/shopspring/decimal" ) const ( // mainnetURL = "https://api.bybit.com" httpTimeout = time.Second * 10 ) type Bybit struct { log *slog.Logger cfg *config.Bybit client *http.Client } func New(log *slog.Logger, cfg *config.Bybit) provider.Provider { return &Bybit{ log: log, cfg: cfg, client: &http.Client{ Timeout: httpTimeout, }, } } func (b *Bybit) symbol(pair *entities.Pair) string { return fmt.Sprintf("%s%s", pair.BaseCurrency, pair.QuoteCurrency) } // Price returns the current price of the pair (base currency / quote currency). // e.g. BTC/USDT. func (b *Bybit) Price(ctx context.Context, pair entities.Pair) (*entities.Price, error) { // build request req := marketOrderbookReq{ Category: categorySpot, Symbol: b.symbol(&pair), } var resp marketOrderbookResp // make request err := b.makeRequest(ctx, http.MethodGet, "/v5/market/orderbook", req, &resp) if err != nil { return nil, fmt.Errorf("failed to make request: %w", err) } // get results if len(resp.A) == 0 || len(resp.B) == 0 { return nil, fmt.Errorf("incorrect response: nil data") } if len(resp.A[0]) != 2 || len(resp.B[0]) != 2 { return nil, fmt.Errorf("incorrect response: nil prices") } askPrice := decimal.RequireFromString(resp.A[0][0]) bidPrice := decimal.RequireFromString(resp.B[0][0]) spread := askPrice.Sub(bidPrice) return &entities.Price{ Ask: askPrice, Bid: bidPrice, Spread: spread, Pair: pair, }, nil }