UNPKG

tardis-dev

Version:

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

370 lines 13.9 kB
import { asNonZeroNumberOrUndefined, lowerCaseSymbols, upperCaseSymbols } from "../handy.js"; // https://binance-docs.github.io/apidocs/voptions/en/#websocket-market-streams export class BinanceEuropeanOptionsTradesMapper { canHandle(message) { if (message.stream === undefined) { return false; } return message.stream.endsWith('@trade'); } getFilters(symbols) { symbols = upperCaseSymbols(symbols); return [ { channel: 'trade', symbols } ]; } *map(binanceTradeResponse, localTimestamp) { const trade = { type: 'trade', symbol: binanceTradeResponse.data.s, exchange: 'binance-european-options', id: binanceTradeResponse.data.t, price: Number(binanceTradeResponse.data.p), amount: Number(binanceTradeResponse.data.q), side: binanceTradeResponse.data.S === '-1' ? 'sell' : 'buy', timestamp: new Date(binanceTradeResponse.data.T), localTimestamp: localTimestamp }; yield trade; } } export class BinanceEuropeanOptionsTradesMapperV2 { canHandle(message) { if (message.stream === undefined) { return false; } return message.stream.endsWith('@optionTrade'); } getFilters(symbols) { symbols = lowerCaseSymbols(symbols); return [ { channel: 'optionTrade', symbols } ]; } *map(binanceTradeResponse, localTimestamp) { const trade = { type: 'trade', symbol: binanceTradeResponse.data.s, exchange: 'binance-european-options', id: String(binanceTradeResponse.data.t), price: Number(binanceTradeResponse.data.p), amount: Number(binanceTradeResponse.data.q), side: binanceTradeResponse.data.m ? 'sell' : 'buy', timestamp: new Date(binanceTradeResponse.data.T), localTimestamp: localTimestamp }; yield trade; } } export class BinanceEuropeanOptionsBookChangeMapper { canHandle(message) { if (message.stream === undefined) { return false; } return message.stream.includes('@depth100'); } getFilters(symbols) { symbols = upperCaseSymbols(symbols); return [ { channel: 'depth100', symbols } ]; } *map(message, localTimestamp) { const bookChange = { type: 'book_change', symbol: message.data.s, exchange: 'binance-european-options', isSnapshot: true, bids: message.data.b.map(this.mapBookLevel), asks: message.data.a.map(this.mapBookLevel), timestamp: message.data.E !== undefined ? new Date(message.data.E) : new Date(message.data.T), localTimestamp }; yield bookChange; } mapBookLevel(level) { const price = Number(level[0]); const amount = Number(level[1]); return { price, amount }; } } export class BinanceEuropeanOptionsBookChangeMapperV2 { canHandle(message) { if (message.stream === undefined) { return false; } return message.stream.includes('@depth20'); } getFilters(symbols) { symbols = lowerCaseSymbols(symbols); return [ { channel: 'depth20', symbols } ]; } *map(message, localTimestamp) { const bookChange = { type: 'book_change', symbol: message.data.s, exchange: 'binance-european-options', isSnapshot: true, bids: message.data.b.map(this.mapBookLevel), asks: message.data.a.map(this.mapBookLevel), timestamp: new Date(message.data.T), localTimestamp }; yield bookChange; } mapBookLevel(level) { const price = Number(level[0]); const amount = Number(level[1]); return { price, amount }; } } export class BinanceEuropeanOptionsBookTickerMapper { canHandle(message) { if (message.stream === undefined) { return false; } return message.stream.endsWith('@bookTicker'); } getFilters(symbols) { symbols = lowerCaseSymbols(symbols); return [ { channel: 'bookTicker', symbols } ]; } *map(message, localTimestamp) { const bestBidPrice = Number(message.data.b); const bestBidAmount = Number(message.data.B); const bestAskPrice = Number(message.data.a); const bestAskAmount = Number(message.data.A); const bookTicker = { type: 'book_ticker', symbol: message.data.s, exchange: 'binance-european-options', bidPrice: bestBidPrice > 0 ? bestBidPrice : undefined, bidAmount: bestBidAmount > 0 ? bestBidAmount : undefined, askPrice: bestAskPrice > 0 ? bestAskPrice : undefined, askAmount: bestAskAmount > 0 ? bestAskAmount : undefined, timestamp: new Date(message.data.T), localTimestamp }; yield bookTicker; } } export class BinanceEuropeanOptionSummaryMapper { _indexPrices = new Map(); _openInterests = new Map(); canHandle(message) { if (message.stream === undefined) { return false; } return message.stream.endsWith('@ticker') || message.stream.endsWith('@index') || message.stream.includes('@openInterest'); } getFilters(symbols) { symbols = upperCaseSymbols(symbols); const indexes = symbols !== undefined ? symbols.map((s) => { const symbolParts = s.split('-'); return `${symbolParts[0]}USDT`; }) : undefined; const underlyings = symbols !== undefined ? symbols.map((s) => { const symbolParts = s.split('-'); return `${symbolParts[0]}`; }) : undefined; return [ { channel: 'ticker', symbols }, { channel: 'index', symbols: indexes }, { channel: 'openInterest', symbols: underlyings } ]; } *map(message, localTimestamp) { if (message.stream.endsWith('@index')) { const lastIndexPrice = Number(message.data.p); if (lastIndexPrice > 0) { this._indexPrices.set(message.data.s, lastIndexPrice); } return; } if (message.stream.includes('@openInterest')) { for (let data of message.data) { const openInterest = Number(data.o); if (openInterest >= 0) { this._openInterests.set(data.s, openInterest); } } return; } const optionInfo = message.data; const [base, expiryPart, strikePrice, optionType] = optionInfo.s.split('-'); const expirationDate = new Date(`20${expiryPart.slice(0, 2)}-${expiryPart.slice(2, 4)}-${expiryPart.slice(4, 6)}Z`); expirationDate.setUTCHours(8); const isPut = optionType === 'P'; const underlyingIndex = `${base}USDT`; const bestBidPrice = asNonZeroNumberOrUndefined(optionInfo.bo); const bestBidIV = bestBidPrice !== undefined ? asNonZeroNumberOrUndefined(optionInfo.b) : undefined; const bestAskPrice = asNonZeroNumberOrUndefined(optionInfo.ao); const bestAskIV = bestAskPrice !== undefined ? asNonZeroNumberOrUndefined(optionInfo.a) : undefined; const optionSummary = { type: 'option_summary', symbol: optionInfo.s, exchange: 'binance-european-options', optionType: isPut ? 'put' : 'call', strikePrice: Number(strikePrice), expirationDate, bestBidPrice, bestBidAmount: asNonZeroNumberOrUndefined(optionInfo.bq), bestBidIV: bestBidIV === -1 ? undefined : bestBidIV, bestAskPrice, bestAskAmount: asNonZeroNumberOrUndefined(optionInfo.aq), bestAskIV: bestAskIV === -1 ? undefined : bestAskIV, lastPrice: asNonZeroNumberOrUndefined(optionInfo.c), openInterest: this._openInterests.get(optionInfo.s), markPrice: asNonZeroNumberOrUndefined(optionInfo.mp), markIV: undefined, delta: asNonZeroNumberOrUndefined(optionInfo.d), gamma: asNonZeroNumberOrUndefined(optionInfo.g), vega: asNonZeroNumberOrUndefined(optionInfo.v), theta: asNonZeroNumberOrUndefined(optionInfo.t), rho: undefined, underlyingPrice: this._indexPrices.get(underlyingIndex), underlyingIndex, timestamp: new Date(optionInfo.E), localTimestamp: localTimestamp }; yield optionSummary; } } export class BinanceEuropeanOptionSummaryMapperV2 { _lastPrices = new Map(); _openInterests = new Map(); canHandle(message) { if (message.stream === undefined) { return false; } return (message.stream.endsWith('@optionMarkPrice') || message.stream.endsWith('@optionTicker') || message.stream.includes('@optionOpenInterest')); } getFilters(symbols) { symbols = lowerCaseSymbols(symbols); const underlyings = symbols !== undefined ? symbols.map((s) => { const symbolParts = s.split('-'); return `${symbolParts[0]}usdt`; }) : undefined; return [ { channel: 'optionMarkPrice', symbols: underlyings }, { channel: 'optionTicker', symbols }, { channel: 'optionOpenInterest', symbols: underlyings } ]; } *map(message, localTimestamp) { // Handle optionTicker messages to track last prices if (message.stream.endsWith('@optionTicker')) { const tickerData = message.data; const lastPrice = Number(tickerData.c); if (lastPrice > 0) { this._lastPrices.set(tickerData.s, lastPrice); } return; } // Handle optionOpenInterest messages to track open interest if (message.stream.includes('@optionOpenInterest')) { const openInterestArray = message.data; for (let oi of openInterestArray) { const openInterest = Number(oi.o); if (openInterest >= 0) { this._openInterests.set(oi.s, openInterest); } } return; } // optionMarkPrice contains all data needed: greeks, IV, best bid/ask, mark price, and index price const markPriceArray = message.data; for (let markData of markPriceArray) { const [base, expiryPart, strikePrice, optionType] = markData.s.split('-'); const expirationDate = new Date(`20${expiryPart.slice(0, 2)}-${expiryPart.slice(2, 4)}-${expiryPart.slice(4, 6)}Z`); expirationDate.setUTCHours(8); const isPut = optionType === 'P'; const underlyingIndex = `${base}USDT`; const bestBidPrice = asNonZeroNumberOrUndefined(markData.bo); const bestBidIV = bestBidPrice !== undefined ? asNonZeroNumberOrUndefined(markData.b) : undefined; const bestAskPrice = asNonZeroNumberOrUndefined(markData.ao); const bestAskIV = bestAskPrice !== undefined ? asNonZeroNumberOrUndefined(markData.a) : undefined; const markPrice = asNonZeroNumberOrUndefined(markData.mp); const markIV = asNonZeroNumberOrUndefined(markData.vo); const delta = asNonZeroNumberOrUndefined(markData.d); const gamma = asNonZeroNumberOrUndefined(markData.g); const vega = asNonZeroNumberOrUndefined(markData.v); const theta = asNonZeroNumberOrUndefined(markData.t); const underlyingPrice = asNonZeroNumberOrUndefined(markData.i); // Index price is included in mark price data const optionSummary = { type: 'option_summary', symbol: markData.s, exchange: 'binance-european-options', optionType: isPut ? 'put' : 'call', strikePrice: Number(strikePrice), expirationDate, bestBidPrice, bestBidAmount: asNonZeroNumberOrUndefined(markData.bq), bestBidIV: bestBidIV === -1 ? undefined : bestBidIV, bestAskPrice, bestAskAmount: asNonZeroNumberOrUndefined(markData.aq), bestAskIV: bestAskIV === -1 ? undefined : bestAskIV, lastPrice: this._lastPrices.get(markData.s), openInterest: this._openInterests.get(markData.s), markPrice, markIV, delta, gamma, vega, theta, rho: undefined, underlyingPrice, underlyingIndex, timestamp: new Date(markData.E), localTimestamp: localTimestamp }; yield optionSummary; } } } //# sourceMappingURL=binanceeuropeanoptions.js.map