tardis-dev
Version:
Convenient access to tick-level historical and real-time cryptocurrency market data via Node.js
370 lines • 13.9 kB
JavaScript
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