From c14fca0fe45442e6dc73fd406ef323c3706cfdd5 Mon Sep 17 00:00:00 2001 From: Thaddeus Date: Tue, 5 Nov 2024 15:08:54 +0100 Subject: [PATCH] feat: add bitget and bitget-futures support --- src/consts.ts | 11 +- src/handy.ts | 5 + src/mappers/bitget.ts | 230 +++++++++++++++++++++++ src/mappers/index.ts | 16 +- src/realtimefeeds/bitget.ts | 59 ++++++ src/realtimefeeds/index.ts | 5 +- test/__snapshots__/mappers.test.ts.snap | 233 ++++++++++++++++++++++++ test/mappers.test.ts | 183 ++++++++++++++++++- 8 files changed, 733 insertions(+), 9 deletions(-) create mode 100644 src/mappers/bitget.ts create mode 100644 src/realtimefeeds/bitget.ts diff --git a/src/consts.ts b/src/consts.ts index 2b81d92..2109574 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -53,7 +53,9 @@ export const EXCHANGES = [ 'kucoin-futures', 'bitnomial', 'woo-x', - 'blockchain-com' + 'blockchain-com', + 'bitget', + 'bitget-futures' ] as const const BINANCE_CHANNELS = ['trade', 'aggTrade', 'ticker', 'depth', 'depthSnapshot', 'bookTicker', 'recentTrades', 'borrowInterest'] as const @@ -472,6 +474,9 @@ const KUCOIN_FUTURES_CHANNELS = [ 'contractMarket/snapshot' ] +const BITGET_CHANNELS = ['trade', 'books1', 'books'] +const BITGET_FUTURES_CHANNELS = ['trade', 'books1', 'books', 'ticker'] + export const EXCHANGE_CHANNELS_INFO = { bitmex: BITMEX_CHANNELS, coinbase: COINBASE_CHANNELS, @@ -527,5 +532,7 @@ export const EXCHANGE_CHANNELS_INFO = { 'blockchain-com': BLOCKCHAIN_COM_CHANNELS, 'binance-european-options': BINANCE_EUROPEAN_OPTIONS_CHANNELS, 'okex-spreads': OKEX_SPREADS_CHANNELS, - 'kucoin-futures': KUCOIN_FUTURES_CHANNELS + 'kucoin-futures': KUCOIN_FUTURES_CHANNELS, + bitget: BITGET_CHANNELS, + 'bitget-futures': BITGET_FUTURES_CHANNELS } diff --git a/src/handy.ts b/src/handy.ts index affaaf3..bca8353 100644 --- a/src/handy.ts +++ b/src/handy.ts @@ -177,6 +177,11 @@ export function* batch(symbols: string[], batchSize: number) { yield symbols.slice(i, i + batchSize) } } +export function* batchObjects(payload: T[], batchSize: number) { + for (let i = 0; i < payload.length; i += batchSize) { + yield payload.slice(i, i + batchSize) + } +} export function parseμs(dateString: string): number { // check if we have ISO 8601 format date string, e.g: 2019-06-01T00:03:03.1238784Z or 2020-07-22T00:09:16.836773Z diff --git a/src/mappers/bitget.ts b/src/mappers/bitget.ts new file mode 100644 index 0000000..3e61951 --- /dev/null +++ b/src/mappers/bitget.ts @@ -0,0 +1,230 @@ +import { upperCaseSymbols } from '../handy' +import { BookChange, BookTicker, DerivativeTicker, Exchange, Trade } from '../types' +import { Mapper, PendingTickerInfoHelper } from './mapper' + +export class BitgetTradesMapper implements Mapper<'bitget' | 'bitget-futures', Trade> { + constructor(private readonly _exchange: Exchange) {} + + canHandle(message: BitgetTradeMessage) { + return message.arg.channel === 'trade' && message.action === 'update' + } + + getFilters(symbols?: string[]) { + symbols = upperCaseSymbols(symbols) + + return [ + { + channel: 'trade', + symbols + } as const + ] + } + + *map(message: BitgetTradeMessage, localTimestamp: Date): IterableIterator { + for (let trade of message.data) { + yield { + type: 'trade', + symbol: message.arg.instId, + exchange: this._exchange, + id: trade.tradeId, + price: Number(trade.price), + amount: Number(trade.size), + side: trade.side === 'buy' ? 'buy' : 'sell', + timestamp: new Date(Number(trade.ts)), + localTimestamp: localTimestamp + } + } + } +} + +function mapPriceLevel(level: [string, string]) { + return { + price: Number(level[0]), + amount: Number(level[1]) + } +} +export class BitgetBookChangeMapper implements Mapper<'bitget' | 'bitget-futures', BookChange> { + constructor(private readonly _exchange: Exchange) {} + + canHandle(message: BitgetOrderbookMessage) { + return message.arg.channel === 'books' && (message.action === 'update' || message.action === 'snapshot') + } + + getFilters(symbols?: string[]) { + symbols = upperCaseSymbols(symbols) + + return [ + { + channel: 'books', + symbols + } as const + ] + } + + *map(message: BitgetOrderbookMessage, localTimestamp: Date): IterableIterator { + for (let orderbookData of message.data) { + yield { + type: 'book_change', + symbol: message.arg.instId, + exchange: this._exchange, + isSnapshot: message.action === 'snapshot', + bids: orderbookData.bids.map(mapPriceLevel), + asks: orderbookData.asks.map(mapPriceLevel), + timestamp: new Date(Number(orderbookData.ts)), + localTimestamp + } + } + } +} + +export class BitgetBookTickerMapper implements Mapper<'bitget' | 'bitget-futures', BookTicker> { + constructor(private readonly _exchange: Exchange) {} + + canHandle(message: BitgetBBoMessage) { + return message.arg.channel === 'books1' && message.action === 'snapshot' + } + + getFilters(symbols?: string[]) { + symbols = upperCaseSymbols(symbols) + + return [ + { + channel: `books1` as const, + symbols + } + ] + } + + *map(message: BitgetBBoMessage, localTimestamp: Date): IterableIterator { + for (const bboMessage of message.data) { + const ticker: BookTicker = { + type: 'book_ticker', + symbol: message.arg.instId, + exchange: this._exchange, + + askAmount: Number(bboMessage.asks[0][1]), + askPrice: Number(bboMessage.asks[0][0]), + + bidPrice: Number(bboMessage.bids[0][0]), + bidAmount: Number(bboMessage.bids[0][1]), + timestamp: new Date(Number(bboMessage.ts)), + localTimestamp: localTimestamp + } + + yield ticker + } + } +} + +export class BitgetDerivativeTickerMapper implements Mapper<'bitget-futures', DerivativeTicker> { + private readonly pendingTickerInfoHelper = new PendingTickerInfoHelper() + + canHandle(message: BitgetTickerMessage) { + return message.arg.channel === 'ticker' && message.action === 'snapshot' + } + + getFilters(symbols?: string[]) { + return [ + { + channel: 'ticker', + symbols + } as const + ] + } + + *map(message: BitgetTickerMessage, localTimestamp: Date): IterableIterator { + for (const tickerMessage of message.data) { + const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(tickerMessage.symbol, 'bitget-futures') + + pendingTickerInfo.updateIndexPrice(Number(tickerMessage.indexPrice)) + pendingTickerInfo.updateMarkPrice(Number(tickerMessage.markPrice)) + pendingTickerInfo.updateOpenInterest(Number(tickerMessage.holdingAmount)) + pendingTickerInfo.updateLastPrice(Number(tickerMessage.lastPr)) + + pendingTickerInfo.updateTimestamp(new Date(Number(tickerMessage.ts))) + + if (tickerMessage.nextFundingTime !== '0') { + pendingTickerInfo.updateFundingTimestamp(new Date(Number(tickerMessage.nextFundingTime))) + pendingTickerInfo.updateFundingRate(Number(tickerMessage.fundingRate)) + } + + if (pendingTickerInfo.hasChanged()) { + yield pendingTickerInfo.getSnapshot(localTimestamp) + } + } + } +} + +type BitgetTradeMessage = { + action: 'update' + arg: { instType: 'SPOT'; channel: 'trade'; instId: 'OPUSDT' } + data: [{ ts: '1730332800983'; price: '1.717'; size: '56.16'; side: 'buy'; tradeId: '1235670816495050754' }] + ts: 1730332800989 +} + +type BitgetOrderbookMessage = + | { + action: 'snapshot' + arg: { instType: 'SPOT'; channel: 'books'; instId: 'SYLOUSDT' } + data: [ + { + asks: [string, string][] + bids: [string, string][] + checksum: 0 + ts: '1730331046984' + } + ] + ts: 1730332800437 + } + | { + action: 'update' + arg: { instType: 'SPOT'; channel: 'books'; instId: 'BANDUSDT' } + data: [ + { + asks: [string, string][] + bids: [string, string][] + checksum: 79466786 + ts: '1730332859977' + } + ] + ts: 1730332859979 + } + +type BitgetBBoMessage = { + action: 'snapshot' + arg: { instType: 'SPOT'; channel: 'books1'; instId: 'METISUSDT' } + data: [{ asks: [['44.90', '0.6927']]; bids: [['44.82', '3.5344']]; checksum: 0; ts: '1730332859988' }] + ts: 1730332859989 +} + +type BitgetTickerMessage = { + action: 'snapshot' + arg: { instType: 'COIN-FUTURES'; channel: 'ticker'; instId: 'BTCUSD' } + data: [ + { + instId: 'BTCUSD' + lastPr: '72331.5' + bidPr: '72331.5' + askPr: '72331.8' + bidSz: '7.296' + askSz: '0.02' + open24h: '72047.8' + high24h: '72934.8' + low24h: '71422.8' + change24h: '-0.00561' + fundingRate: '0.000116' + nextFundingTime: string + markPrice: string + indexPrice: string + holdingAmount: string + baseVolume: '7543.376' + quoteVolume: '544799876.924' + openUtc: '72335.3' + symbolType: '1' + symbol: 'BTCUSD' + deliveryPrice: '0' + ts: '1730332823217' + } + ] + ts: 1730332823220 +} diff --git a/src/mappers/index.ts b/src/mappers/index.ts index c707d07..20b7d0c 100644 --- a/src/mappers/index.ts +++ b/src/mappers/index.ts @@ -24,6 +24,7 @@ import { BitfinexTradesMapper } from './bitfinex' import { BitflyerBookChangeMapper, bitflyerBookTickerMapper, bitflyerTradesMapper } from './bitflyer' +import { BitgetBookChangeMapper, BitgetBookTickerMapper, BitgetDerivativeTickerMapper, BitgetTradesMapper } from './bitget' import { BitmexBookChangeMapper, BitmexDerivativeTickerMapper, @@ -271,7 +272,9 @@ const tradesMappers = { 'blockchain-com': () => new BlockchainComTradesMapper(), 'bybit-options': () => new BybitV5TradesMapper('bybit-options'), 'binance-european-options': () => new BinanceEuropeanOptionsTradesMapper(), - 'okex-spreads': () => new OkexSpreadsTradesMapper() + 'okex-spreads': () => new OkexSpreadsTradesMapper(), + bitget: () => new BitgetTradesMapper('bitget'), + 'bitget-futures': () => new BitgetTradesMapper('bitget-futures') } const bookChangeMappers = { @@ -359,7 +362,9 @@ const bookChangeMappers = { 'blockchain-com': () => new BlockchainComBookChangeMapper(), 'bybit-options': () => new BybitV5BookChangeMapper('bybit-options', 25), 'binance-european-options': () => new BinanceEuropeanOptionsBookChangeMapper(), - 'okex-spreads': () => new OkexSpreadsBookChangeMapper() + 'okex-spreads': () => new OkexSpreadsBookChangeMapper(), + bitget: () => new BitgetBookChangeMapper('bitget'), + 'bitget-futures': () => new BitgetBookChangeMapper('bitget-futures') } const derivativeTickersMappers = { @@ -393,7 +398,8 @@ const derivativeTickersMappers = { 'crypto-com-derivatives': () => new CryptoComDerivativeTickerMapper('crypto-com-derivatives'), 'crypto-com': () => new CryptoComDerivativeTickerMapper('crypto-com'), 'woo-x': () => new WooxDerivativeTickerMapper(), - 'kucoin-futures': () => new KucoinFuturesDerivativeTickerMapper() + 'kucoin-futures': () => new KucoinFuturesDerivativeTickerMapper(), + 'bitget-futures': () => new BitgetDerivativeTickerMapper() } const optionsSummaryMappers = { @@ -485,7 +491,9 @@ const bookTickersMappers = { bybit: () => new BybitV5BookTickerMapper('bybit'), 'gate-io': () => new GateIOV4BookTickerMapper('gate-io'), 'okex-spreads': () => new OkexSpreadsBookTickerMapper(), - 'kucoin-futures': () => new KucoinFuturesBookTickerMapper() + 'kucoin-futures': () => new KucoinFuturesBookTickerMapper(), + bitget: () => new BitgetBookTickerMapper('bitget'), + 'bitget-futures': () => new BitgetBookTickerMapper('bitget-futures') } export const normalizeTrades = (exchange: T, localTimestamp: Date): Mapper => { diff --git a/src/realtimefeeds/bitget.ts b/src/realtimefeeds/bitget.ts new file mode 100644 index 0000000..8fab4ce --- /dev/null +++ b/src/realtimefeeds/bitget.ts @@ -0,0 +1,59 @@ +import { batchObjects } from '../handy' +import { Filter } from '../types' +import { RealTimeFeedBase } from './realtimefeed' + +abstract class BitgetRealTimeFeedBase extends RealTimeFeedBase { + protected throttleSubscribeMS = 100 + protected readonly wssURL = 'wss://ws.bitget.com/v2/ws/public' + + protected mapToSubscribeMessages(filters: Filter[]): any[] { + const argsInputs = filters.flatMap((filter) => { + if (!filter.symbols || filter.symbols.length === 0) { + throw new Error('BitgetRealTimeFeed requires explicitly specified symbols when subscribing to live feed') + } + + return filter.symbols.map((symbol) => { + return { + instType: this.getInstType(symbol), + channel: filter.channel, + instId: symbol + } + }) + }) + + const payload = [...batchObjects(argsInputs, 5)].map((args) => { + return { + op: 'subscribe', + args + } + }) + + return payload + } + + protected messageIsError(message: any): boolean { + return message.event === 'error' + } + + abstract getInstType(symbol: string): string +} + +export class BitgetRealTimeFeed extends BitgetRealTimeFeedBase { + getInstType(_: string) { + return 'SPOT' + } +} + +export class BitgetFuturesRealTimeFeed extends BitgetRealTimeFeedBase { + getInstType(symbol: string) { + if (symbol.endsWith('USDT')) { + return 'USDT-FUTURES' + } + + if (symbol.endsWith('PERP')) { + return 'USDC-FUTURES' + } + + return 'COIN-FUTURES' + } +} diff --git a/src/realtimefeeds/index.ts b/src/realtimefeeds/index.ts index aec194b..1322a12 100644 --- a/src/realtimefeeds/index.ts +++ b/src/realtimefeeds/index.ts @@ -50,6 +50,7 @@ import { BinanceEuropeanOptionsRealTimeFeed } from './binanceeuropeanoptions' import { OkexSpreadsRealTimeFeed } from './okexspreads' import { KucoinFuturesRealTimeFeed } from './kucoinfutures' import { DydxV4RealTimeFeed } from './dydx_v4' +import { BitgetFuturesRealTimeFeed, BitgetRealTimeFeed } from './bitget' export * from './realtimefeed' @@ -110,7 +111,9 @@ const realTimeFeedsMap: { 'binance-european-options': BinanceEuropeanOptionsRealTimeFeed, 'okex-spreads': OkexSpreadsRealTimeFeed, 'kucoin-futures': KucoinFuturesRealTimeFeed, - 'dydx-v4': DydxV4RealTimeFeed + 'dydx-v4': DydxV4RealTimeFeed, + bitget: BitgetRealTimeFeed, + 'bitget-futures': BitgetFuturesRealTimeFeed } export function getRealTimeFeedFactory(exchange: Exchange): RealTimeFeed { diff --git a/test/__snapshots__/mappers.test.ts.snap b/test/__snapshots__/mappers.test.ts.snap index c1f8636..c9a3ef7 100644 --- a/test/__snapshots__/mappers.test.ts.snap +++ b/test/__snapshots__/mappers.test.ts.snap @@ -135,6 +135,239 @@ Array [ ] `; +exports[`map bitget messages 1`] = ` +Array [ + Object { + "amount": 0.001373, + "exchange": "bitget", + "id": "1235671058980749319", + "localTimestamp": 2024-08-23T00:00:00.498Z, + "price": 72389.99, + "side": "buy", + "symbol": "BTCUSDT", + "timestamp": 2024-10-31T00:00:58.796Z, + "type": "trade", + }, +] +`; + +exports[`map bitget messages 2`] = `Array []`; + +exports[`map bitget messages 3`] = `Array []`; + +exports[`map bitget messages 4`] = ` +Array [ + Object { + "amount": 22.75, + "exchange": "bitget", + "id": "1235671063632220170", + "localTimestamp": 2024-08-23T00:00:00.498Z, + "price": 4.987, + "side": "sell", + "symbol": "RENDERUSDT", + "timestamp": 2024-10-31T00:00:59.905Z, + "type": "trade", + }, +] +`; + +exports[`map bitget messages 5`] = `Array []`; + +exports[`map bitget messages 6`] = ` +Array [ + Object { + "asks": Array [ + Object { + "amount": 1600, + "price": 0.09707, + }, + Object { + "amount": 511.87, + "price": 0.09711, + }, + ], + "bids": Array [ + Object { + "amount": 1213.91, + "price": 0.09694, + }, + ], + "exchange": "bitget", + "isSnapshot": true, + "localTimestamp": 2024-08-23T00:00:00.498Z, + "symbol": "BRETTUSDT", + "timestamp": 2024-10-31T00:00:00.391Z, + "type": "book_change", + }, +] +`; + +exports[`map bitget messages 7`] = ` +Array [ + Object { + "asks": Array [], + "bids": Array [ + Object { + "amount": 3326.85, + "price": 0.12306, + }, + ], + "exchange": "bitget", + "isSnapshot": false, + "localTimestamp": 2024-08-23T00:00:00.498Z, + "symbol": "LMWRUSDT", + "timestamp": 2024-10-31T00:00:59.981Z, + "type": "book_change", + }, +] +`; + +exports[`map bitget messages 8`] = ` +Array [ + Object { + "askAmount": 0.00408, + "askPrice": 72377.25, + "bidAmount": 1.1646, + "bidPrice": 72354.67, + "exchange": "bitget", + "localTimestamp": 2024-08-23T00:00:00.498Z, + "symbol": "BTCUSDC", + "timestamp": 2024-10-31T00:00:59.984Z, + "type": "book_ticker", + }, +] +`; + +exports[`map bitget-futures messages 1`] = `Array []`; + +exports[`map bitget-futures messages 2`] = `Array []`; + +exports[`map bitget-futures messages 3`] = ` +Array [ + Object { + "amount": 447, + "exchange": "bitget-futures", + "id": "1235671063871070209", + "localTimestamp": 2024-08-23T00:00:00.498Z, + "price": 0.168385, + "side": "buy", + "symbol": "DOGEUSD", + "timestamp": 2024-10-31T00:00:59.962Z, + "type": "trade", + }, +] +`; + +exports[`map bitget-futures messages 4`] = ` +Array [ + Object { + "asks": Array [ + Object { + "amount": 51, + "price": 0.5426, + }, + ], + "bids": Array [ + Object { + "amount": 468, + "price": 0.542, + }, + Object { + "amount": 1576, + "price": 0.5419, + }, + ], + "exchange": "bitget-futures", + "isSnapshot": true, + "localTimestamp": 2024-08-23T00:00:00.498Z, + "symbol": "BELUSDT", + "timestamp": 2024-10-31T00:00:00.303Z, + "type": "book_change", + }, +] +`; + +exports[`map bitget-futures messages 5`] = ` +Array [ + Object { + "asks": Array [ + Object { + "amount": 144.6, + "price": 5.085, + }, + ], + "bids": Array [ + Object { + "amount": 536.2, + "price": 5.081, + }, + ], + "exchange": "bitget-futures", + "isSnapshot": false, + "localTimestamp": 2024-08-23T00:00:00.498Z, + "symbol": "AXSUSDT", + "timestamp": 2024-10-31T00:00:59.988Z, + "type": "book_change", + }, +] +`; + +exports[`map bitget-futures messages 6`] = ` +Array [ + Object { + "askAmount": 10230, + "askPrice": 0.3555, + "bidAmount": 112096, + "bidPrice": 0.3553, + "exchange": "bitget-futures", + "localTimestamp": 2024-08-23T00:00:00.498Z, + "symbol": "ADAUSDT", + "timestamp": 2024-10-31T00:00:59.988Z, + "type": "book_ticker", + }, +] +`; + +exports[`map bitget-futures messages 7`] = `Array []`; + +exports[`map bitget-futures messages 8`] = ` +Array [ + Object { + "exchange": "bitget-futures", + "fundingRate": 0.0001, + "fundingTimestamp": 2024-10-31T08:00:00.000Z, + "indexPrice": 0.47080586, + "lastPrice": 0.4706, + "localTimestamp": 2024-08-23T00:00:00.498Z, + "markPrice": 0.4705, + "openInterest": 8572031.1, + "predictedFundingRate": undefined, + "symbol": "STORJUSDT", + "timestamp": 2024-10-31T00:00:23.155Z, + "type": "derivative_ticker", + }, +] +`; + +exports[`map bitget-futures messages 9`] = ` +Array [ + Object { + "exchange": "bitget-futures", + "fundingRate": undefined, + "fundingTimestamp": undefined, + "indexPrice": 72327.926666, + "lastPrice": 73733.7, + "localTimestamp": 2024-08-23T00:00:00.498Z, + "markPrice": 73731.8, + "openInterest": 57.714, + "predictedFundingRate": undefined, + "symbol": "BTCUSDZ24", + "timestamp": 2024-10-31T00:00:23.217Z, + "type": "derivative_ticker", + }, +] +`; + exports[`map bitnomial messages 1`] = ` Array [ Object { diff --git a/test/mappers.test.ts b/test/mappers.test.ts index ebf7612..c8098e9 100644 --- a/test/mappers.test.ts +++ b/test/mappers.test.ts @@ -33,7 +33,8 @@ const exchangesWithDerivativeInfo: Exchange[] = [ 'crypto-com-derivatives', 'crypto-com', 'woo-x', - 'kucoin-futures' + 'kucoin-futures', + 'bitget-futures' ] const exchangesWithBookTickerInfo: Exchange[] = [ @@ -72,7 +73,9 @@ const exchangesWithBookTickerInfo: Exchange[] = [ 'bybit', 'gate-io', 'okex-spreads', - 'kucoin-futures' + 'kucoin-futures', + 'bitget', + 'bitget-futures' ] const exchangesWithOptionsSummary: Exchange[] = [ @@ -9110,3 +9113,179 @@ test('map dydx-v4 messages', () => { expect(mappedMessages).toMatchSnapshot() } }) + +test('map bitget messages', () => { + const messages = [ + { + action: 'update', + arg: { instType: 'SPOT', channel: 'trade', instId: 'BTCUSDT' }, + data: [{ ts: '1730332858796', price: '72389.99', size: '0.001373', side: 'buy', tradeId: '1235671058980749319' }], + ts: 1730332858805 + }, + { event: 'subscribe', arg: { instType: 'SPOT', channel: 'trade', instId: 'XAUTUSDT' } }, + { + action: 'snapshot', + arg: { instType: 'SPOT', channel: 'trade', instId: 'SNEKUSDT' }, + data: [{ ts: '1730332799292', price: '0.0010074', size: '23617.11', side: 'sell', tradeId: '1235670809402884097' }], + ts: 1730332800338 + }, + { + action: 'update', + arg: { instType: 'SPOT', channel: 'trade', instId: 'RENDERUSDT' }, + data: [{ ts: '1730332859905', price: '4.987', size: '22.75', side: 'sell', tradeId: '1235671063632220170' }], + ts: 1730332859917 + }, + { event: 'subscribe', arg: { instType: 'SPOT', channel: 'books', instId: 'LUMIAUSDT' } }, + { + action: 'snapshot', + arg: { instType: 'SPOT', channel: 'books', instId: 'BRETTUSDT' }, + data: [ + { + asks: [ + ['0.09707', '1600.00'], + ['0.09711', '511.87'] + ], + bids: [['0.09694', '1213.91']], + checksum: 0, + ts: '1730332800391' + } + ], + ts: 1730332800436 + }, + { + action: 'update', + arg: { instType: 'SPOT', channel: 'books', instId: 'LMWRUSDT' }, + data: [{ asks: [], bids: [['0.12306', '3326.85']], checksum: 1073441524, ts: '1730332859981' }], + ts: 1730332859983 + }, + { + action: 'snapshot', + arg: { instType: 'SPOT', channel: 'books1', instId: 'BTCUSDC' }, + data: [{ asks: [['72377.25', '0.00408']], bids: [['72354.67', '1.16460']], checksum: 0, ts: '1730332859984' }], + ts: 1730332859985 + } + ] + const mapper = createMapper('bitget', new Date('2024-08-23T00:00:00.4985250Z')) + + for (const message of messages) { + const mappedMessages = mapper.map(message, new Date('2024-08-23T00:00:00.4985250Z')) + expect(mappedMessages).toMatchSnapshot() + } +}) + +test('map bitget-futures messages', () => { + const messages = [ + { event: 'subscribe', arg: { instType: 'USDT-FUTURES', channel: 'trade', instId: 'LTCUSDT' } }, + { event: 'subscribe', arg: { instType: 'USDC-FUTURES', channel: 'trade', instId: 'BTCPERP' } }, + { + action: 'update', + arg: { instType: 'COIN-FUTURES', channel: 'trade', instId: 'DOGEUSD' }, + data: [{ ts: '1730332859962', price: '0.168385', size: '447', side: 'buy', tradeId: '1235671063871070209' }], + ts: 1730332859979 + }, + { + action: 'snapshot', + arg: { instType: 'USDT-FUTURES', channel: 'books', instId: 'BELUSDT' }, + data: [ + { + asks: [['0.5426', '51']], + bids: [ + ['0.5420', '468'], + ['0.5419', '1576'] + ], + checksum: 0, + ts: '1730332800303' + } + ], + ts: 1730332800315 + }, + { + action: 'update', + arg: { instType: 'USDT-FUTURES', channel: 'books', instId: 'AXSUSDT' }, + data: [ + { + asks: [['5.085', '144.6']], + bids: [['5.081', '536.2']], + checksum: -1258619028, + ts: '1730332859988' + } + ], + ts: 1730332859991 + }, + { + action: 'snapshot', + arg: { instType: 'USDT-FUTURES', channel: 'books1', instId: 'ADAUSDT' }, + data: [{ asks: [['0.3555', '10230']], bids: [['0.3553', '112096']], checksum: 0, ts: '1730332859988' }], + ts: 1730332859989 + }, + { event: 'subscribe', arg: { instType: 'USDT-FUTURES', channel: 'ticker', instId: 'BTCUSDT' } }, + { + action: 'snapshot', + arg: { instType: 'USDT-FUTURES', channel: 'ticker', instId: 'STORJUSDT' }, + data: [ + { + instId: 'STORJUSDT', + lastPr: '0.4706', + bidPr: '0.4705', + askPr: '0.4708', + bidSz: '1367.3', + askSz: '2535.3', + open24h: '0.4737', + high24h: '0.4784', + low24h: '0.463', + change24h: '-0.01569', + fundingRate: '0.0001', + nextFundingTime: '1730361600000', + markPrice: '0.4705', + indexPrice: '0.47080586', + holdingAmount: '8572031.1', + baseVolume: '2878355.9', + quoteVolume: '1355483.45089', + openUtc: '0.4706', + symbolType: '1', + symbol: 'STORJUSDT', + deliveryPrice: '0', + ts: '1730332823155' + } + ], + ts: 1730332823157 + }, + { + action: 'snapshot', + arg: { instType: 'COIN-FUTURES', channel: 'ticker', instId: 'BTCUSDZ24' }, + data: [ + { + instId: 'BTCUSDZ24', + lastPr: '73733.7', + bidPr: '73737.6', + askPr: '73742.1', + bidSz: '0.246', + askSz: '0.094', + open24h: '73465.6', + high24h: '74358.2', + low24h: '72872.2', + change24h: '-0.00612', + fundingRate: '0', + nextFundingTime: '0', + markPrice: '73731.8', + indexPrice: '72327.926666', + holdingAmount: '57.714', + baseVolume: '21.427', + quoteVolume: '1579692.8012', + openUtc: '73731.0', + symbolType: '2', + symbol: 'BTCUSDZ24', + ts: '1730332823217' + } + ], + ts: 1730332823221 + } + ] + + const mapper = createMapper('bitget-futures', new Date('2024-08-23T00:00:00.4985250Z')) + + for (const message of messages) { + const mappedMessages = mapper.map(message, new Date('2024-08-23T00:00:00.4985250Z')) + expect(mappedMessages).toMatchSnapshot() + } +})