Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

tardis-dev

Package Overview
Dependencies
Maintainers
1
Versions
272
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tardis-dev - npm Package Compare versions

Comparing version 13.20.5 to 13.21.0

2

dist/consts.d.ts

@@ -37,3 +37,3 @@ export declare const EXCHANGES: readonly ["bitmex", "deribit", "binance-futures", "binance-delivery", "binance-options", "binance", "ftx", "okex-futures", "okex-options", "okex-swap", "okex", "huobi-dm", "huobi-dm-swap", "huobi-dm-linear-swap", "huobi", "bitfinex-derivatives", "bitfinex", "coinbase", "cryptofacilities", "kraken", "bitstamp", "gemini", "poloniex", "bybit", "bybit-spot", "bybit-options", "phemex", "delta", "ftx-us", "binance-us", "gate-io-futures", "gate-io", "okcoin", "bitflyer", "hitbtc", "coinflex", "binance-jersey", "binance-dex", "upbit", "ascendex", "dydx", "serum", "mango", "huobi-dm-options", "star-atlas", "crypto-com", "crypto-com-derivatives", "kucoin", "bitnomial", "woo-x", "blockchain-com"];

delta: readonly ["l2_orderbook", "recent_trade", "recent_trade_snapshot", "mark_price", "spot_price", "funding_rate", "product_updates", "announcements", "all_trades", "v2/ticker", "l1_orderbook", "l2_updates", "spot_30mtwap_price"];
'gate-io': readonly ["trades", "depth", "ticker"];
'gate-io': readonly ["trades", "depth", "ticker", "book_ticker", "order_book_update"];
'gate-io-futures': readonly ["trades", "order_book", "tickers", "book_ticker"];

@@ -40,0 +40,0 @@ poloniex: readonly ["price_aggregated_book", "trades", "ticker", "book_lv2"];

@@ -362,3 +362,3 @@ "use strict";

];
const GATE_IO_CHANNELS = ['trades', 'depth', 'ticker'];
const GATE_IO_CHANNELS = ['trades', 'depth', 'ticker', 'book_ticker', 'order_book_update'];
const GATE_IO_FUTURES_CHANNELS = ['trades', 'order_book', 'tickers', 'book_ticker'];

@@ -365,0 +365,0 @@ const POLONIEX_CHANNELS = ['price_aggregated_book', 'trades', 'ticker', 'book_lv2'];

@@ -1,3 +0,42 @@

import { BookChange, Exchange, Trade } from '../types';
import { CircularBuffer } from '../handy';
import { BookChange, BookTicker, Exchange, Trade } from '../types';
import { Mapper } from './mapper';
export declare class GateIOV4BookChangeMapper implements Mapper<'gate-io', BookChange> {
protected readonly exchange: Exchange;
protected readonly symbolToDepthInfoMapping: {
[key: string]: LocalDepthInfo;
};
constructor(exchange: Exchange);
canHandle(message: GateV4OrderBookUpdate | Gatev4OrderBookSnapshot): boolean;
getFilters(symbols?: string[]): {
readonly channel: "order_book_update";
readonly symbols: string[] | undefined;
}[];
map(message: GateV4OrderBookUpdate | Gatev4OrderBookSnapshot, localTimestamp: Date): Generator<BookChange, void, unknown>;
protected mapBookDepthUpdate(depthUpdateData: DepthData, localTimestamp: Date): BookChange | undefined;
protected mapBookLevel(level: [string, string]): {
price: number;
amount: number;
};
}
export declare class GateIOV4BookTickerMapper implements Mapper<'gate-io', BookTicker> {
private readonly _exchange;
constructor(_exchange: Exchange);
canHandle(message: GateV4BookTicker): boolean;
getFilters(symbols?: string[]): {
readonly channel: "book_ticker";
readonly symbols: string[] | undefined;
}[];
map(bookTickerResponse: GateV4BookTicker, localTimestamp: Date): Generator<BookTicker, void, unknown>;
}
export declare class GateIOV4TradesMapper implements Mapper<'gate-io', Trade> {
private readonly _exchange;
constructor(_exchange: Exchange);
canHandle(message: GateV4Trade): boolean;
getFilters(symbols?: string[]): {
readonly channel: "trades";
readonly symbols: string[] | undefined;
}[];
map(tradesMessage: GateV4Trade, localTimestamp: Date): IterableIterator<Trade>;
}
export declare class GateIOTradesMapper implements Mapper<'gate-io', Trade> {

@@ -47,3 +86,78 @@ private readonly _exchange;

};
type GateV4Trade = {
time: 1682689046;
time_ms: 1682689046133;
channel: 'spot.trades';
event: 'update';
result: {
id: 5541729596;
create_time: 1682689046;
create_time_ms: '1682689046123.0';
side: 'sell';
currency_pair: 'SUSD_USDT';
amount: '8.5234';
price: '0.9782';
};
};
type GateV4BookTicker = {
time: 1682689046;
time_ms: 1682689046142;
channel: 'spot.book_ticker';
event: 'update';
result: {
t: 1682689046131;
u: 517377894;
s: 'ETC_ETH';
b: '0.010326';
B: '0.001';
a: '0.010366';
A: '10';
};
};
type Gatev4OrderBookSnapshot = {
channel: 'spot.order_book_update';
event: 'snapshot';
generated: true;
symbol: '1ART_USDT';
result: {
id: 154857784;
current: 1682689045318;
update: 1682689045056;
asks: [string, string][];
bids: [string, string][];
};
};
type GateV4OrderBookUpdate = {
time: 1682689045;
time_ms: 1682689045532;
channel: 'spot.order_book_update';
event: 'update';
result: {
lastUpdateId: undefined;
t: 1682689045424;
e: 'depthUpdate';
E: 1682689045;
s: '1ART_USDT';
U: 154857785;
u: 154857785;
b: [string, string][];
a: [string, string][];
};
};
type LocalDepthInfo = {
bufferedUpdates: CircularBuffer<DepthData>;
snapshotProcessed?: boolean;
lastUpdateId?: number;
validatedFirstUpdate?: boolean;
};
type DepthData = {
lastUpdateId: undefined;
t: number;
s: string;
U: number;
u: number;
b: [string, string][];
a: [string, string][];
};
export {};
//# sourceMappingURL=gateio.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GateIOBookChangeMapper = exports.GateIOTradesMapper = void 0;
exports.GateIOBookChangeMapper = exports.GateIOTradesMapper = exports.GateIOV4TradesMapper = exports.GateIOV4BookTickerMapper = exports.GateIOV4BookChangeMapper = void 0;
const handy_1 = require("../handy");
// https://www.gate.io/docs/websocket/index.html
//v4
class GateIOV4BookChangeMapper {
constructor(exchange) {
this.exchange = exchange;
this.symbolToDepthInfoMapping = {};
}
canHandle(message) {
if (message.channel === undefined) {
return false;
}
if (message.event !== 'update' && message.event !== 'snapshot') {
return false;
}
return message.channel.endsWith('order_book_update');
}
getFilters(symbols) {
symbols = (0, handy_1.upperCaseSymbols)(symbols);
return [
{
channel: 'order_book_update',
symbols
}
];
}
*map(message, localTimestamp) {
const symbol = message.event === 'snapshot' ? message.symbol : message.result.s;
if (this.symbolToDepthInfoMapping[symbol] === undefined) {
this.symbolToDepthInfoMapping[symbol] = {
bufferedUpdates: new handy_1.CircularBuffer(2000)
};
}
const symbolDepthInfo = this.symbolToDepthInfoMapping[symbol];
const snapshotAlreadyProcessed = symbolDepthInfo.snapshotProcessed;
// first check if received message is snapshot and process it as such if it is
if (message.event === 'snapshot') {
// if we've already received 'manual' snapshot, ignore if there is another one
if (snapshotAlreadyProcessed) {
return;
}
// produce snapshot book_change
const snapshotData = message.result;
// mark given symbol depth info that has snapshot processed
symbolDepthInfo.lastUpdateId = snapshotData.id;
symbolDepthInfo.snapshotProcessed = true;
// if there were any depth updates buffered, let's proccess those by adding to or updating the initial snapshot
for (const update of symbolDepthInfo.bufferedUpdates.items()) {
const bookChange = this.mapBookDepthUpdate(update, localTimestamp);
if (bookChange !== undefined) {
for (const bid of update.b) {
const matchingBid = snapshotData.bids.find((b) => b[0] === bid[0]);
if (matchingBid !== undefined) {
matchingBid[1] = bid[1];
}
else {
snapshotData.bids.push(bid);
}
}
for (const ask of update.a) {
const matchingAsk = snapshotData.asks.find((a) => a[0] === ask[0]);
if (matchingAsk !== undefined) {
matchingAsk[1] = ask[1];
}
else {
snapshotData.asks.push(ask);
}
}
}
}
// remove all buffered updates
symbolDepthInfo.bufferedUpdates.clear();
const bookChange = {
type: 'book_change',
symbol,
exchange: this.exchange,
isSnapshot: true,
bids: snapshotData.bids.map(this.mapBookLevel),
asks: snapshotData.asks.map(this.mapBookLevel),
timestamp: new Date(snapshotData.update),
localTimestamp
};
yield bookChange;
}
else if (snapshotAlreadyProcessed) {
// snapshot was already processed let's map the message as normal book_change
const bookChange = this.mapBookDepthUpdate(message.result, localTimestamp);
if (bookChange !== undefined) {
yield bookChange;
}
}
else {
const depthUpdate = message.result;
symbolDepthInfo.bufferedUpdates.append(depthUpdate);
}
}
mapBookDepthUpdate(depthUpdateData, localTimestamp) {
// we can safely assume here that depthContext and lastUpdateId aren't null here as this is method only works
// when we've already processed the snapshot
const depthContext = this.symbolToDepthInfoMapping[depthUpdateData.s];
const lastUpdateId = depthContext.lastUpdateId;
// Drop any event where u is <= lastUpdateId in the snapshot
if (depthUpdateData.u <= lastUpdateId) {
return;
}
// The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
if (!depthContext.validatedFirstUpdate) {
// if there is new instrument added it can have empty book at first and that's normal
const bookSnapshotIsEmpty = lastUpdateId == -1;
if ((depthUpdateData.U <= lastUpdateId + 1 && depthUpdateData.u >= lastUpdateId + 1) || bookSnapshotIsEmpty) {
depthContext.validatedFirstUpdate = true;
}
else {
const message = `Book depth snapshot has no overlap with first update, update ${JSON.stringify(depthUpdateData)}, lastUpdateId: ${lastUpdateId}, exchange ${this.exchange}`;
throw new Error(message);
}
}
return {
type: 'book_change',
symbol: depthUpdateData.s,
exchange: this.exchange,
isSnapshot: false,
bids: depthUpdateData.b.map(this.mapBookLevel),
asks: depthUpdateData.a.map(this.mapBookLevel),
timestamp: new Date(depthUpdateData.t),
localTimestamp: localTimestamp
};
}
mapBookLevel(level) {
const price = Number(level[0]);
const amount = Number(level[1]);
return { price, amount };
}
}
exports.GateIOV4BookChangeMapper = GateIOV4BookChangeMapper;
class GateIOV4BookTickerMapper {
constructor(_exchange) {
this._exchange = _exchange;
}
canHandle(message) {
if (message.channel === undefined) {
return false;
}
if (message.event !== 'update') {
return false;
}
return message.channel.endsWith('book_ticker');
}
getFilters(symbols) {
symbols = (0, handy_1.upperCaseSymbols)(symbols);
return [
{
channel: 'book_ticker',
symbols
}
];
}
*map(bookTickerResponse, localTimestamp) {
const gateBookTicker = bookTickerResponse.result;
const ticker = {
type: 'book_ticker',
symbol: gateBookTicker.s,
exchange: this._exchange,
askAmount: gateBookTicker.A !== undefined ? Number(gateBookTicker.A) : undefined,
askPrice: gateBookTicker.a !== undefined ? Number(gateBookTicker.a) : undefined,
bidPrice: gateBookTicker.b !== undefined ? Number(gateBookTicker.b) : undefined,
bidAmount: gateBookTicker.B !== undefined ? Number(gateBookTicker.B) : undefined,
timestamp: gateBookTicker.t !== undefined ? new Date(gateBookTicker.t) : localTimestamp,
localTimestamp: localTimestamp
};
yield ticker;
}
}
exports.GateIOV4BookTickerMapper = GateIOV4BookTickerMapper;
class GateIOV4TradesMapper {
constructor(_exchange) {
this._exchange = _exchange;
}
canHandle(message) {
if (message.channel === undefined) {
return false;
}
if (message.event !== 'update') {
return false;
}
return message.channel.endsWith('trades');
}
getFilters(symbols) {
symbols = (0, handy_1.upperCaseSymbols)(symbols);
return [
{
channel: 'trades',
symbols
}
];
}
*map(tradesMessage, localTimestamp) {
yield {
type: 'trade',
symbol: tradesMessage.result.currency_pair,
exchange: this._exchange,
id: tradesMessage.result.id.toString(),
price: Number(tradesMessage.result.price),
amount: Number(tradesMessage.result.amount),
side: tradesMessage.result.side == 'sell' ? 'sell' : 'buy',
timestamp: new Date(Number(tradesMessage.result.create_time_ms)),
localTimestamp: localTimestamp
};
}
}
exports.GateIOV4TradesMapper = GateIOV4TradesMapper;
// v3 https://www.gate.io/docs/websocket/index.html
class GateIOTradesMapper {

@@ -7,0 +216,0 @@ constructor(_exchange) {

@@ -9,3 +9,3 @@ import { BookChange, DerivativeTicker, Liquidation, OptionSummary, BookTicker, Trade } from '../types';

export declare const normalizeLiquidations: <T extends "bitmex" | "deribit" | "binance-futures" | "binance-delivery" | "ftx" | "okex-futures" | "okex-swap" | "huobi-dm" | "huobi-dm-swap" | "huobi-dm-linear-swap" | "bitfinex-derivatives" | "cryptofacilities" | "bybit">(exchange: T, localTimestamp: Date) => Mapper<T, Liquidation>;
export declare const normalizeBookTickers: <T extends "bitmex" | "deribit" | "binance-futures" | "binance-delivery" | "binance" | "ftx" | "okex-futures" | "okex-options" | "okex-swap" | "okex" | "huobi-dm" | "huobi-dm-swap" | "huobi-dm-linear-swap" | "huobi" | "bitfinex-derivatives" | "bitfinex" | "coinbase" | "cryptofacilities" | "kraken" | "bybit" | "bybit-spot" | "delta" | "ftx-us" | "binance-us" | "gate-io-futures" | "okcoin" | "bitflyer" | "binance-dex" | "ascendex" | "serum" | "mango" | "star-atlas" | "crypto-com" | "crypto-com-derivatives" | "kucoin" | "woo-x">(exchange: T, localTimestamp: Date) => Mapper<T, BookTicker>;
export declare const normalizeBookTickers: <T extends "bitmex" | "deribit" | "binance-futures" | "binance-delivery" | "binance" | "ftx" | "okex-futures" | "okex-options" | "okex-swap" | "okex" | "huobi-dm" | "huobi-dm-swap" | "huobi-dm-linear-swap" | "huobi" | "bitfinex-derivatives" | "bitfinex" | "coinbase" | "cryptofacilities" | "kraken" | "bybit" | "bybit-spot" | "delta" | "ftx-us" | "binance-us" | "gate-io-futures" | "gate-io" | "okcoin" | "bitflyer" | "binance-dex" | "ascendex" | "serum" | "mango" | "star-atlas" | "crypto-com" | "crypto-com-derivatives" | "kucoin" | "woo-x">(exchange: T, localTimestamp: Date) => Mapper<T, BookTicker>;
//# sourceMappingURL=index.d.ts.map

@@ -91,2 +91,6 @@ "use strict";

};
const GATE_IO_V4_API_SWITCH_DATE = new Date('2023-04-29T00:00:00.000Z');
const shouldUseGateIOV4Mappers = (localTimestamp) => {
return isRealTime(localTimestamp) || localTimestamp.valueOf() >= GATE_IO_V4_API_SWITCH_DATE.valueOf();
};
const tradesMappers = {

@@ -125,3 +129,3 @@ bitmex: () => bitmex_1.bitmexTradesMapper,

delta: (localTimestamp) => new delta_1.DeltaTradesMapper(localTimestamp.valueOf() >= new Date('2020-10-14').valueOf()),
'gate-io': () => new gateio_1.GateIOTradesMapper('gate-io'),
'gate-io': (localTimestamp) => shouldUseGateIOV4Mappers(localTimestamp) ? new gateio_1.GateIOV4TradesMapper('gate-io') : new gateio_1.GateIOTradesMapper('gate-io'),
'gate-io-futures': () => new gateiofutures_1.GateIOFuturesTradesMapper('gate-io-futures'),

@@ -192,3 +196,3 @@ poloniex: (localTimestamp) => shouldUsePoloniexV2Mappers(localTimestamp) ? new poloniex_1.PoloniexV2TradesMapper() : new poloniex_1.PoloniexTradesMapper(),

delta: (localTimestamp) => new delta_1.DeltaBookChangeMapper(localTimestamp.valueOf() >= new Date('2023-04-01').valueOf()),
'gate-io': () => new gateio_1.GateIOBookChangeMapper('gate-io'),
'gate-io': (localTimestamp) => shouldUseGateIOV4Mappers(localTimestamp) ? new gateio_1.GateIOV4BookChangeMapper('gate-io') : new gateio_1.GateIOBookChangeMapper('gate-io'),
'gate-io-futures': () => new gateiofutures_1.GateIOFuturesBookChangeMapper('gate-io-futures'),

@@ -306,3 +310,4 @@ poloniex: (localTimestamp) => shouldUsePoloniexV2Mappers(localTimestamp) ? new poloniex_1.PoloniexV2BookChangeMapper() : new poloniex_1.PoloniexBookChangeMapper(),

delta: () => new delta_1.DeltaBookTickerMapper(),
bybit: () => new bybit_1.BybitV5BookTickerMapper('bybit')
bybit: () => new bybit_1.BybitV5BookTickerMapper('bybit'),
'gate-io': () => new gateio_1.GateIOV4BookTickerMapper('gate-io')
};

@@ -309,0 +314,0 @@ const normalizeTrades = (exchange, localTimestamp) => {

import { Filter } from '../types';
import { RealTimeFeedBase } from './realtimefeed';
export declare class GateIORealTimeFeed extends RealTimeFeedBase {
protected readonly wssURL = "wss://ws.gate.io/v3/";
protected readonly wssURL = "wss://api.gateio.ws/ws/v4/";
protected httpURL: string;
protected mapToSubscribeMessages(filters: Filter<string>[]): any[];
protected messageIsError(message: any): boolean;
protected provideManualSnapshots(filters: Filter<string>[], shouldCancel: () => boolean): Promise<void>;
}
//# sourceMappingURL=gateio.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GateIORealTimeFeed = void 0;
const handy_1 = require("../handy");
const realtimefeed_1 = require("./realtimefeed");

@@ -8,6 +9,6 @@ class GateIORealTimeFeed extends realtimefeed_1.RealTimeFeedBase {

super(...arguments);
this.wssURL = 'wss://ws.gate.io/v3/';
this.wssURL = 'wss://api.gateio.ws/ws/v4/';
this.httpURL = 'https://api.gateio.ws/api/v4';
}
mapToSubscribeMessages(filters) {
const id = 1;
const payload = filters.map((filter) => {

@@ -17,20 +18,26 @@ if (!filter.symbols || filter.symbols.length === 0) {

}
if (filter.channel === 'depth') {
return {
id,
method: `${filter.channel}.subscribe`,
params: filter.symbols.map((s) => {
return [s, 30, '0'];
})
};
if (filter.channel === 'order_book_update') {
return filter.symbols.map((symbol) => {
return {
time: new Date().valueOf(),
channel: `spot.${filter.channel}`,
event: 'subscribe',
method: `${filter.channel}.subscribe`,
payload: [symbol, '100ms']
};
});
}
else {
return {
id,
method: `${filter.channel}.subscribe`,
params: filter.symbols
};
return filter.symbols.map((symbol) => {
return {
time: new Date().valueOf(),
channel: `spot.${filter.channel}`,
event: 'subscribe',
method: `${filter.channel}.subscribe`,
payload: [symbol]
};
});
}
});
return payload;
return payload.flatMap((f) => f);
}

@@ -43,4 +50,28 @@ messageIsError(message) {

}
async provideManualSnapshots(filters, shouldCancel) {
const orderBookFilter = filters.find((f) => f.channel === 'order_book_update');
if (!orderBookFilter) {
return;
}
this.debug('requesting manual snapshots for: %s', orderBookFilter.symbols);
for (let symbol of orderBookFilter.symbols) {
if (shouldCancel()) {
return;
}
const depthSnapshotResponse = await handy_1.httpClient
.get(`${this.httpURL}/spot/order_book?currency_pair=${symbol}&limit=100&with_id=true`)
.json();
const snapshot = {
result: depthSnapshotResponse,
event: 'snapshot',
channel: `spot.order_book_update`,
symbol,
generated: true
};
this.manualSnapshotsBuffer.push(snapshot);
}
this.debug('requested manual snapshots successfully for: %s ', orderBookFilter.symbols);
}
}
exports.GateIORealTimeFeed = GateIORealTimeFeed;
//# sourceMappingURL=gateio.js.map
{
"name": "tardis-dev",
"version": "13.20.5",
"version": "13.21.0",
"engines": {

@@ -5,0 +5,0 @@ "node": ">=12"

@@ -394,3 +394,3 @@ export const EXCHANGES = [

const GATE_IO_CHANNELS = ['trades', 'depth', 'ticker'] as const
const GATE_IO_CHANNELS = ['trades', 'depth', 'ticker', 'book_ticker', 'order_book_update'] as const
const GATE_IO_FUTURES_CHANNELS = ['trades', 'order_book', 'tickers', 'book_ticker'] as const

@@ -397,0 +397,0 @@ const POLONIEX_CHANNELS = ['price_aggregated_book', 'trades', 'ticker', 'book_lv2'] as const

@@ -1,7 +0,246 @@

import { upperCaseSymbols } from '../handy'
import { BookChange, Exchange, Trade } from '../types'
import { CircularBuffer, upperCaseSymbols } from '../handy'
import { BookChange, BookTicker, Exchange, Trade } from '../types'
import { Mapper } from './mapper'
// https://www.gate.io/docs/websocket/index.html
//v4
export class GateIOV4BookChangeMapper implements Mapper<'gate-io', BookChange> {
protected readonly symbolToDepthInfoMapping: {
[key: string]: LocalDepthInfo
} = {}
constructor(protected readonly exchange: Exchange) {}
canHandle(message: GateV4OrderBookUpdate | Gatev4OrderBookSnapshot) {
if (message.channel === undefined) {
return false
}
if (message.event !== 'update' && message.event !== 'snapshot') {
return false
}
return message.channel.endsWith('order_book_update')
}
getFilters(symbols?: string[]) {
symbols = upperCaseSymbols(symbols)
return [
{
channel: 'order_book_update',
symbols
} as const
]
}
*map(message: GateV4OrderBookUpdate | Gatev4OrderBookSnapshot, localTimestamp: Date) {
const symbol = message.event === 'snapshot' ? message.symbol : message.result.s
if (this.symbolToDepthInfoMapping[symbol] === undefined) {
this.symbolToDepthInfoMapping[symbol] = {
bufferedUpdates: new CircularBuffer<DepthData>(2000)
}
}
const symbolDepthInfo = this.symbolToDepthInfoMapping[symbol]
const snapshotAlreadyProcessed = symbolDepthInfo.snapshotProcessed
// first check if received message is snapshot and process it as such if it is
if (message.event === 'snapshot') {
// if we've already received 'manual' snapshot, ignore if there is another one
if (snapshotAlreadyProcessed) {
return
}
// produce snapshot book_change
const snapshotData = message.result
// mark given symbol depth info that has snapshot processed
symbolDepthInfo.lastUpdateId = snapshotData.id
symbolDepthInfo.snapshotProcessed = true
// if there were any depth updates buffered, let's proccess those by adding to or updating the initial snapshot
for (const update of symbolDepthInfo.bufferedUpdates.items()) {
const bookChange = this.mapBookDepthUpdate(update, localTimestamp)
if (bookChange !== undefined) {
for (const bid of update.b) {
const matchingBid = snapshotData.bids.find((b) => b[0] === bid[0])
if (matchingBid !== undefined) {
matchingBid[1] = bid[1]
} else {
snapshotData.bids.push(bid)
}
}
for (const ask of update.a) {
const matchingAsk = snapshotData.asks.find((a) => a[0] === ask[0])
if (matchingAsk !== undefined) {
matchingAsk[1] = ask[1]
} else {
snapshotData.asks.push(ask)
}
}
}
}
// remove all buffered updates
symbolDepthInfo.bufferedUpdates.clear()
const bookChange: BookChange = {
type: 'book_change',
symbol,
exchange: this.exchange,
isSnapshot: true,
bids: snapshotData.bids.map(this.mapBookLevel),
asks: snapshotData.asks.map(this.mapBookLevel),
timestamp: new Date(snapshotData.update),
localTimestamp
}
yield bookChange
} else if (snapshotAlreadyProcessed) {
// snapshot was already processed let's map the message as normal book_change
const bookChange = this.mapBookDepthUpdate(message.result as DepthData, localTimestamp)
if (bookChange !== undefined) {
yield bookChange
}
} else {
const depthUpdate = message.result as DepthData
symbolDepthInfo.bufferedUpdates.append(depthUpdate)
}
}
protected mapBookDepthUpdate(depthUpdateData: DepthData, localTimestamp: Date): BookChange | undefined {
// we can safely assume here that depthContext and lastUpdateId aren't null here as this is method only works
// when we've already processed the snapshot
const depthContext = this.symbolToDepthInfoMapping[depthUpdateData.s]!
const lastUpdateId = depthContext.lastUpdateId!
// Drop any event where u is <= lastUpdateId in the snapshot
if (depthUpdateData.u <= lastUpdateId) {
return
}
// The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
if (!depthContext.validatedFirstUpdate) {
// if there is new instrument added it can have empty book at first and that's normal
const bookSnapshotIsEmpty = lastUpdateId == -1
if ((depthUpdateData.U <= lastUpdateId + 1 && depthUpdateData.u >= lastUpdateId + 1) || bookSnapshotIsEmpty) {
depthContext.validatedFirstUpdate = true
} else {
const message = `Book depth snapshot has no overlap with first update, update ${JSON.stringify(
depthUpdateData
)}, lastUpdateId: ${lastUpdateId}, exchange ${this.exchange}`
throw new Error(message)
}
}
return {
type: 'book_change',
symbol: depthUpdateData.s,
exchange: this.exchange,
isSnapshot: false,
bids: depthUpdateData.b.map(this.mapBookLevel),
asks: depthUpdateData.a.map(this.mapBookLevel),
timestamp: new Date(depthUpdateData.t),
localTimestamp: localTimestamp
}
}
protected mapBookLevel(level: [string, string]) {
const price = Number(level[0])
const amount = Number(level[1])
return { price, amount }
}
}
export class GateIOV4BookTickerMapper implements Mapper<'gate-io', BookTicker> {
constructor(private readonly _exchange: Exchange) {}
canHandle(message: GateV4BookTicker) {
if (message.channel === undefined) {
return false
}
if (message.event !== 'update') {
return false
}
return message.channel.endsWith('book_ticker')
}
getFilters(symbols?: string[]) {
symbols = upperCaseSymbols(symbols)
return [
{
channel: 'book_ticker',
symbols
} as const
]
}
*map(bookTickerResponse: GateV4BookTicker, localTimestamp: Date) {
const gateBookTicker = bookTickerResponse.result
const ticker: BookTicker = {
type: 'book_ticker',
symbol: gateBookTicker.s,
exchange: this._exchange,
askAmount: gateBookTicker.A !== undefined ? Number(gateBookTicker.A) : undefined,
askPrice: gateBookTicker.a !== undefined ? Number(gateBookTicker.a) : undefined,
bidPrice: gateBookTicker.b !== undefined ? Number(gateBookTicker.b) : undefined,
bidAmount: gateBookTicker.B !== undefined ? Number(gateBookTicker.B) : undefined,
timestamp: gateBookTicker.t !== undefined ? new Date(gateBookTicker.t) : localTimestamp,
localTimestamp: localTimestamp
}
yield ticker
}
}
export class GateIOV4TradesMapper implements Mapper<'gate-io', Trade> {
constructor(private readonly _exchange: Exchange) {}
canHandle(message: GateV4Trade) {
if (message.channel === undefined) {
return false
}
if (message.event !== 'update') {
return false
}
return message.channel.endsWith('trades')
}
getFilters(symbols?: string[]) {
symbols = upperCaseSymbols(symbols)
return [
{
channel: 'trades',
symbols
} as const
]
}
*map(tradesMessage: GateV4Trade, localTimestamp: Date): IterableIterator<Trade> {
yield {
type: 'trade',
symbol: tradesMessage.result.currency_pair,
exchange: this._exchange,
id: tradesMessage.result.id.toString(),
price: Number(tradesMessage.result.price),
amount: Number(tradesMessage.result.amount),
side: tradesMessage.result.side == 'sell' ? 'sell' : 'buy',
timestamp: new Date(Number(tradesMessage.result.create_time_ms)),
localTimestamp: localTimestamp
}
}
}
// v3 https://www.gate.io/docs/websocket/index.html
export class GateIOTradesMapper implements Mapper<'gate-io', Trade> {

@@ -127,1 +366,74 @@ private readonly _seenSymbols = new Set<string>()

}
type GateV4Trade = {
time: 1682689046
time_ms: 1682689046133
channel: 'spot.trades'
event: 'update'
result: {
id: 5541729596
create_time: 1682689046
create_time_ms: '1682689046123.0'
side: 'sell'
currency_pair: 'SUSD_USDT'
amount: '8.5234'
price: '0.9782'
}
}
type GateV4BookTicker = {
time: 1682689046
time_ms: 1682689046142
channel: 'spot.book_ticker'
event: 'update'
result: { t: 1682689046131; u: 517377894; s: 'ETC_ETH'; b: '0.010326'; B: '0.001'; a: '0.010366'; A: '10' }
}
type Gatev4OrderBookSnapshot = {
channel: 'spot.order_book_update'
event: 'snapshot'
generated: true
symbol: '1ART_USDT'
result: {
id: 154857784
current: 1682689045318
update: 1682689045056
asks: [string, string][]
bids: [string, string][]
}
}
type GateV4OrderBookUpdate = {
time: 1682689045
time_ms: 1682689045532
channel: 'spot.order_book_update'
event: 'update'
result: {
lastUpdateId: undefined
t: 1682689045424
e: 'depthUpdate'
E: 1682689045
s: '1ART_USDT'
U: 154857785
u: 154857785
b: [string, string][]
a: [string, string][]
}
}
type LocalDepthInfo = {
bufferedUpdates: CircularBuffer<DepthData>
snapshotProcessed?: boolean
lastUpdateId?: number
validatedFirstUpdate?: boolean
}
type DepthData = {
lastUpdateId: undefined
t: number
s: string
U: number
u: number
b: [string, string][]
a: [string, string][]
}

@@ -66,4 +66,10 @@ import { ONE_SEC_IN_MS } from '../handy'

import { FTXBookChangeMapper, FTXDerivativeTickerMapper, FTXLiquidationsMapper, FTXBookTickerMapper, FTXTradesMapper } from './ftx'
import { GateIOBookChangeMapper, GateIOTradesMapper } from './gateio'
import {
GateIOBookChangeMapper,
GateIOTradesMapper,
GateIOV4BookChangeMapper,
GateIOV4BookTickerMapper,
GateIOV4TradesMapper
} from './gateio'
import {
GateIOFuturesBookChangeMapper,

@@ -162,2 +168,8 @@ GateIOFuturesBookTickerMapper,

const GATE_IO_V4_API_SWITCH_DATE = new Date('2023-04-29T00:00:00.000Z')
const shouldUseGateIOV4Mappers = (localTimestamp: Date) => {
return isRealTime(localTimestamp) || localTimestamp.valueOf() >= GATE_IO_V4_API_SWITCH_DATE.valueOf()
}
const tradesMappers = {

@@ -206,3 +218,4 @@ bitmex: () => bitmexTradesMapper,

delta: (localTimestamp: Date) => new DeltaTradesMapper(localTimestamp.valueOf() >= new Date('2020-10-14').valueOf()),
'gate-io': () => new GateIOTradesMapper('gate-io'),
'gate-io': (localTimestamp: Date) =>
shouldUseGateIOV4Mappers(localTimestamp) ? new GateIOV4TradesMapper('gate-io') : new GateIOTradesMapper('gate-io'),
'gate-io-futures': () => new GateIOFuturesTradesMapper('gate-io-futures'),

@@ -290,3 +303,4 @@ poloniex: (localTimestamp: Date) =>

delta: (localTimestamp: Date) => new DeltaBookChangeMapper(localTimestamp.valueOf() >= new Date('2023-04-01').valueOf()),
'gate-io': () => new GateIOBookChangeMapper('gate-io'),
'gate-io': (localTimestamp: Date) =>
shouldUseGateIOV4Mappers(localTimestamp) ? new GateIOV4BookChangeMapper('gate-io') : new GateIOBookChangeMapper('gate-io'),
'gate-io-futures': () => new GateIOFuturesBookChangeMapper('gate-io-futures'),

@@ -428,3 +442,4 @@ poloniex: (localTimestamp: Date) =>

delta: () => new DeltaBookTickerMapper(),
bybit: () => new BybitV5BookTickerMapper('bybit')
bybit: () => new BybitV5BookTickerMapper('bybit'),
'gate-io': () => new GateIOV4BookTickerMapper('gate-io')
}

@@ -431,0 +446,0 @@

@@ -0,1 +1,2 @@

import { httpClient } from '../handy'
import { Filter } from '../types'

@@ -5,6 +6,6 @@ import { RealTimeFeedBase } from './realtimefeed'

export class GateIORealTimeFeed extends RealTimeFeedBase {
protected readonly wssURL = 'wss://ws.gate.io/v3/'
protected readonly wssURL = 'wss://api.gateio.ws/ws/v4/'
protected httpURL = 'https://api.gateio.ws/api/v4'
protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
const id = 1
const payload = filters.map((filter) => {

@@ -15,20 +16,26 @@ if (!filter.symbols || filter.symbols.length === 0) {

if (filter.channel === 'depth') {
return {
id,
method: `${filter.channel}.subscribe`,
params: filter.symbols.map((s) => {
return [s, 30, '0']
})
}
if (filter.channel === 'order_book_update') {
return filter.symbols!.map((symbol) => {
return {
time: new Date().valueOf(),
channel: `spot.${filter.channel}`,
event: 'subscribe',
method: `${filter.channel}.subscribe`,
payload: [symbol, '100ms']
}
})
} else {
return {
id,
method: `${filter.channel}.subscribe`,
params: filter.symbols
}
return filter.symbols!.map((symbol) => {
return {
time: new Date().valueOf(),
channel: `spot.${filter.channel}`,
event: 'subscribe',
method: `${filter.channel}.subscribe`,
payload: [symbol]
}
})
}
})
return payload
return payload.flatMap((f) => f)
}

@@ -43,2 +50,33 @@

}
protected async provideManualSnapshots(filters: Filter<string>[], shouldCancel: () => boolean) {
const orderBookFilter = filters.find((f) => f.channel === 'order_book_update')
if (!orderBookFilter) {
return
}
this.debug('requesting manual snapshots for: %s', orderBookFilter.symbols!)
for (let symbol of orderBookFilter.symbols!) {
if (shouldCancel()) {
return
}
const depthSnapshotResponse = await httpClient
.get(`${this.httpURL}/spot/order_book?currency_pair=${symbol}&limit=100&with_id=true`)
.json()
const snapshot = {
result: depthSnapshotResponse,
event: 'snapshot',
channel: `spot.order_book_update`,
symbol,
generated: true
}
this.manualSnapshotsBuffer.push(snapshot)
}
this.debug('requested manual snapshots successfully for: %s ', orderBookFilter.symbols!)
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc