UNPKG

tardis-dev

Version:

Convenient access to tick-level historical and real-time cryptocurrency market data via Node.js

107 lines 4.13 kB
import { RealTimeFeedBase, PoolingClientBase, MultiConnectionRealTimeFeedBase } from "./realtimefeed.js"; import { batch, getJSON } from "../handy.js"; class FTXRealTimeFeedBase extends MultiConnectionRealTimeFeedBase { *_getRealTimeFeeds(exchange, filters, timeoutIntervalMS, onError) { const wsFilters = filters.filter((f) => f.channel !== 'instrument'); if (wsFilters.length > 0) { yield new FtxSingleConnectionRealTimeFeed(exchange, wsFilters, this.wssURL, timeoutIntervalMS, onError); } const instrumentInfoFilters = filters.filter((f) => f.channel === 'instrument'); if (instrumentInfoFilters.length > 0) { const instruments = instrumentInfoFilters.flatMap((s) => s.symbols); if (instruments.length > 0) { yield new FTXInstrumentInfoClient(exchange, this.httpURL, instruments, onError); } } } } class FtxSingleConnectionRealTimeFeed extends RealTimeFeedBase { wssURL; constructor(exchange, filters, wssURL, timeoutIntervalMS, onError) { super(exchange, filters, timeoutIntervalMS, onError); this.wssURL = wssURL; } mapToSubscribeMessages(filters) { return filters .map((filter) => { if (!filter.symbols || filter.symbols.length === 0) { throw new Error('FtxRealTimeFeed requires explicitly specified symbols when subscribing to live feed'); } return filter.symbols.map((symbol) => { return { op: 'subscribe', channel: filter.channel, market: symbol }; }); }) .flatMap((c) => c); } messageIsError(message) { return message.type === 'error'; } isIgnoredError(message) { // ignore market not found errors return message.code == 404; } sendCustomPing = () => { this.send({ op: 'ping' }); }; messageIsHeartbeat(msg) { return msg.type === 'pong'; } } class FTXInstrumentInfoClient extends PoolingClientBase { _httpURL; _instruments; constructor(exchange, _httpURL, _instruments, onError) { super(exchange, 5, onError); this._httpURL = _httpURL; this._instruments = _instruments; } async poolDataToStream(outputStream) { for (const instruments of batch(this._instruments, 10)) { await Promise.allSettled(instruments.map(async (instrument) => { if (outputStream.destroyed) { return; } try { const responses = await Promise.all([ getJSON(`${this._httpURL}/futures/${instrument}/stats`, { timeout: 10000 }), getJSON(`${this._httpURL}/futures/${instrument}`, { timeout: 10000 }) ]); if (responses.some((response) => response.data.success === false)) { return; } const instrumentMessage = { channel: 'instrument', generated: true, market: instrument, type: 'update', data: { stats: responses[0].data.result, info: responses[1].data.result } }; if (outputStream.writable) { outputStream.write(instrumentMessage); } } catch (err) { if (this.onError !== undefined) { this.onError(err); } } })); } } } export class FtxRealTimeFeed extends FTXRealTimeFeedBase { wssURL = 'wss://ftx.com/ws'; httpURL = 'https://ftx.com/api'; } export class FtxUSRealTimeFeed extends FTXRealTimeFeedBase { wssURL = 'wss://ftx.us/ws/'; httpURL = 'https://ftx.us/api'; } //# sourceMappingURL=ftx.js.map