tardis-dev 16.2.1 → 16.3.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/computable/computable.js.map +1 -1
- package/dist/handy.d.ts +1 -0
- package/dist/handy.d.ts.map +1 -1
- package/dist/handy.js +5 -2
- package/dist/handy.js.map +1 -1
- package/dist/instrumentinfo.js +1 -1
- package/dist/instrumentinfo.js.map +1 -1
- package/dist/mappers/binance.d.ts.map +1 -1
- package/dist/mappers/binance.js.map +1 -1
- package/dist/mappers/bybit.d.ts.map +1 -1
- package/dist/mappers/bybit.js.map +1 -1
- package/dist/mappers/gateio.d.ts.map +1 -1
- package/dist/mappers/gateio.js.map +1 -1
- package/dist/mappers/gateiofutures.js.map +1 -1
- package/dist/mappers/huobi.d.ts.map +1 -1
- package/dist/mappers/huobi.js.map +1 -1
- package/dist/mappers/index.js.map +1 -1
- package/dist/mappers/kucoin.d.ts.map +1 -1
- package/dist/mappers/kucoin.js.map +1 -1
- package/dist/mappers/okex.d.ts.map +1 -1
- package/dist/mappers/okex.js.map +1 -1
- package/dist/mappers/phemex.d.ts.map +1 -1
- package/dist/mappers/phemex.js +11 -2
- package/dist/mappers/phemex.js.map +1 -1
- package/dist/realtimefeeds/bitnomial.d.ts.map +1 -1
- package/dist/realtimefeeds/bitnomial.js.map +1 -1
- package/dist/realtimefeeds/coinbase.d.ts.map +1 -1
- package/dist/realtimefeeds/coinbase.js.map +1 -1
- package/dist/realtimefeeds/hitbtc.d.ts.map +1 -1
- package/dist/realtimefeeds/hitbtc.js.map +1 -1
- package/dist/realtimefeeds/huobi.d.ts.map +1 -1
- package/dist/realtimefeeds/huobi.js.map +1 -1
- package/dist/realtimefeeds/kucoinfutures.d.ts.map +1 -1
- package/dist/realtimefeeds/kucoinfutures.js.map +1 -1
- package/dist/realtimefeeds/realtimefeed.d.ts.map +1 -1
- package/dist/realtimefeeds/realtimefeed.js.map +1 -1
- package/dist/realtimefeeds/serum.d.ts.map +1 -1
- package/dist/realtimefeeds/serum.js.map +1 -1
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +11 -7
- package/dist/replay.js.map +1 -1
- package/dist/worker.d.ts +1 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +58 -24
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
- package/src/computable/computable.ts +14 -11
- package/src/handy.ts +34 -20
- package/src/instrumentinfo.ts +1 -1
- package/src/mappers/binance.ts +16 -8
- package/src/mappers/bybit.ts +21 -16
- package/src/mappers/gateio.ts +4 -1
- package/src/mappers/gateiofutures.ts +2 -2
- package/src/mappers/huobi.ts +8 -6
- package/src/mappers/index.ts +2 -2
- package/src/mappers/kucoin.ts +4 -1
- package/src/mappers/okex.ts +24 -6
- package/src/mappers/phemex.ts +14 -4
- package/src/realtimefeeds/bitnomial.ts +14 -11
- package/src/realtimefeeds/coinbase.ts +14 -11
- package/src/realtimefeeds/hitbtc.ts +10 -7
- package/src/realtimefeeds/huobi.ts +10 -2
- package/src/realtimefeeds/kucoinfutures.ts +4 -1
- package/src/realtimefeeds/realtimefeed.ts +5 -1
- package/src/realtimefeeds/serum.ts +14 -11
- package/src/replay.ts +14 -10
- package/src/worker.ts +68 -27
package/src/handy.ts
CHANGED
|
@@ -71,13 +71,21 @@ export function* sequence(end: number, seed = 0) {
|
|
|
71
71
|
export const ONE_SEC_IN_MS = 1000
|
|
72
72
|
|
|
73
73
|
export class HttpError extends Error {
|
|
74
|
-
constructor(
|
|
74
|
+
constructor(
|
|
75
|
+
public readonly status: number,
|
|
76
|
+
public readonly responseText: string,
|
|
77
|
+
public readonly url: string
|
|
78
|
+
) {
|
|
75
79
|
super(`HttpError: status code: ${status}, response text: ${responseText}`)
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
class HttpClientError extends Error {
|
|
80
|
-
constructor(
|
|
84
|
+
constructor(
|
|
85
|
+
public readonly response: HttpResponse,
|
|
86
|
+
public readonly method: string,
|
|
87
|
+
public readonly url: string
|
|
88
|
+
) {
|
|
81
89
|
super(`HTTP ${method} ${url} failed with status ${response.statusCode}`)
|
|
82
90
|
}
|
|
83
91
|
}
|
|
@@ -160,24 +168,27 @@ export async function* normalizeMessages(
|
|
|
160
168
|
export function getFilters<T extends Exchange>(mappers: Mapper<T, any>[], symbols?: string[]) {
|
|
161
169
|
const filters = mappers.flatMap((mapper) => mapper.getFilters(symbols))
|
|
162
170
|
|
|
163
|
-
const deduplicatedFilters = filters.reduce(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (matchingExisting
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
matchingExisting.symbols.
|
|
171
|
+
const deduplicatedFilters = filters.reduce(
|
|
172
|
+
(prev, current) => {
|
|
173
|
+
const matchingExisting = prev.find((c) => c.channel === current.channel)
|
|
174
|
+
if (matchingExisting !== undefined) {
|
|
175
|
+
if (matchingExisting.symbols !== undefined && current.symbols) {
|
|
176
|
+
for (let symbol of current.symbols) {
|
|
177
|
+
if (matchingExisting.symbols.includes(symbol) === false) {
|
|
178
|
+
matchingExisting.symbols.push(symbol)
|
|
179
|
+
}
|
|
170
180
|
}
|
|
181
|
+
} else if (current.symbols) {
|
|
182
|
+
matchingExisting.symbols = [...current.symbols]
|
|
171
183
|
}
|
|
172
|
-
} else
|
|
173
|
-
|
|
184
|
+
} else {
|
|
185
|
+
prev.push(current)
|
|
174
186
|
}
|
|
175
|
-
} else {
|
|
176
|
-
prev.push(current)
|
|
177
|
-
}
|
|
178
187
|
|
|
179
|
-
|
|
180
|
-
|
|
188
|
+
return prev
|
|
189
|
+
},
|
|
190
|
+
[] as FilterForExchange[T][]
|
|
191
|
+
)
|
|
181
192
|
|
|
182
193
|
return deduplicatedFilters
|
|
183
194
|
}
|
|
@@ -255,8 +266,8 @@ export const httpsProxyAgent: Agent | undefined =
|
|
|
255
266
|
process.env.HTTP_PROXY !== undefined
|
|
256
267
|
? new HttpsProxyAgent(process.env.HTTP_PROXY)
|
|
257
268
|
: process.env.SOCKS_PROXY !== undefined
|
|
258
|
-
|
|
259
|
-
|
|
269
|
+
? new SocksProxyAgent(process.env.SOCKS_PROXY)
|
|
270
|
+
: undefined
|
|
260
271
|
|
|
261
272
|
const DEFAULT_FETCH_RETRY_LIMIT = 2
|
|
262
273
|
|
|
@@ -296,7 +307,7 @@ type RetrySettings = {
|
|
|
296
307
|
function getRetrySettings(method: string, retry?: HttpRetryOptions): RetrySettings {
|
|
297
308
|
const retryOptions = typeof retry === 'object' ? retry : undefined
|
|
298
309
|
const retryEnabled = method === 'GET' || retry !== undefined
|
|
299
|
-
const limit = typeof retry === 'number' ? retry : retryOptions?.limit ?? (retryEnabled ? DEFAULT_FETCH_RETRY_LIMIT : 0)
|
|
310
|
+
const limit = typeof retry === 'number' ? retry : (retryOptions?.limit ?? (retryEnabled ? DEFAULT_FETCH_RETRY_LIMIT : 0))
|
|
300
311
|
|
|
301
312
|
return {
|
|
302
313
|
limit,
|
|
@@ -617,6 +628,7 @@ async function _downloadFile(requestOptions: RequestOptions, url: string, downlo
|
|
|
617
628
|
|
|
618
629
|
try {
|
|
619
630
|
// based on https://github.com/nodejs/node/issues/28172 - only reliable way to consume response stream and avoiding all the 'gotchas'
|
|
631
|
+
let responseHeaders: Record<string, string> = {}
|
|
620
632
|
await new Promise<void>((resolve, reject) => {
|
|
621
633
|
const req = https
|
|
622
634
|
.get(url, requestOptions, (res) => {
|
|
@@ -631,6 +643,7 @@ async function _downloadFile(requestOptions: RequestOptions, url: string, downlo
|
|
|
631
643
|
reject(new HttpError(statusCode!, body, url))
|
|
632
644
|
})
|
|
633
645
|
} else {
|
|
646
|
+
responseHeaders = parseNodeResponseHeaders(res.headers)
|
|
634
647
|
if (appendContentEncodingExtension) {
|
|
635
648
|
const contentEncoding = asSingleHeaderValue(res.headers['content-encoding'])
|
|
636
649
|
if (contentEncoding === 'zstd') {
|
|
@@ -671,7 +684,8 @@ async function _downloadFile(requestOptions: RequestOptions, url: string, downlo
|
|
|
671
684
|
await rename(tmpFilePath, finalDownloadPath)
|
|
672
685
|
|
|
673
686
|
return {
|
|
674
|
-
downloadPath: finalDownloadPath
|
|
687
|
+
downloadPath: finalDownloadPath,
|
|
688
|
+
headers: responseHeaders
|
|
675
689
|
}
|
|
676
690
|
} finally {
|
|
677
691
|
tmpFileCleanups.delete(tmpFilePath)
|
package/src/instrumentinfo.ts
CHANGED
|
@@ -66,7 +66,7 @@ export async function findInstrumentSymbols(
|
|
|
66
66
|
|
|
67
67
|
return {
|
|
68
68
|
exchange,
|
|
69
|
-
symbols: instruments.map((instrument) => (selector === 'datasetId' ? instrument.datasetId ?? instrument.id : instrument.id))
|
|
69
|
+
symbols: instruments.map((instrument) => (selector === 'datasetId' ? (instrument.datasetId ?? instrument.id) : instrument.id))
|
|
70
70
|
}
|
|
71
71
|
})
|
|
72
72
|
)
|
package/src/mappers/binance.ts
CHANGED
|
@@ -5,9 +5,10 @@ import { Mapper, PendingTickerInfoHelper } from './mapper.ts'
|
|
|
5
5
|
|
|
6
6
|
// https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md
|
|
7
7
|
|
|
8
|
-
export class BinanceTradesMapper
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
export class BinanceTradesMapper implements Mapper<
|
|
9
|
+
'binance' | 'binance-jersey' | 'binance-us' | 'binance-futures' | 'binance-delivery',
|
|
10
|
+
Trade
|
|
11
|
+
> {
|
|
11
12
|
constructor(private readonly _exchange: Exchange) {}
|
|
12
13
|
|
|
13
14
|
canHandle(message: BinanceResponse<any>) {
|
|
@@ -53,14 +54,18 @@ export class BinanceTradesMapper
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
export class BinanceBookChangeMapper
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
export class BinanceBookChangeMapper implements Mapper<
|
|
58
|
+
'binance' | 'binance-jersey' | 'binance-us' | 'binance-futures' | 'binance-delivery',
|
|
59
|
+
BookChange
|
|
60
|
+
> {
|
|
59
61
|
protected readonly symbolToDepthInfoMapping: {
|
|
60
62
|
[key: string]: LocalDepthInfo
|
|
61
63
|
} = {}
|
|
62
64
|
|
|
63
|
-
constructor(
|
|
65
|
+
constructor(
|
|
66
|
+
protected readonly exchange: Exchange,
|
|
67
|
+
protected readonly ignoreBookSnapshotOverlapError: boolean
|
|
68
|
+
) {}
|
|
64
69
|
|
|
65
70
|
canHandle(message: BinanceResponse<any>) {
|
|
66
71
|
if (message.stream === undefined) {
|
|
@@ -217,7 +222,10 @@ export class BinanceFuturesBookChangeMapper
|
|
|
217
222
|
extends BinanceBookChangeMapper
|
|
218
223
|
implements Mapper<'binance-futures' | 'binance-delivery', BookChange>
|
|
219
224
|
{
|
|
220
|
-
constructor(
|
|
225
|
+
constructor(
|
|
226
|
+
protected readonly exchange: Exchange,
|
|
227
|
+
protected readonly ignoreBookSnapshotOverlapError: boolean
|
|
228
|
+
) {
|
|
221
229
|
super(exchange, ignoreBookSnapshotOverlapError)
|
|
222
230
|
}
|
|
223
231
|
|
package/src/mappers/bybit.ts
CHANGED
|
@@ -44,7 +44,10 @@ export class BybitV5TradesMapper implements Mapper<'bybit' | 'bybit-spot' | 'byb
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export class BybitV5BookChangeMapper implements Mapper<'bybit' | 'bybit-spot' | 'bybit-options', BookChange> {
|
|
47
|
-
constructor(
|
|
47
|
+
constructor(
|
|
48
|
+
protected readonly _exchange: Exchange,
|
|
49
|
+
private readonly _depth: number
|
|
50
|
+
) {}
|
|
48
51
|
|
|
49
52
|
canHandle(message: BybitV5OrderBookMessage) {
|
|
50
53
|
if (message.topic === undefined) {
|
|
@@ -388,8 +391,8 @@ export class BybitTradesMapper implements Mapper<'bybit', Trade> {
|
|
|
388
391
|
'trade_time_ms' in trade
|
|
389
392
|
? new Date(Number(trade.trade_time_ms))
|
|
390
393
|
: 'tradeTimeMs' in trade
|
|
391
|
-
|
|
392
|
-
|
|
394
|
+
? new Date(Number(trade.tradeTimeMs))
|
|
395
|
+
: new Date(trade.timestamp)
|
|
393
396
|
|
|
394
397
|
yield {
|
|
395
398
|
type: 'trade',
|
|
@@ -407,7 +410,10 @@ export class BybitTradesMapper implements Mapper<'bybit', Trade> {
|
|
|
407
410
|
}
|
|
408
411
|
|
|
409
412
|
export class BybitBookChangeMapper implements Mapper<'bybit', BookChange> {
|
|
410
|
-
constructor(
|
|
413
|
+
constructor(
|
|
414
|
+
protected readonly _exchange: Exchange,
|
|
415
|
+
private readonly _canUseBook200Channel: boolean
|
|
416
|
+
) {}
|
|
411
417
|
|
|
412
418
|
canHandle(message: BybitDataMessage) {
|
|
413
419
|
if (message.topic === undefined) {
|
|
@@ -449,8 +455,8 @@ export class BybitBookChangeMapper implements Mapper<'bybit', BookChange> {
|
|
|
449
455
|
? 'order_book' in message.data
|
|
450
456
|
? message.data.order_book
|
|
451
457
|
: 'orderBook' in message.data
|
|
452
|
-
|
|
453
|
-
|
|
458
|
+
? message.data.orderBook
|
|
459
|
+
: message.data
|
|
454
460
|
: [...message.data.delete, ...message.data.update, ...message.data.insert]
|
|
455
461
|
|
|
456
462
|
const timestampBybit = message.timestamp_e6 !== undefined ? Number(message.timestamp_e6) : Number(message.timestampE6)
|
|
@@ -858,16 +864,15 @@ type BybitInstrumentUpdate = {
|
|
|
858
864
|
ask1Price: '21213.50'
|
|
859
865
|
}
|
|
860
866
|
|
|
861
|
-
type BybitInstrumentDataMessage =
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
}
|
|
867
|
+
type BybitInstrumentDataMessage = BybitDataMessage & {
|
|
868
|
+
timestamp_e6: string
|
|
869
|
+
timestampE6: string
|
|
870
|
+
data:
|
|
871
|
+
| BybitInstrumentUpdate
|
|
872
|
+
| {
|
|
873
|
+
update: [BybitInstrumentUpdate]
|
|
874
|
+
}
|
|
875
|
+
}
|
|
871
876
|
|
|
872
877
|
type BybitLiquidationMessage = BybitDataMessage & {
|
|
873
878
|
generated: true
|
package/src/mappers/gateio.ts
CHANGED
|
@@ -60,7 +60,10 @@ export class GateIOV4BookChangeMapper implements Mapper<'gate-io', BookChange> {
|
|
|
60
60
|
[key: string]: LocalDepthInfo
|
|
61
61
|
} = {}
|
|
62
62
|
|
|
63
|
-
constructor(
|
|
63
|
+
constructor(
|
|
64
|
+
protected readonly exchange: Exchange,
|
|
65
|
+
protected readonly ignoreBookSnapshotOverlapError: boolean
|
|
66
|
+
) {}
|
|
64
67
|
|
|
65
68
|
canHandle(message: GateV4OrderBookUpdate | Gatev4OrderBookSnapshot) {
|
|
66
69
|
if (message.channel === undefined) {
|
|
@@ -76,8 +76,8 @@ export class GateIOFuturesBookChangeMapper implements Mapper<'gate-io-futures',
|
|
|
76
76
|
depthMessage.time_ms !== undefined
|
|
77
77
|
? new Date(depthMessage.time_ms)
|
|
78
78
|
: depthMessage.result.t !== undefined
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
? new Date(depthMessage.result.t)
|
|
80
|
+
: new Date(depthMessage.time * 1000)
|
|
81
81
|
|
|
82
82
|
// snapshot
|
|
83
83
|
yield {
|
package/src/mappers/huobi.ts
CHANGED
|
@@ -5,9 +5,10 @@ import { Mapper, PendingTickerInfoHelper } from './mapper.ts'
|
|
|
5
5
|
// https://huobiapi.github.io/docs/spot/v1/en/#websocket-market-data
|
|
6
6
|
// https://github.com/huobiapi/API_Docs_en/wiki/WS_api_reference_en
|
|
7
7
|
|
|
8
|
-
export class HuobiTradesMapper
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
export class HuobiTradesMapper implements Mapper<
|
|
9
|
+
'huobi' | 'huobi-dm' | 'huobi-dm-swap' | 'huobi-dm-linear-swap' | 'huobi-dm-options',
|
|
10
|
+
Trade
|
|
11
|
+
> {
|
|
11
12
|
constructor(private readonly _exchange: Exchange) {}
|
|
12
13
|
canHandle(message: HuobiDataMessage) {
|
|
13
14
|
if (message.ch === undefined) {
|
|
@@ -46,9 +47,10 @@ export class HuobiTradesMapper
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
export class HuobiBookChangeMapper
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
export class HuobiBookChangeMapper implements Mapper<
|
|
51
|
+
'huobi' | 'huobi-dm' | 'huobi-dm-swap' | 'huobi-dm-linear-swap' | 'huobi-dm-options',
|
|
52
|
+
BookChange
|
|
53
|
+
> {
|
|
52
54
|
constructor(protected readonly _exchange: Exchange) {}
|
|
53
55
|
|
|
54
56
|
canHandle(message: HuobiDataMessage) {
|
package/src/mappers/index.ts
CHANGED
|
@@ -409,8 +409,8 @@ const bookChangeMappers = {
|
|
|
409
409
|
shouldUseGateIOV4OrderBookV2Mappers(localTimestamp)
|
|
410
410
|
? new GateIOV4OrderBookV2ChangeMapper('gate-io')
|
|
411
411
|
: shouldUseGateIOV4Mappers(localTimestamp)
|
|
412
|
-
|
|
413
|
-
|
|
412
|
+
? new GateIOV4BookChangeMapper('gate-io', isRealTime(localTimestamp) == false)
|
|
413
|
+
: new GateIOBookChangeMapper('gate-io'),
|
|
414
414
|
'gate-io-futures': () => new GateIOFuturesBookChangeMapper('gate-io-futures'),
|
|
415
415
|
poloniex: (localTimestamp: Date) =>
|
|
416
416
|
shouldUsePoloniexV2Mappers(localTimestamp) ? new PoloniexV2BookChangeMapper() : new PoloniexBookChangeMapper(),
|
package/src/mappers/kucoin.ts
CHANGED
|
@@ -46,7 +46,10 @@ export class KucoinBookChangeMapper implements Mapper<'kucoin', BookChange> {
|
|
|
46
46
|
[key: string]: LocalDepthInfo
|
|
47
47
|
} = {}
|
|
48
48
|
|
|
49
|
-
constructor(
|
|
49
|
+
constructor(
|
|
50
|
+
protected readonly _exchange: Exchange,
|
|
51
|
+
private readonly ignoreBookSnapshotOverlapError: boolean
|
|
52
|
+
) {}
|
|
50
53
|
|
|
51
54
|
canHandle(message: KucoinLevel2SnapshotMessage | KucoinLevel2UpdateMessage) {
|
|
52
55
|
return message.type === 'message' && message.topic.startsWith('/market/level2')
|
package/src/mappers/okex.ts
CHANGED
|
@@ -6,7 +6,10 @@ import { Mapper, PendingTickerInfoHelper } from './mapper.ts'
|
|
|
6
6
|
// https://www.okex.com/docs-v5/en/#websocket-api-public-channel-trades-channel
|
|
7
7
|
|
|
8
8
|
export class OkexV5TradesMapper implements Mapper<OKEX_EXCHANGES, Trade> {
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(
|
|
10
|
+
private readonly _exchange: Exchange,
|
|
11
|
+
private readonly _useTradesAll: boolean
|
|
12
|
+
) {}
|
|
10
13
|
|
|
11
14
|
canHandle(message: any) {
|
|
12
15
|
if (message.event !== undefined || message.arg === undefined) {
|
|
@@ -62,7 +65,10 @@ const mapV5BookLevel = (level: OkexV5BookLevel) => {
|
|
|
62
65
|
export class OkexV5BookChangeMapper implements Mapper<OKEX_EXCHANGES, BookChange> {
|
|
63
66
|
private _channelName: string
|
|
64
67
|
|
|
65
|
-
constructor(
|
|
68
|
+
constructor(
|
|
69
|
+
private readonly _exchange: Exchange,
|
|
70
|
+
usePublicBooksChannel: boolean
|
|
71
|
+
) {
|
|
66
72
|
this._channelName = this._getBooksChannelName(usePublicBooksChannel)
|
|
67
73
|
}
|
|
68
74
|
|
|
@@ -137,7 +143,10 @@ export class OkexV5BookChangeMapper implements Mapper<OKEX_EXCHANGES, BookChange
|
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
export class OkexV5BookTickerMapper implements Mapper<OKEX_EXCHANGES, BookTicker> {
|
|
140
|
-
constructor(
|
|
146
|
+
constructor(
|
|
147
|
+
private readonly _exchange: Exchange,
|
|
148
|
+
private readonly _useTbtTickerChannel: boolean
|
|
149
|
+
) {}
|
|
141
150
|
|
|
142
151
|
canHandle(message: any) {
|
|
143
152
|
if (message.event !== undefined || message.arg === undefined) {
|
|
@@ -726,7 +735,10 @@ type OkexV5SummaryMessage = {
|
|
|
726
735
|
// https://www.okex.com/docs/en/#ws_swap-README
|
|
727
736
|
|
|
728
737
|
export class OkexTradesMapper implements Mapper<OKEX_EXCHANGES, Trade> {
|
|
729
|
-
constructor(
|
|
738
|
+
constructor(
|
|
739
|
+
private readonly _exchange: Exchange,
|
|
740
|
+
private readonly _market: OKEX_MARKETS
|
|
741
|
+
) {}
|
|
730
742
|
|
|
731
743
|
canHandle(message: OkexDataMessage) {
|
|
732
744
|
return message.table === `${this._market}/trade`
|
|
@@ -991,7 +1003,10 @@ export class OkexOptionSummaryMapper implements Mapper<'okex-options', OptionSum
|
|
|
991
1003
|
}
|
|
992
1004
|
|
|
993
1005
|
export class OkexLiquidationsMapper implements Mapper<OKEX_EXCHANGES, Liquidation> {
|
|
994
|
-
constructor(
|
|
1006
|
+
constructor(
|
|
1007
|
+
private readonly _exchange: Exchange,
|
|
1008
|
+
private readonly _market: OKEX_MARKETS
|
|
1009
|
+
) {}
|
|
995
1010
|
|
|
996
1011
|
canHandle(message: OkexDataMessage) {
|
|
997
1012
|
return message.table === `${this._market}/liquidation`
|
|
@@ -1027,7 +1042,10 @@ export class OkexLiquidationsMapper implements Mapper<OKEX_EXCHANGES, Liquidatio
|
|
|
1027
1042
|
}
|
|
1028
1043
|
|
|
1029
1044
|
export class OkexBookTickerMapper implements Mapper<OKEX_EXCHANGES, BookTicker> {
|
|
1030
|
-
constructor(
|
|
1045
|
+
constructor(
|
|
1046
|
+
private readonly _exchange: Exchange,
|
|
1047
|
+
private readonly _market: OKEX_MARKETS
|
|
1048
|
+
) {}
|
|
1031
1049
|
|
|
1032
1050
|
canHandle(message: OkexDataMessage) {
|
|
1033
1051
|
return message.table === `${this._market}/ticker`
|
package/src/mappers/phemex.ts
CHANGED
|
@@ -27,6 +27,11 @@ function getQtyScale(symbol: string) {
|
|
|
27
27
|
return 1
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function isExcludedFromNormalizedOutput(symbol: string) {
|
|
31
|
+
// Matches tardis-api metadata: Phemex spot OL/USDT uses sOLUSDT, which collides with SOLUSDT when normalized.
|
|
32
|
+
return symbol === 'sOLUSDT'
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
const COINS_STARTING_WITH_S = [
|
|
31
36
|
'SOLUSD',
|
|
32
37
|
'SUSHIUSD',
|
|
@@ -163,10 +168,13 @@ export const phemexTradesMapper: Mapper<'phemex', Trade> = {
|
|
|
163
168
|
},
|
|
164
169
|
|
|
165
170
|
*map(message: PhemexTradeMessage, localTimestamp: Date): IterableIterator<Trade> {
|
|
171
|
+
const symbol = message.symbol
|
|
172
|
+
if (isExcludedFromNormalizedOutput(symbol)) {
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
166
176
|
if ('trades' in message) {
|
|
167
177
|
for (const [timestamp, side, priceEp, qty] of message.trades) {
|
|
168
|
-
const symbol = message.symbol
|
|
169
|
-
|
|
170
178
|
yield {
|
|
171
179
|
type: 'trade',
|
|
172
180
|
symbol: symbol.toUpperCase(),
|
|
@@ -181,8 +189,6 @@ export const phemexTradesMapper: Mapper<'phemex', Trade> = {
|
|
|
181
189
|
}
|
|
182
190
|
} else if ('trades_p' in message) {
|
|
183
191
|
for (const [timestamp, side, price, qty] of message.trades_p) {
|
|
184
|
-
const symbol = message.symbol
|
|
185
|
-
|
|
186
192
|
yield {
|
|
187
193
|
type: 'trade',
|
|
188
194
|
symbol: symbol.toUpperCase(),
|
|
@@ -253,6 +259,10 @@ export const phemexBookChangeMapper: Mapper<'phemex', BookChange> = {
|
|
|
253
259
|
|
|
254
260
|
*map(message: PhemexBookMessage, localTimestamp: Date): IterableIterator<BookChange> {
|
|
255
261
|
const symbol = message.symbol
|
|
262
|
+
if (isExcludedFromNormalizedOutput(symbol)) {
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
|
|
256
266
|
if ('book' in message) {
|
|
257
267
|
const mapBookLevel = mapBookLevelForSymbol(symbol)
|
|
258
268
|
yield {
|
|
@@ -27,20 +27,23 @@ export class BitnomialRealTimeFeed extends RealTimeFeedBase {
|
|
|
27
27
|
product_codes: filter.symbols
|
|
28
28
|
}
|
|
29
29
|
})
|
|
30
|
-
.reduce(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
matchingExisting.product_codes.
|
|
30
|
+
.reduce(
|
|
31
|
+
(prev, current) => {
|
|
32
|
+
const matchingExisting = prev.find((c) => c.name === current.name)
|
|
33
|
+
if (matchingExisting !== undefined) {
|
|
34
|
+
for (const symbol of current.product_codes) {
|
|
35
|
+
if (matchingExisting.product_codes.includes(symbol) === false) {
|
|
36
|
+
matchingExisting.product_codes.push(symbol)
|
|
37
|
+
}
|
|
36
38
|
}
|
|
39
|
+
} else {
|
|
40
|
+
prev.push(current)
|
|
37
41
|
}
|
|
38
|
-
} else {
|
|
39
|
-
prev.push(current)
|
|
40
|
-
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
return prev
|
|
44
|
+
},
|
|
45
|
+
[] as { name: string; product_codes: string[] }[]
|
|
46
|
+
)
|
|
44
47
|
|
|
45
48
|
return [
|
|
46
49
|
{
|
|
@@ -53,20 +53,23 @@ export class CoinbaseRealTimeFeed extends RealTimeFeedBase {
|
|
|
53
53
|
product_ids: filter.symbols
|
|
54
54
|
}
|
|
55
55
|
})
|
|
56
|
-
.reduce(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
matchingExisting.product_ids.
|
|
56
|
+
.reduce(
|
|
57
|
+
(prev, current) => {
|
|
58
|
+
const matchingExisting = prev.find((c) => c.name === current.name)
|
|
59
|
+
if (matchingExisting !== undefined) {
|
|
60
|
+
for (const symbol of current.product_ids) {
|
|
61
|
+
if (matchingExisting.product_ids.includes(symbol) === false) {
|
|
62
|
+
matchingExisting.product_ids.push(symbol)
|
|
63
|
+
}
|
|
62
64
|
}
|
|
65
|
+
} else {
|
|
66
|
+
prev.push(current)
|
|
63
67
|
}
|
|
64
|
-
} else {
|
|
65
|
-
prev.push(current)
|
|
66
|
-
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
return prev
|
|
70
|
+
},
|
|
71
|
+
[] as { name: string; product_ids: string[] }[]
|
|
72
|
+
)
|
|
70
73
|
|
|
71
74
|
if (this._hasCredentials) {
|
|
72
75
|
const authParams = this.getAuthParams()
|
|
@@ -36,14 +36,17 @@ export class HitBtcRealTimeFeed extends RealTimeFeedBase {
|
|
|
36
36
|
})
|
|
37
37
|
})
|
|
38
38
|
.flatMap((s) => s)
|
|
39
|
-
.reduce(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
.reduce(
|
|
40
|
+
(prev, current) => {
|
|
41
|
+
const matchingExisting = prev.find((c) => c.method === current.method && c.symbol === current.symbol)
|
|
42
|
+
if (matchingExisting === undefined) {
|
|
43
|
+
prev.push(current)
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
return prev
|
|
47
|
+
},
|
|
48
|
+
[] as { method: string; symbol: string }[]
|
|
49
|
+
)
|
|
47
50
|
|
|
48
51
|
return subscriptions.map((subscription, index) => {
|
|
49
52
|
return {
|
|
@@ -271,7 +271,11 @@ class HuobiOpenInterestClient extends PoolingClientBase {
|
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
class HuobiOptionsMarketIndexClient extends PoolingClientBase {
|
|
274
|
-
constructor(
|
|
274
|
+
constructor(
|
|
275
|
+
exchange: string,
|
|
276
|
+
private readonly _httpURL: string,
|
|
277
|
+
private readonly _instruments: string[]
|
|
278
|
+
) {
|
|
275
279
|
super(exchange, 4)
|
|
276
280
|
}
|
|
277
281
|
|
|
@@ -306,7 +310,11 @@ class HuobiOptionsMarketIndexClient extends PoolingClientBase {
|
|
|
306
310
|
}
|
|
307
311
|
|
|
308
312
|
class HuobiOptionsIndexClient extends PoolingClientBase {
|
|
309
|
-
constructor(
|
|
313
|
+
constructor(
|
|
314
|
+
exchange: string,
|
|
315
|
+
private readonly _httpURL: string,
|
|
316
|
+
private readonly _instruments: string[]
|
|
317
|
+
) {
|
|
310
318
|
super(exchange, 4)
|
|
311
319
|
}
|
|
312
320
|
|
|
@@ -109,7 +109,10 @@ export class KucoinFuturesSingleConnectionRealTimeFeed extends RealTimeFeedBase
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
class KucoinFuturesContractDetailsClient extends PoolingClientBase {
|
|
112
|
-
constructor(
|
|
112
|
+
constructor(
|
|
113
|
+
exchange: string,
|
|
114
|
+
private readonly _httpURL: string
|
|
115
|
+
) {
|
|
113
116
|
super(exchange, 6)
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -377,7 +377,11 @@ export abstract class PoolingClientBase implements RealTimeFeedIterable {
|
|
|
377
377
|
protected readonly debug: dbg.Debugger
|
|
378
378
|
private _tid: NodeJS.Timeout | undefined = undefined
|
|
379
379
|
|
|
380
|
-
constructor(
|
|
380
|
+
constructor(
|
|
381
|
+
exchange: string,
|
|
382
|
+
private readonly _poolingIntervalSeconds: number,
|
|
383
|
+
protected readonly onError?: (error: Error) => void
|
|
384
|
+
) {
|
|
381
385
|
this.debug = dbg(`tardis-dev:pooling-client:${exchange}`)
|
|
382
386
|
}
|
|
383
387
|
|
|
@@ -40,20 +40,23 @@ export class SerumRealTimeFeed extends RealTimeFeedBase {
|
|
|
40
40
|
markets: filter.symbols
|
|
41
41
|
}
|
|
42
42
|
})
|
|
43
|
-
.reduce(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
matchingExisting.markets.
|
|
43
|
+
.reduce(
|
|
44
|
+
(prev, current) => {
|
|
45
|
+
const matchingExisting = prev.find((c) => c.channel === current.channel)
|
|
46
|
+
if (matchingExisting !== undefined) {
|
|
47
|
+
for (const market of current.markets) {
|
|
48
|
+
if (matchingExisting.markets.includes(market) === false) {
|
|
49
|
+
matchingExisting.markets.push(market)
|
|
50
|
+
}
|
|
49
51
|
}
|
|
52
|
+
} else {
|
|
53
|
+
prev.push(current)
|
|
50
54
|
}
|
|
51
|
-
} else {
|
|
52
|
-
prev.push(current)
|
|
53
|
-
}
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
return prev
|
|
57
|
+
},
|
|
58
|
+
[] as { channel: string; markets: string[] }[]
|
|
59
|
+
)
|
|
57
60
|
|
|
58
61
|
return subs
|
|
59
62
|
}
|