UNPKG

@hackape/tardis-dev

Version:

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

247 lines 9.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OkexOptionSummaryMapper = exports.OkexDerivativeTickerMapper = exports.OkexBookChangeMapper = exports.OkexTradesMapper = void 0; const mapper_1 = require("./mapper"); // https://www.okex.com/docs/en/#ws_swap-README class OkexTradesMapper { constructor(_exchange, _market) { this._exchange = _exchange; this._market = _market; this._seenSymbols = new Set(); } canHandle(message) { return message.table === `${this._market}/trade`; } getFilters(symbols) { return [ { channel: `${this._market}/trade`, symbols } ]; } *map(okexTradesMessage, localTimestamp) { for (const okexTrade of okexTradesMessage.data) { const symbol = okexTrade.instrument_id; // always ignore first returned trade as it's a 'stale' trade, which has already been published before disconnect if (this._seenSymbols.has(symbol) === false) { this._seenSymbols.add(symbol); break; } yield { type: 'trade', symbol, exchange: this._exchange, id: typeof okexTrade.trade_id === 'string' ? okexTrade.trade_id : undefined, price: Number(okexTrade.price), amount: okexTrade.qty !== undefined ? Number(okexTrade.qty) : Number(okexTrade.size), side: okexTrade.side, timestamp: new Date(okexTrade.timestamp), localTimestamp: localTimestamp }; } } } exports.OkexTradesMapper = OkexTradesMapper; const mapBookLevel = (level) => { const price = Number(level[0]); const amount = Number(level[1]); return { price, amount }; }; class OkexBookChangeMapper { constructor(_exchange, _market, _canUseTickByTickChannel) { this._exchange = _exchange; this._market = _market; this._canUseTickByTickChannel = _canUseTickByTickChannel; } canHandle(message) { const channelSuffix = this._canUseTickByTickChannel ? 'depth_l2_tbt' : 'depth'; return message.table === `${this._market}/${channelSuffix}`; } getFilters(symbols) { if (this._canUseTickByTickChannel) { return [ { channel: `${this._market}/depth_l2_tbt`, symbols } ]; } // subscribe to both book channels and in canHandle decide which one to use // as one can subscribe to date range period that overlaps both when only depth channel has been available // and when both were available (both depth and depth_l2_tbt) return [ { channel: `${this._market}/depth_l2_tbt`, symbols }, { channel: `${this._market}/depth`, symbols } ]; } *map(okexDepthDataMessage, localTimestamp) { for (const message of okexDepthDataMessage.data) { if (message.bids.length === 0 && message.asks.length === 0) { continue; } const timestamp = new Date(message.timestamp); if (timestamp.valueOf() === 0) { continue; } yield { type: 'book_change', symbol: message.instrument_id, exchange: this._exchange, isSnapshot: okexDepthDataMessage.action === 'partial', bids: message.bids.map(mapBookLevel), asks: message.asks.map(mapBookLevel), timestamp, localTimestamp: localTimestamp }; } } } exports.OkexBookChangeMapper = OkexBookChangeMapper; class OkexDerivativeTickerMapper { constructor(_exchange) { this._exchange = _exchange; this.pendingTickerInfoHelper = new mapper_1.PendingTickerInfoHelper(); this._futuresChannels = ['futures/ticker', 'futures/mark_price']; this._swapChannels = ['swap/ticker', 'swap/mark_price', 'swap/funding_rate']; } canHandle(message) { const channels = this._exchange === 'okex-futures' ? this._futuresChannels : this._swapChannels; return channels.includes(message.table); } getFilters(symbols) { const channels = this._exchange === 'okex-futures' ? this._futuresChannels : this._swapChannels; return channels.map((channel) => { return { channel, symbols }; }); } *map(message, localTimestamp) { for (const okexMessage of message.data) { const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(okexMessage.instrument_id, this._exchange); if ('funding_rate' in okexMessage) { pendingTickerInfo.updateFundingRate(Number(okexMessage.funding_rate)); pendingTickerInfo.updateFundingTimestamp(new Date(okexMessage.funding_time)); if (okexMessage.estimated_rate !== undefined) { pendingTickerInfo.updatePredictedFundingRate(Number(okexMessage.estimated_rate)); } } if ('mark_price' in okexMessage) { pendingTickerInfo.updateMarkPrice(Number(okexMessage.mark_price)); } if ('open_interest' in okexMessage) { const openInterest = Number(okexMessage.open_interest); if (openInterest > 0) { pendingTickerInfo.updateOpenInterest(Number(okexMessage.open_interest)); } } if ('last' in okexMessage) { pendingTickerInfo.updateLastPrice(Number(okexMessage.last)); } if (okexMessage.timestamp !== undefined) { pendingTickerInfo.updateTimestamp(new Date(okexMessage.timestamp)); } if (pendingTickerInfo.hasChanged()) { yield pendingTickerInfo.getSnapshot(localTimestamp); } } } } exports.OkexDerivativeTickerMapper = OkexDerivativeTickerMapper; function asNumberIfValid(val) { if (val === undefined || val === null) { return; } var asNumber = Number(val); if (isNaN(asNumber) || isFinite(asNumber) === false) { return; } if (asNumber === 0) { return; } return asNumber; } class OkexOptionSummaryMapper { constructor() { this._indexPrices = new Map(); this.expiration_regex = /(\d{2})(\d{2})(\d{2})/; } canHandle(message) { return message.table === 'index/ticker' || message.table === 'option/summary'; } getFilters(symbols) { const indexes = symbols !== undefined ? symbols.map((s) => { const symbolParts = s.split('-'); return `${symbolParts[0]}-${symbolParts[1]}`; }) : undefined; return [ { channel: `option/summary`, symbols }, { channel: `index/ticker`, indexes } ]; } *map(message, localTimestamp) { if (message.table === 'index/ticker') { for (const index of message.data) { const lastIndexPrice = Number(index.last); if (lastIndexPrice > 0) { this._indexPrices.set(index.instrument_id, lastIndexPrice); } } return; } for (const summary of message.data) { const symbolParts = summary.instrument_id.split('-'); const isPut = symbolParts[4] === 'P'; const strikePrice = Number(symbolParts[3]); var dateArray = this.expiration_regex.exec(symbolParts[2]); const expirationDate = new Date(Date.UTC(+('20' + dateArray[1]), +dateArray[2] - 1, +dateArray[3], 8, 0, 0, 0)); const lastUnderlyingPrice = this._indexPrices.get(summary.underlying); const optionSummary = { type: 'option_summary', symbol: summary.instrument_id, exchange: 'okex-options', optionType: isPut ? 'put' : 'call', strikePrice, expirationDate, bestBidPrice: asNumberIfValid(summary.best_bid), bestBidAmount: asNumberIfValid(summary.best_bid_size), bestBidIV: asNumberIfValid(summary.bid_vol), bestAskPrice: asNumberIfValid(summary.best_ask), bestAskAmount: asNumberIfValid(summary.best_ask_size), bestAskIV: asNumberIfValid(summary.ask_vol), lastPrice: asNumberIfValid(summary.last), openInterest: asNumberIfValid(summary.open_interest), markPrice: asNumberIfValid(summary.mark_price), markIV: asNumberIfValid(summary.mark_vol), delta: asNumberIfValid(summary.delta), gamma: asNumberIfValid(summary.gamma), vega: asNumberIfValid(summary.vega), theta: asNumberIfValid(summary.theta), rho: undefined, underlyingPrice: lastUnderlyingPrice, underlyingIndex: summary.underlying, timestamp: new Date(summary.timestamp), localTimestamp: localTimestamp }; yield optionSummary; } } } exports.OkexOptionSummaryMapper = OkexOptionSummaryMapper; //# sourceMappingURL=okex.js.map