tardis-dev 13.31.1 → 13.32.0

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.
Files changed (36) hide show
  1. package/dist/consts.d.ts +2 -1
  2. package/dist/consts.d.ts.map +1 -1
  3. package/dist/consts.js +5 -2
  4. package/dist/consts.js.map +1 -1
  5. package/dist/mappers/bitget.d.ts +7 -23
  6. package/dist/mappers/bitget.d.ts.map +1 -1
  7. package/dist/mappers/bitget.js +2 -2
  8. package/dist/mappers/bitget.js.map +1 -1
  9. package/dist/mappers/bybit.d.ts +2 -2
  10. package/dist/mappers/bybitspot.d.ts +1 -1
  11. package/dist/mappers/coinbaseinternational.d.ts +81 -0
  12. package/dist/mappers/coinbaseinternational.d.ts.map +1 -0
  13. package/dist/mappers/coinbaseinternational.js +202 -0
  14. package/dist/mappers/coinbaseinternational.js.map +1 -0
  15. package/dist/mappers/cryptocom.d.ts +1 -1
  16. package/dist/mappers/huobi.d.ts +3 -3
  17. package/dist/mappers/index.d.ts +4 -4
  18. package/dist/mappers/index.d.ts.map +1 -1
  19. package/dist/mappers/index.js +9 -4
  20. package/dist/mappers/index.js.map +1 -1
  21. package/dist/realtimefeeds/coinbase.js.map +1 -1
  22. package/dist/realtimefeeds/coinbaseinternational.d.ts +10 -0
  23. package/dist/realtimefeeds/coinbaseinternational.d.ts.map +1 -0
  24. package/dist/realtimefeeds/coinbaseinternational.js +57 -0
  25. package/dist/realtimefeeds/coinbaseinternational.js.map +1 -0
  26. package/dist/realtimefeeds/index.d.ts.map +1 -1
  27. package/dist/realtimefeeds/index.js +3 -1
  28. package/dist/realtimefeeds/index.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/consts.ts +5 -3
  31. package/src/mappers/bitget.ts +14 -28
  32. package/src/mappers/coinbaseinternational.ts +322 -0
  33. package/src/mappers/index.ts +14 -4
  34. package/src/realtimefeeds/coinbase.ts +1 -1
  35. package/src/realtimefeeds/coinbaseinternational.ts +60 -0
  36. package/src/realtimefeeds/index.ts +3 -1
@@ -0,0 +1,322 @@
1
+ import { addMinutes, upperCaseSymbols } from '../handy'
2
+ import { BookChange, BookPriceLevel, BookTicker, DerivativeTicker, Trade } from '../types'
3
+ import { Mapper, PendingTickerInfoHelper } from './mapper'
4
+
5
+ export const coinbaseInternationalTradesMapper: Mapper<'coinbase-international', Trade> = {
6
+ canHandle(message: CoinbaseInternationalTradeMessage) {
7
+ return message.channel === 'MATCH' && message.type === 'UPDATE'
8
+ },
9
+
10
+ getFilters(symbols?: string[]) {
11
+ symbols = upperCaseSymbols(symbols)
12
+
13
+ return [
14
+ {
15
+ channel: 'MATCH',
16
+ symbols
17
+ }
18
+ ]
19
+ },
20
+
21
+ *map(message: CoinbaseInternationalTradeMessage, localTimestamp: Date): IterableIterator<Trade> {
22
+ yield {
23
+ type: 'trade',
24
+ symbol: message.product_id,
25
+ exchange: 'coinbase-international',
26
+ id: message.match_id,
27
+ price: Number(message.trade_price),
28
+ amount: Number(message.trade_qty),
29
+ side: message.aggressor_side === 'SELL' ? 'sell' : message.aggressor_side === 'BUY' ? 'buy' : 'unknown',
30
+ timestamp: new Date(message.time),
31
+ localTimestamp: localTimestamp
32
+ }
33
+ }
34
+ }
35
+
36
+ const mapUpdateBookLevel = (level: CoinbaseInternationalUpdateBookLevel) => {
37
+ const price = Number(level[1])
38
+ const amount = Number(level[2])
39
+
40
+ return { price, amount }
41
+ }
42
+
43
+ const mapSnapshotBookLevel = (level: CoinbaseInternationalSnapshotBookLevel) => {
44
+ const price = Number(level[0])
45
+ const amount = Number(level[1])
46
+
47
+ return { price, amount }
48
+ }
49
+
50
+ const validAmountsOnly = (level: BookPriceLevel) => {
51
+ if (Number.isNaN(level.amount)) {
52
+ return false
53
+ }
54
+ if (level.amount < 0) {
55
+ return false
56
+ }
57
+
58
+ return true
59
+ }
60
+
61
+ export class CoinbaseInternationalBookChangMapper implements Mapper<'coinbase-international', BookChange> {
62
+ canHandle(message: CoinbaseInternationalLevel2Snapshot | CoinbaseInternationalLevel2Update) {
63
+ return message.channel === 'LEVEL2' && (message.type === 'SNAPSHOT' || message.type === 'UPDATE')
64
+ }
65
+
66
+ getFilters(symbols?: string[]) {
67
+ symbols = upperCaseSymbols(symbols)
68
+
69
+ return [
70
+ {
71
+ channel: 'LEVEL2',
72
+ symbols
73
+ } as const
74
+ ]
75
+ }
76
+
77
+ *map(
78
+ message: CoinbaseInternationalLevel2Snapshot | CoinbaseInternationalLevel2Update,
79
+ localTimestamp: Date
80
+ ): IterableIterator<BookChange> {
81
+ if (message.type === 'SNAPSHOT') {
82
+ let timestamp
83
+ if (message.time !== undefined) {
84
+ timestamp = new Date(message.time)
85
+ if (timestamp.valueOf() < 0) {
86
+ timestamp = localTimestamp
87
+ }
88
+ } else {
89
+ timestamp = localTimestamp
90
+ }
91
+
92
+ yield {
93
+ type: 'book_change',
94
+ symbol: message.product_id,
95
+ exchange: 'coinbase-international',
96
+ isSnapshot: true,
97
+ bids: message.bids.map(mapSnapshotBookLevel).filter(validAmountsOnly),
98
+ asks: message.asks.map(mapSnapshotBookLevel).filter(validAmountsOnly),
99
+ timestamp,
100
+ localTimestamp
101
+ }
102
+ } else {
103
+ let timestamp = new Date(message.time)
104
+
105
+ yield {
106
+ type: 'book_change',
107
+ symbol: message.product_id,
108
+ exchange: 'coinbase-international',
109
+ isSnapshot: false,
110
+ bids: message.changes.filter((c) => c[0] === 'BUY').map(mapUpdateBookLevel),
111
+ asks: message.changes.filter((c) => c[0] === 'SELL').map(mapUpdateBookLevel),
112
+ timestamp,
113
+ localTimestamp: localTimestamp
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ export const coinbaseInternationalBookTickerMapper: Mapper<'coinbase-international', BookTicker> = {
120
+ canHandle(message: CoinbaseInternationalLevel1Message) {
121
+ return message.channel === 'LEVEL1' && (message.type === 'SNAPSHOT' || message.type === 'UPDATE')
122
+ },
123
+
124
+ getFilters(symbols?: string[]) {
125
+ symbols = upperCaseSymbols(symbols)
126
+
127
+ return [
128
+ {
129
+ channel: 'LEVEL1',
130
+ symbols
131
+ }
132
+ ]
133
+ },
134
+
135
+ *map(message: CoinbaseInternationalLevel1Message, localTimestamp: Date): IterableIterator<BookTicker> {
136
+ let timestamp = new Date(message.time)
137
+
138
+ if (message.time === undefined || timestamp.valueOf() < 0) {
139
+ timestamp = localTimestamp
140
+ }
141
+
142
+ yield {
143
+ type: 'book_ticker',
144
+ symbol: message.product_id,
145
+ exchange: 'coinbase-international',
146
+ askAmount: message.ask_qty !== undefined ? Number(message.ask_qty) : undefined,
147
+ askPrice: message.ask_price !== undefined ? Number(message.ask_price) : undefined,
148
+ bidPrice: message.bid_price !== undefined ? Number(message.bid_price) : undefined,
149
+ bidAmount: message.bid_qty !== undefined ? Number(message.bid_qty) : undefined,
150
+ timestamp,
151
+ localTimestamp: localTimestamp
152
+ }
153
+ }
154
+ }
155
+
156
+ export class CoinbaseInternationalDerivativeTickerMapper implements Mapper<'coinbase-international', DerivativeTicker> {
157
+ private readonly pendingTickerInfoHelper = new PendingTickerInfoHelper()
158
+
159
+ canHandle(message: CoinbaseInternationalTradeMessage | CoinbaseInternationalRiskMessage | CoinbaseInternationalFundingMessage) {
160
+ // perps only
161
+ if (message.product_id === undefined || message.product_id.endsWith('-PERP') === false) {
162
+ return false
163
+ }
164
+
165
+ if (message.channel === 'MATCH' && message.type === 'UPDATE') {
166
+ return true
167
+ }
168
+
169
+ if (message.channel === 'FUNDING' && message.type === 'UPDATE') {
170
+ return true
171
+ }
172
+
173
+ if (message.channel === 'RISK') {
174
+ return true
175
+ }
176
+
177
+ return false
178
+ }
179
+
180
+ getFilters(symbols?: string[]) {
181
+ symbols = upperCaseSymbols(symbols)
182
+
183
+ return [
184
+ {
185
+ channel: 'MATCH',
186
+ symbols
187
+ } as const,
188
+ {
189
+ channel: 'RISK',
190
+ symbols
191
+ } as const,
192
+ {
193
+ channel: 'FUNDING',
194
+ symbols
195
+ } as const
196
+ ]
197
+ }
198
+
199
+ *map(
200
+ message: CoinbaseInternationalTradeMessage | CoinbaseInternationalRiskMessage | CoinbaseInternationalFundingMessage,
201
+ localTimestamp: Date
202
+ ): IterableIterator<DerivativeTicker> {
203
+ if (message.channel === 'MATCH') {
204
+ const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(message.product_id, 'coinbase-international')
205
+ pendingTickerInfo.updateLastPrice(Number(message.trade_price))
206
+
207
+ return
208
+ }
209
+ const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(message.product_id, 'coinbase-international')
210
+
211
+ if (message.channel === 'RISK') {
212
+ pendingTickerInfo.updateIndexPrice(Number(message.index_price))
213
+ pendingTickerInfo.updateMarkPrice(Number(message.mark_price))
214
+ pendingTickerInfo.updateOpenInterest(Number(message.open_interest))
215
+ }
216
+
217
+ if (message.channel === 'FUNDING') {
218
+ let nextFundingTime = new Date(message.time)
219
+ if (message.is_final === false) {
220
+ // If the field is_final is false, the message indicates the predicted funding rate for the next funding interval.
221
+ // https://docs.cdp.coinbase.com/intx/docs/websocket-channels#funding-channel
222
+ nextFundingTime.setUTCMinutes(0, 0, 0)
223
+ nextFundingTime = addMinutes(nextFundingTime, 60)
224
+
225
+ pendingTickerInfo.updateFundingTimestamp(nextFundingTime)
226
+ }
227
+
228
+ pendingTickerInfo.updateFundingRate(Number(message.funding_rate))
229
+ }
230
+
231
+ pendingTickerInfo.updateTimestamp(new Date(message.time))
232
+
233
+ if (pendingTickerInfo.hasChanged()) {
234
+ yield pendingTickerInfo.getSnapshot(localTimestamp)
235
+ }
236
+ }
237
+ }
238
+
239
+ // TODO: real-time
240
+
241
+ type CoinbaseInternationalTradeMessage = {
242
+ sequence: 80
243
+ match_id: '374491377330814981'
244
+ trade_price: '0.009573'
245
+ trade_qty: '1651'
246
+ aggressor_side: 'BUY' | 'SELL' | 'OPENING_FILL'
247
+ channel: 'MATCH'
248
+ type: 'UPDATE'
249
+ time: '2024-10-30T10:55:02.069Z'
250
+ product_id: 'MEW-PERP'
251
+ }
252
+
253
+ type CoinbaseInternationalSnapshotBookLevel = [string, string]
254
+
255
+ type CoinbaseInternationalLevel2Snapshot = {
256
+ sequence: 81053126
257
+ bids: CoinbaseInternationalSnapshotBookLevel[]
258
+ asks: CoinbaseInternationalSnapshotBookLevel[]
259
+ channel: 'LEVEL2'
260
+ type: 'SNAPSHOT'
261
+ time: '2024-11-06T23:59:59.812Z'
262
+ product_id: 'BB-PERP'
263
+ }
264
+
265
+ type CoinbaseInternationalUpdateBookLevel = ['BUY' | 'SELL', string, string]
266
+
267
+ type CoinbaseInternationalLevel2Update = {
268
+ sequence: 162
269
+ changes: CoinbaseInternationalUpdateBookLevel[]
270
+ channel: 'LEVEL2'
271
+ type: 'UPDATE'
272
+ time: '2024-10-30T10:55:02.348Z'
273
+ product_id: 'NOT-PERP'
274
+ }
275
+
276
+ type CoinbaseInternationalLevel1Message =
277
+ | {
278
+ sequence: 65960075
279
+ bid_price: '27.03'
280
+ bid_qty: '24.404'
281
+ ask_price: '27.037'
282
+ ask_qty: '32.302'
283
+ channel: 'LEVEL1'
284
+ type: 'SNAPSHOT'
285
+ time: '2024-11-07T00:00:00.121Z'
286
+ product_id: 'AVAX-PERP'
287
+ }
288
+ | {
289
+ sequence: 120100774
290
+ bid_price: '2719.96'
291
+ bid_qty: '0.3676'
292
+ ask_price: '2720.25'
293
+ ask_qty: '0.919'
294
+ channel: 'LEVEL1'
295
+ type: 'UPDATE'
296
+ time: '2024-11-07T00:00:59.979Z'
297
+ product_id: 'ETH-USDC'
298
+ }
299
+
300
+ type CoinbaseInternationalRiskMessage = {
301
+ sequence: 108523490
302
+ limit_up: '0.5107'
303
+ limit_down: '0.4621'
304
+ index_price: '0.4864755122500001'
305
+ mark_price: '0.4863'
306
+ settlement_price: '0.4864'
307
+ open_interest: '153090'
308
+ channel: 'RISK'
309
+ type: 'UPDATE'
310
+ time: '2024-11-07T00:00:59.950Z'
311
+ product_id: 'ENA-PERP'
312
+ }
313
+
314
+ type CoinbaseInternationalFundingMessage = {
315
+ sequence: 108521023
316
+ funding_rate: '0.000009'
317
+ is_final: false
318
+ channel: 'FUNDING'
319
+ type: 'UPDATE'
320
+ time: '2024-11-07T00:00:51.068Z'
321
+ product_id: 'DEGEN-PERP'
322
+ }
@@ -49,6 +49,12 @@ import {
49
49
  } from './bybit'
50
50
  import { BybitSpotBookChangeMapper, BybitSpotBookTickerMapper, BybitSpotTradesMapper } from './bybitspot'
51
51
  import { CoinbaseBookChangMapper, coinbaseBookTickerMapper, coinbaseTradesMapper } from './coinbase'
52
+ import {
53
+ CoinbaseInternationalBookChangMapper,
54
+ coinbaseInternationalBookTickerMapper,
55
+ CoinbaseInternationalDerivativeTickerMapper,
56
+ coinbaseInternationalTradesMapper
57
+ } from './coinbaseinternational'
52
58
  import { coinflexBookChangeMapper, CoinflexDerivativeTickerMapper, coinflexTradesMapper } from './coinflex'
53
59
  import { CryptoComBookChangeMapper, CryptoComBookTickerMapper, CryptoComDerivativeTickerMapper, CryptoComTradesMapper } from './cryptocom'
54
60
  import {
@@ -274,7 +280,8 @@ const tradesMappers = {
274
280
  'binance-european-options': () => new BinanceEuropeanOptionsTradesMapper(),
275
281
  'okex-spreads': () => new OkexSpreadsTradesMapper(),
276
282
  bitget: () => new BitgetTradesMapper('bitget'),
277
- 'bitget-futures': () => new BitgetTradesMapper('bitget-futures')
283
+ 'bitget-futures': () => new BitgetTradesMapper('bitget-futures'),
284
+ 'coinbase-international': () => coinbaseInternationalTradesMapper
278
285
  }
279
286
 
280
287
  const bookChangeMappers = {
@@ -364,7 +371,8 @@ const bookChangeMappers = {
364
371
  'binance-european-options': () => new BinanceEuropeanOptionsBookChangeMapper(),
365
372
  'okex-spreads': () => new OkexSpreadsBookChangeMapper(),
366
373
  bitget: () => new BitgetBookChangeMapper('bitget'),
367
- 'bitget-futures': () => new BitgetBookChangeMapper('bitget-futures')
374
+ 'bitget-futures': () => new BitgetBookChangeMapper('bitget-futures'),
375
+ 'coinbase-international': () => new CoinbaseInternationalBookChangMapper()
368
376
  }
369
377
 
370
378
  const derivativeTickersMappers = {
@@ -399,7 +407,8 @@ const derivativeTickersMappers = {
399
407
  'crypto-com': () => new CryptoComDerivativeTickerMapper('crypto-com'),
400
408
  'woo-x': () => new WooxDerivativeTickerMapper(),
401
409
  'kucoin-futures': () => new KucoinFuturesDerivativeTickerMapper(),
402
- 'bitget-futures': () => new BitgetDerivativeTickerMapper()
410
+ 'bitget-futures': () => new BitgetDerivativeTickerMapper(),
411
+ 'coinbase-international': () => new CoinbaseInternationalDerivativeTickerMapper()
403
412
  }
404
413
 
405
414
  const optionsSummaryMappers = {
@@ -493,7 +502,8 @@ const bookTickersMappers = {
493
502
  'okex-spreads': () => new OkexSpreadsBookTickerMapper(),
494
503
  'kucoin-futures': () => new KucoinFuturesBookTickerMapper(),
495
504
  bitget: () => new BitgetBookTickerMapper('bitget'),
496
- 'bitget-futures': () => new BitgetBookTickerMapper('bitget-futures')
505
+ 'bitget-futures': () => new BitgetBookTickerMapper('bitget-futures'),
506
+ 'coinbase-international': () => coinbaseInternationalBookTickerMapper
497
507
  }
498
508
 
499
509
  export const normalizeTrades = <T extends keyof typeof tradesMappers>(exchange: T, localTimestamp: Date): Mapper<T, Trade> => {
@@ -1,4 +1,4 @@
1
- import crypto, { sign } from 'crypto'
1
+ import crypto from 'crypto'
2
2
  import { Filter } from '../types'
3
3
  import { RealTimeFeedBase } from './realtimefeed'
4
4
 
@@ -0,0 +1,60 @@
1
+ import crypto from 'crypto'
2
+ import { Filter } from '../types'
3
+ import { RealTimeFeedBase } from './realtimefeed'
4
+
5
+ export class CoinbaseInternationalRealTimeFeed extends RealTimeFeedBase {
6
+ private _hasCredentials =
7
+ process.env.COINBASE_INTERNATIONAL_API_KEY !== undefined &&
8
+ process.env.COINBASE_INTERNATIONAL_API_SECRET !== undefined &&
9
+ process.env.COINBASE_INTERNATIONAL_API_PASSPHRASE !== undefined
10
+
11
+ protected get wssURL() {
12
+ return 'wss://ws-md.international.coinbase.com'
13
+ }
14
+
15
+ protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
16
+ if (this._hasCredentials == false) {
17
+ throw new Error(
18
+ 'CoinbaseInternationalRealTimeFeed requires auth credentials env vars set(COINBASE_INTERNATIONAL_API_KEY, COINBASE_INTERNATIONAL_API_SECRET, COINBASE_INTERNATIONAL_API_PASSPHRASE)'
19
+ )
20
+ }
21
+
22
+ const authParams = this.getAuthParams()
23
+
24
+ return filters.map((filter) => {
25
+ if (!filter.symbols || filter.symbols.length === 0) {
26
+ throw new Error('CoinbaseInternationalRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
27
+ }
28
+
29
+ return {
30
+ type: 'SUBSCRIBE',
31
+ product_ids: filter.symbols,
32
+ channels: [filter.channel],
33
+ signature: authParams.signature,
34
+ key: authParams.key,
35
+ time: authParams.time,
36
+ passphrase: authParams.passphrase
37
+ }
38
+ })
39
+ }
40
+
41
+ private getAuthParams() {
42
+ const time = Date.now().valueOf() / 1000
43
+ const apiSecret = process.env.COINBASE_INTERNATIONAL_API_SECRET!
44
+ const message = `${time}${process.env.COINBASE_INTERNATIONAL_API_KEY}CBINTLMD${process.env.COINBASE_INTERNATIONAL_API_PASSPHRASE}`
45
+
46
+ const hmac = crypto.createHmac('sha256', Buffer.from(apiSecret, 'base64'))
47
+ const signature = hmac.update(message).digest('base64')
48
+
49
+ return {
50
+ signature,
51
+ key: process.env.COINBASE_INTERNATIONAL_API_KEY!,
52
+ passphrase: process.env.COINBASE_INTERNATIONAL_API_PASSPHRASE!,
53
+ time
54
+ }
55
+ }
56
+
57
+ protected messageIsError(message: any): boolean {
58
+ return message.type === 'REJECT'
59
+ }
60
+ }
@@ -51,6 +51,7 @@ import { OkexSpreadsRealTimeFeed } from './okexspreads'
51
51
  import { KucoinFuturesRealTimeFeed } from './kucoinfutures'
52
52
  import { DydxV4RealTimeFeed } from './dydx_v4'
53
53
  import { BitgetFuturesRealTimeFeed, BitgetRealTimeFeed } from './bitget'
54
+ import { CoinbaseInternationalRealTimeFeed } from './coinbaseinternational'
54
55
 
55
56
  export * from './realtimefeed'
56
57
 
@@ -113,7 +114,8 @@ const realTimeFeedsMap: {
113
114
  'kucoin-futures': KucoinFuturesRealTimeFeed,
114
115
  'dydx-v4': DydxV4RealTimeFeed,
115
116
  bitget: BitgetRealTimeFeed,
116
- 'bitget-futures': BitgetFuturesRealTimeFeed
117
+ 'bitget-futures': BitgetFuturesRealTimeFeed,
118
+ 'coinbase-international': CoinbaseInternationalRealTimeFeed
117
119
  }
118
120
 
119
121
  export function getRealTimeFeedFactory(exchange: Exchange): RealTimeFeed {