UNPKG

tardis-dev

Version:

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

526 lines 21.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BybitLiquidationsMapper = exports.BybitDerivativeTickerMapper = exports.BybitBookChangeMapper = exports.BybitTradesMapper = exports.BybitV5OptionSummaryMapper = exports.BybitV5LiquidationsMapper = exports.BybitV5DerivativeTickerMapper = exports.BybitV5BookTickerMapper = exports.BybitV5BookChangeMapper = exports.BybitV5TradesMapper = void 0; const handy_1 = require("../handy"); const mapper_1 = require("./mapper"); // v5 https://bybit-exchange.github.io/docs/v5/ws/connect class BybitV5TradesMapper { constructor(_exchange) { this._exchange = _exchange; } canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith('publicTrade.'); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'publicTrade', symbols } ]; } *map(message, localTimestamp) { for (const trade of message.data) { yield { type: 'trade', symbol: trade.s, exchange: this._exchange, id: trade.i, price: Number(trade.p), amount: Number(trade.v), side: trade.S == 'Buy' ? 'buy' : trade.S === 'Sell' ? 'sell' : 'unknown', timestamp: new Date(trade.T), localTimestamp }; } } } exports.BybitV5TradesMapper = BybitV5TradesMapper; class BybitV5BookChangeMapper { constructor(_exchange, _depth) { this._exchange = _exchange; this._depth = _depth; } canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith(`orderbook.${this._depth}.`); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: `orderbook.${this._depth}`, symbols } ]; } *map(message, localTimestamp) { yield { type: 'book_change', symbol: message.data.s, exchange: this._exchange, isSnapshot: message.type === 'snapshot', bids: message.data.b.map(this._mapBookLevel), asks: message.data.a.map(this._mapBookLevel), timestamp: new Date(message.ts), localTimestamp }; } _mapBookLevel(level) { return { price: Number(level[0]), amount: Number(level[1]) }; } } exports.BybitV5BookChangeMapper = BybitV5BookChangeMapper; class BybitV5BookTickerMapper { constructor(_exchange) { this._exchange = _exchange; this._snapshots = {}; } canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith(`orderbook.1.`); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'orderbook.1', symbols } ]; } *map(message, localTimestamp) { const bestAsk = message.data.a.filter((ask) => ask[1] != '0')[0]; const bestBid = message.data.b.filter((bid) => bid[1] != '0')[0]; if (message.type === 'snapshot') { this._snapshots[message.data.s] = { askAmount: bestAsk !== undefined ? Number(bestAsk[1]) : undefined, askPrice: bestAsk !== undefined ? Number(bestAsk[0]) : undefined, bidPrice: bestBid !== undefined ? Number(bestBid[0]) : undefined, bidAmount: bestBid !== undefined ? Number(bestBid[1]) : undefined }; } const matchingSnapshot = this._snapshots[message.data.s]; if (!matchingSnapshot) { return; } const bookTicker = { type: 'book_ticker', symbol: message.data.s, exchange: this._exchange, askAmount: bestAsk !== undefined ? Number(bestAsk[1]) : matchingSnapshot.askAmount, askPrice: bestAsk !== undefined ? Number(bestAsk[0]) : matchingSnapshot.askPrice, bidPrice: bestBid !== undefined ? Number(bestBid[0]) : matchingSnapshot.bidPrice, bidAmount: bestBid !== undefined ? Number(bestBid[1]) : matchingSnapshot.bidAmount, timestamp: new Date(message.ts), localTimestamp: localTimestamp }; this._snapshots[message.data.s] = { askAmount: bookTicker.askAmount, askPrice: bookTicker.askPrice, bidPrice: bookTicker.bidPrice, bidAmount: bookTicker.bidAmount }; yield bookTicker; } } exports.BybitV5BookTickerMapper = BybitV5BookTickerMapper; class BybitV5DerivativeTickerMapper { constructor() { this.pendingTickerInfoHelper = new mapper_1.PendingTickerInfoHelper(); } canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith('tickers.'); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'tickers', symbols } ]; } *map(message, localTimestamp) { const instrumentInfo = message.data; const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(instrumentInfo.symbol, 'bybit'); if (instrumentInfo.fundingRate !== undefined && instrumentInfo.fundingRate !== '') { pendingTickerInfo.updateFundingRate(Number(instrumentInfo.fundingRate)); } if (instrumentInfo.nextFundingTime !== undefined && instrumentInfo.nextFundingTime !== '') { pendingTickerInfo.updateFundingTimestamp(new Date(Number(instrumentInfo.nextFundingTime))); } if (instrumentInfo.indexPrice !== undefined && instrumentInfo.indexPrice !== '') { pendingTickerInfo.updateIndexPrice(Number(instrumentInfo.indexPrice)); } if (instrumentInfo.markPrice !== undefined && instrumentInfo.markPrice !== '') { pendingTickerInfo.updateMarkPrice(Number(instrumentInfo.markPrice)); } if (instrumentInfo.openInterest !== undefined && instrumentInfo.openInterest !== '') { pendingTickerInfo.updateOpenInterest(Number(instrumentInfo.openInterest)); } if (instrumentInfo.lastPrice !== undefined && instrumentInfo.lastPrice !== '') { pendingTickerInfo.updateLastPrice(Number(instrumentInfo.lastPrice)); } pendingTickerInfo.updateTimestamp(new Date(message.ts)); if (pendingTickerInfo.hasChanged()) { yield pendingTickerInfo.getSnapshot(localTimestamp); } } } exports.BybitV5DerivativeTickerMapper = BybitV5DerivativeTickerMapper; class BybitV5LiquidationsMapper { constructor(_exchange) { this._exchange = _exchange; } canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith('liquidation.'); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'liquidation', symbols } ]; } *map(message, localTimestamp) { // from bybit telegram: When "side":"Buy", a long position was liquidated. Will fix the docs. const bybitLiquidation = message.data; const liquidation = { type: 'liquidation', symbol: bybitLiquidation.symbol, exchange: this._exchange, id: undefined, price: Number(bybitLiquidation.price), amount: Number(bybitLiquidation.size), side: bybitLiquidation.side == 'Buy' ? 'sell' : 'buy', timestamp: new Date(message.ts), localTimestamp }; yield liquidation; } } exports.BybitV5LiquidationsMapper = BybitV5LiquidationsMapper; class BybitV5OptionSummaryMapper { canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith('tickers.'); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'tickers', symbols } ]; } *map(message, localTimestamp) { const symbolParts = message.data.symbol.split('-'); const isPut = symbolParts[3] === 'P'; const strikePrice = Number(symbolParts[2]); const expirationDate = new Date(symbolParts[1] + 'Z'); expirationDate.setUTCHours(8); const optionSummary = { type: 'option_summary', symbol: message.data.symbol, exchange: 'bybit-options', optionType: isPut ? 'put' : 'call', strikePrice, expirationDate, bestBidPrice: (0, handy_1.asNumberIfValid)(message.data.bidPrice), bestBidAmount: (0, handy_1.asNumberIfValid)(message.data.bidSize), bestBidIV: (0, handy_1.asNumberIfValid)(message.data.bidIv), bestAskPrice: (0, handy_1.asNumberIfValid)(message.data.askPrice), bestAskAmount: (0, handy_1.asNumberIfValid)(message.data.askSize), bestAskIV: (0, handy_1.asNumberIfValid)(message.data.askIv), lastPrice: (0, handy_1.asNumberIfValid)(message.data.lastPrice), openInterest: (0, handy_1.asNumberIfValid)(message.data.openInterest), markPrice: (0, handy_1.asNumberIfValid)(message.data.markPrice), markIV: (0, handy_1.asNumberIfValid)(message.data.markPriceIv), delta: (0, handy_1.asNumberIfValid)(message.data.delta), gamma: (0, handy_1.asNumberIfValid)(message.data.gamma), vega: (0, handy_1.asNumberIfValid)(message.data.vega), theta: (0, handy_1.asNumberIfValid)(message.data.theta), rho: undefined, underlyingPrice: (0, handy_1.asNumberIfValid)(message.data.underlyingPrice), underlyingIndex: '', timestamp: new Date(message.ts), localTimestamp: localTimestamp }; yield optionSummary; } } exports.BybitV5OptionSummaryMapper = BybitV5OptionSummaryMapper; // https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md class BybitTradesMapper { constructor(_exchange) { this._exchange = _exchange; } canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith('trade.'); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'trade', symbols } ]; } *map(message, localTimestamp) { for (const trade of message.data) { const timestamp = 'trade_time_ms' in trade ? new Date(Number(trade.trade_time_ms)) : 'tradeTimeMs' in trade ? new Date(Number(trade.tradeTimeMs)) : new Date(trade.timestamp); yield { type: 'trade', symbol: trade.symbol, exchange: this._exchange, id: 'trade_id' in trade ? trade.trade_id : trade.tradeId, price: Number(trade.price), amount: trade.size, side: trade.side == 'Buy' ? 'buy' : trade.side === 'Sell' ? 'sell' : 'unknown', timestamp, localTimestamp }; } } } exports.BybitTradesMapper = BybitTradesMapper; class BybitBookChangeMapper { constructor(_exchange, _canUseBook200Channel) { this._exchange = _exchange; this._canUseBook200Channel = _canUseBook200Channel; } canHandle(message) { if (message.topic === undefined) { return false; } if (this._canUseBook200Channel) { return message.topic.startsWith('orderBook_200.'); } else { return message.topic.startsWith('orderBookL2_25.'); } } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); if (this._canUseBook200Channel) { return [ { channel: 'orderBook_200', symbols } ]; } return [ { channel: 'orderBookL2_25', symbols } ]; } *map(message, localTimestamp) { const topicArray = message.topic.split('.'); const symbol = topicArray[topicArray.length - 1]; const data = message.type === 'snapshot' ? 'order_book' in message.data ? message.data.order_book : 'orderBook' in message.data ? message.data.orderBook : message.data : [...message.data.delete, ...message.data.update, ...message.data.insert]; const timestampBybit = message.timestamp_e6 !== undefined ? Number(message.timestamp_e6) : Number(message.timestampE6); const timestamp = new Date(timestampBybit / 1000); timestamp.μs = timestampBybit % 1000; yield { type: 'book_change', symbol, exchange: this._exchange, isSnapshot: message.type === 'snapshot', bids: data.filter((d) => d.side === 'Buy').map(this._mapBookLevel), asks: data.filter((d) => d.side === 'Sell').map(this._mapBookLevel), timestamp, localTimestamp }; } _mapBookLevel(level) { return { price: Number(level.price), amount: level.size !== undefined ? level.size : 0 }; } } exports.BybitBookChangeMapper = BybitBookChangeMapper; class BybitDerivativeTickerMapper { constructor() { this.pendingTickerInfoHelper = new mapper_1.PendingTickerInfoHelper(); } canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith('instrument_info.'); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'instrument_info', symbols } ]; } *map(message, localTimestamp) { const instrumentInfo = 'symbol' in message.data ? message.data : message.data.update[0]; const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(instrumentInfo.symbol, 'bybit'); const fundingRate = 'funding_rate_e6' in instrumentInfo ? instrumentInfo.funding_rate_e6 : instrumentInfo.fundingRateE6; pendingTickerInfo.updateFundingRate(fundingRate !== undefined ? Number(fundingRate) / 1000000 : undefined); const predictedFundingRate = 'predicted_funding_rate_e6' in instrumentInfo ? instrumentInfo.predicted_funding_rate_e6 : instrumentInfo.predictedFundingRateE6; pendingTickerInfo.updatePredictedFundingRate(predictedFundingRate !== undefined ? Number(predictedFundingRate) / 1000000 : undefined); const nextFundingTime = 'next_funding_time' in instrumentInfo ? instrumentInfo.next_funding_time : instrumentInfo.nextFundingTime; pendingTickerInfo.updateFundingTimestamp(nextFundingTime !== undefined && new Date(nextFundingTime).valueOf() > 0 ? new Date(nextFundingTime) : undefined); if (instrumentInfo.index_price !== undefined) { pendingTickerInfo.updateIndexPrice(Number(instrumentInfo.index_price)); } else if (instrumentInfo.indexPrice !== undefined) { pendingTickerInfo.updateIndexPrice(Number(instrumentInfo.indexPrice)); } else if (instrumentInfo.index_price_e4 !== undefined) { pendingTickerInfo.updateIndexPrice(Number(instrumentInfo.index_price_e4) / 10000); } else if (instrumentInfo.indexPriceE4 !== undefined) { pendingTickerInfo.updateIndexPrice(Number(instrumentInfo.indexPriceE4) / 10000); } if (instrumentInfo.mark_price !== undefined) { pendingTickerInfo.updateMarkPrice(Number(instrumentInfo.mark_price)); } else if (instrumentInfo.markPrice !== undefined) { pendingTickerInfo.updateMarkPrice(Number(instrumentInfo.markPrice)); } else if (instrumentInfo.mark_price_e4 !== undefined) { pendingTickerInfo.updateMarkPrice(Number(instrumentInfo.mark_price_e4) / 10000); } else if (instrumentInfo.markPriceE4 !== undefined) { pendingTickerInfo.updateMarkPrice(Number(instrumentInfo.markPriceE4) / 10000); } if (instrumentInfo.open_interest !== undefined) { pendingTickerInfo.updateOpenInterest(instrumentInfo.open_interest); } else if (instrumentInfo.openInterestE8 !== undefined) { pendingTickerInfo.updateOpenInterest(Number(instrumentInfo.openInterestE8) / 100000000); } else if (instrumentInfo.open_interest_e8 !== undefined) { pendingTickerInfo.updateOpenInterest(instrumentInfo.open_interest_e8 / 100000000); } if (instrumentInfo.last_price !== undefined) { pendingTickerInfo.updateLastPrice(Number(instrumentInfo.last_price)); } else if (instrumentInfo.lastPrice !== undefined) { pendingTickerInfo.updateLastPrice(Number(instrumentInfo.lastPrice)); } else if (instrumentInfo.last_price_e4 !== undefined) { pendingTickerInfo.updateLastPrice(Number(instrumentInfo.last_price_e4) / 10000); } else if (instrumentInfo.lastPriceE4) { pendingTickerInfo.updateLastPrice(Number(instrumentInfo.lastPriceE4) / 10000); } if (message.timestamp_e6 !== undefined) { const timestampBybit = Number(message.timestamp_e6); const timestamp = new Date(timestampBybit / 1000); timestamp.μs = timestampBybit % 1000; pendingTickerInfo.updateTimestamp(timestamp); } else if (message.timestampE6 !== undefined) { const timestampBybit = Number(message.timestampE6); const timestamp = new Date(timestampBybit / 1000); timestamp.μs = timestampBybit % 1000; pendingTickerInfo.updateTimestamp(timestamp); } else { pendingTickerInfo.updateTimestamp(new Date(instrumentInfo.updated_at)); } if (pendingTickerInfo.hasChanged()) { yield pendingTickerInfo.getSnapshot(localTimestamp); } } } exports.BybitDerivativeTickerMapper = BybitDerivativeTickerMapper; class BybitLiquidationsMapper { constructor(_exchange) { this._exchange = _exchange; } canHandle(message) { if (message.topic === undefined) { return false; } return message.topic.startsWith('liquidation.'); } getFilters(symbols) { symbols = (0, handy_1.upperCaseSymbols)(symbols); return [ { channel: 'liquidation', symbols } ]; } *map(message, localTimestamp) { // from bybit telegram: When "side":"Buy", a long position was liquidated. Will fix the docs. if (message.generated) { for (const bybitLiquidation of message.data) { const liquidation = { type: 'liquidation', symbol: bybitLiquidation.symbol, exchange: this._exchange, id: String(bybitLiquidation.id), price: Number(bybitLiquidation.price), amount: bybitLiquidation.qty, side: bybitLiquidation.side == 'Buy' ? 'sell' : 'buy', timestamp: new Date(bybitLiquidation.time), localTimestamp }; yield liquidation; } } else { const bybitLiquidation = message.data; const liquidation = { type: 'liquidation', symbol: bybitLiquidation.symbol, exchange: this._exchange, id: undefined, price: Number(bybitLiquidation.price), amount: Number(bybitLiquidation.qty), side: bybitLiquidation.side == 'Buy' ? 'sell' : 'buy', timestamp: new Date(bybitLiquidation.time), localTimestamp }; yield liquidation; } } } exports.BybitLiquidationsMapper = BybitLiquidationsMapper; //# sourceMappingURL=bybit.js.map