UNPKG

tardis-dev

Version:

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

248 lines 10.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.bitmexBookTickerMapper = exports.bitmexLiquidationsMapper = exports.BitmexDerivativeTickerMapper = exports.BitmexBookChangeMapper = exports.bitmexTradesMapper = void 0; const handy_1 = require("../handy"); const mapper_1 = require("./mapper"); // https://www.bitmex.com/app/wsAPI exports.bitmexTradesMapper = { canHandle(message) { return message.table === 'trade' && message.action === 'insert'; }, getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'trade', symbols } ]; }, *map(bitmexTradesMessage, localTimestamp) { for (const bitmexTrade of bitmexTradesMessage.data) { const trade = { type: 'trade', symbol: bitmexTrade.symbol, exchange: 'bitmex', id: bitmexTrade.trdMatchID, price: bitmexTrade.price, amount: bitmexTrade.size, side: bitmexTrade.side !== undefined ? (bitmexTrade.side === 'Buy' ? 'buy' : 'sell') : 'unknown', timestamp: new Date(bitmexTrade.timestamp), localTimestamp: localTimestamp }; yield trade; } } }; class BitmexBookChangeMapper { constructor() { this._idToPriceLevelMap = new Map(); } canHandle(message) { return message.table === 'orderBookL2'; } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'orderBookL2', symbols } ]; } *map(bitmexOrderBookL2Message, localTimestamp) { var _a, _b; let bitmexBookMessagesGrouppedBySymbol; // only partial messages can contain different symbols (when subscribed via {"op": "subscribe", "args": ["orderBookL2"]} for example) if (bitmexOrderBookL2Message.action === 'partial') { bitmexBookMessagesGrouppedBySymbol = bitmexOrderBookL2Message.data.reduce((prev, current) => { if (prev[current.symbol]) { prev[current.symbol].push(current); } else { prev[current.symbol] = [current]; } return prev; }, {}); if (bitmexOrderBookL2Message.data.length === 0 && ((_a = bitmexOrderBookL2Message.filter) === null || _a === void 0 ? void 0 : _a.symbol) !== undefined) { const emptySnapshot = { type: 'book_change', symbol: (_b = bitmexOrderBookL2Message.filter) === null || _b === void 0 ? void 0 : _b.symbol, exchange: 'bitmex', isSnapshot: true, bids: [], asks: [], timestamp: localTimestamp, localTimestamp: localTimestamp }; yield emptySnapshot; } } else { // in case of other messages types BitMEX always returns data for single symbol bitmexBookMessagesGrouppedBySymbol = { [bitmexOrderBookL2Message.data[0].symbol]: bitmexOrderBookL2Message.data }; } for (let symbol in bitmexBookMessagesGrouppedBySymbol) { const bids = []; const asks = []; let latestBitmexTimestamp = undefined; for (const item of bitmexBookMessagesGrouppedBySymbol[symbol]) { if (item.timestamp !== undefined) { const priceLevelTimestamp = new Date(item.timestamp); if (latestBitmexTimestamp === undefined) { latestBitmexTimestamp = priceLevelTimestamp; } else { if (priceLevelTimestamp.valueOf() > latestBitmexTimestamp.valueOf()) { latestBitmexTimestamp = priceLevelTimestamp; } } } // https://www.bitmex.com/app/restAPI#OrderBookL2 if (item.price !== undefined) { // store the mapping from id to price level if price is specified // only partials and inserts have price set this._idToPriceLevelMap.set(item.id, item.price); } const price = this._idToPriceLevelMap.get(item.id); const amount = item.size || 0; // delete messages do not have size specified // if we still don't have a price it means that there was an update before partial message - let's skip it if (price === undefined) { continue; } if (item.side === 'Buy') { bids.push({ price, amount }); } else { asks.push({ price, amount }); } // remove meta info for deleted level if (bitmexOrderBookL2Message.action === 'delete') { this._idToPriceLevelMap.delete(item.id); } } const isSnapshot = bitmexOrderBookL2Message.action === 'partial'; if (bids.length > 0 || asks.length > 0 || isSnapshot) { const bookChange = { type: 'book_change', symbol, exchange: 'bitmex', isSnapshot, bids, asks, timestamp: latestBitmexTimestamp !== undefined ? latestBitmexTimestamp : localTimestamp, localTimestamp: localTimestamp }; yield bookChange; } } } } exports.BitmexBookChangeMapper = BitmexBookChangeMapper; class BitmexDerivativeTickerMapper { constructor() { this.pendingTickerInfoHelper = new mapper_1.PendingTickerInfoHelper(); } canHandle(message) { return message.table === 'instrument'; } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'instrument', symbols } ]; } *map(message, localTimestamp) { for (const bitmexInstrument of message.data) { // process instrument messages only if: // - we already have seen their 'partials' or already have 'pending info' // - and instruments aren't settled or unlisted already const isOpen = bitmexInstrument.state === undefined || bitmexInstrument.state === 'Open' || bitmexInstrument.state === 'Closed'; const isPartial = message.action === 'partial'; const hasPendingInfo = this.pendingTickerInfoHelper.hasPendingTickerInfo(bitmexInstrument.symbol); if ((isPartial || hasPendingInfo) && isOpen) { const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(bitmexInstrument.symbol, 'bitmex'); pendingTickerInfo.updateFundingRate(bitmexInstrument.fundingRate); pendingTickerInfo.updatePredictedFundingRate(bitmexInstrument.indicativeFundingRate); pendingTickerInfo.updateFundingTimestamp(bitmexInstrument.fundingTimestamp ? new Date(bitmexInstrument.fundingTimestamp) : undefined); pendingTickerInfo.updateIndexPrice(bitmexInstrument.indicativeSettlePrice); pendingTickerInfo.updateMarkPrice(bitmexInstrument.markPrice); pendingTickerInfo.updateOpenInterest(bitmexInstrument.openInterest); pendingTickerInfo.updateLastPrice(bitmexInstrument.lastPrice); if (bitmexInstrument.timestamp !== undefined) { pendingTickerInfo.updateTimestamp(new Date(bitmexInstrument.timestamp)); } if (pendingTickerInfo.hasChanged()) { yield pendingTickerInfo.getSnapshot(localTimestamp); } } } } } exports.BitmexDerivativeTickerMapper = BitmexDerivativeTickerMapper; exports.bitmexLiquidationsMapper = { canHandle(message) { return message.table === 'liquidation' && message.action === 'insert'; }, getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'liquidation', symbols } ]; }, *map(bitmexLiquiationsMessage, localTimestamp) { for (const bitmexLiquidation of bitmexLiquiationsMessage.data) { const liquidation = { type: 'liquidation', symbol: bitmexLiquidation.symbol, exchange: 'bitmex', id: bitmexLiquidation.orderID, price: bitmexLiquidation.price, amount: bitmexLiquidation.leavesQty, side: bitmexLiquidation.side === 'Buy' ? 'buy' : 'sell', timestamp: localTimestamp, localTimestamp: localTimestamp }; yield liquidation; } } }; exports.bitmexBookTickerMapper = { canHandle(message) { return message.table === 'quote' && message.action === 'insert'; }, getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'quote', symbols } ]; }, *map(bitmexQuoteMessage, localTimestamp) { for (const bitmexQuote of bitmexQuoteMessage.data) { const ticker = { type: 'book_ticker', symbol: bitmexQuote.symbol, exchange: 'bitmex', askAmount: (0, handy_1.asNumberIfValid)(bitmexQuote.askSize), askPrice: (0, handy_1.asNumberIfValid)(bitmexQuote.askPrice), bidPrice: (0, handy_1.asNumberIfValid)(bitmexQuote.bidPrice), bidAmount: (0, handy_1.asNumberIfValid)(bitmexQuote.bidSize), timestamp: new Date(bitmexQuote.timestamp), localTimestamp: localTimestamp }; yield ticker; } } }; //# sourceMappingURL=bitmex.js.map