UNPKG

@hackape/tardis-dev

Version:

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

185 lines 7.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.bitmexLiquidationsMapper = exports.BitmexDerivativeTickerMapper = exports.BitmexBookChangeMapper = exports.bitmexTradesMapper = void 0; const mapper_1 = require("./mapper"); // https://www.bitmex.com/app/wsAPI exports.bitmexTradesMapper = { canHandle(message) { return message.table === 'trade' && message.action === 'insert'; }, getFilters(symbols) { return [ { channel: 'trade', symbols } ]; }, *map(bitmexTradesMessage, localTimestamp) { for (const bitmexTrade of bitmexTradesMessage.data) { yield { 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 }; } } }; class BitmexBookChangeMapper { constructor() { this._idToPriceLevelMap = new Map(); } canHandle(message) { return message.table === 'orderBookL2'; } getFilters(symbols) { return [ { channel: 'orderBookL2', symbols } ]; } *map(bitmexOrderBookL2Message, localTimestamp) { 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; }, {}); } 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 = []; for (const item of bitmexBookMessagesGrouppedBySymbol[symbol]) { // 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); } } if (bids.length > 0 || asks.length > 0) { const bookChange = { type: 'book_change', symbol, exchange: 'bitmex', isSnapshot: bitmexOrderBookL2Message.action === 'partial', bids, asks, timestamp: 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) { 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) { 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; } } }; //# sourceMappingURL=bitmex.js.map