diff --git a/pkg/exchange/binance/exchange.go b/pkg/exchange/binance/exchange.go index b0f060c9c..6ad1b7b1b 100644 --- a/pkg/exchange/binance/exchange.go +++ b/pkg/exchange/binance/exchange.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strconv" + "strings" "time" "github.com/adshao/go-binance/v2" @@ -48,8 +49,45 @@ func (e *Exchange) Name() types.ExchangeName { return types.ExchangeBinance } -func (e *Exchange) QueryTicker(ctx context.Context, symbol string) (types.Ticker, error) { - return types.Ticker{}, nil +func (e *Exchange) QueryTickers(ctx context.Context, symbol ...string) (map[string]types.Ticker, error) { + var ret = make(map[string]types.Ticker) + listPriceChangeStatsService := e.Client.NewListPriceChangeStatsService() + + if len(symbol) == 1 { + listPriceChangeStatsService.Symbol(symbol[0]) + } + + changeStats, err := listPriceChangeStatsService.Do(ctx) + if err != nil { + return nil, err + } + + m := make(map[string]bool) + + for _, s := range symbol { + m[strings.ToUpper(s)] = true + } + + for _, retrievedStats := range changeStats { + if _, ok := m[retrievedStats.Symbol]; len(symbol) != 0 && !ok { + continue + } + + tick := types.Ticker{ + Volume: util.MustParseFloat(retrievedStats.Volume), + Last: util.MustParseFloat(retrievedStats.LastPrice), + Open: util.MustParseFloat(retrievedStats.OpenPrice), + High: util.MustParseFloat(retrievedStats.HighPrice), + Low: util.MustParseFloat(retrievedStats.LowPrice), + Buy: util.MustParseFloat(retrievedStats.BidPrice), + Sell: util.MustParseFloat(retrievedStats.AskPrice), + Time: time.Unix(retrievedStats.CloseTime, 0), + } + + ret[retrievedStats.Symbol] = tick + } + + return ret, nil } func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) { diff --git a/pkg/exchange/binance/ticker_test.go b/pkg/exchange/binance/ticker_test.go new file mode 100644 index 000000000..2b8d3958b --- /dev/null +++ b/pkg/exchange/binance/ticker_test.go @@ -0,0 +1,49 @@ +package binance + +import ( + "context" + "fmt" + "testing" +) + +func TestAllSymbols(t *testing.T) { + e := New("mock_key", "mock_secret") + got, err := e.QueryTickers(context.Background()) + if err != nil { + t.Errorf("Binance Exchange: Fail to get ticker for all symbols: %s", err) + return + } + if len(got) <= 1 { + t.Errorf("Binance Exchange: Attempting to get all symbol tickers, but get 1 or less") + } + +} + +func TestSomeSymbols(t *testing.T) { + e := New("mock_key", "mock_secret") + got, err := e.QueryTickers(context.Background(), "BTCUSDT", "ETHUSDT") + + if err != nil { + t.Errorf("Binance Exchange: Fail to get ticker for some symbols: %s", err) + } + + if len(got) != 2 { + fmt.Println(got) + t.Errorf("Binance Exchange: Attempting to get two symbols, but number of tickers do not match") + + } +} + +func TestSingleSymbol(t *testing.T) { + e := New("mock_key", "mock_secret") + got, err := e.QueryTickers(context.Background(), "BTCUSDT") + if err != nil { + t.Errorf("Binance Exchange: Fail to get ticker for single symbol: %s", err) + } + + if len(got) != 1 { + fmt.Println(got) + t.Errorf("Binance Exchange: Attempting to get one symbol, but number of tickers do not match") + + } +} diff --git a/pkg/exchange/max/exchange.go b/pkg/exchange/max/exchange.go index 5081bd8de..44b2e7a6d 100644 --- a/pkg/exchange/max/exchange.go +++ b/pkg/exchange/max/exchange.go @@ -5,6 +5,7 @@ import ( "fmt" "math" "os" + "strings" "time" "github.com/google/uuid" @@ -43,8 +44,53 @@ func (e *Exchange) Name() types.ExchangeName { return types.ExchangeMax } -func (e *Exchange) QueryTicker(ctx context.Context) (types.Ticker, error) { - return types.Ticker{}, nil +func (e *Exchange) QueryTickers(ctx context.Context, symbol ...string) (map[string]types.Ticker, error) { + var ret = make(map[string]types.Ticker) + + if len(symbol) == 1 { + ticker, err := e.client.PublicService.Ticker(strings.ToLower(symbol[0])) + if err != nil { + return nil, err + } + ret[strings.ToUpper(symbol[0])] = types.Ticker{ + Time: ticker.Time, + Volume: util.MustParseFloat(ticker.Volume), + Last: util.MustParseFloat(ticker.Last), + Open: util.MustParseFloat(ticker.Open), + High: util.MustParseFloat(ticker.High), + Low: util.MustParseFloat(ticker.Low), + Buy: util.MustParseFloat(ticker.Buy), + Sell: util.MustParseFloat(ticker.Sell), + } + } else { + tickers, err := e.client.PublicService.Tickers() + if err != nil { + return nil, err + } + + m := make(map[string]bool) + for _, s := range symbol { + m[strings.ToUpper(s)] = true + } + + for k, v := range tickers { + if _, ok := m[strings.ToUpper(k)]; !ok { + continue + } + ret[strings.ToUpper(k)] = types.Ticker{ + Time: v.Time, + Volume: util.MustParseFloat(v.Volume), + Last: util.MustParseFloat(v.Last), + Open: util.MustParseFloat(v.Open), + High: util.MustParseFloat(v.High), + Low: util.MustParseFloat(v.Low), + Buy: util.MustParseFloat(v.Buy), + Sell: util.MustParseFloat(v.Sell), + } + } + } + + return ret, nil } func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) { diff --git a/pkg/exchange/max/ticker_test.go b/pkg/exchange/max/ticker_test.go new file mode 100644 index 000000000..21532a109 --- /dev/null +++ b/pkg/exchange/max/ticker_test.go @@ -0,0 +1,48 @@ +package max + +import ( + "context" + "fmt" + "testing" +) + +func TestAllSymbols(t *testing.T) { + e := New("mock_key", "mock_secret") + got, err := e.QueryTickers(context.Background()) + if err != nil { + t.Errorf("Max Exchange: Fail to get ticker for all symbols: %s", err) + } + if len(got) <= 1 { + t.Errorf("Max Exchange: Attempting to get all symbol tickers, but get 1 or less") + } + +} + +func TestSomeSymbols(t *testing.T) { + e := New("mock_key", "mock_secret") + got, err := e.QueryTickers(context.Background(), "BTCUSDT", "ETHUSDT") + + if err != nil { + t.Errorf("Max Exchange: Fail to get ticker for some symbols: %s", err) + } + + if len(got) != 2 { + fmt.Println(got) + t.Errorf("Max Exchange: Attempting to get two symbols, but number of tickers do not match") + + } +} + +func TestSingleSymbol(t *testing.T) { + e := New("mock_key", "mock_secret") + got, err := e.QueryTickers(context.Background(), "BTCUSDT") + if err != nil { + t.Errorf("Max Exchange: Fail to get ticker for single symbol: %s", err) + } + + if len(got) != 1 { + fmt.Println(got) + t.Errorf("Max Exchange: Attempting to get one symbol, but number of tickers do not match") + + } +} diff --git a/pkg/types/exchange.go b/pkg/types/exchange.go index 642e48ce7..236f80bac 100644 --- a/pkg/types/exchange.go +++ b/pkg/types/exchange.go @@ -45,7 +45,7 @@ type Exchange interface { QueryAccountBalances(ctx context.Context) (BalanceMap, error) - QueryTicker(ctx context.Context, symbol string) (Ticker, error) + QueryTickers(ctx context.Context, symbol ...string) (map[string]Ticker, error) QueryKLines(ctx context.Context, symbol string, interval Interval, options KLineQueryOptions) ([]KLine, error) diff --git a/pkg/types/ticker.go b/pkg/types/ticker.go index 1fe06ba72..ad3610a55 100644 --- a/pkg/types/ticker.go +++ b/pkg/types/ticker.go @@ -6,11 +6,11 @@ import ( type Ticker struct { Time time.Time - Volume string // `volume` from Max & binance - Last string // `last` from Max, `lastPrice` from binance - Open string // `open` from Max, `openPrice` from binance - High string // `high` from Max, `highPrice` from binance - Low string // `low` from Max, `lowPrice` from binance - Buy string // `buy` from Max, `bidPrice` from binance - Sell string // `sell` from Max, `askPrice` from binance + Volume float64 // `volume` from Max & binance + Last float64 // `last` from Max, `lastPrice` from binance + Open float64 // `open` from Max, `openPrice` from binance + High float64 // `high` from Max, `highPrice` from binance + Low float64 // `low` from Max, `lowPrice` from binance + Buy float64 // `buy` from Max, `bidPrice` from binance + Sell float64 // `sell` from Max, `askPrice` from binance }