UNPKG

tardis-dev

Version:

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

126 lines 4.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.combine = void 0; const stream_1 = require("stream"); const events_1 = require("events"); const DATE_MAX = new Date(8640000000000000); async function nextWithIndex(iterator, index) { if ('offsetMS' in iterator) { const result = await iterator.stream.next(); if (!result.done) { const offsetMS = typeof iterator.offsetMS === 'function' ? iterator.offsetMS(result.value) : iterator.offsetMS; if (offsetMS !== 0) { result.value.localTimestamp.setUTCMilliseconds(result.value.localTimestamp.getUTCMilliseconds() + offsetMS); } } return { result, index }; } else { const result = await iterator.next(); return { result, index }; } } function findOldestResult(oldest, current) { if (oldest.result.done) { return oldest; } if (current.result.done) { return current; } const currentTimestamp = current.result.value.localTimestamp.valueOf(); const oldestTimestamp = oldest.result.value.localTimestamp.valueOf(); if (currentTimestamp < oldestTimestamp) { return current; } if (currentTimestamp === oldestTimestamp) { const currentTimestampMicroSeconds = current.result.value.localTimestamp.μs || 0; const oldestTimestampMicroSeconds = oldest.result.value.localTimestamp.μs || 0; if (currentTimestampMicroSeconds < oldestTimestampMicroSeconds) { return current; } } return oldest; } // combines multiple iterators from for example multiple exchanges // works both for real-time and historical data async function* combine(...iteratorsPayload) { const iterators = iteratorsPayload.map((payload) => { if ('stream' in payload) { return payload.stream; } return payload; }); if (iterators.length === 0) { return; } // decide based on first provided iterator if we're dealing with real-time or historical data streams if (iterators[0].__realtime__) { const combinedStream = new stream_1.PassThrough({ objectMode: true, highWaterMark: 8096 }); combinedStream.setMaxListeners(iterators.length + 1); iterators.forEach(async function writeMessagesToCombinedStream(messages) { for await (const message of messages) { if (combinedStream.destroyed) { return; } if (!combinedStream.write(message)) { // Handle backpressure on write await (0, events_1.once)(combinedStream, 'drain'); } } }); for await (const message of combinedStream) { yield message; } } else { return yield* combineHistorical(iteratorsPayload); } } exports.combine = combine; async function* combineHistorical(iterators) { try { // wait for all results to resolve const results = await Promise.all(iterators.map(nextWithIndex)); let aliveIteratorsCount = results.length; do { // if we're dealing with historical data replay // and need to return combined messages iterable sorted by local timestamp in ascending order // find resolved one that is the 'oldest' const oldestResult = results.reduce(findOldestResult, results[0]); const { result, index } = oldestResult; if (result.done) { aliveIteratorsCount--; // we don't want finished iterators to every be considered 'oldest' again // hence provide them with result that has local timestamp set to DATE_MAX // and that is not done results[index].result = { done: false, value: { localTimestamp: DATE_MAX } }; } else { // yield oldest value and replace with next value from iterable for given index yield result.value; results[index] = await nextWithIndex(iterators[index], index); } } while (aliveIteratorsCount > 0); } finally { for (let iterator of iterators) { ; iterator.return(); } } } //# sourceMappingURL=combine.js.map