tardis-dev 16.5.0 → 16.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/dist/consts.d.ts +3 -1
  2. package/dist/consts.d.ts.map +1 -1
  3. package/dist/consts.js +21 -1
  4. package/dist/consts.js.map +1 -1
  5. package/dist/handy.d.ts.map +1 -1
  6. package/dist/handy.js +6 -3
  7. package/dist/handy.js.map +1 -1
  8. package/dist/mappers/ascendex.d.ts +6 -99
  9. package/dist/mappers/ascendex.d.ts.map +1 -1
  10. package/dist/mappers/ascendex.js +13 -4
  11. package/dist/mappers/ascendex.js.map +1 -1
  12. package/dist/mappers/binance.d.ts +27 -154
  13. package/dist/mappers/binance.d.ts.map +1 -1
  14. package/dist/mappers/binance.js +49 -15
  15. package/dist/mappers/binance.js.map +1 -1
  16. package/dist/mappers/binancedex.d.ts +7 -4
  17. package/dist/mappers/binancedex.d.ts.map +1 -1
  18. package/dist/mappers/binancedex.js +11 -3
  19. package/dist/mappers/binancedex.js.map +1 -1
  20. package/dist/mappers/binanceeuropeanoptions.d.ts +6 -216
  21. package/dist/mappers/binanceeuropeanoptions.d.ts.map +1 -1
  22. package/dist/mappers/binanceeuropeanoptions.js +26 -7
  23. package/dist/mappers/binanceeuropeanoptions.js.map +1 -1
  24. package/dist/mappers/bitfinex.d.ts +15 -100
  25. package/dist/mappers/bitfinex.d.ts.map +1 -1
  26. package/dist/mappers/bitfinex.js +20 -5
  27. package/dist/mappers/bitfinex.js.map +1 -1
  28. package/dist/mappers/bitflyer.d.ts +5 -41
  29. package/dist/mappers/bitflyer.d.ts.map +1 -1
  30. package/dist/mappers/bitflyer.js +11 -3
  31. package/dist/mappers/bitflyer.js.map +1 -1
  32. package/dist/mappers/bitget.d.ts +12 -269
  33. package/dist/mappers/bitget.d.ts.map +1 -1
  34. package/dist/mappers/bitget.js +46 -9
  35. package/dist/mappers/bitget.js.map +1 -1
  36. package/dist/mappers/bitmex.d.ts +8 -56
  37. package/dist/mappers/bitmex.d.ts.map +1 -1
  38. package/dist/mappers/bitmex.js +15 -5
  39. package/dist/mappers/bitmex.js.map +1 -1
  40. package/dist/mappers/bitnomial.d.ts +4 -44
  41. package/dist/mappers/bitnomial.d.ts.map +1 -1
  42. package/dist/mappers/bitnomial.js +9 -2
  43. package/dist/mappers/bitnomial.js.map +1 -1
  44. package/dist/mappers/bitstamp.d.ts +4 -44
  45. package/dist/mappers/bitstamp.d.ts.map +1 -1
  46. package/dist/mappers/bitstamp.js +9 -2
  47. package/dist/mappers/bitstamp.js.map +1 -1
  48. package/dist/mappers/blockchaincom.d.ts +4 -63
  49. package/dist/mappers/blockchaincom.d.ts.map +1 -1
  50. package/dist/mappers/blockchaincom.js +9 -2
  51. package/dist/mappers/blockchaincom.js.map +1 -1
  52. package/dist/mappers/bullish.d.ts +8 -158
  53. package/dist/mappers/bullish.d.ts.map +1 -1
  54. package/dist/mappers/bullish.js +19 -7
  55. package/dist/mappers/bullish.js.map +1 -1
  56. package/dist/mappers/bybit.d.ts +61 -407
  57. package/dist/mappers/bybit.d.ts.map +1 -1
  58. package/dist/mappers/bybit.js +60 -15
  59. package/dist/mappers/bybit.js.map +1 -1
  60. package/dist/mappers/bybitspot.d.ts +1 -1
  61. package/dist/mappers/coinbase.d.ts +6 -39
  62. package/dist/mappers/coinbase.d.ts.map +1 -1
  63. package/dist/mappers/coinbase.js +11 -3
  64. package/dist/mappers/coinbase.js.map +1 -1
  65. package/dist/mappers/coinbaseinternational.d.ts +7 -78
  66. package/dist/mappers/coinbaseinternational.d.ts.map +1 -1
  67. package/dist/mappers/coinbaseinternational.js +13 -4
  68. package/dist/mappers/coinbaseinternational.js.map +1 -1
  69. package/dist/mappers/coinflex.d.ts +6 -24
  70. package/dist/mappers/coinflex.d.ts.map +1 -1
  71. package/dist/mappers/coinflex.js +11 -3
  72. package/dist/mappers/coinflex.js.map +1 -1
  73. package/dist/mappers/cryptocom.d.ts +22 -328
  74. package/dist/mappers/cryptocom.d.ts.map +1 -1
  75. package/dist/mappers/cryptocom.js +13 -4
  76. package/dist/mappers/cryptocom.js.map +1 -1
  77. package/dist/mappers/cryptofacilities.d.ts +8 -96
  78. package/dist/mappers/cryptofacilities.d.ts.map +1 -1
  79. package/dist/mappers/cryptofacilities.js +24 -7
  80. package/dist/mappers/cryptofacilities.js.map +1 -1
  81. package/dist/mappers/delta.d.ts +7 -114
  82. package/dist/mappers/delta.d.ts.map +1 -1
  83. package/dist/mappers/delta.js +30 -10
  84. package/dist/mappers/delta.js.map +1 -1
  85. package/dist/mappers/deribit.d.ts +8 -66
  86. package/dist/mappers/deribit.d.ts.map +1 -1
  87. package/dist/mappers/deribit.js +17 -6
  88. package/dist/mappers/deribit.js.map +1 -1
  89. package/dist/mappers/dydx.d.ts +5 -146
  90. package/dist/mappers/dydx.d.ts.map +1 -1
  91. package/dist/mappers/dydx.js +11 -3
  92. package/dist/mappers/dydx.js.map +1 -1
  93. package/dist/mappers/dydxv4.d.ts +6 -173
  94. package/dist/mappers/dydxv4.d.ts.map +1 -1
  95. package/dist/mappers/dydxv4.js +13 -4
  96. package/dist/mappers/dydxv4.js.map +1 -1
  97. package/dist/mappers/ftx.d.ts +12 -107
  98. package/dist/mappers/ftx.d.ts.map +1 -1
  99. package/dist/mappers/ftx.js +21 -6
  100. package/dist/mappers/ftx.js.map +1 -1
  101. package/dist/mappers/gateio.d.ts +6 -191
  102. package/dist/mappers/gateio.d.ts.map +1 -1
  103. package/dist/mappers/gateio.js +28 -8
  104. package/dist/mappers/gateio.js.map +1 -1
  105. package/dist/mappers/gateiofutures.d.ts +7 -122
  106. package/dist/mappers/gateiofutures.d.ts.map +1 -1
  107. package/dist/mappers/gateiofutures.js +13 -4
  108. package/dist/mappers/gateiofutures.js.map +1 -1
  109. package/dist/mappers/gemini.d.ts +6 -3
  110. package/dist/mappers/gemini.d.ts.map +1 -1
  111. package/dist/mappers/gemini.js +9 -2
  112. package/dist/mappers/gemini.js.map +1 -1
  113. package/dist/mappers/hitbtc.d.ts +6 -3
  114. package/dist/mappers/hitbtc.d.ts.map +1 -1
  115. package/dist/mappers/hitbtc.js +9 -2
  116. package/dist/mappers/hitbtc.js.map +1 -1
  117. package/dist/mappers/huobi.d.ts +118 -289
  118. package/dist/mappers/huobi.d.ts.map +1 -1
  119. package/dist/mappers/huobi.js +45 -7
  120. package/dist/mappers/huobi.js.map +1 -1
  121. package/dist/mappers/hyperliquid.d.ts +6 -86
  122. package/dist/mappers/hyperliquid.d.ts.map +1 -1
  123. package/dist/mappers/hyperliquid.js +13 -4
  124. package/dist/mappers/hyperliquid.js.map +1 -1
  125. package/dist/mappers/index.d.ts +784 -272
  126. package/dist/mappers/index.d.ts.map +1 -1
  127. package/dist/mappers/index.js +64 -432
  128. package/dist/mappers/index.js.map +1 -1
  129. package/dist/mappers/kraken.d.ts +7 -4
  130. package/dist/mappers/kraken.d.ts.map +1 -1
  131. package/dist/mappers/kraken.js +11 -3
  132. package/dist/mappers/kraken.js.map +1 -1
  133. package/dist/mappers/kucoin.d.ts +6 -122
  134. package/dist/mappers/kucoin.d.ts.map +1 -1
  135. package/dist/mappers/kucoin.js +13 -5
  136. package/dist/mappers/kucoin.js.map +1 -1
  137. package/dist/mappers/kucoinfutures.d.ts +6 -207
  138. package/dist/mappers/kucoinfutures.d.ts.map +1 -1
  139. package/dist/mappers/kucoinfutures.js +15 -6
  140. package/dist/mappers/kucoinfutures.js.map +1 -1
  141. package/dist/mappers/lighter.d.ts +8 -156
  142. package/dist/mappers/lighter.d.ts.map +1 -1
  143. package/dist/mappers/lighter.js +15 -5
  144. package/dist/mappers/lighter.js.map +1 -1
  145. package/dist/mappers/mapper.d.ts +3 -1
  146. package/dist/mappers/mapper.d.ts.map +1 -1
  147. package/dist/mappers/mapper.js.map +1 -1
  148. package/dist/mappers/mexc.d.ts +122 -0
  149. package/dist/mappers/mexc.d.ts.map +1 -0
  150. package/dist/mappers/mexc.js +205 -0
  151. package/dist/mappers/mexc.js.map +1 -0
  152. package/dist/mappers/mexcfutures.d.ts +137 -0
  153. package/dist/mappers/mexcfutures.d.ts.map +1 -0
  154. package/dist/mappers/mexcfutures.js +225 -0
  155. package/dist/mappers/mexcfutures.js.map +1 -0
  156. package/dist/mappers/okex.d.ts +36 -473
  157. package/dist/mappers/okex.d.ts.map +1 -1
  158. package/dist/mappers/okex.js +143 -37
  159. package/dist/mappers/okex.js.map +1 -1
  160. package/dist/mappers/phemex.d.ts +5 -82
  161. package/dist/mappers/phemex.d.ts.map +1 -1
  162. package/dist/mappers/phemex.js +22 -4
  163. package/dist/mappers/phemex.js.map +1 -1
  164. package/dist/mappers/poloniex.d.ts +5 -90
  165. package/dist/mappers/poloniex.d.ts.map +1 -1
  166. package/dist/mappers/poloniex.js +18 -4
  167. package/dist/mappers/poloniex.js.map +1 -1
  168. package/dist/mappers/polymarket.d.ts +7 -31
  169. package/dist/mappers/polymarket.d.ts.map +1 -1
  170. package/dist/mappers/polymarket.js +11 -3
  171. package/dist/mappers/polymarket.js.map +1 -1
  172. package/dist/mappers/registry.d.ts +41 -0
  173. package/dist/mappers/registry.d.ts.map +1 -0
  174. package/dist/mappers/registry.js +111 -0
  175. package/dist/mappers/registry.js.map +1 -0
  176. package/dist/mappers/serum.d.ts +16 -89
  177. package/dist/mappers/serum.d.ts.map +1 -1
  178. package/dist/mappers/serum.js +21 -3
  179. package/dist/mappers/serum.js.map +1 -1
  180. package/dist/mappers/upbit.d.ts +5 -47
  181. package/dist/mappers/upbit.d.ts.map +1 -1
  182. package/dist/mappers/upbit.js +9 -2
  183. package/dist/mappers/upbit.js.map +1 -1
  184. package/dist/mappers/woox.d.ts +6 -107
  185. package/dist/mappers/woox.d.ts.map +1 -1
  186. package/dist/mappers/woox.js +13 -4
  187. package/dist/mappers/woox.js.map +1 -1
  188. package/dist/okexsymbols.d.ts +3 -0
  189. package/dist/okexsymbols.d.ts.map +1 -0
  190. package/dist/okexsymbols.js +10 -0
  191. package/dist/okexsymbols.js.map +1 -0
  192. package/dist/realtimefeeds/hyperliquid.d.ts.map +1 -1
  193. package/dist/realtimefeeds/hyperliquid.js +3 -1
  194. package/dist/realtimefeeds/hyperliquid.js.map +1 -1
  195. package/dist/realtimefeeds/index.d.ts.map +1 -1
  196. package/dist/realtimefeeds/index.js +4 -0
  197. package/dist/realtimefeeds/index.js.map +1 -1
  198. package/dist/realtimefeeds/mexc.d.ts +35 -0
  199. package/dist/realtimefeeds/mexc.d.ts.map +1 -0
  200. package/dist/realtimefeeds/mexc.js +261 -0
  201. package/dist/realtimefeeds/mexc.js.map +1 -0
  202. package/dist/realtimefeeds/mexcfutures.d.ts +25 -0
  203. package/dist/realtimefeeds/mexcfutures.d.ts.map +1 -0
  204. package/dist/realtimefeeds/mexcfutures.js +203 -0
  205. package/dist/realtimefeeds/mexcfutures.js.map +1 -0
  206. package/dist/realtimefeeds/okex.d.ts +1 -0
  207. package/dist/realtimefeeds/okex.d.ts.map +1 -1
  208. package/dist/realtimefeeds/okex.js +7 -5
  209. package/dist/realtimefeeds/okex.js.map +1 -1
  210. package/dist/realtimefeeds/realtimefeed.d.ts +1 -0
  211. package/dist/realtimefeeds/realtimefeed.d.ts.map +1 -1
  212. package/dist/realtimefeeds/realtimefeed.js +4 -1
  213. package/dist/realtimefeeds/realtimefeed.js.map +1 -1
  214. package/dist/replay.d.ts.map +1 -1
  215. package/dist/replay.js +62 -13
  216. package/dist/replay.js.map +1 -1
  217. package/example.js +133 -31
  218. package/package.json +2 -1
  219. package/src/consts.ts +22 -1
  220. package/src/handy.ts +6 -3
  221. package/src/mappers/ascendex.ts +14 -4
  222. package/src/mappers/binance.ts +61 -21
  223. package/src/mappers/binancedex.ts +12 -3
  224. package/src/mappers/binanceeuropeanoptions.ts +28 -7
  225. package/src/mappers/bitfinex.ts +21 -5
  226. package/src/mappers/bitflyer.ts +12 -3
  227. package/src/mappers/bitget.ts +48 -9
  228. package/src/mappers/bitmex.ts +16 -5
  229. package/src/mappers/bitnomial.ts +10 -2
  230. package/src/mappers/bitstamp.ts +10 -2
  231. package/src/mappers/blockchaincom.ts +10 -2
  232. package/src/mappers/bullish.ts +21 -8
  233. package/src/mappers/bybit.ts +70 -15
  234. package/src/mappers/coinbase.ts +12 -3
  235. package/src/mappers/coinbaseinternational.ts +14 -4
  236. package/src/mappers/coinflex.ts +12 -3
  237. package/src/mappers/cryptocom.ts +14 -4
  238. package/src/mappers/cryptofacilities.ts +30 -6
  239. package/src/mappers/delta.ts +41 -7
  240. package/src/mappers/deribit.ts +18 -6
  241. package/src/mappers/dydx.ts +12 -3
  242. package/src/mappers/dydxv4.ts +14 -4
  243. package/src/mappers/ftx.ts +22 -6
  244. package/src/mappers/gateio.ts +34 -8
  245. package/src/mappers/gateiofutures.ts +14 -4
  246. package/src/mappers/gemini.ts +10 -2
  247. package/src/mappers/hitbtc.ts +10 -2
  248. package/src/mappers/huobi.ts +47 -10
  249. package/src/mappers/hyperliquid.ts +14 -4
  250. package/src/mappers/index.ts +133 -679
  251. package/src/mappers/kraken.ts +12 -3
  252. package/src/mappers/kucoin.ts +19 -5
  253. package/src/mappers/kucoinfutures.ts +20 -5
  254. package/src/mappers/lighter.ts +16 -5
  255. package/src/mappers/mapper.ts +3 -1
  256. package/src/mappers/mexc.ts +324 -0
  257. package/src/mappers/mexcfutures.ts +352 -0
  258. package/src/mappers/okex.ts +178 -37
  259. package/src/mappers/phemex.ts +25 -4
  260. package/src/mappers/poloniex.ts +20 -4
  261. package/src/mappers/polymarket.ts +12 -3
  262. package/src/mappers/registry.ts +203 -0
  263. package/src/mappers/serum.ts +22 -3
  264. package/src/mappers/upbit.ts +10 -2
  265. package/src/mappers/woox.ts +14 -4
  266. package/src/okexsymbols.ts +10 -0
  267. package/src/realtimefeeds/hyperliquid.ts +4 -1
  268. package/src/realtimefeeds/index.ts +4 -0
  269. package/src/realtimefeeds/mexc.ts +327 -0
  270. package/src/realtimefeeds/mexcfutures.ts +269 -0
  271. package/src/realtimefeeds/okex.ts +7 -5
  272. package/src/realtimefeeds/realtimefeed.ts +5 -1
  273. package/src/replay.ts +73 -15
@@ -1,10 +1,19 @@
1
1
  import { asNonZeroNumberOrUndefined, upperCaseSymbols } from '../handy.ts'
2
2
  import { BookChange, BookTicker, Trade } from '../types.ts'
3
3
  import { Mapper } from './mapper.ts'
4
+ import { exchangeMappers } from './registry.ts'
4
5
 
5
6
  // https://www.kraken.com/features/websocket-api
6
7
 
7
- export const krakenTradesMapper: Mapper<'kraken', Trade> = {
8
+ export const krakenMappers = exchangeMappers({
9
+ kraken: {
10
+ trades: () => krakenTradesMapper,
11
+ bookChanges: () => krakenBookChangeMapper,
12
+ bookTickers: () => krakenBookTickerMapper
13
+ }
14
+ })
15
+
16
+ const krakenTradesMapper: Mapper<'kraken', Trade> = {
8
17
  canHandle(message: KrakenTrades) {
9
18
  if (!Array.isArray(message)) {
10
19
  return false
@@ -64,7 +73,7 @@ const getLatestTimestamp = (bids: KrakenBookLevel[], asks: KrakenBookLevel[]): D
64
73
  return timestamp
65
74
  }
66
75
 
67
- export const krakenBookChangeMapper: Mapper<'kraken', BookChange> = {
76
+ const krakenBookChangeMapper: Mapper<'kraken', BookChange> = {
68
77
  canHandle(message: KrakenBookSnapshot | KrakenBookUpdate) {
69
78
  if (!Array.isArray(message)) {
70
79
  return false
@@ -122,7 +131,7 @@ export const krakenBookChangeMapper: Mapper<'kraken', BookChange> = {
122
131
  }
123
132
  }
124
133
 
125
- export const krakenBookTickerMapper: Mapper<'kraken', BookTicker> = {
134
+ const krakenBookTickerMapper: Mapper<'kraken', BookTicker> = {
126
135
  canHandle(message: KrakenSpread) {
127
136
  if (!Array.isArray(message)) {
128
137
  return false
@@ -2,8 +2,18 @@ import { debug } from '../debug.ts'
2
2
  import { CircularBuffer, upperCaseSymbols } from '../handy.ts'
3
3
  import { BookChange, Exchange, BookTicker, Trade, BookPriceLevel } from '../types.ts'
4
4
  import { Mapper } from './mapper.ts'
5
+ import { exchangeMappers, isRealTime } from './registry.ts'
6
+
7
+ export const kucoinMappers = exchangeMappers({
8
+ kucoin: {
9
+ trades: () => new KucoinTradesMapper('kucoin'),
10
+ bookChanges: (localTimestamp) =>
11
+ new KucoinBookChangeMapper('kucoin', { ignoreBookSnapshotOverlapError: isRealTime(localTimestamp) === false }),
12
+ bookTickers: () => new KucoinBookTickerMapper('kucoin')
13
+ }
14
+ })
5
15
 
6
- export class KucoinTradesMapper implements Mapper<'kucoin', Trade> {
16
+ class KucoinTradesMapper implements Mapper<'kucoin', Trade> {
7
17
  constructor(private readonly _exchange: Exchange) {}
8
18
  canHandle(message: KucoinTradeMessage) {
9
19
  return message.type === 'message' && message.topic.startsWith('/market/match')
@@ -41,15 +51,19 @@ export class KucoinTradesMapper implements Mapper<'kucoin', Trade> {
41
51
  }
42
52
  }
43
53
 
44
- export class KucoinBookChangeMapper implements Mapper<'kucoin', BookChange> {
54
+ class KucoinBookChangeMapper implements Mapper<'kucoin', BookChange> {
45
55
  protected readonly symbolToDepthInfoMapping: {
46
56
  [key: string]: LocalDepthInfo
47
57
  } = {}
48
58
 
49
59
  constructor(
50
60
  protected readonly _exchange: Exchange,
51
- private readonly ignoreBookSnapshotOverlapError: boolean
52
- ) {}
61
+ { ignoreBookSnapshotOverlapError }: { ignoreBookSnapshotOverlapError: boolean }
62
+ ) {
63
+ this.ignoreBookSnapshotOverlapError = ignoreBookSnapshotOverlapError
64
+ }
65
+
66
+ private readonly ignoreBookSnapshotOverlapError: boolean
53
67
 
54
68
  canHandle(message: KucoinLevel2SnapshotMessage | KucoinLevel2UpdateMessage) {
55
69
  return message.type === 'message' && message.topic.startsWith('/market/level2')
@@ -221,7 +235,7 @@ export class KucoinBookChangeMapper implements Mapper<'kucoin', BookChange> {
221
235
  }
222
236
  }
223
237
 
224
- export class KucoinBookTickerMapper implements Mapper<'kucoin', BookTicker> {
238
+ class KucoinBookTickerMapper implements Mapper<'kucoin', BookTicker> {
225
239
  constructor(protected readonly _exchange: Exchange) {}
226
240
 
227
241
  canHandle(message: KucoinTickerMessage) {
@@ -2,8 +2,19 @@ import { debug } from '../debug.ts'
2
2
  import { asNonZeroNumberOrUndefined, CircularBuffer, upperCaseSymbols } from '../handy.ts'
3
3
  import { BookChange, BookTicker, DerivativeTicker, Trade } from '../types.ts'
4
4
  import { Mapper, PendingTickerInfoHelper } from './mapper.ts'
5
+ import { exchangeMappers, isRealTime } from './registry.ts'
6
+
7
+ export const kucoinFuturesMappers = exchangeMappers({
8
+ 'kucoin-futures': {
9
+ trades: () => new KucoinFuturesTradesMapper(),
10
+ bookChanges: (localTimestamp) =>
11
+ new KucoinFuturesBookChangeMapper({ ignoreBookSnapshotOverlapError: isRealTime(localTimestamp) === false }),
12
+ derivativeTickers: () => new KucoinFuturesDerivativeTickerMapper(),
13
+ bookTickers: () => new KucoinFuturesBookTickerMapper()
14
+ }
15
+ })
5
16
 
6
- export class KucoinFuturesTradesMapper implements Mapper<'kucoin-futures', Trade> {
17
+ class KucoinFuturesTradesMapper implements Mapper<'kucoin-futures', Trade> {
7
18
  canHandle(message: KucoinFuturesTradeMessage) {
8
19
  return message.type === 'message' && message.topic.startsWith('/contractMarket/execution')
9
20
  }
@@ -38,12 +49,16 @@ export class KucoinFuturesTradesMapper implements Mapper<'kucoin-futures', Trade
38
49
  }
39
50
  }
40
51
 
41
- export class KucoinFuturesBookChangeMapper implements Mapper<'kucoin-futures', BookChange> {
52
+ class KucoinFuturesBookChangeMapper implements Mapper<'kucoin-futures', BookChange> {
42
53
  protected readonly symbolToDepthInfoMapping: {
43
54
  [key: string]: LocalDepthInfo
44
55
  } = {}
45
56
 
46
- constructor(private readonly ignoreBookSnapshotOverlapError: boolean) {}
57
+ private readonly ignoreBookSnapshotOverlapError: boolean
58
+
59
+ constructor({ ignoreBookSnapshotOverlapError }: { ignoreBookSnapshotOverlapError: boolean }) {
60
+ this.ignoreBookSnapshotOverlapError = ignoreBookSnapshotOverlapError
61
+ }
47
62
 
48
63
  canHandle(message: KucoinFuturesLevel2SnapshotMessage | KucoinFuturesLevel2UpdateMessage) {
49
64
  return message.type === 'message' && message.topic.startsWith('/contractMarket/level2')
@@ -229,7 +244,7 @@ export class KucoinFuturesBookChangeMapper implements Mapper<'kucoin-futures', B
229
244
  }
230
245
  }
231
246
 
232
- export class KucoinFuturesBookTickerMapper implements Mapper<'kucoin-futures', BookTicker> {
247
+ class KucoinFuturesBookTickerMapper implements Mapper<'kucoin-futures', BookTicker> {
233
248
  canHandle(message: KucoinFuturesTickerMessage) {
234
249
  return message.type === 'message' && message.topic.startsWith('/contractMarket/tickerV2')
235
250
  }
@@ -267,7 +282,7 @@ export class KucoinFuturesBookTickerMapper implements Mapper<'kucoin-futures', B
267
282
  }
268
283
  }
269
284
 
270
- export class KucoinFuturesDerivativeTickerMapper implements Mapper<'kucoin-futures', DerivativeTicker> {
285
+ class KucoinFuturesDerivativeTickerMapper implements Mapper<'kucoin-futures', DerivativeTicker> {
271
286
  private readonly pendingTickerInfoHelper = new PendingTickerInfoHelper()
272
287
  private readonly _lastPrices = new Map<string, number>()
273
288
  private readonly _openInterests = new Map<string, number>()
@@ -1,6 +1,17 @@
1
1
  import { asNonZeroNumberOrUndefined } from '../handy.ts'
2
2
  import { BookChange, BookTicker, DerivativeTicker, Liquidation, Trade } from '../types.ts'
3
3
  import { Mapper, PendingTickerInfoHelper } from './mapper.ts'
4
+ import { exchangeMappers } from './registry.ts'
5
+
6
+ export const lighterMappers = exchangeMappers({
7
+ lighter: {
8
+ trades: () => new LighterTradesMapper(),
9
+ bookChanges: () => new LighterBookChangeMapper(),
10
+ derivativeTickers: () => new LighterDerivativeTickerMapper(),
11
+ liquidations: () => new LighterLiquidationMapper(),
12
+ bookTickers: () => new LighterBookTickerMapper()
13
+ }
14
+ })
4
15
 
5
16
  function parseChannelMarketId(channel: string): string | undefined {
6
17
  const colonIndex = channel.indexOf(':')
@@ -14,7 +25,7 @@ function parseChannelMarketId(channel: string): string | undefined {
14
25
  return suffix
15
26
  }
16
27
 
17
- export class LighterTradesMapper implements Mapper<'lighter', Trade> {
28
+ class LighterTradesMapper implements Mapper<'lighter', Trade> {
18
29
  canHandle(message: LighterTradeMessage) {
19
30
  return message.type === 'update/trade'
20
31
  }
@@ -45,7 +56,7 @@ export class LighterTradesMapper implements Mapper<'lighter', Trade> {
45
56
  }
46
57
  }
47
58
 
48
- export class LighterLiquidationMapper implements Mapper<'lighter', Liquidation> {
59
+ class LighterLiquidationMapper implements Mapper<'lighter', Liquidation> {
49
60
  canHandle(message: LighterTradeMessage) {
50
61
  return message.type === 'update/trade'
51
62
  }
@@ -84,7 +95,7 @@ export class LighterLiquidationMapper implements Mapper<'lighter', Liquidation>
84
95
  }
85
96
  }
86
97
 
87
- export class LighterBookChangeMapper implements Mapper<'lighter', BookChange> {
98
+ class LighterBookChangeMapper implements Mapper<'lighter', BookChange> {
88
99
  canHandle(message: LighterOrderBookMessage) {
89
100
  return message.type === 'subscribed/order_book' || message.type === 'update/order_book'
90
101
  }
@@ -122,7 +133,7 @@ export class LighterBookChangeMapper implements Mapper<'lighter', BookChange> {
122
133
  }
123
134
  }
124
135
 
125
- export class LighterBookTickerMapper implements Mapper<'lighter', BookTicker> {
136
+ class LighterBookTickerMapper implements Mapper<'lighter', BookTicker> {
126
137
  canHandle(message: LighterTickerMessage) {
127
138
  return message.type === 'update/ticker'
128
139
  }
@@ -154,7 +165,7 @@ export class LighterBookTickerMapper implements Mapper<'lighter', BookTicker> {
154
165
  }
155
166
  }
156
167
 
157
- export class LighterDerivativeTickerMapper implements Mapper<'lighter', DerivativeTicker> {
168
+ class LighterDerivativeTickerMapper implements Mapper<'lighter', DerivativeTicker> {
158
169
  private readonly pendingTickerInfoHelper = new PendingTickerInfoHelper()
159
170
 
160
171
  canHandle(message: LighterMarketStatsMessage) {
@@ -8,7 +8,9 @@ export type Mapper<T extends Exchange, U extends NormalizedData> = {
8
8
  getFilters: (symbols?: string[]) => FilterForExchange[T][]
9
9
  }
10
10
 
11
- export type MapperFactory<T extends Exchange, U extends NormalizedData> = (exchange: T, localTimestamp: Date) => Mapper<T, U>
11
+ export type MapperFactory<T extends Exchange, U extends NormalizedData> = ((exchange: T, localTimestamp: Date) => Mapper<T, U>) & {
12
+ getSwitchDates?: (exchange: T) => readonly Date[]
13
+ }
12
14
 
13
15
  type Writeable<T> = { -readonly [P in keyof T]: T[P] }
14
16
 
@@ -0,0 +1,324 @@
1
+ import { debug } from '../debug.ts'
2
+ import { asNumberOrUndefined, CircularBuffer, upperCaseSymbols } from '../handy.ts'
3
+ import { BookChange, BookPriceLevel, BookTicker, Trade } from '../types.ts'
4
+ import { Mapper } from './mapper.ts'
5
+ import { exchangeMappers, isRealTime } from './registry.ts'
6
+
7
+ export const mexcMappers = exchangeMappers({
8
+ mexc: {
9
+ trades: () => new MexcTradesMapper(),
10
+ bookChanges: (localTimestamp) => new MexcBookChangeMapper({ ignoreBookSnapshotOverlapError: isRealTime(localTimestamp) === false }),
11
+ bookTickers: () => new MexcBookTickerMapper()
12
+ }
13
+ })
14
+
15
+ export class MexcTradesMapper implements Mapper<'mexc', Trade> {
16
+ private readonly channel = 'spot@public.aggre.deals.v3.api.pb@10ms'
17
+
18
+ canHandle(message: MexcTradeMessage | MexcControlMessage) {
19
+ return message.channel?.startsWith(`${this.channel}@`) === true && 'publicAggreDeals' in message
20
+ }
21
+
22
+ getFilters(symbols?: string[]) {
23
+ return [{ channel: this.channel, symbols: upperCaseSymbols(symbols) } as const]
24
+ }
25
+
26
+ *map(message: MexcTradeMessage, localTimestamp: Date): IterableIterator<Trade> {
27
+ for (const trade of message.publicAggreDeals.deals) {
28
+ yield {
29
+ type: 'trade',
30
+ symbol: message.symbol,
31
+ exchange: 'mexc',
32
+ id: trade.tradeId,
33
+ price: Number(trade.price),
34
+ amount: Number(trade.quantity),
35
+ side: trade.tradeType === MexcTradeType.Buy ? 'buy' : 'sell',
36
+ timestamp: new Date(Number(trade.time)),
37
+ localTimestamp
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ export class MexcBookChangeMapper implements Mapper<'mexc', BookChange> {
44
+ private readonly channel = 'spot@public.aggre.depth.v3.api.pb@10ms'
45
+ private readonly snapshotChannel = 'spot@public.aggre.depth.snapshot.v3.api.pb@10ms'
46
+ private readonly symbolDepthInfo: Record<string, MexcDepthInfo> = {}
47
+ private readonly ignoreBookSnapshotOverlapError: boolean
48
+
49
+ constructor({ ignoreBookSnapshotOverlapError }: { ignoreBookSnapshotOverlapError: boolean }) {
50
+ this.ignoreBookSnapshotOverlapError = ignoreBookSnapshotOverlapError
51
+ }
52
+
53
+ canHandle(message: MexcDepthSnapshotMessage | MexcDepthUpdateMessage) {
54
+ return message.generated === true
55
+ ? message.channel?.startsWith(`${this.snapshotChannel}@`) === true && 'publicAggreDepthsSnapshot' in message
56
+ : message.channel?.startsWith(`${this.channel}@`) === true && 'publicAggreDepths' in message
57
+ }
58
+
59
+ getFilters(symbols?: string[]) {
60
+ const normalizedSymbols = upperCaseSymbols(symbols)
61
+
62
+ return [
63
+ { channel: this.channel, symbols: normalizedSymbols } as const,
64
+ { channel: this.snapshotChannel, symbols: normalizedSymbols } as const
65
+ ]
66
+ }
67
+
68
+ *map(message: MexcDepthSnapshotMessage | MexcDepthUpdateMessage, localTimestamp: Date): IterableIterator<BookChange> {
69
+ const depthInfo = this.getDepthInfoFor(message.symbol)
70
+ if (message.generated === true) {
71
+ if (depthInfo.snapshotEmitted) {
72
+ return
73
+ }
74
+
75
+ let currentBookVersion = Number(message.publicAggreDepthsSnapshot.lastUpdateId)
76
+ const bids = message.publicAggreDepthsSnapshot.bids.map(this.mapDepthSnapshotLevel)
77
+ const asks = message.publicAggreDepthsSnapshot.asks.map(this.mapDepthSnapshotLevel)
78
+
79
+ for (const update of depthInfo.updates.items()) {
80
+ const fromVersion = Number(update.publicAggreDepths.fromVersion)
81
+ const toVersion = Number(update.publicAggreDepths.toVersion)
82
+ if (currentBookVersion >= toVersion) {
83
+ continue
84
+ }
85
+
86
+ if (!depthInfo.isContinuityValidated) {
87
+ if (fromVersion > currentBookVersion + 1 || toVersion < currentBookVersion + 1) {
88
+ const message = `MEXC depth snapshot has no overlap with first update, update ${JSON.stringify(update)}, currentBookVersion: ${currentBookVersion}`
89
+ if (this.ignoreBookSnapshotOverlapError) {
90
+ depthInfo.isContinuityValidated = true
91
+ debug(message)
92
+ } else {
93
+ throw new Error(message)
94
+ }
95
+ } else {
96
+ depthInfo.isContinuityValidated = true
97
+ }
98
+ }
99
+
100
+ for (const bid of update.publicAggreDepths.bids ?? []) {
101
+ this.applyLevel(bids, this.mapBookLevel(bid))
102
+ }
103
+ for (const ask of update.publicAggreDepths.asks ?? []) {
104
+ this.applyLevel(asks, this.mapBookLevel(ask))
105
+ }
106
+ currentBookVersion = toVersion
107
+ }
108
+
109
+ depthInfo.updates.clear()
110
+ depthInfo.currentBookVersion = currentBookVersion
111
+ depthInfo.snapshotEmitted = true
112
+
113
+ yield {
114
+ type: 'book_change',
115
+ symbol: message.symbol,
116
+ exchange: 'mexc',
117
+ isSnapshot: true,
118
+ bids,
119
+ asks,
120
+ timestamp: new Date(Number(message.publicAggreDepthsSnapshot.timestamp)),
121
+ localTimestamp
122
+ }
123
+
124
+ return
125
+ }
126
+
127
+ if (!depthInfo.snapshotEmitted) {
128
+ depthInfo.updates.append(message)
129
+ return
130
+ }
131
+
132
+ const fromVersion = Number(message.publicAggreDepths.fromVersion)
133
+ const toVersion = Number(message.publicAggreDepths.toVersion)
134
+ if (toVersion <= depthInfo.currentBookVersion!) {
135
+ return
136
+ }
137
+
138
+ if (!depthInfo.isContinuityValidated) {
139
+ if (fromVersion > depthInfo.currentBookVersion! + 1 || toVersion < depthInfo.currentBookVersion! + 1) {
140
+ const errorMessage = `MEXC depth snapshot has no overlap with first update, update ${JSON.stringify(message)}, currentBookVersion: ${depthInfo.currentBookVersion}`
141
+ if (this.ignoreBookSnapshotOverlapError) {
142
+ depthInfo.isContinuityValidated = true
143
+ debug(errorMessage)
144
+ } else {
145
+ throw new Error(errorMessage)
146
+ }
147
+ } else {
148
+ depthInfo.isContinuityValidated = true
149
+ }
150
+ }
151
+
152
+ depthInfo.currentBookVersion = toVersion
153
+
154
+ yield {
155
+ type: 'book_change',
156
+ symbol: message.symbol,
157
+ exchange: 'mexc',
158
+ isSnapshot: false,
159
+ bids: (message.publicAggreDepths.bids ?? []).map(this.mapBookLevel),
160
+ asks: (message.publicAggreDepths.asks ?? []).map(this.mapBookLevel),
161
+ timestamp: new Date(Number(message.sendTime)),
162
+ localTimestamp
163
+ }
164
+ }
165
+
166
+ private applyLevel(bookSide: BookPriceLevel[], levelUpdate: BookPriceLevel) {
167
+ const existingIndex = bookSide.findIndex((level) => level.price === levelUpdate.price)
168
+ if (levelUpdate.amount === 0) {
169
+ if (existingIndex !== -1) {
170
+ bookSide.splice(existingIndex, 1)
171
+ }
172
+ return
173
+ }
174
+
175
+ if (existingIndex === -1) {
176
+ bookSide.push(levelUpdate)
177
+ } else {
178
+ bookSide[existingIndex] = levelUpdate
179
+ }
180
+ }
181
+
182
+ private mapBookLevel(level: MexcPriceLevel) {
183
+ return {
184
+ price: Number(level.price),
185
+ amount: Number(level.quantity)
186
+ }
187
+ }
188
+
189
+ private mapDepthSnapshotLevel([price, quantity]: MexcDepthSnapshotLevel) {
190
+ return {
191
+ price: Number(price),
192
+ amount: Number(quantity)
193
+ }
194
+ }
195
+
196
+ private getDepthInfoFor(symbol: string) {
197
+ if (this.symbolDepthInfo[symbol] === undefined) {
198
+ this.symbolDepthInfo[symbol] = { updates: new CircularBuffer<MexcDepthUpdateMessage>(2000) }
199
+ }
200
+
201
+ return this.symbolDepthInfo[symbol]
202
+ }
203
+ }
204
+
205
+ export class MexcBookTickerMapper implements Mapper<'mexc', BookTicker> {
206
+ private readonly channel = 'spot@public.aggre.bookTicker.v3.api.pb@10ms'
207
+
208
+ canHandle(message: MexcBookTickerMessage | MexcControlMessage) {
209
+ return message.channel?.startsWith(`${this.channel}@`) === true && 'publicAggreBookTicker' in message
210
+ }
211
+
212
+ getFilters(symbols?: string[]) {
213
+ return [{ channel: this.channel, symbols: upperCaseSymbols(symbols) } as const]
214
+ }
215
+
216
+ *map(message: MexcBookTickerMessage, localTimestamp: Date): IterableIterator<BookTicker> {
217
+ yield {
218
+ type: 'book_ticker',
219
+ symbol: message.symbol,
220
+ exchange: 'mexc',
221
+ askAmount: asNumberOrUndefined(message.publicAggreBookTicker.askQuantity),
222
+ askPrice: asNumberOrUndefined(message.publicAggreBookTicker.askPrice),
223
+ bidAmount: asNumberOrUndefined(message.publicAggreBookTicker.bidQuantity),
224
+ bidPrice: asNumberOrUndefined(message.publicAggreBookTicker.bidPrice),
225
+ timestamp: new Date(Number(message.sendTime)),
226
+ localTimestamp
227
+ }
228
+ }
229
+ }
230
+
231
+ type MexcControlMessage = {
232
+ channel?: undefined
233
+ id?: number
234
+ code?: number
235
+ msg?: string
236
+ }
237
+
238
+ type MexcMappedChannel =
239
+ | 'spot@public.aggre.deals.v3.api.pb@10ms'
240
+ | 'spot@public.aggre.depth.v3.api.pb@10ms'
241
+ | 'spot@public.aggre.depth.snapshot.v3.api.pb@10ms'
242
+ | 'spot@public.aggre.bookTicker.v3.api.pb@10ms'
243
+ type MexcChannelWithSymbol<TChannel extends MexcMappedChannel> = `${TChannel}@${string}`
244
+
245
+ type MexcProtobufMessage<TChannel extends MexcMappedChannel> = {
246
+ channel: MexcChannelWithSymbol<TChannel>
247
+ symbol: string
248
+ symbolId?: string
249
+ createTime?: string | number
250
+ sendTime: string | number
251
+ }
252
+
253
+ type MexcTradeMessage = MexcProtobufMessage<'spot@public.aggre.deals.v3.api.pb@10ms'> & {
254
+ publicAggreDeals: {
255
+ eventType: 'spot@public.aggre.deals.v3.api.pb@10ms'
256
+ deals: MexcTrade[]
257
+ }
258
+ }
259
+
260
+ type MexcTrade = {
261
+ price: string
262
+ quantity: string
263
+ tradeType: MexcTradeType
264
+ time: string | number
265
+ tradeId?: string
266
+ }
267
+
268
+ enum MexcTradeType {
269
+ Buy = 1,
270
+ Sell = 2
271
+ }
272
+
273
+ export type MexcDepthSnapshotMessage = {
274
+ channel: MexcChannelWithSymbol<'spot@public.aggre.depth.snapshot.v3.api.pb@10ms'>
275
+ symbol: string
276
+ generated: true
277
+ publicAggreDepthsSnapshot: MexcAggreDepthsSnapshot
278
+ }
279
+
280
+ type MexcAggreDepthsSnapshot = {
281
+ asks: MexcDepthSnapshotLevel[]
282
+ bids: MexcDepthSnapshotLevel[]
283
+ lastUpdateId: number
284
+ timestamp: number
285
+ }
286
+
287
+ type MexcDepthSnapshotLevel = [string, string]
288
+
289
+ type MexcDepthUpdateMessage = MexcProtobufMessage<'spot@public.aggre.depth.v3.api.pb@10ms'> & {
290
+ generated?: undefined
291
+ publicAggreDepths: MexcAggreDepths
292
+ }
293
+
294
+ type MexcAggreDepths = {
295
+ eventType: 'spot@public.aggre.depth.v3.api.pb@10ms'
296
+ asks?: MexcPriceLevel[]
297
+ bids?: MexcPriceLevel[]
298
+ fromVersion: string
299
+ toVersion: string
300
+ lastOrderCreateTime?: string | number
301
+ }
302
+
303
+ type MexcPriceLevel = {
304
+ price: string
305
+ quantity: string
306
+ }
307
+
308
+ type MexcBookTickerMessage = MexcProtobufMessage<'spot@public.aggre.bookTicker.v3.api.pb@10ms'> & {
309
+ publicAggreBookTicker: {
310
+ bidPrice?: string
311
+ bidQuantity?: string
312
+ askPrice?: string
313
+ askQuantity?: string
314
+ version?: string
315
+ lastOrderCreateTime?: string | number
316
+ }
317
+ }
318
+
319
+ type MexcDepthInfo = {
320
+ isContinuityValidated?: boolean
321
+ currentBookVersion?: number
322
+ snapshotEmitted?: boolean
323
+ updates: CircularBuffer<MexcDepthUpdateMessage>
324
+ }