tardis-dev
Version:
Convenient access to tick-level historical and real-time cryptocurrency market data via Node.js
95 lines • 5.34 kB
JavaScript
import { onlyUnique } from "../handy.js";
import { MultiConnectionRealTimeFeedBase, RealTimeFeedBase } from "./realtimefeed.js";
export class BinanceEuropeanOptionsRealTimeFeed extends MultiConnectionRealTimeFeedBase {
*_getRealTimeFeeds(exchange, filters, timeoutIntervalMS, onError) {
// V2 API uses two separate WebSocket endpoints:
// 1. Public path: for optionTrade, depth20, bookTicker, optionTicker
// 2. Market path: for optionIndexPrice, optionMarkPrice, optionOpenInterest
const publicChannels = ['optionTrade', 'depth20', 'bookTicker', 'optionTicker'];
const marketChannels = ['optionIndexPrice', 'optionMarkPrice', 'optionOpenInterest'];
const publicFilters = filters.filter((f) => publicChannels.includes(f.channel));
const marketFilters = filters.filter((f) => marketChannels.includes(f.channel));
if (publicFilters.length > 0) {
yield new BinanceEuropeanOptionsSingleFeed('wss://fstream.binance.com/public/stream', exchange, publicFilters, filters, // Pass all filters so we can look up optionTicker when processing optionOpenInterest
timeoutIntervalMS, onError);
}
if (marketFilters.length > 0) {
yield new BinanceEuropeanOptionsSingleFeed('wss://fstream.binance.com/market/stream', exchange, marketFilters, filters, // Pass all filters so we can look up optionTicker when processing optionOpenInterest
timeoutIntervalMS, onError);
}
}
}
class BinanceEuropeanOptionsSingleFeed extends RealTimeFeedBase {
wssURL;
_allFilters;
constructor(wssURL, exchange, filters, _allFilters, timeoutIntervalMS, onError) {
super(exchange, filters, timeoutIntervalMS, onError);
this.wssURL = wssURL;
this._allFilters = _allFilters;
}
mapToSubscribeMessages(filters) {
const payload = filters.map((filter, index) => {
if (!filter.symbols || filter.symbols.length === 0) {
throw new Error('BinanceEuropeanOptionsRealTimeFeed requires explicitly specified symbols when subscribing to live feed');
}
return {
method: 'SUBSCRIBE',
params: filter.symbols
.map((symbol) => {
const lowerSymbol = symbol.toLowerCase();
// Public path channels - use lowercase symbol directly
if (filter.channel === 'optionTrade') {
return [`${lowerSymbol}@${filter.channel}`];
}
if (filter.channel === 'depth20') {
return [`${lowerSymbol}@${filter.channel}@100ms`];
}
if (filter.channel === 'bookTicker') {
return [`${lowerSymbol}@${filter.channel}`];
}
if (filter.channel === 'optionTicker') {
return [`${lowerSymbol}@${filter.channel}`];
}
// Market path channels - use lowercase underlying
if (filter.channel === 'optionIndexPrice' || filter.channel === 'optionMarkPrice') {
// Symbol is the underlying (e.g., 'btcusdt')
return [`${lowerSymbol}@${filter.channel}`];
}
if (filter.channel === 'optionOpenInterest') {
// Need to extract expirations from option symbols
// The symbol here is the underlying (e.g., 'btcusdt')
// We need to find all option symbols that match this underlying to extract expirations
// Look for optionTicker filter in all filters to get actual option symbols
const optionTickerFilter = this._allFilters.find((f) => f.channel === 'optionTicker');
if (optionTickerFilter !== undefined) {
// Extract expirations from option symbols that match this underlying
const underlyingBase = lowerSymbol.replace('usdt', '').toUpperCase();
const expirations = optionTickerFilter
.symbols.filter((s) => s.toUpperCase().startsWith(underlyingBase + '-'))
.map((s) => {
const symbolParts = s.split('-');
return symbolParts[1]; // Extract expiration (e.g., '251219')
})
.filter(onlyUnique)
.map((exp) => exp.toLowerCase());
return expirations.map((expiration) => {
return `${lowerSymbol}@${filter.channel}@${expiration}`;
});
}
}
return [`${lowerSymbol}@${filter.channel}`];
})
.flatMap((s) => s),
id: index + 1
};
});
return payload;
}
messageIsError(message) {
if (message.data !== undefined && message.data.e === 'error') {
return true;
}
return false;
}
}
//# sourceMappingURL=binanceeuropeanoptions.js.map