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.
- package/dist/consts.d.ts +2 -1
- package/dist/consts.d.ts.map +1 -1
- package/dist/consts.js +5 -2
- package/dist/consts.js.map +1 -1
- package/dist/mappers/bitget.d.ts +7 -23
- package/dist/mappers/bitget.d.ts.map +1 -1
- package/dist/mappers/bitget.js +2 -2
- package/dist/mappers/bitget.js.map +1 -1
- package/dist/mappers/bybit.d.ts +2 -2
- package/dist/mappers/bybitspot.d.ts +1 -1
- package/dist/mappers/coinbaseinternational.d.ts +81 -0
- package/dist/mappers/coinbaseinternational.d.ts.map +1 -0
- package/dist/mappers/coinbaseinternational.js +202 -0
- package/dist/mappers/coinbaseinternational.js.map +1 -0
- package/dist/mappers/cryptocom.d.ts +1 -1
- package/dist/mappers/huobi.d.ts +3 -3
- package/dist/mappers/index.d.ts +4 -4
- package/dist/mappers/index.d.ts.map +1 -1
- package/dist/mappers/index.js +9 -4
- package/dist/mappers/index.js.map +1 -1
- package/dist/realtimefeeds/coinbase.js.map +1 -1
- package/dist/realtimefeeds/coinbaseinternational.d.ts +10 -0
- package/dist/realtimefeeds/coinbaseinternational.d.ts.map +1 -0
- package/dist/realtimefeeds/coinbaseinternational.js +57 -0
- package/dist/realtimefeeds/coinbaseinternational.js.map +1 -0
- package/dist/realtimefeeds/index.d.ts.map +1 -1
- package/dist/realtimefeeds/index.js +3 -1
- package/dist/realtimefeeds/index.js.map +1 -1
- package/package.json +1 -1
- package/src/consts.ts +5 -3
- package/src/mappers/bitget.ts +14 -28
- package/src/mappers/coinbaseinternational.ts +322 -0
- package/src/mappers/index.ts +14 -4
- package/src/realtimefeeds/coinbase.ts +1 -1
- package/src/realtimefeeds/coinbaseinternational.ts +60 -0
- 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
|
+
}
|
package/src/mappers/index.ts
CHANGED
|
@@ -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> => {
|
|
@@ -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 {
|