tardis-dev 16.1.1 → 16.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { upperCaseSymbols } from '../handy.ts'
2
- import { BookChange, BookTicker, DerivativeTicker, Exchange, Trade } from '../types.ts'
1
+ import { asNumberIfValid, upperCaseSymbols } from '../handy.ts'
2
+ import { BookChange, BookTicker, DerivativeTicker, Exchange, Liquidation, Trade } from '../types.ts'
3
3
  import { Mapper, PendingTickerInfoHelper } from './mapper.ts'
4
4
 
5
5
  export class BitgetTradesMapper implements Mapper<'bitget' | 'bitget-futures', Trade> {
@@ -155,6 +155,182 @@ export class BitgetDerivativeTickerMapper implements Mapper<'bitget-futures', De
155
155
  }
156
156
  }
157
157
 
158
+ export class BitgetV3TradesMapper implements Mapper<'bitget' | 'bitget-futures', Trade> {
159
+ constructor(private readonly _exchange: Exchange) {}
160
+
161
+ canHandle(message: BitgetV3TradeMessage) {
162
+ return message.arg.topic === 'publicTrade' && message.action === 'update'
163
+ }
164
+
165
+ getFilters(symbols?: string[]) {
166
+ symbols = upperCaseSymbols(symbols)
167
+
168
+ return [
169
+ {
170
+ channel: 'publicTrade',
171
+ symbols
172
+ } as const
173
+ ]
174
+ }
175
+
176
+ *map(message: BitgetV3TradeMessage, localTimestamp: Date): IterableIterator<Trade> {
177
+ for (const trade of message.data) {
178
+ yield {
179
+ type: 'trade',
180
+ symbol: message.arg.symbol,
181
+ exchange: this._exchange,
182
+ id: trade.i,
183
+ price: Number(trade.p),
184
+ amount: Number(trade.v),
185
+ side: trade.S === 'buy' ? 'buy' : 'sell',
186
+ timestamp: new Date(Number(trade.T)),
187
+ localTimestamp
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ export class BitgetV3BookChangeMapper implements Mapper<'bitget' | 'bitget-futures', BookChange> {
194
+ constructor(private readonly _exchange: Exchange) {}
195
+
196
+ canHandle(message: BitgetV3OrderbookMessage) {
197
+ return message.arg.topic === 'books' && (message.action === 'snapshot' || message.action === 'update')
198
+ }
199
+
200
+ getFilters(symbols?: string[]) {
201
+ symbols = upperCaseSymbols(symbols)
202
+
203
+ return [
204
+ {
205
+ channel: 'books',
206
+ symbols
207
+ } as const
208
+ ]
209
+ }
210
+
211
+ *map(message: BitgetV3OrderbookMessage, localTimestamp: Date): IterableIterator<BookChange> {
212
+ for (const orderbookData of message.data) {
213
+ yield {
214
+ type: 'book_change',
215
+ symbol: message.arg.symbol,
216
+ exchange: this._exchange,
217
+ isSnapshot: message.action === 'snapshot',
218
+ bids: orderbookData.b.map(mapPriceLevel),
219
+ asks: orderbookData.a.map(mapPriceLevel),
220
+ timestamp: new Date(Number(orderbookData.ts)),
221
+ localTimestamp
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ export class BitgetV3BookTickerMapper implements Mapper<'bitget' | 'bitget-futures', BookTicker> {
228
+ constructor(private readonly _exchange: Exchange) {}
229
+
230
+ canHandle(message: BitgetV3BBoMessage) {
231
+ return message.arg.topic === 'books1' && message.action === 'snapshot'
232
+ }
233
+
234
+ getFilters(symbols?: string[]) {
235
+ symbols = upperCaseSymbols(symbols)
236
+
237
+ return [
238
+ {
239
+ channel: 'books1',
240
+ symbols
241
+ } as const
242
+ ]
243
+ }
244
+
245
+ *map(message: BitgetV3BBoMessage, localTimestamp: Date): IterableIterator<BookTicker> {
246
+ for (const bboMessage of message.data) {
247
+ yield {
248
+ type: 'book_ticker',
249
+ symbol: message.arg.symbol,
250
+ exchange: this._exchange,
251
+ askAmount: bboMessage.a[0] ? asNumberIfValid(bboMessage.a[0][1]) : undefined,
252
+ askPrice: bboMessage.a[0] ? asNumberIfValid(bboMessage.a[0][0]) : undefined,
253
+ bidPrice: bboMessage.b[0] ? asNumberIfValid(bboMessage.b[0][0]) : undefined,
254
+ bidAmount: bboMessage.b[0] ? asNumberIfValid(bboMessage.b[0][1]) : undefined,
255
+ timestamp: new Date(Number(bboMessage.ts)),
256
+ localTimestamp
257
+ }
258
+ }
259
+ }
260
+ }
261
+
262
+ export class BitgetV3DerivativeTickerMapper implements Mapper<'bitget-futures', DerivativeTicker> {
263
+ private readonly pendingTickerInfoHelper = new PendingTickerInfoHelper()
264
+
265
+ canHandle(message: BitgetV3TickerMessage) {
266
+ return message.arg.topic === 'ticker' && (message.action === 'snapshot' || message.action === 'update')
267
+ }
268
+
269
+ getFilters(symbols?: string[]) {
270
+ symbols = upperCaseSymbols(symbols)
271
+
272
+ return [
273
+ {
274
+ channel: 'ticker',
275
+ symbols
276
+ } as const
277
+ ]
278
+ }
279
+
280
+ *map(message: BitgetV3TickerMessage, localTimestamp: Date): IterableIterator<DerivativeTicker> {
281
+ for (const tickerMessage of message.data) {
282
+ const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(message.arg.symbol, 'bitget-futures')
283
+
284
+ pendingTickerInfo.updateIndexPrice(Number(tickerMessage.indexPrice))
285
+ pendingTickerInfo.updateMarkPrice(Number(tickerMessage.markPrice))
286
+ pendingTickerInfo.updateOpenInterest(Number(tickerMessage.openInterest))
287
+ pendingTickerInfo.updateLastPrice(Number(tickerMessage.lastPrice))
288
+ pendingTickerInfo.updateTimestamp(new Date(Number(message.ts)))
289
+
290
+ if (tickerMessage.nextFundingTime !== '' && tickerMessage.nextFundingTime !== '0') {
291
+ pendingTickerInfo.updateFundingTimestamp(new Date(Number(tickerMessage.nextFundingTime)))
292
+ pendingTickerInfo.updateFundingRate(Number(tickerMessage.fundingRate))
293
+ }
294
+
295
+ if (pendingTickerInfo.hasChanged()) {
296
+ yield pendingTickerInfo.getSnapshot(localTimestamp)
297
+ }
298
+ }
299
+ }
300
+ }
301
+
302
+ export class BitgetV3LiquidationsMapper implements Mapper<'bitget-futures', Liquidation> {
303
+ canHandle(message: BitgetV3LiquidationMessage) {
304
+ return message.arg.topic === 'liquidation' && message.action === 'update'
305
+ }
306
+
307
+ getFilters() {
308
+ return [
309
+ {
310
+ channel: 'liquidation',
311
+ symbols: undefined
312
+ } as const
313
+ ]
314
+ }
315
+
316
+ *map(message: BitgetV3LiquidationMessage, localTimestamp: Date): IterableIterator<Liquidation> {
317
+ for (const liquidation of message.data) {
318
+ yield {
319
+ type: 'liquidation',
320
+ symbol: liquidation.symbol,
321
+ exchange: 'bitget-futures',
322
+ id: undefined,
323
+ price: Number(liquidation.price),
324
+ amount: Number(liquidation.amount),
325
+ // Bitget side is position side, normalized side is the liquidated aggressor side.
326
+ side: liquidation.side === 'buy' ? 'sell' : 'buy',
327
+ timestamp: new Date(Number(liquidation.ts)),
328
+ localTimestamp
329
+ }
330
+ }
331
+ }
332
+ }
333
+
158
334
  type BitgetTradeMessage = {
159
335
  action: 'update'
160
336
  arg: { instType: 'SPOT'; channel: 'trade'; instId: 'OPUSDT' }
@@ -214,3 +390,62 @@ type BitgetTickerMessage = {
214
390
  ]
215
391
  ts: 1730332823220
216
392
  }
393
+
394
+ type BitgetV3TradeMessage = {
395
+ action: 'snapshot' | 'update'
396
+ arg: { instType: string; topic: 'publicTrade'; symbol: string }
397
+ data: { i: string; p: string; v: string; S: 'buy' | 'sell'; T: string; L: string; isRPI?: string }[]
398
+ ts: number
399
+ }
400
+
401
+ type BitgetV3BookLevel = [string, string]
402
+
403
+ type BitgetV3OrderbookMessage = {
404
+ action: 'snapshot' | 'update'
405
+ arg: { instType: string; topic: 'books'; symbol: string }
406
+ data: { a: BitgetV3BookLevel[]; b: BitgetV3BookLevel[]; checksum: number; seq: number; pseq: number; ts: string }[]
407
+ ts: number
408
+ }
409
+
410
+ type BitgetV3BBoMessage = {
411
+ action: 'snapshot'
412
+ arg: { instType: string; topic: 'books1'; symbol: string }
413
+ data: { a: BitgetV3BookLevel[]; b: BitgetV3BookLevel[]; checksum: number; seq: number; pseq: number; ts: string }[]
414
+ ts: number
415
+ }
416
+
417
+ type BitgetV3TickerMessage = {
418
+ action: 'snapshot' | 'update'
419
+ arg: { instType: string; topic: 'ticker'; symbol: string }
420
+ data: [
421
+ {
422
+ highPrice24h: string
423
+ lowPrice24h: string
424
+ openPrice24h: string
425
+ lastPrice: string
426
+ turnover24h: string
427
+ volume24h: string
428
+ bid1Price: string
429
+ ask1Price: string
430
+ bid1Size: string
431
+ ask1Size: string
432
+ price24hPcnt: string
433
+ indexPrice: string
434
+ markPrice: string
435
+ fundingRate: string
436
+ openInterest: string
437
+ deliveryTime: string
438
+ deliveryStartTime: string
439
+ deliveryStatus: string
440
+ nextFundingTime: string
441
+ }
442
+ ]
443
+ ts: number
444
+ }
445
+
446
+ type BitgetV3LiquidationMessage = {
447
+ action: 'update'
448
+ arg: { instType: string; topic: 'liquidation' }
449
+ data: { symbol: string; side: 'buy' | 'sell'; price: string; amount: string; ts: string }[]
450
+ ts: number
451
+ }
@@ -27,7 +27,17 @@ import {
27
27
  BitfinexTradesMapper
28
28
  } from './bitfinex.ts'
29
29
  import { BitflyerBookChangeMapper, bitflyerBookTickerMapper, bitflyerTradesMapper } from './bitflyer.ts'
30
- import { BitgetBookChangeMapper, BitgetBookTickerMapper, BitgetDerivativeTickerMapper, BitgetTradesMapper } from './bitget.ts'
30
+ import {
31
+ BitgetBookChangeMapper,
32
+ BitgetBookTickerMapper,
33
+ BitgetDerivativeTickerMapper,
34
+ BitgetTradesMapper,
35
+ BitgetV3BookChangeMapper,
36
+ BitgetV3BookTickerMapper,
37
+ BitgetV3DerivativeTickerMapper,
38
+ BitgetV3LiquidationsMapper,
39
+ BitgetV3TradesMapper
40
+ } from './bitget.ts'
31
41
  import {
32
42
  BitmexBookChangeMapper,
33
43
  BitmexDerivativeTickerMapper,
@@ -208,6 +218,12 @@ const shouldUseBybitAllLiquidationFeed = (localTimestamp: Date) => {
208
218
  return isRealTime(localTimestamp) || localTimestamp.valueOf() >= BYBIT_V5_API_ALL_LIQUIDATION_SUPPORT_DATE.valueOf()
209
219
  }
210
220
 
221
+ const BITGET_V3_API_SWITCH_DATE = new Date('2026-04-28T00:00:00.000Z')
222
+
223
+ const shouldUseBitgetV3Mappers = (localTimestamp: Date) => {
224
+ return isRealTime(localTimestamp) || localTimestamp.valueOf() >= BITGET_V3_API_SWITCH_DATE.valueOf()
225
+ }
226
+
211
227
  const OKCOIN_V5_API_SWITCH_DATE = new Date('2023-04-27T00:00:00.000Z')
212
228
  const shouldUseOkcoinV5Mappers = (localTimestamp: Date) => {
213
229
  return isRealTime(localTimestamp) || localTimestamp.valueOf() >= OKCOIN_V5_API_SWITCH_DATE.valueOf()
@@ -320,8 +336,10 @@ const tradesMappers = {
320
336
  ? new BinanceEuropeanOptionsTradesMapperV2()
321
337
  : new BinanceEuropeanOptionsTradesMapper(),
322
338
  'okex-spreads': () => new OkexSpreadsTradesMapper(),
323
- bitget: () => new BitgetTradesMapper('bitget'),
324
- 'bitget-futures': () => new BitgetTradesMapper('bitget-futures'),
339
+ bitget: (localTimestamp: Date) =>
340
+ shouldUseBitgetV3Mappers(localTimestamp) ? new BitgetV3TradesMapper('bitget') : new BitgetTradesMapper('bitget'),
341
+ 'bitget-futures': (localTimestamp: Date) =>
342
+ shouldUseBitgetV3Mappers(localTimestamp) ? new BitgetV3TradesMapper('bitget-futures') : new BitgetTradesMapper('bitget-futures'),
325
343
  'coinbase-international': () => coinbaseInternationalTradesMapper,
326
344
  hyperliquid: () => new HyperliquidTradesMapper(),
327
345
  lighter: () => new LighterTradesMapper()
@@ -416,8 +434,12 @@ const bookChangeMappers = {
416
434
  ? new BinanceEuropeanOptionsBookChangeMapperV2()
417
435
  : new BinanceEuropeanOptionsBookChangeMapper(),
418
436
  'okex-spreads': () => new OkexSpreadsBookChangeMapper(),
419
- bitget: () => new BitgetBookChangeMapper('bitget'),
420
- 'bitget-futures': () => new BitgetBookChangeMapper('bitget-futures'),
437
+ bitget: (localTimestamp: Date) =>
438
+ shouldUseBitgetV3Mappers(localTimestamp) ? new BitgetV3BookChangeMapper('bitget') : new BitgetBookChangeMapper('bitget'),
439
+ 'bitget-futures': (localTimestamp: Date) =>
440
+ shouldUseBitgetV3Mappers(localTimestamp)
441
+ ? new BitgetV3BookChangeMapper('bitget-futures')
442
+ : new BitgetBookChangeMapper('bitget-futures'),
421
443
  'coinbase-international': () => new CoinbaseInternationalBookChangMapper(),
422
444
  hyperliquid: () => new HyperliquidBookChangeMapper(),
423
445
  lighter: () => new LighterBookChangeMapper()
@@ -454,7 +476,8 @@ const derivativeTickersMappers = {
454
476
  'crypto-com': () => new CryptoComDerivativeTickerMapper('crypto-com'),
455
477
  'woo-x': () => new WooxDerivativeTickerMapper(),
456
478
  'kucoin-futures': () => new KucoinFuturesDerivativeTickerMapper(),
457
- 'bitget-futures': () => new BitgetDerivativeTickerMapper(),
479
+ 'bitget-futures': (localTimestamp: Date) =>
480
+ shouldUseBitgetV3Mappers(localTimestamp) ? new BitgetV3DerivativeTickerMapper() : new BitgetDerivativeTickerMapper(),
458
481
  'coinbase-international': () => new CoinbaseInternationalDerivativeTickerMapper(),
459
482
  hyperliquid: () => new HyperliquidDerivativeTickerMapper(),
460
483
  lighter: () => new LighterDerivativeTickerMapper()
@@ -496,7 +519,8 @@ const liquidationsMappers = {
496
519
  ? new OkexV5LiquidationsMapper('okex-futures')
497
520
  : new OkexLiquidationsMapper('okex-futures', 'futures'),
498
521
  'okex-swap': (localTimestamp: Date) =>
499
- shouldUseOkexV5Mappers(localTimestamp) ? new OkexV5LiquidationsMapper('okex-swap') : new OkexLiquidationsMapper('okex-swap', 'swap')
522
+ shouldUseOkexV5Mappers(localTimestamp) ? new OkexV5LiquidationsMapper('okex-swap') : new OkexLiquidationsMapper('okex-swap', 'swap'),
523
+ 'bitget-futures': () => new BitgetV3LiquidationsMapper()
500
524
  }
501
525
 
502
526
  const bookTickersMappers = {
@@ -556,8 +580,12 @@ const bookTickersMappers = {
556
580
  'gate-io': () => new GateIOV4BookTickerMapper('gate-io'),
557
581
  'okex-spreads': () => new OkexSpreadsBookTickerMapper(),
558
582
  'kucoin-futures': () => new KucoinFuturesBookTickerMapper(),
559
- bitget: () => new BitgetBookTickerMapper('bitget'),
560
- 'bitget-futures': () => new BitgetBookTickerMapper('bitget-futures'),
583
+ bitget: (localTimestamp: Date) =>
584
+ shouldUseBitgetV3Mappers(localTimestamp) ? new BitgetV3BookTickerMapper('bitget') : new BitgetBookTickerMapper('bitget'),
585
+ 'bitget-futures': (localTimestamp: Date) =>
586
+ shouldUseBitgetV3Mappers(localTimestamp)
587
+ ? new BitgetV3BookTickerMapper('bitget-futures')
588
+ : new BitgetBookTickerMapper('bitget-futures'),
561
589
  'coinbase-international': () => coinbaseInternationalBookTickerMapper,
562
590
  hyperliquid: () => new HyperliquidBookTickerMapper(),
563
591
  lighter: () => new LighterBookTickerMapper(),
@@ -4,10 +4,19 @@ import { RealTimeFeedBase } from './realtimefeed.ts'
4
4
 
5
5
  abstract class BitgetRealTimeFeedBase extends RealTimeFeedBase {
6
6
  protected throttleSubscribeMS = 100
7
- protected readonly wssURL = 'wss://ws.bitget.com/v2/ws/public'
7
+ protected readonly wssURL = 'wss://ws.bitget.com/v3/ws/public'
8
8
 
9
9
  protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
10
10
  const argsInputs = filters.flatMap((filter) => {
11
+ if (filter.channel === 'liquidation') {
12
+ return this.getLiquidationInstTypes().map((instType) => {
13
+ return {
14
+ instType,
15
+ topic: filter.channel
16
+ }
17
+ })
18
+ }
19
+
11
20
  if (!filter.symbols || filter.symbols.length === 0) {
12
21
  throw new Error('BitgetRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
13
22
  }
@@ -15,8 +24,8 @@ abstract class BitgetRealTimeFeedBase extends RealTimeFeedBase {
15
24
  return filter.symbols.map((symbol) => {
16
25
  return {
17
26
  instType: this.getInstType(symbol),
18
- channel: filter.channel,
19
- instId: symbol
27
+ topic: filter.channel,
28
+ symbol
20
29
  }
21
30
  })
22
31
  })
@@ -36,24 +45,32 @@ abstract class BitgetRealTimeFeedBase extends RealTimeFeedBase {
36
45
  }
37
46
 
38
47
  abstract getInstType(symbol: string): string
48
+
49
+ protected getLiquidationInstTypes(): string[] {
50
+ return []
51
+ }
39
52
  }
40
53
 
41
54
  export class BitgetRealTimeFeed extends BitgetRealTimeFeedBase {
42
55
  getInstType(_: string) {
43
- return 'SPOT'
56
+ return 'spot'
44
57
  }
45
58
  }
46
59
 
47
60
  export class BitgetFuturesRealTimeFeed extends BitgetRealTimeFeedBase {
48
61
  getInstType(symbol: string) {
49
62
  if (symbol.endsWith('USDT')) {
50
- return 'USDT-FUTURES'
63
+ return 'usdt-futures'
51
64
  }
52
65
 
53
66
  if (symbol.endsWith('PERP')) {
54
- return 'USDC-FUTURES'
67
+ return 'usdc-futures'
55
68
  }
56
69
 
57
- return 'COIN-FUTURES'
70
+ return 'coin-futures'
71
+ }
72
+
73
+ protected getLiquidationInstTypes() {
74
+ return ['usdt-futures', 'usdc-futures', 'coin-futures']
58
75
  }
59
76
  }
package/src/types.ts CHANGED
@@ -117,7 +117,7 @@ export type Liquidation = {
117
117
  readonly id: string | undefined
118
118
  readonly price: number
119
119
  readonly amount: number
120
- readonly side: 'buy' | 'sell' | 'unknown'
120
+ readonly side: 'buy' | 'sell' | 'unknown' // buy = short position liquidated, sell = long position liquidated
121
121
  readonly timestamp: Date
122
122
  readonly localTimestamp: Date
123
123
  }