@hackape/tardis-dev
Version:
Convenient access to tick-level historical and real-time cryptocurrency market data via Node.js
271 lines • 10.6 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.BitfinexLiquidationsMapper = exports.BitfinexDerivativeTickerMapper = exports.BitfinexBookChangeMapper = exports.BitfinexTradesMapper = void 0;
const mapper_1 = require("./mapper");
// https://docs.bitfinex.com/v2/docs/ws-general
class BitfinexTradesMapper {
constructor(_exchange) {
this._exchange = _exchange;
this._channelIdToSymbolMap = new Map();
}
canHandle(message) {
// non sub messages are provided as arrays
if (Array.isArray(message)) {
// first test if message itself provides channel name and if so if it's trades
const channelName = message[message.length - 2];
if (typeof channelName === 'string') {
return channelName === 'trades';
}
// otherwise use channel to id mapping
return this._channelIdToSymbolMap.get(message[0]) !== undefined;
}
// store mapping between channel id and symbols
if (message.event === 'subscribed') {
const isTradeChannel = message.channel === 'trades';
if (isTradeChannel) {
this._channelIdToSymbolMap.set(message.chanId, message.pair);
}
}
return false;
}
getFilters(symbols) {
return [
{
channel: 'trades',
symbols
}
];
}
*map(message, localTimestamp) {
const symbolFromMessage = message[message.length - 1];
const symbol = typeof symbolFromMessage === 'string' ? symbolFromMessage : this._channelIdToSymbolMap.get(message[0]);
// ignore if we don't have matching symbol
if (symbol === undefined) {
return;
}
// ignore heartbeats
if (message[1] === 'hb') {
return;
}
// ignore snapshots
if (message[1] !== 'te') {
return;
}
const [id, timestamp, amount, price] = message[2];
const trade = {
type: 'trade',
symbol,
exchange: this._exchange,
id: String(id),
price,
amount: Math.abs(amount),
side: amount < 0 ? 'sell' : 'buy',
timestamp: new Date(timestamp),
localTimestamp: localTimestamp
};
yield trade;
}
}
exports.BitfinexTradesMapper = BitfinexTradesMapper;
class BitfinexBookChangeMapper {
constructor(_exchange) {
this._exchange = _exchange;
this._channelIdToSymbolMap = new Map();
}
canHandle(message) {
// non sub messages are provided as arrays
if (Array.isArray(message)) {
// first test if message itself provides channel name and if so if it's a book
const channelName = message[message.length - 2];
if (typeof channelName === 'string') {
return channelName === 'book';
}
// otherwise use channel to id mapping
return this._channelIdToSymbolMap.get(message[0]) !== undefined;
}
// store mapping between channel id and symbols
if (message.event === 'subscribed') {
const isBookP0Channel = message.channel === 'book' && message.prec === 'P0';
if (isBookP0Channel) {
this._channelIdToSymbolMap.set(message.chanId, message.pair);
}
}
return false;
}
getFilters(symbols) {
return [
{
channel: 'book',
symbols
}
];
}
*map(message, localTimestamp) {
const symbolFromMessage = message[message.length - 1];
const symbol = typeof symbolFromMessage === 'string' ? symbolFromMessage : this._channelIdToSymbolMap.get(message[0]);
// ignore if we don't have matching symbol
if (symbol === undefined) {
return;
}
// ignore heartbeats
if (message[1] === 'hb') {
return;
}
const isSnapshot = Array.isArray(message[1][0]);
const bookLevels = (isSnapshot ? message[1] : [message[1]]);
const asks = bookLevels.filter((level) => level[2] < 0);
const bids = bookLevels.filter((level) => level[2] > 0);
const bookChange = {
type: 'book_change',
symbol,
exchange: this._exchange,
isSnapshot,
bids: bids.map(this._mapBookLevel),
asks: asks.map(this._mapBookLevel),
timestamp: new Date(message[3]),
localTimestamp: localTimestamp
};
yield bookChange;
}
_mapBookLevel(level) {
const [price, count, bitfinexAmount] = level;
const amount = count === 0 ? 0 : Math.abs(bitfinexAmount);
return { price, amount };
}
}
exports.BitfinexBookChangeMapper = BitfinexBookChangeMapper;
class BitfinexDerivativeTickerMapper {
constructor() {
this._channelIdToSymbolMap = new Map();
this.pendingTickerInfoHelper = new mapper_1.PendingTickerInfoHelper();
}
canHandle(message) {
// non sub messages are provided as arrays
if (Array.isArray(message)) {
// first test if message itself provides channel name and if so if it's a status
const channelName = message[message.length - 2];
if (typeof channelName === 'string') {
return channelName === 'status';
}
// otherwise use channel to id mapping
return this._channelIdToSymbolMap.get(message[0]) !== undefined;
}
// store mapping between channel id and symbols
if (message.event === 'subscribed') {
const isDerivStatusChannel = message.channel === 'status' && message.key && message.key.startsWith('deriv:');
if (isDerivStatusChannel) {
this._channelIdToSymbolMap.set(message.chanId, message.key.replace('deriv:t', ''));
}
}
return false;
}
getFilters(symbols) {
return [
{
channel: 'status',
symbols
}
];
}
*map(message, localTimestamp) {
const symbolFromMessage = message[message.length - 1];
const symbol = typeof symbolFromMessage === 'string' ? symbolFromMessage : this._channelIdToSymbolMap.get(message[0]);
// ignore if we don't have matching symbol
if (symbol === undefined) {
return;
}
// ignore heartbeats
if (message[1] === 'hb') {
return;
}
const statusInfo = message[1];
// https://docs.bitfinex.com/v2/reference#ws-public-status
const fundingRate = statusInfo[11];
const indexPrice = statusInfo[3];
const lastPrice = statusInfo[2];
const markPrice = statusInfo[14];
const openInterest = statusInfo[17];
const nextFundingTimestamp = statusInfo[7];
const predictedFundingRate = statusInfo[8];
const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(symbol, 'bitfinex-derivatives');
pendingTickerInfo.updateFundingRate(fundingRate);
pendingTickerInfo.updateFundingTimestamp(nextFundingTimestamp !== undefined ? new Date(nextFundingTimestamp) : undefined);
pendingTickerInfo.updatePredictedFundingRate(predictedFundingRate);
pendingTickerInfo.updateIndexPrice(indexPrice);
pendingTickerInfo.updateLastPrice(lastPrice);
pendingTickerInfo.updateMarkPrice(markPrice);
pendingTickerInfo.updateOpenInterest(openInterest);
pendingTickerInfo.updateTimestamp(new Date(message[3]));
if (pendingTickerInfo.hasChanged()) {
yield pendingTickerInfo.getSnapshot(localTimestamp);
}
}
}
exports.BitfinexDerivativeTickerMapper = BitfinexDerivativeTickerMapper;
class BitfinexLiquidationsMapper {
constructor(_exchange) {
this._exchange = _exchange;
this._liquidationsChannelId = undefined;
}
canHandle(message) {
// non sub messages are provided as arrays
if (Array.isArray(message)) {
// first test if message itself provides channel name and if so if it's liquidations
const channelName = message[message.length - 2];
if (typeof channelName === 'string') {
return channelName === 'liquidations';
}
// otherwise use channel id
return this._liquidationsChannelId === message[0];
}
// store liquidation channel id
if (message.event === 'subscribed') {
const isLiquidationsChannel = message.channel === 'status' && message.key === 'liq:global';
if (isLiquidationsChannel) {
this._liquidationsChannelId = message.chanId;
}
}
return false;
}
getFilters() {
// liquidations channel is global, not per symbol
return [
{
channel: 'liquidations'
}
];
}
*map(message, localTimestamp) {
// ignore heartbeats
if (message[1] === 'hb') {
return;
}
// see https://docs.bitfinex.com/reference#ws-public-status
for (let bitfinexLiquidation of message[1]) {
const isInitialLiquidationTrigger = bitfinexLiquidation[8] === 0;
// process only initial liquidation triggers not subsequent 'matches', assumption here is that
// there's only single initial liquidation trigger but there can be multiple matches for single liquidation
if (isInitialLiquidationTrigger) {
const id = String(bitfinexLiquidation[1]);
const timestamp = new Date(bitfinexLiquidation[2]);
const symbol = bitfinexLiquidation[4].replace('t', '');
const price = bitfinexLiquidation[6];
const amount = bitfinexLiquidation[5];
const liquidation = {
type: 'liquidation',
symbol,
exchange: this._exchange,
id,
price,
amount: Math.abs(amount),
side: amount < 0 ? 'buy' : 'sell',
timestamp,
localTimestamp: localTimestamp
};
yield liquidation;
}
}
}
}
exports.BitfinexLiquidationsMapper = BitfinexLiquidationsMapper;
//# sourceMappingURL=bitfinex.js.map
;