Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions core/src/feeds/binance/binance-feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ export class BinanceFeed extends BaseDataFeed {
}

protected async fetchTickerImpl(symbol: string): Promise<Ticker> {
const cached = this.latestTickers.get(symbol);
const normalizedSymbol = symbol.toUpperCase();
const cached = this.latestTickers.get(normalizedSymbol);
if (cached) return cached;

await this.ensureConnected();
Expand All @@ -110,7 +111,7 @@ export class BinanceFeed extends BaseDataFeed {
reject(new Error(`BinanceFeed: timed out waiting for trade on ${symbol} (10s)`));
}, 10_000);

const cleanup = this.watchTickerImpl(symbol, (ticker) => {
const cleanup = this.watchTickerImpl(normalizedSymbol, (ticker) => {
clearTimeout(timeout);
cleanup();
resolve(ticker);
Expand All @@ -130,7 +131,7 @@ export class BinanceFeed extends BaseDataFeed {
}

protected watchTickerImpl(symbol: string, callback: (ticker: Ticker) => void): () => void {
const sub: Subscription = { symbol, callback };
const sub: Subscription = { symbol: symbol.toUpperCase(), callback };
this.subscriptions.push(sub);
this.ensureConnected().catch((err: unknown) => {
logger.error('[BinanceFeed] initial connect failed in watchTickerImpl', {
Expand Down
44 changes: 44 additions & 0 deletions core/test/unit/dataFeeds.core.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
import { ExchangeNotAvailable, NotSupported } from '../../src/errors';
import { BinanceFeed } from '../../src/feeds/binance';
import { normalizeTradeToTicker } from '../../src/feeds/binance/normalizer';
import { BinanceRelayTradeEvent } from '../../src/feeds/binance/types';
import { ChainlinkFeed } from '../../src/feeds/chainlink/chainlink-feed';
import { Ticker } from '../../src/feeds/types';

class TestBinanceFeed extends BinanceFeed {
async connect(): Promise<void> {}

cacheTicker(ticker: Ticker): void {
const internals = this as unknown as { latestTickers: Map<string, Ticker> };
internals.latestTickers.set(ticker.symbol, ticker);
}

emitTrade(event: BinanceRelayTradeEvent): void {
const internals = this as unknown as { handleMessage(data: string): void };
internals.handleMessage(JSON.stringify(event));
}
}

describe('Data feed backend errors', () => {
test('Binance fetchTicker names the missing relay URL setting', async () => {
Expand All @@ -22,6 +39,33 @@ describe('Data feed backend errors', () => {
} satisfies Partial<NotSupported>);
});

test('Binance normalizes symbol case for cached and watched tickers', async () => {
const feed = new TestBinanceFeed({ wsUrl: '', apiKey: '' });
const event: BinanceRelayTradeEvent = {
op: 'event',
source: 'binance',
symbol: 'BTCUSDT',
trade_id: 1,
price: '65000',
quantity: '0.01',
event_time_ms: 1_700_000_000_000,
trade_time_ms: 1_700_000_000_000,
is_buyer_maker: false,
timestamp_received_ms: 1_700_000_000_001,
};
const ticker = normalizeTradeToTicker(event);
feed.cacheTicker(ticker);

await expect(feed.fetchTicker('btc/usdt')).resolves.toBe(ticker);

const callback = jest.fn();
const unsubscribe = feed.watchTicker('btc/usdt', callback);
feed.emitTrade(event);

expect(callback).toHaveBeenCalledWith(expect.objectContaining({ symbol: 'BTC/USDT' }));
unsubscribe();
});

test('Chainlink oracle calls name the missing REST API URL setting', async () => {
const feed = new ChainlinkFeed({ baseUrl: '', apiKey: '', wsUrl: '' });

Expand Down