tardis-dev 13.29.15 → 13.30.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.
@@ -0,0 +1,339 @@
1
+ import { upperCaseSymbols } from '../handy'
2
+ import { BookChange, DerivativeTicker, Liquidation, Trade } from '../types'
3
+ import { Mapper, PendingTickerInfoHelper } from './mapper'
4
+
5
+ export class DydxV4TradesMapper implements Mapper<'dydx-v4', Trade> {
6
+ canHandle(message: DyDxTrade) {
7
+ return message.channel === 'v4_trades' && message.type === 'channel_data'
8
+ }
9
+
10
+ getFilters(symbols?: string[]) {
11
+ symbols = upperCaseSymbols(symbols)
12
+
13
+ return [
14
+ {
15
+ channel: 'v4_trades',
16
+ symbols
17
+ } as const
18
+ ]
19
+ }
20
+
21
+ *map(message: DyDxTrade, localTimestamp: Date): IterableIterator<Trade> {
22
+ for (let trade of message.contents.trades) {
23
+ yield {
24
+ type: 'trade',
25
+ symbol: message.id,
26
+ exchange: 'dydx-v4',
27
+ id: trade.id,
28
+ price: Number(trade.price),
29
+ amount: Number(trade.size),
30
+ side: trade.side === 'SELL' ? 'sell' : 'buy',
31
+ timestamp: trade.createdAt ? new Date(trade.createdAt) : localTimestamp,
32
+ localTimestamp: localTimestamp
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ function mapSnapshotPriceLevel(level: { price: string; size: string }) {
39
+ return {
40
+ price: Number(level.price),
41
+ amount: Number(level.size)
42
+ }
43
+ }
44
+
45
+ function mapUpdatePriceLevel(level: [string, string]) {
46
+ return {
47
+ price: Number(level[0]),
48
+ amount: Number(level[1])
49
+ }
50
+ }
51
+ export class DydxV4BookChangeMapper implements Mapper<'dydx-v4', BookChange> {
52
+ canHandle(message: DyDxOrderbookSnapshot | DyDxOrderBookUpdate) {
53
+ return message.channel === 'v4_orderbook'
54
+ }
55
+
56
+ getFilters(symbols?: string[]) {
57
+ symbols = upperCaseSymbols(symbols)
58
+
59
+ return [
60
+ {
61
+ channel: 'v4_orderbook',
62
+ symbols
63
+ } as const
64
+ ]
65
+ }
66
+
67
+ *map(message: DyDxOrderbookSnapshot | DyDxOrderBookUpdate, localTimestamp: Date): IterableIterator<BookChange> {
68
+ if (message.type === 'subscribed') {
69
+ yield {
70
+ type: 'book_change',
71
+ symbol: message.id,
72
+ exchange: 'dydx-v4',
73
+ isSnapshot: true,
74
+ bids: message.contents.bids.map(mapSnapshotPriceLevel),
75
+ asks: message.contents.asks.map(mapSnapshotPriceLevel),
76
+ timestamp: localTimestamp,
77
+ localTimestamp
78
+ }
79
+ } else {
80
+ if (!message.contents) {
81
+ return
82
+ }
83
+
84
+ const bookChange: BookChange = {
85
+ type: 'book_change',
86
+ symbol: message.id,
87
+ exchange: 'dydx-v4',
88
+ isSnapshot: false,
89
+ bids: message.contents.bids !== undefined ? message.contents.bids.map(mapUpdatePriceLevel) : [],
90
+ asks: message.contents.asks !== undefined ? message.contents.asks.map(mapUpdatePriceLevel) : [],
91
+ timestamp: localTimestamp,
92
+ localTimestamp
93
+ }
94
+
95
+ if (bookChange.bids.length > 0 || bookChange.asks.length > 0) {
96
+ yield bookChange
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ export class DydxV4DerivativeTickerMapper implements Mapper<'dydx-v4', DerivativeTicker> {
103
+ private readonly pendingTickerInfoHelper = new PendingTickerInfoHelper()
104
+
105
+ canHandle(message: DydxMarketsSnapshot | DyDxMarketsUpdate | DyDxTrade) {
106
+ return message.channel === 'v4_markets' || (message.channel === 'v4_trades' && message.type === 'channel_data')
107
+ }
108
+
109
+ getFilters(symbols?: string[]) {
110
+ symbols = upperCaseSymbols(symbols)
111
+
112
+ return [
113
+ {
114
+ channel: 'v4_markets',
115
+ symbols: [] as string[]
116
+ } as const,
117
+ {
118
+ channel: 'v4_trades',
119
+ symbols
120
+ } as const
121
+ ]
122
+ }
123
+
124
+ *map(message: DydxMarketsSnapshot | DyDxMarketsUpdate | DyDxTrade, localTimestamp: Date): IterableIterator<DerivativeTicker> {
125
+ if (message.channel === 'v4_trades') {
126
+ const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(message.id, 'dydx-v4')
127
+ pendingTickerInfo.updateLastPrice(Number(message.contents.trades[message.contents.trades.length - 1].price))
128
+
129
+ return
130
+ }
131
+ if (message.type === 'subscribed' || (message.type === 'channel_data' && message.contents.trading !== undefined)) {
132
+ const contents = message.type === 'subscribed' ? message.contents.markets : message.contents.trading
133
+ for (const key in contents) {
134
+ const marketInfo = (contents as any)[key] as DydxMarketsSnapshotContent | DydxMarketTradeUpdate
135
+
136
+ const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(key, 'dydx-v4')
137
+
138
+ if (marketInfo.oraclePrice !== undefined) {
139
+ pendingTickerInfo.updateMarkPrice(Number(marketInfo.oraclePrice))
140
+ }
141
+ if (marketInfo.openInterest !== undefined) {
142
+ pendingTickerInfo.updateOpenInterest(Number(marketInfo.openInterest))
143
+ }
144
+
145
+ if (marketInfo.nextFundingRate !== undefined) {
146
+ pendingTickerInfo.updateFundingRate(Number(marketInfo.nextFundingRate))
147
+ }
148
+
149
+ pendingTickerInfo.updateTimestamp(localTimestamp)
150
+
151
+ if (pendingTickerInfo.hasChanged()) {
152
+ yield pendingTickerInfo.getSnapshot(localTimestamp)
153
+ }
154
+ }
155
+ }
156
+
157
+ if (message.type === 'channel_data' && message.contents.oraclePrices !== undefined) {
158
+ for (const key in message.contents.oraclePrices) {
159
+ const oraclePriceInfo = (message.contents.oraclePrices as any)[key] as OraclePriceInfo
160
+ const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(key, 'dydx-v4')
161
+ if (oraclePriceInfo.oraclePrice !== undefined) {
162
+ pendingTickerInfo.updateMarkPrice(Number(oraclePriceInfo.oraclePrice))
163
+ }
164
+ pendingTickerInfo.updateTimestamp(localTimestamp)
165
+
166
+ if (pendingTickerInfo.hasChanged()) {
167
+ yield pendingTickerInfo.getSnapshot(localTimestamp)
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ export class DydxV4LiquidationsMapper implements Mapper<'dydx-v4', Liquidation> {
175
+ canHandle(message: DyDxTrade) {
176
+ return message.channel === 'v4_trades' && message.type === 'channel_data'
177
+ }
178
+
179
+ getFilters(symbols?: string[]) {
180
+ symbols = upperCaseSymbols(symbols)
181
+
182
+ return [
183
+ {
184
+ channel: 'v4_trades',
185
+ symbols
186
+ } as const
187
+ ]
188
+ }
189
+
190
+ *map(message: DyDxTrade, localTimestamp: Date): IterableIterator<Liquidation> {
191
+ for (let trade of message.contents.trades) {
192
+ if (trade.type === 'LIQUIDATED') {
193
+ yield {
194
+ type: 'liquidation',
195
+ symbol: message.id,
196
+ exchange: 'dydx-v4',
197
+ id: trade.id,
198
+ price: Number(trade.price),
199
+ amount: Number(trade.size),
200
+ side: trade.side === 'SELL' ? 'sell' : 'buy',
201
+ timestamp: trade.createdAt ? new Date(trade.createdAt) : localTimestamp,
202
+ localTimestamp: localTimestamp
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ type DyDxTrade = {
210
+ type: 'channel_data'
211
+ connection_id: '3a2e4c0c-7579-4bf6-a570-e0979418bbe9'
212
+ message_id: 15897
213
+ id: 'BTC-USD'
214
+ channel: 'v4_trades'
215
+ version: '2.1.0'
216
+ contents: {
217
+ trades: [
218
+ {
219
+ id: '0165e6170000000200000002'
220
+ size: '0.0001'
221
+ price: '60392'
222
+ side: 'BUY' | 'SELL'
223
+ createdAt: '2024-08-23T00:00:57.627Z'
224
+ type: 'LIMIT' | 'LIQUIDATED'
225
+ }
226
+ ]
227
+ }
228
+ }
229
+
230
+ type DyDxOrderbookSnapshot = {
231
+ type: 'subscribed'
232
+ connection_id: '67838890-75de-4bf3-a638-d7bcdea5f245'
233
+ message_id: 7
234
+ channel: 'v4_orderbook'
235
+ id: 'GRT-USD'
236
+ contents: {
237
+ bids: [{ price: '0.1547'; size: '35520' }]
238
+ asks: [{ price: '0.155'; size: '3220' }]
239
+ }
240
+ }
241
+
242
+ type DyDxOrderBookUpdate = {
243
+ type: 'channel_data'
244
+ connection_id: '00908030-4a70-43aa-9263-8ccdf57b5d40'
245
+ message_id: 10290
246
+ id: 'EOS-USD'
247
+ channel: 'v4_orderbook'
248
+ version: '1.0.0'
249
+ contents: { bids: [['0.1003', '2017130']]; asks: undefined | [['0.1003', '2017130']] }
250
+ }
251
+
252
+ type DydxMarketsSnapshot = {
253
+ type: 'subscribed'
254
+ connection_id: '3a2e4c0c-7579-4bf6-a570-e0979418bbe9'
255
+ message_id: 17
256
+ channel: 'v4_markets'
257
+ contents: {
258
+ markets: {
259
+ [key: string]: DydxMarketsSnapshotContent
260
+ }
261
+ }
262
+ }
263
+ type DydxMarketsSnapshotContent = {
264
+ clobPairId: '0'
265
+ ticker: 'BTC-USD'
266
+ status: 'ACTIVE'
267
+ oraclePrice: '60387.51779'
268
+ priceChange24H: '-782.58326'
269
+ volume24H: '247515340.0835'
270
+ trades24H: 73556
271
+ nextFundingRate: '0.00001351666666666667'
272
+ initialMarginFraction: '0.05'
273
+ maintenanceMarginFraction: '0.03'
274
+ openInterest: '648.2389'
275
+ atomicResolution: -10
276
+ quantumConversionExponent: -9
277
+ tickSize: '1'
278
+ stepSize: '0.0001'
279
+ stepBaseQuantums: 1000000
280
+ subticksPerTick: 100000
281
+ marketType: 'CROSS'
282
+ openInterestLowerCap: '0'
283
+ openInterestUpperCap: '0'
284
+ baseOpenInterest: '648.4278'
285
+ }
286
+
287
+ type DyDxMarketsUpdate =
288
+ | {
289
+ type: 'channel_data'
290
+ connection_id: '3a2e4c0c-7579-4bf6-a570-e0979418bbe9'
291
+ message_id: 15871
292
+ channel: 'v4_markets'
293
+ version: '1.0.0'
294
+ contents: {
295
+ oraclePrices: undefined
296
+ trading: {
297
+ 'ETH-USD': DydxMarketTradeUpdate
298
+ }
299
+ }
300
+ }
301
+ | {
302
+ type: 'channel_data'
303
+ connection_id: '3a2e4c0c-7579-4bf6-a570-e0979418bbe9'
304
+ message_id: 50
305
+ channel: 'v4_markets'
306
+ version: '1.0.0'
307
+ contents: {
308
+ trading: undefined
309
+ oraclePrices: {
310
+ 'ZERO-USD': OraclePriceInfo
311
+ }
312
+ }
313
+ }
314
+
315
+ type OraclePriceInfo = { oraclePrice: string; effectiveAt: string; effectiveAtHeight: string; marketId: number }
316
+
317
+ type DydxMarketTradeUpdate = {
318
+ id?: string
319
+ clobPairId?: string
320
+ ticker?: string
321
+ marketId?: number
322
+ oraclePrice: undefined
323
+ baseAsset?: string
324
+ quoteAsset?: string
325
+ initialMarginFraction?: string
326
+ maintenanceMarginFraction?: string
327
+ basePositionSize?: string
328
+ incrementalPositionSize?: string
329
+ maxPositionSize?: string
330
+ openInterest?: string
331
+ quantumConversionExponent?: number
332
+ atomicResolution?: number
333
+ subticksPerTick?: number
334
+ stepBaseQuantums?: number
335
+ priceChange24H?: string
336
+ volume24H?: string
337
+ trades24H?: number
338
+ nextFundingRate?: string
339
+ }
@@ -67,6 +67,7 @@ import {
67
67
  deribitTradesMapper
68
68
  } from './deribit'
69
69
  import { DydxBookChangeMapper, DydxDerivativeTickerMapper, DydxTradesMapper } from './dydx'
70
+ import { DydxV4BookChangeMapper, DydxV4DerivativeTickerMapper, DydxV4LiquidationsMapper, DydxV4TradesMapper } from './dydxv4'
70
71
  import { FTXBookChangeMapper, FTXDerivativeTickerMapper, FTXLiquidationsMapper, FTXBookTickerMapper, FTXTradesMapper } from './ftx'
71
72
  import {
72
73
  GateIOBookChangeMapper,
@@ -255,6 +256,7 @@ const tradesMappers = {
255
256
  upbit: () => new UpbitTradesMapper(),
256
257
  ascendex: () => new AscendexTradesMapper(),
257
258
  dydx: () => new DydxTradesMapper(),
259
+ 'dydx-v4': () => new DydxV4TradesMapper(),
258
260
  serum: () => new SerumTradesMapper('serum'),
259
261
  'star-atlas': () => new SerumTradesMapper('star-atlas'),
260
262
  mango: () => new SerumTradesMapper('mango'),
@@ -344,6 +346,7 @@ const bookChangeMappers = {
344
346
  upbit: () => new UpbitBookChangeMapper(),
345
347
  ascendex: () => new AscendexBookChangeMapper(),
346
348
  dydx: () => new DydxBookChangeMapper(),
349
+ 'dydx-v4': () => new DydxV4BookChangeMapper(),
347
350
  serum: () => new SerumBookChangeMapper('serum'),
348
351
  'star-atlas': () => new SerumBookChangeMapper('star-atlas'),
349
352
  mango: () => new SerumBookChangeMapper('mango'),
@@ -386,6 +389,7 @@ const derivativeTickersMappers = {
386
389
  coinflex: () => new CoinflexDerivativeTickerMapper(),
387
390
  ascendex: () => new AscendexDerivativeTickerMapper(),
388
391
  dydx: () => new DydxDerivativeTickerMapper(),
392
+ 'dydx-v4': () => new DydxV4DerivativeTickerMapper(),
389
393
  'crypto-com-derivatives': () => new CryptoComDerivativeTickerMapper('crypto-com-derivatives'),
390
394
  'crypto-com': () => new CryptoComDerivativeTickerMapper('crypto-com'),
391
395
  'woo-x': () => new WooxDerivativeTickerMapper(),
@@ -411,6 +415,7 @@ const liquidationsMappers = {
411
415
  'bitfinex-derivatives': () => new BitfinexLiquidationsMapper('bitfinex-derivatives'),
412
416
  cryptofacilities: () => cryptofacilitiesLiquidationsMapper,
413
417
  'huobi-dm': () => new HuobiLiquidationsMapper('huobi-dm'),
418
+ 'dydx-v4': () => new DydxV4LiquidationsMapper(),
414
419
  'huobi-dm-swap': () => new HuobiLiquidationsMapper('huobi-dm-swap'),
415
420
  'huobi-dm-linear-swap': () => new HuobiLiquidationsMapper('huobi-dm-linear-swap'),
416
421
  bybit: (localTimestamp: Date) =>
@@ -0,0 +1,39 @@
1
+ import { Filter } from '../types'
2
+ import { RealTimeFeedBase } from './realtimefeed'
3
+
4
+ export class DydxV4RealTimeFeed extends RealTimeFeedBase {
5
+ protected readonly wssURL = 'wss://indexer.dydx.trade/v4/ws'
6
+
7
+ protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
8
+ const subs = filters
9
+ .map((filter) => {
10
+ if (filter.channel === 'v4_markets') {
11
+ return [
12
+ {
13
+ type: 'subscribe',
14
+ channel: 'v4_markets'
15
+ }
16
+ ]
17
+ }
18
+
19
+ if (!filter.symbols || filter.symbols.length === 0) {
20
+ throw new Error('DydxV4RealTimeFeed requires explicitly specified symbols when subscribing to live feed')
21
+ }
22
+
23
+ return filter.symbols.map((symbol) => {
24
+ return {
25
+ type: 'subscribe',
26
+ channel: filter.channel,
27
+ id: symbol
28
+ }
29
+ })
30
+ })
31
+ .flatMap((f) => f)
32
+
33
+ return subs
34
+ }
35
+
36
+ protected messageIsError(message: any): boolean {
37
+ return message.type === 'error'
38
+ }
39
+ }
@@ -49,6 +49,7 @@ import { BlockchainComRealTimeFeed } from './blockchaincom'
49
49
  import { BinanceEuropeanOptionsRealTimeFeed } from './binanceeuropeanoptions'
50
50
  import { OkexSpreadsRealTimeFeed } from './okexspreads'
51
51
  import { KucoinFuturesRealTimeFeed } from './kucoinfutures'
52
+ import { DydxV4RealTimeFeed } from './dydx_v4'
52
53
 
53
54
  export * from './realtimefeed'
54
55
 
@@ -108,7 +109,8 @@ const realTimeFeedsMap: {
108
109
  'blockchain-com': BlockchainComRealTimeFeed,
109
110
  'binance-european-options': BinanceEuropeanOptionsRealTimeFeed,
110
111
  'okex-spreads': OkexSpreadsRealTimeFeed,
111
- 'kucoin-futures': KucoinFuturesRealTimeFeed
112
+ 'kucoin-futures': KucoinFuturesRealTimeFeed,
113
+ 'dydx-v4': DydxV4RealTimeFeed
112
114
  }
113
115
 
114
116
  export function getRealTimeFeedFactory(exchange: Exchange): RealTimeFeed {