tardis-machine 18.6.2 → 18.8.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/ws/subscriptionsmappers.d.ts.map +1 -1
- package/dist/ws/subscriptionsmappers.js +5 -0
- package/dist/ws/subscriptionsmappers.js.map +1 -1
- package/node_modules/tardis-dev/dist/consts.d.ts +2 -1
- package/node_modules/tardis-dev/dist/consts.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/consts.js +13 -0
- package/node_modules/tardis-dev/dist/consts.js.map +1 -1
- package/node_modules/tardis-dev/dist/downloaddatasets.d.ts +2 -0
- package/node_modules/tardis-dev/dist/downloaddatasets.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/downloaddatasets.js +21 -11
- package/node_modules/tardis-dev/dist/downloaddatasets.js.map +1 -1
- package/node_modules/tardis-dev/dist/handy.d.ts +16 -0
- package/node_modules/tardis-dev/dist/handy.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/handy.js +211 -15
- package/node_modules/tardis-dev/dist/handy.js.map +1 -1
- package/node_modules/tardis-dev/dist/instrumentinfo.d.ts +1 -1
- package/node_modules/tardis-dev/dist/instrumentinfo.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/mappers/ascendex.d.ts +4 -4
- package/node_modules/tardis-dev/dist/mappers/aster.d.ts +8 -8
- package/node_modules/tardis-dev/dist/mappers/binance.d.ts +18 -18
- package/node_modules/tardis-dev/dist/mappers/binanceeuropeanoptions.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/bitfinex.d.ts +8 -8
- package/node_modules/tardis-dev/dist/mappers/bitflyer.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/bitget.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/bithumb.d.ts +2 -2
- package/node_modules/tardis-dev/dist/mappers/bitmex.d.ts +2 -2
- package/node_modules/tardis-dev/dist/mappers/bitnomial.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/bitstamp.d.ts +2 -2
- package/node_modules/tardis-dev/dist/mappers/bitvavo.d.ts +3 -3
- package/node_modules/tardis-dev/dist/mappers/blockchaincom.d.ts +2 -2
- package/node_modules/tardis-dev/dist/mappers/bullish.d.ts +5 -5
- package/node_modules/tardis-dev/dist/mappers/bybit.d.ts +10 -10
- package/node_modules/tardis-dev/dist/mappers/bybitspot.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/coinbase.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/coinbasederivatives.d.ts +4 -4
- package/node_modules/tardis-dev/dist/mappers/coinbaseinternational.d.ts +2 -2
- package/node_modules/tardis-dev/dist/mappers/coinflex.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/cryptocom.d.ts +5 -5
- package/node_modules/tardis-dev/dist/mappers/cryptofacilities.d.ts +2 -2
- package/node_modules/tardis-dev/dist/mappers/delta.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/delta_india.d.ts +11 -0
- package/node_modules/tardis-dev/dist/mappers/delta_india.d.ts.map +1 -0
- package/node_modules/tardis-dev/dist/mappers/delta_india.js +197 -0
- package/node_modules/tardis-dev/dist/mappers/delta_india.js.map +1 -0
- package/node_modules/tardis-dev/dist/mappers/deribit.d.ts +3 -3
- package/node_modules/tardis-dev/dist/mappers/dydx.d.ts +3 -3
- package/node_modules/tardis-dev/dist/mappers/dydxv4.d.ts +4 -4
- package/node_modules/tardis-dev/dist/mappers/ftx.d.ts +8 -8
- package/node_modules/tardis-dev/dist/mappers/gateio.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/gateiofutures.d.ts +4 -4
- package/node_modules/tardis-dev/dist/mappers/gemini.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/huobi.d.ts +26 -26
- package/node_modules/tardis-dev/dist/mappers/hyperliquid.d.ts +3 -3
- package/node_modules/tardis-dev/dist/mappers/index.d.ts +202 -190
- package/node_modules/tardis-dev/dist/mappers/index.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/mappers/index.js +2 -1
- package/node_modules/tardis-dev/dist/mappers/index.js.map +1 -1
- package/node_modules/tardis-dev/dist/mappers/kucoin.d.ts +3 -3
- package/node_modules/tardis-dev/dist/mappers/kucoinfutures.d.ts +4 -4
- package/node_modules/tardis-dev/dist/mappers/lighter.d.ts +10 -10
- package/node_modules/tardis-dev/dist/mappers/mexc.d.ts +3 -3
- package/node_modules/tardis-dev/dist/mappers/mexcfutures.d.ts +3 -3
- package/node_modules/tardis-dev/dist/mappers/okex.d.ts +15 -15
- package/node_modules/tardis-dev/dist/mappers/phemex.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/polymarket.d.ts +3 -3
- package/node_modules/tardis-dev/dist/mappers/serum.d.ts +9 -9
- package/node_modules/tardis-dev/dist/mappers/upbit.d.ts +2 -2
- package/node_modules/tardis-dev/dist/realtimefeeds/delta_india.d.ts +32 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/delta_india.d.ts.map +1 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/delta_india.js +117 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/delta_india.js.map +1 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/dydx_v4.d.ts +1 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/dydx_v4.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/dydx_v4.js +1 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/dydx_v4.js.map +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/index.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/index.js +2 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/index.js.map +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/realtimefeed.d.ts +1 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/realtimefeed.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/realtimefeed.js +3 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/realtimefeed.js.map +1 -1
- package/node_modules/tardis-dev/dist/worker.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/worker.js +1 -2
- package/node_modules/tardis-dev/dist/worker.js.map +1 -1
- package/node_modules/tardis-dev/dist/zstd.d.ts +1 -2
- package/node_modules/tardis-dev/dist/zstd.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/zstd.js +4 -118
- package/node_modules/tardis-dev/dist/zstd.js.map +1 -1
- package/node_modules/tardis-dev/package.json +1 -1
- package/node_modules/tardis-dev/src/consts.ts +14 -0
- package/node_modules/tardis-dev/src/downloaddatasets.ts +31 -12
- package/node_modules/tardis-dev/src/handy.ts +261 -14
- package/node_modules/tardis-dev/src/instrumentinfo.ts +1 -1
- package/node_modules/tardis-dev/src/mappers/delta_india.ts +290 -0
- package/node_modules/tardis-dev/src/mappers/index.ts +2 -0
- package/node_modules/tardis-dev/src/realtimefeeds/delta_india.ts +131 -0
- package/node_modules/tardis-dev/src/realtimefeeds/dydx_v4.ts +1 -0
- package/node_modules/tardis-dev/src/realtimefeeds/index.ts +2 -0
- package/node_modules/tardis-dev/src/realtimefeeds/realtimefeed.ts +2 -1
- package/node_modules/tardis-dev/src/worker.ts +1 -2
- package/node_modules/tardis-dev/src/zstd.ts +4 -135
- package/package.json +2 -2
- package/src/ws/subscriptionsmappers.ts +6 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { asNonZeroNumberOrUndefined, asNumberOrUndefined, fromMicroSecondsToDate, upperCaseSymbols } from '../handy.ts'
|
|
2
|
+
import type { BookChange, BookTicker, DerivativeTicker, OptionSummary, Trade } from '../types.ts'
|
|
3
|
+
import { type Mapper, PendingTickerInfoHelper } from './mapper.ts'
|
|
4
|
+
import { exchangeMappers } from './registry.ts'
|
|
5
|
+
|
|
6
|
+
export const deltaIndiaMappers = exchangeMappers({
|
|
7
|
+
'delta-india': {
|
|
8
|
+
trades: () => new DeltaIndiaTradesMapper(),
|
|
9
|
+
bookChanges: () => new DeltaIndiaBookChangeMapper(),
|
|
10
|
+
bookTickers: () => new DeltaIndiaBookTickerMapper(),
|
|
11
|
+
derivativeTickers: () => new DeltaIndiaDerivativeTickerMapper(),
|
|
12
|
+
optionsSummary: () => new DeltaIndiaOptionSummaryMapper()
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
class DeltaIndiaTradesMapper implements Mapper<'delta-india', Trade> {
|
|
17
|
+
canHandle(message: DeltaIndiaTrade) {
|
|
18
|
+
return message.type === 'trades'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getFilters(symbols?: string[]) {
|
|
22
|
+
return [{ channel: 'trades', symbols: upperCaseSymbols(symbols) } as const]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
*map(message: DeltaIndiaTrade, localTimestamp: Date): IterableIterator<Trade> {
|
|
26
|
+
yield {
|
|
27
|
+
type: 'trade',
|
|
28
|
+
exchange: 'delta-india',
|
|
29
|
+
symbol: message.sy,
|
|
30
|
+
id: undefined,
|
|
31
|
+
price: Number(message.p),
|
|
32
|
+
amount: Number(message.s),
|
|
33
|
+
side: message.r === 't' ? 'buy' : 'sell',
|
|
34
|
+
timestamp: fromMicroSecondsToDate(message.t),
|
|
35
|
+
localTimestamp
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class DeltaIndiaBookChangeMapper implements Mapper<'delta-india', BookChange> {
|
|
41
|
+
private readonly initialized = new Set<string>()
|
|
42
|
+
|
|
43
|
+
canHandle(message: DeltaIndiaDepth) {
|
|
44
|
+
return message.type === 'ob_updates' && (message.action === 'snapshot' || message.action === 'update')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getFilters(symbols?: string[]) {
|
|
48
|
+
return [{ channel: 'ob_updates', symbols: upperCaseSymbols(symbols) } as const]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
*map(message: DeltaIndiaDepth, localTimestamp: Date): IterableIterator<BookChange> {
|
|
52
|
+
if (message.action === 'snapshot') {
|
|
53
|
+
this.initialized.add(message.sy)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!this.initialized.has(message.sy)) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
yield {
|
|
61
|
+
type: 'book_change',
|
|
62
|
+
exchange: 'delta-india',
|
|
63
|
+
symbol: message.sy,
|
|
64
|
+
isSnapshot: message.action === 'snapshot',
|
|
65
|
+
bids: message.b.map(this.mapLevel),
|
|
66
|
+
asks: message.a.map(this.mapLevel),
|
|
67
|
+
timestamp: fromMicroSecondsToDate(message.ts),
|
|
68
|
+
localTimestamp
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private mapLevel([price, amount]: DeltaIndiaBookLevel) {
|
|
73
|
+
return { price: Number(price), amount: Number(amount) }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class DeltaIndiaBookTickerMapper implements Mapper<'delta-india', BookTicker> {
|
|
78
|
+
canHandle(message: DeltaIndiaBBO) {
|
|
79
|
+
return message.type === 'ob_l1'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getFilters(symbols?: string[]) {
|
|
83
|
+
return [{ channel: 'ob_l1', symbols: upperCaseSymbols(symbols) } as const]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
*map(message: DeltaIndiaBBO, localTimestamp: Date): IterableIterator<BookTicker> {
|
|
87
|
+
yield {
|
|
88
|
+
type: 'book_ticker',
|
|
89
|
+
exchange: 'delta-india',
|
|
90
|
+
symbol: message.sy,
|
|
91
|
+
askPrice: asNonZeroNumberOrUndefined(message.ap),
|
|
92
|
+
askAmount: asNonZeroNumberOrUndefined(message.as),
|
|
93
|
+
bidPrice: asNonZeroNumberOrUndefined(message.bp),
|
|
94
|
+
bidAmount: asNonZeroNumberOrUndefined(message.bs),
|
|
95
|
+
timestamp: fromMicroSecondsToDate(message.lts),
|
|
96
|
+
localTimestamp
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class DeltaIndiaDerivativeTickerMapper implements Mapper<'delta-india', DerivativeTicker> {
|
|
102
|
+
private readonly pending = new PendingTickerInfoHelper()
|
|
103
|
+
|
|
104
|
+
canHandle(message: DeltaIndiaTicker | DeltaIndiaPrice | DeltaIndiaFunding) {
|
|
105
|
+
return message.type === 'ticker' || message.type === 'mark_price' || message.type === 'funding_rate'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
getFilters(symbols?: string[]) {
|
|
109
|
+
const uppercaseSymbols = upperCaseSymbols(symbols)
|
|
110
|
+
const markPriceSymbols = uppercaseSymbols?.filter(isDerivative).map((symbol) => `MARK:${symbol}`)
|
|
111
|
+
const perpetualSymbols = uppercaseSymbols?.filter((symbol) => !symbol.includes('-') && !symbol.includes('_'))
|
|
112
|
+
return [
|
|
113
|
+
{ channel: 'ticker', symbols: uppercaseSymbols } as const,
|
|
114
|
+
...(markPriceSymbols?.length === 0 ? [] : [{ channel: 'mark_price', symbols: markPriceSymbols } as const]),
|
|
115
|
+
...(perpetualSymbols?.length === 0 ? [] : [{ channel: 'funding_rate', symbols: perpetualSymbols } as const])
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
*map(message: DeltaIndiaTicker | DeltaIndiaPrice | DeltaIndiaFunding, localTimestamp: Date): IterableIterator<DerivativeTicker> {
|
|
120
|
+
const timestamp = fromMicroSecondsToDate(message.ts)
|
|
121
|
+
|
|
122
|
+
if (message.type === 'ticker') {
|
|
123
|
+
for (const ticker of message.d) {
|
|
124
|
+
if (!isDerivative(ticker.s)) {
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const pending = this.pending.getPendingTickerInfo(ticker.s, 'delta-india')
|
|
129
|
+
pending.updateLastPrice(asNumberOrUndefined(ticker.ohlc[3]))
|
|
130
|
+
pending.updateOpenInterest(asNumberOrUndefined(ticker.oi[0]))
|
|
131
|
+
pending.updateIndexPrice(asNumberOrUndefined(message.sp))
|
|
132
|
+
pending.updateTimestamp(timestamp)
|
|
133
|
+
|
|
134
|
+
if (pending.hasChanged()) {
|
|
135
|
+
yield pending.getSnapshot(localTimestamp)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const symbol = message.sy.replace(/^MARK:/, '')
|
|
142
|
+
if (!isDerivative(symbol)) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const pending = this.pending.getPendingTickerInfo(symbol, 'delta-india')
|
|
147
|
+
if (message.type === 'mark_price') {
|
|
148
|
+
pending.updateMarkPrice(asNumberOrUndefined(message.p))
|
|
149
|
+
}
|
|
150
|
+
if (message.type === 'funding_rate') {
|
|
151
|
+
// Delta publishes funding rates in percentage points; normalized rates are fractions.
|
|
152
|
+
pending.updateFundingRate(Number(message.fr) / 100)
|
|
153
|
+
pending.updateFundingTimestamp(fromMicroSecondsToDate(message.nfr))
|
|
154
|
+
}
|
|
155
|
+
pending.updateTimestamp(timestamp)
|
|
156
|
+
|
|
157
|
+
if (pending.hasChanged()) {
|
|
158
|
+
yield pending.getSnapshot(localTimestamp)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
class DeltaIndiaOptionSummaryMapper implements Mapper<'delta-india', OptionSummary> {
|
|
164
|
+
canHandle(message: DeltaIndiaTicker) {
|
|
165
|
+
return message.type === 'ticker'
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getFilters(symbols?: string[]) {
|
|
169
|
+
return [{ channel: 'ticker', symbols: upperCaseSymbols(symbols) } as const]
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
*map(message: DeltaIndiaTicker, localTimestamp: Date): IterableIterator<OptionSummary> {
|
|
173
|
+
for (const ticker of message.d) {
|
|
174
|
+
if (!isOption(ticker.s)) {
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
const [side, asset, strike, expiry] = ticker.s.split('-')
|
|
178
|
+
|
|
179
|
+
yield {
|
|
180
|
+
type: 'option_summary',
|
|
181
|
+
exchange: 'delta-india',
|
|
182
|
+
symbol: ticker.s,
|
|
183
|
+
optionType: side === 'C' ? 'call' : 'put',
|
|
184
|
+
strikePrice: Number(strike),
|
|
185
|
+
expirationDate: this.parseExpirationDate(expiry, asset === 'XAUT' ? 16 : 12),
|
|
186
|
+
bestAskPrice: asNonZeroNumberOrUndefined(ticker.q[0]),
|
|
187
|
+
bestAskAmount: asNonZeroNumberOrUndefined(ticker.q[1]),
|
|
188
|
+
bestBidPrice: asNonZeroNumberOrUndefined(ticker.q[2]),
|
|
189
|
+
bestBidAmount: asNonZeroNumberOrUndefined(ticker.q[3]),
|
|
190
|
+
bestAskIV: this.mapIV(ticker.qiv[0]),
|
|
191
|
+
bestBidIV: this.mapIV(ticker.qiv[1]),
|
|
192
|
+
markIV: this.mapIV(ticker.qiv[2]),
|
|
193
|
+
lastPrice: asNumberOrUndefined(ticker.ohlc[3]),
|
|
194
|
+
openInterest: asNumberOrUndefined(ticker.oi[0]),
|
|
195
|
+
markPrice: asNumberOrUndefined(ticker.m),
|
|
196
|
+
delta: asNumberOrUndefined(ticker.g[0]),
|
|
197
|
+
gamma: asNumberOrUndefined(ticker.g[1]),
|
|
198
|
+
rho: asNumberOrUndefined(ticker.g[2]),
|
|
199
|
+
theta: asNumberOrUndefined(ticker.g[3]),
|
|
200
|
+
vega: asNumberOrUndefined(ticker.g[4]),
|
|
201
|
+
underlyingPrice: asNumberOrUndefined(message.sp),
|
|
202
|
+
underlyingIndex: asset === 'BTC' ? '.DEXBTUSD' : `.DE${asset}USD`,
|
|
203
|
+
timestamp: fromMicroSecondsToDate(message.ts),
|
|
204
|
+
localTimestamp
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** DDMMYY encodes no time; the UTC expiry hour depends on the asset. */
|
|
210
|
+
private parseExpirationDate(expiry: string, expiryHour: number) {
|
|
211
|
+
const day = Number(expiry.slice(0, 2))
|
|
212
|
+
const month = Number(expiry.slice(2, 4))
|
|
213
|
+
const year = 2000 + Number(expiry.slice(4))
|
|
214
|
+
return new Date(Date.UTC(year, month - 1, day, expiryHour))
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private mapIV(value: string | null) {
|
|
218
|
+
const iv = asNumberOrUndefined(value)
|
|
219
|
+
return iv !== undefined && iv >= 0 ? iv : undefined
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function isDerivative(symbol: string) {
|
|
224
|
+
return !isOption(symbol) && !symbol.endsWith('_INR')
|
|
225
|
+
}
|
|
226
|
+
function isOption(symbol: string) {
|
|
227
|
+
return symbol.startsWith('C-') || symbol.startsWith('P-')
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
type DeltaIndiaChannel =
|
|
231
|
+
'trades' | 'ob_updates' | 'ob_l1' | 'ticker' | 'mark_price' | 'funding_rate' | 'spot_price' | 'product_updates' | 'system_status'
|
|
232
|
+
|
|
233
|
+
type DeltaIndiaMessage<Channel extends DeltaIndiaChannel> = {
|
|
234
|
+
type: Channel
|
|
235
|
+
sy: string
|
|
236
|
+
ts: number
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
type DeltaIndiaTrade = DeltaIndiaMessage<'trades'> & {
|
|
240
|
+
p: string
|
|
241
|
+
s: number
|
|
242
|
+
r: 't' | 'm'
|
|
243
|
+
t: number
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
type DeltaIndiaDepth = DeltaIndiaMessage<'ob_updates'> & {
|
|
247
|
+
action: 'snapshot' | 'update'
|
|
248
|
+
a: DeltaIndiaBookLevel[]
|
|
249
|
+
b: DeltaIndiaBookLevel[]
|
|
250
|
+
seq: number
|
|
251
|
+
cs: number
|
|
252
|
+
}
|
|
253
|
+
type DeltaIndiaBookLevel = [price: string, size: string]
|
|
254
|
+
|
|
255
|
+
type DeltaIndiaBBO = DeltaIndiaMessage<'ob_l1'> & {
|
|
256
|
+
ap: string | null
|
|
257
|
+
as: string | null
|
|
258
|
+
bp: string | null
|
|
259
|
+
bs: string | null
|
|
260
|
+
lts: number
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
type DeltaIndiaPrice = DeltaIndiaMessage<'mark_price'> & {
|
|
264
|
+
p: string
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
type DeltaIndiaFunding = DeltaIndiaMessage<'funding_rate'> & {
|
|
268
|
+
fr: number
|
|
269
|
+
fi: number
|
|
270
|
+
nfr: number
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
type DeltaIndiaTicker = DeltaIndiaMessage<'ticker'> & {
|
|
274
|
+
sp: string | null
|
|
275
|
+
d: DeltaIndiaTickerContract[]
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
type DeltaIndiaTickerContract = {
|
|
279
|
+
i: number
|
|
280
|
+
s: string
|
|
281
|
+
m: string | null
|
|
282
|
+
m24hc: string
|
|
283
|
+
ohlc: [open: number | null, high: number | null, low: number | null, close: number | null]
|
|
284
|
+
oi: [contracts: string | null, change: string | null]
|
|
285
|
+
pb: [lower: string | null, upper: string | null]
|
|
286
|
+
q: [askPrice: string | null, askSize: string | null, bidPrice: string | null, bidSize: string | null, impactMidPrice: string | null]
|
|
287
|
+
qiv: [ask: string | null, bid: string | null, mark: string | null]
|
|
288
|
+
g: [delta: string | null, gamma: string | null, rho: string | null, theta: string | null, vega: string | null]
|
|
289
|
+
to: [turnover: number | null, turnoverUSD: number | null]
|
|
290
|
+
}
|
|
@@ -22,6 +22,7 @@ import { coinflexMappers } from './coinflex.ts'
|
|
|
22
22
|
import { cryptoComMappers } from './cryptocom.ts'
|
|
23
23
|
import { cryptofacilitiesMappers } from './cryptofacilities.ts'
|
|
24
24
|
import { deltaMappers } from './delta.ts'
|
|
25
|
+
import { deltaIndiaMappers } from './delta_india.ts'
|
|
25
26
|
import { deribitMappers } from './deribit.ts'
|
|
26
27
|
import { dydxMappers } from './dydx.ts'
|
|
27
28
|
import { dydxV4Mappers } from './dydxv4.ts'
|
|
@@ -81,6 +82,7 @@ const registeredMappers = mergeExchangeMappers(
|
|
|
81
82
|
cryptoComMappers,
|
|
82
83
|
cryptofacilitiesMappers,
|
|
83
84
|
deltaMappers,
|
|
85
|
+
deltaIndiaMappers,
|
|
84
86
|
deribitMappers,
|
|
85
87
|
dydxMappers,
|
|
86
88
|
dydxV4Mappers,
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { optimizeFilters, wait } from '../handy.ts'
|
|
2
|
+
import type { Filter } from '../types.ts'
|
|
3
|
+
import { MultiConnectionRealTimeFeedBase, RealTimeFeedBase } from './realtimefeed.ts'
|
|
4
|
+
|
|
5
|
+
const productCategories = new Set(['all', 'call_options', 'put_options', 'move_options', 'spot', 'futures', 'perpetual_futures'])
|
|
6
|
+
|
|
7
|
+
export class DeltaIndiaRealTimeFeed extends MultiConnectionRealTimeFeedBase {
|
|
8
|
+
protected *_getRealTimeFeeds(exchange: string, filters: Filter<string>[], timeoutIntervalMS?: number, onError?: (error: Error) => void) {
|
|
9
|
+
const marketFilters: Filter<string>[] = []
|
|
10
|
+
const depthFilters: Filter<string>[] = []
|
|
11
|
+
|
|
12
|
+
for (const filter of optimizeFilters(filters)) {
|
|
13
|
+
if (filter.channel !== 'product_updates' && filter.channel !== 'system_status' && !filter.symbols?.length) {
|
|
14
|
+
throw new Error('DeltaIndiaRealTimeFeed requires explicitly specified symbols for market data channels')
|
|
15
|
+
}
|
|
16
|
+
if (filter.channel === 'ob_updates') {
|
|
17
|
+
for (const symbol of filter.symbols!) {
|
|
18
|
+
if (productCategories.has(symbol.toLowerCase()) || /^[A-Z]+-\d{6}$/i.test(symbol)) {
|
|
19
|
+
throw new Error('Delta India ob_updates requires individual instrument symbols')
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
depthFilters.push(filter)
|
|
23
|
+
} else {
|
|
24
|
+
marketFilters.push(filter)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const feeds: DeltaIndiaWebSocketRealTimeFeed[] = [
|
|
29
|
+
marketFilters.length ? new DeltaIndiaWebSocketRealTimeFeed(exchange, marketFilters, timeoutIntervalMS, onError) : undefined,
|
|
30
|
+
depthFilters.length ? new DeltaIndiaWebSocketRealTimeFeed(exchange, depthFilters, timeoutIntervalMS, onError) : undefined
|
|
31
|
+
].filter((feed) => feed !== undefined)
|
|
32
|
+
|
|
33
|
+
yield* feeds
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class DeltaIndiaWebSocketRealTimeFeed extends RealTimeFeedBase {
|
|
38
|
+
protected readonly wssURL: string = 'wss://public-socket.india.delta.exchange'
|
|
39
|
+
private readonly sequences = new Map<string, number>()
|
|
40
|
+
private readonly pendingSnapshots = new Set<string>()
|
|
41
|
+
private retrySnapshotsAt = 0
|
|
42
|
+
|
|
43
|
+
protected mapToSubscribeMessages(filters: Filter<string>[]) {
|
|
44
|
+
this.sequences.clear()
|
|
45
|
+
this.pendingSnapshots.clear()
|
|
46
|
+
this.retrySnapshotsAt = Date.now() + 5000
|
|
47
|
+
|
|
48
|
+
const channels = filters.map((filter) => {
|
|
49
|
+
if (filter.channel === 'product_updates' || filter.channel === 'system_status') {
|
|
50
|
+
return { name: filter.channel }
|
|
51
|
+
}
|
|
52
|
+
const symbols = [
|
|
53
|
+
...new Set(
|
|
54
|
+
filter.symbols!.map((symbol) => {
|
|
55
|
+
if (productCategories.has(symbol)) {
|
|
56
|
+
return symbol
|
|
57
|
+
}
|
|
58
|
+
const id = symbol.toUpperCase()
|
|
59
|
+
if (filter.channel === 'mark_price') {
|
|
60
|
+
return id.startsWith('MARK:') ? id : `MARK:${id}`
|
|
61
|
+
}
|
|
62
|
+
return id
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
]
|
|
66
|
+
if (filter.channel === 'ob_updates') {
|
|
67
|
+
for (const symbol of symbols) {
|
|
68
|
+
this.pendingSnapshots.add(symbol)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { name: filter.channel, symbols }
|
|
72
|
+
})
|
|
73
|
+
return [{ type: 'subscribe', payload: { channels } }, { type: 'enable_heartbeat' }]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
protected messageIsError(message: any): boolean {
|
|
77
|
+
return (
|
|
78
|
+
message.type === 'error' ||
|
|
79
|
+
message.error != null ||
|
|
80
|
+
(message.type === 'subscriptions' && message.channels.some((channel: { error?: unknown }) => channel.error != null)) ||
|
|
81
|
+
(message.type === 'ob_updates' &&
|
|
82
|
+
message.action === 'error' &&
|
|
83
|
+
message.msg !== 'Snapshot load failed. Verify if product is live and resubscribe after a few secs.')
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
protected override onMessage(message: any) {
|
|
88
|
+
if (message.type !== 'ob_updates') {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
if (message.action === 'error') {
|
|
92
|
+
const symbol = message.symbol
|
|
93
|
+
this.sequences.delete(symbol)
|
|
94
|
+
this.pendingSnapshots.add(symbol)
|
|
95
|
+
} else if (message.action === 'snapshot') {
|
|
96
|
+
this.sequences.set(message.sy, message.seq)
|
|
97
|
+
this.pendingSnapshots.delete(message.sy)
|
|
98
|
+
} else if (message.action === 'update') {
|
|
99
|
+
const previous = this.sequences.get(message.sy)
|
|
100
|
+
if (previous === undefined) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
if (message.seq !== previous + 1) {
|
|
104
|
+
throw new Error(`Delta India order book sequence gap for ${message.sy}: ${previous} -> ${message.seq}`)
|
|
105
|
+
}
|
|
106
|
+
this.sequences.set(message.sy, message.seq)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected override async provideManualSnapshots(filters: Filter<string>[], shouldCancel: () => boolean) {
|
|
111
|
+
if (!filters.some((filter) => filter.channel === 'ob_updates')) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
// Native snapshots can be missing without an error; retry only those symbols on the same connection.
|
|
115
|
+
while (!shouldCancel()) {
|
|
116
|
+
if (this.pendingSnapshots.size && Date.now() >= this.retrySnapshotsAt) {
|
|
117
|
+
const channels = [{ name: 'ob_updates', symbols: [...this.pendingSnapshots] }]
|
|
118
|
+
this.send({ type: 'unsubscribe', payload: { channels } })
|
|
119
|
+
this.send({ type: 'subscribe', payload: { channels } })
|
|
120
|
+
this.retrySnapshotsAt = Date.now() + 5000
|
|
121
|
+
}
|
|
122
|
+
await wait(1000)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
protected override sendCustomPing = () => this.send({ type: 'ping' })
|
|
127
|
+
|
|
128
|
+
protected override messageIsHeartbeat(message: any) {
|
|
129
|
+
return message.type === 'pong' || message.type === 'heartbeat'
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -3,6 +3,7 @@ import { RealTimeFeedBase } from './realtimefeed.ts'
|
|
|
3
3
|
|
|
4
4
|
export class DydxV4RealTimeFeed extends RealTimeFeedBase {
|
|
5
5
|
protected readonly wssURL = 'wss://indexer.dydx.trade/v4/ws'
|
|
6
|
+
protected readonly perMessageDeflate = true
|
|
6
7
|
|
|
7
8
|
protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
|
|
8
9
|
const subs = filters
|
|
@@ -33,6 +33,7 @@ import { OkexOptionsRealTimeFeed, OkexRealTimeFeed } from './okex.ts'
|
|
|
33
33
|
import { RealTimeFeed } from './realtimefeed.ts'
|
|
34
34
|
import { PhemexRealTimeFeed } from './phemex.ts'
|
|
35
35
|
import { DeltaRealTimeFeed } from './delta.ts'
|
|
36
|
+
import { DeltaIndiaRealTimeFeed } from './delta_india.ts'
|
|
36
37
|
import { GateIORealTimeFeed } from './gateio.ts'
|
|
37
38
|
import { GateIOFuturesRealTimeFeed } from './gateiofutures.ts'
|
|
38
39
|
import { PoloniexRealTimeFeed } from './poloniex.ts'
|
|
@@ -98,6 +99,7 @@ const realTimeFeedsMap: {
|
|
|
98
99
|
hitbtc: HitBtcRealTimeFeed,
|
|
99
100
|
phemex: PhemexRealTimeFeed,
|
|
100
101
|
delta: DeltaRealTimeFeed,
|
|
102
|
+
'delta-india': DeltaIndiaRealTimeFeed,
|
|
101
103
|
'gate-io': GateIORealTimeFeed,
|
|
102
104
|
'gate-io-futures': GateIOFuturesRealTimeFeed,
|
|
103
105
|
'mexc-futures': MexcFuturesRealTimeFeed,
|
|
@@ -47,6 +47,7 @@ export abstract class RealTimeFeedBase implements RealTimeFeedIterable {
|
|
|
47
47
|
private _connection?: RealTimeFeedConnection
|
|
48
48
|
protected readonly originHeader: string | undefined = undefined
|
|
49
49
|
protected readonly extraHeaders: Record<string, string> | undefined = undefined
|
|
50
|
+
protected readonly perMessageDeflate: boolean = false
|
|
50
51
|
|
|
51
52
|
constructor(
|
|
52
53
|
protected readonly _exchange: string,
|
|
@@ -63,7 +64,6 @@ export abstract class RealTimeFeedBase implements RealTimeFeedIterable {
|
|
|
63
64
|
'User-Agent':
|
|
64
65
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36'
|
|
65
66
|
},
|
|
66
|
-
perMessageDeflate: false,
|
|
67
67
|
handshakeTimeout: 10 * ONE_SEC_IN_MS,
|
|
68
68
|
skipUTF8Validation: true
|
|
69
69
|
} as any
|
|
@@ -123,6 +123,7 @@ export abstract class RealTimeFeedBase implements RealTimeFeedIterable {
|
|
|
123
123
|
Object.assign((this._wsClientOptions as any).headers, this.extraHeaders)
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
;(this._wsClientOptions as any).perMessageDeflate = this.perMessageDeflate
|
|
126
127
|
this._wsClientOptions.agent = getProxyAgent(finalWssUrl)
|
|
127
128
|
const ws = new WebSocket(finalWssUrl, this._wsClientOptions)
|
|
128
129
|
const currentConnection: RealTimeFeedConnection = {
|
|
@@ -163,8 +163,7 @@ async function getDataFeedSlice(
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
let url = `${endpoint}/data-feeds/${exchange}?from=${fromDate.toISOString()}&offset=${offset}&compression=${requestCompression}`
|
|
166
|
+
let url = `${endpoint}/data-feeds/${exchange}?from=${fromDate.toISOString()}&offset=${offset}`
|
|
168
167
|
if (requestedSliceSize > DEFAULT_DATA_FEED_SLICE_SIZE) {
|
|
169
168
|
url += `&sliceSize=${requestedSliceSize}`
|
|
170
169
|
}
|
|
@@ -1,139 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { open, type FileHandle } from 'node:fs/promises'
|
|
3
|
-
import { Readable } from 'node:stream'
|
|
1
|
+
import { createReadStream } from 'node:fs'
|
|
4
2
|
import { createZstdDecompress } from 'node:zlib'
|
|
5
3
|
|
|
6
|
-
const ZSTD_FRAME_MAGIC = 0xfd2fb528
|
|
7
|
-
const DICTIONARY_ID_SIZES = [0, 1, 2, 4]
|
|
8
|
-
|
|
9
4
|
export function createZstdDecompressionStream(path: string, chunkSize: number) {
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function* decompressZstdFrames(path: string, chunkSize: number) {
|
|
14
|
-
const file = await open(path, 'r')
|
|
15
|
-
try {
|
|
16
|
-
const fileSize = (await file.stat()).size
|
|
17
|
-
const frames = await findZstdFrames(file, path, fileSize)
|
|
18
|
-
|
|
19
|
-
for (const frame of frames) {
|
|
20
|
-
const source = Readable.from(readFileRange(file, frame.offset, frame.length, chunkSize), { objectMode: false })
|
|
21
|
-
const decompressor = createZstdDecompress({ chunkSize })
|
|
22
|
-
source.on('error', (error) => decompressor.destroy(error))
|
|
23
|
-
source.pipe(decompressor)
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
for await (const chunk of decompressor) {
|
|
27
|
-
yield chunk
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (decompressor.bytesWritten !== frame.length) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
`Zstd decoder consumed ${decompressor.bytesWritten} of ${frame.length} bytes at offset ${frame.offset} in ${path}`
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
} finally {
|
|
36
|
-
source.unpipe(decompressor)
|
|
37
|
-
source.destroy()
|
|
38
|
-
decompressor.destroy()
|
|
39
|
-
if (!source.closed) {
|
|
40
|
-
await once(source, 'close')
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
} finally {
|
|
45
|
-
await file.close()
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function findZstdFrames(file: FileHandle, path: string, fileSize: number) {
|
|
50
|
-
if (fileSize === 0) {
|
|
51
|
-
throw new Error(`Empty zstd file: ${path}`)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const bytes = Buffer.allocUnsafe(4)
|
|
55
|
-
const frames: { offset: number; length: number }[] = []
|
|
56
|
-
let offset = 0
|
|
57
|
-
|
|
58
|
-
while (offset < fileSize) {
|
|
59
|
-
const frameStart = offset
|
|
60
|
-
await readExactly(file, bytes, 4, offset, fileSize, path)
|
|
61
|
-
if (bytes.readUInt32LE(0) !== ZSTD_FRAME_MAGIC) {
|
|
62
|
-
throw new Error(`Invalid zstd frame magic at offset ${offset} in ${path}`)
|
|
63
|
-
}
|
|
64
|
-
offset += 4
|
|
65
|
-
|
|
66
|
-
await readExactly(file, bytes, 1, offset, fileSize, path)
|
|
67
|
-
const descriptor = bytes[0]
|
|
68
|
-
if ((descriptor & 0x08) !== 0) {
|
|
69
|
-
throw new Error(`Unsupported reserved zstd frame descriptor bit at offset ${offset} in ${path}`)
|
|
70
|
-
}
|
|
71
|
-
offset++
|
|
72
|
-
|
|
73
|
-
const singleSegment = (descriptor & 0x20) !== 0
|
|
74
|
-
const contentChecksum = (descriptor & 0x04) !== 0
|
|
75
|
-
const dictionaryIdSize = DICTIONARY_ID_SIZES[descriptor & 0x03]
|
|
76
|
-
const contentSizeFlag = descriptor >>> 6
|
|
77
|
-
const contentSizeSize = contentSizeFlag === 0 ? (singleSegment ? 1 : 0) : 1 << contentSizeFlag
|
|
78
|
-
const windowDescriptorSize = singleSegment ? 0 : 1
|
|
79
|
-
offset += windowDescriptorSize + dictionaryIdSize + contentSizeSize
|
|
80
|
-
ensureAvailable(offset, 0, fileSize, path)
|
|
81
|
-
|
|
82
|
-
let lastBlock = false
|
|
83
|
-
while (!lastBlock) {
|
|
84
|
-
await readExactly(file, bytes, 3, offset, fileSize, path)
|
|
85
|
-
const blockHeader = bytes.readUIntLE(0, 3)
|
|
86
|
-
offset += 3
|
|
87
|
-
lastBlock = (blockHeader & 1) !== 0
|
|
88
|
-
const blockType = (blockHeader >>> 1) & 3
|
|
89
|
-
if (blockType === 3) {
|
|
90
|
-
throw new Error(`Invalid reserved zstd block type at offset ${offset - 3} in ${path}`)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const blockSize = blockHeader >>> 3
|
|
94
|
-
// RLE blocks store one repeated byte; other block types store blockSize bytes.
|
|
95
|
-
offset += blockType === 1 ? 1 : blockSize
|
|
96
|
-
ensureAvailable(offset, 0, fileSize, path)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (contentChecksum) {
|
|
100
|
-
offset += 4
|
|
101
|
-
ensureAvailable(offset, 0, fileSize, path)
|
|
102
|
-
}
|
|
103
|
-
frames.push({ offset: frameStart, length: offset - frameStart })
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return frames
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async function* readFileRange(file: FileHandle, position: number, length: number, chunkSize: number) {
|
|
110
|
-
let bytesRemaining = length
|
|
111
|
-
while (bytesRemaining > 0) {
|
|
112
|
-
const buffer = Buffer.allocUnsafe(Math.min(bytesRemaining, chunkSize))
|
|
113
|
-
const { bytesRead } = await file.read(buffer, 0, buffer.length, position)
|
|
114
|
-
if (bytesRead === 0) {
|
|
115
|
-
throw new Error(`Expected ${length} compressed bytes, read ${length - bytesRemaining}`)
|
|
116
|
-
}
|
|
117
|
-
yield bytesRead === buffer.length ? buffer : buffer.subarray(0, bytesRead)
|
|
118
|
-
position += bytesRead
|
|
119
|
-
bytesRemaining -= bytesRead
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async function readExactly(file: FileHandle, buffer: Buffer, length: number, position: number, fileSize: number, path: string) {
|
|
124
|
-
ensureAvailable(position, length, fileSize, path)
|
|
125
|
-
let bytesRead = 0
|
|
126
|
-
while (bytesRead < length) {
|
|
127
|
-
const result = await file.read(buffer, bytesRead, length - bytesRead, position + bytesRead)
|
|
128
|
-
if (result.bytesRead === 0) {
|
|
129
|
-
throw new Error(`Truncated zstd frame at offset ${position + bytesRead} in ${path}`)
|
|
130
|
-
}
|
|
131
|
-
bytesRead += result.bytesRead
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function ensureAvailable(position: number, length: number, fileSize: number, path: string) {
|
|
136
|
-
if (position < 0 || length < 0 || position > fileSize - length) {
|
|
137
|
-
throw new Error(`Truncated zstd frame at offset ${position} in ${path}`)
|
|
138
|
-
}
|
|
5
|
+
// Node supports rejectGarbageAfterEnd for Zstd; @types/node does not declare it yet.
|
|
6
|
+
const decoderOptions = { chunkSize, rejectGarbageAfterEnd: true }
|
|
7
|
+
return createReadStream(path, { highWaterMark: chunkSize }).compose(createZstdDecompress(decoderOptions))
|
|
139
8
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tardis-machine",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.8.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": "^24.22.1 || >=26.12.1"
|
|
6
6
|
},
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"license": "MPL-2.0",
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"debug": "^4.4.3",
|
|
79
|
-
"tardis-dev": "^18.
|
|
79
|
+
"tardis-dev": "^18.8.0",
|
|
80
80
|
"ws": "^8.21.3"
|
|
81
81
|
},
|
|
82
82
|
"bundleDependencies": true,
|
|
@@ -413,6 +413,11 @@ const deltaMapper: SubscriptionMapper = {
|
|
|
413
413
|
}
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
+
const deltaIndiaMapper: SubscriptionMapper = {
|
|
417
|
+
canHandle: (message: any) => message.type === 'subscribe',
|
|
418
|
+
map: (message: any) => message.payload.channels.map((channel: any) => ({ channel: channel.name, symbols: channel.symbols }))
|
|
419
|
+
}
|
|
420
|
+
|
|
416
421
|
const gateIOMapper: SubscriptionMapper = {
|
|
417
422
|
canHandle: (message: any) => {
|
|
418
423
|
return message.method !== undefined && message.method.endsWith('.subscribe')
|
|
@@ -865,6 +870,7 @@ export const subscriptionsMappers: Record<Exchange, SubscriptionMapper> = {
|
|
|
865
870
|
coinflex: coinflexMapper,
|
|
866
871
|
phemex: phemexMapper,
|
|
867
872
|
delta: deltaMapper,
|
|
873
|
+
'delta-india': deltaIndiaMapper,
|
|
868
874
|
'gate-io': gateIOMapper,
|
|
869
875
|
'gate-io-futures': gateIOFuturesMapper,
|
|
870
876
|
poloniex: poloniexMapper,
|