crypto_alert_bot/internal/provider/bybit/bybit.go

75 lines
1.8 KiB
Go

// 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.Instrument) 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, instrument entities.Instrument) (*entities.Price, error) {
// build request
req := marketOrderbookReq{
Category: categorySpot,
Symbol: b.symbol(&instrument),
}
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,
Instrument: instrument,
}, nil
}