@catalyst-team/poly-sdk 0.2.1 → 0.4.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 (277) hide show
  1. package/README.md +665 -807
  2. package/README.zh-CN.md +645 -342
  3. package/dist/__tests__/integration/arbitrage-service.integration.test.d.ts +12 -0
  4. package/dist/__tests__/integration/arbitrage-service.integration.test.d.ts.map +1 -0
  5. package/dist/__tests__/integration/arbitrage-service.integration.test.js +267 -0
  6. package/dist/__tests__/integration/arbitrage-service.integration.test.js.map +1 -0
  7. package/dist/__tests__/integration/data-api.integration.test.js +6 -3
  8. package/dist/__tests__/integration/data-api.integration.test.js.map +1 -1
  9. package/dist/__tests__/integration/market-service.integration.test.d.ts +10 -0
  10. package/dist/__tests__/integration/market-service.integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/integration/market-service.integration.test.js +173 -0
  12. package/dist/__tests__/integration/market-service.integration.test.js.map +1 -0
  13. package/dist/__tests__/integration/realtime-service-v2.integration.test.d.ts +10 -0
  14. package/dist/__tests__/integration/realtime-service-v2.integration.test.d.ts.map +1 -0
  15. package/dist/__tests__/integration/realtime-service-v2.integration.test.js +307 -0
  16. package/dist/__tests__/integration/realtime-service-v2.integration.test.js.map +1 -0
  17. package/dist/__tests__/integration/trading-service.integration.test.d.ts +10 -0
  18. package/dist/__tests__/integration/trading-service.integration.test.d.ts.map +1 -0
  19. package/dist/__tests__/integration/trading-service.integration.test.js +58 -0
  20. package/dist/__tests__/integration/trading-service.integration.test.js.map +1 -0
  21. package/dist/clients/clob-api.d.ts +73 -0
  22. package/dist/clients/clob-api.d.ts.map +1 -1
  23. package/dist/clients/clob-api.js +60 -0
  24. package/dist/clients/clob-api.js.map +1 -1
  25. package/dist/clients/ctf-client.d.ts +6 -4
  26. package/dist/clients/ctf-client.d.ts.map +1 -1
  27. package/dist/clients/ctf-client.js.map +1 -1
  28. package/dist/clients/data-api.d.ts +333 -15
  29. package/dist/clients/data-api.d.ts.map +1 -1
  30. package/dist/clients/data-api.js +398 -26
  31. package/dist/clients/data-api.js.map +1 -1
  32. package/dist/clients/gamma-api.d.ts +5 -0
  33. package/dist/clients/gamma-api.d.ts.map +1 -1
  34. package/dist/clients/gamma-api.js +2 -0
  35. package/dist/clients/gamma-api.js.map +1 -1
  36. package/dist/clients/subgraph.d.ts +196 -0
  37. package/dist/clients/subgraph.d.ts.map +1 -0
  38. package/dist/clients/subgraph.js +332 -0
  39. package/dist/clients/subgraph.js.map +1 -0
  40. package/dist/clients/websocket-manager.d.ts +3 -0
  41. package/dist/clients/websocket-manager.d.ts.map +1 -1
  42. package/dist/clients/websocket-manager.js +10 -3
  43. package/dist/clients/websocket-manager.js.map +1 -1
  44. package/dist/core/cache.d.ts +3 -0
  45. package/dist/core/cache.d.ts.map +1 -1
  46. package/dist/core/cache.js +5 -0
  47. package/dist/core/cache.js.map +1 -1
  48. package/dist/core/errors.d.ts +2 -1
  49. package/dist/core/errors.d.ts.map +1 -1
  50. package/dist/core/errors.js +2 -0
  51. package/dist/core/errors.js.map +1 -1
  52. package/dist/core/rate-limiter.d.ts +3 -1
  53. package/dist/core/rate-limiter.d.ts.map +1 -1
  54. package/dist/core/rate-limiter.js +12 -0
  55. package/dist/core/rate-limiter.js.map +1 -1
  56. package/dist/core/types.d.ts +205 -13
  57. package/dist/core/types.d.ts.map +1 -1
  58. package/dist/core/types.js +30 -0
  59. package/dist/core/types.js.map +1 -1
  60. package/dist/core/types.test.d.ts +7 -0
  61. package/dist/core/types.test.d.ts.map +1 -0
  62. package/dist/core/types.test.js +122 -0
  63. package/dist/core/types.test.js.map +1 -0
  64. package/dist/index.d.ts +84 -18
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +139 -132
  67. package/dist/index.js.map +1 -1
  68. package/dist/scripts/dip-arb/auto-trade.d.ts +20 -0
  69. package/dist/scripts/dip-arb/auto-trade.d.ts.map +1 -0
  70. package/dist/scripts/dip-arb/auto-trade.js +373 -0
  71. package/dist/scripts/dip-arb/auto-trade.js.map +1 -0
  72. package/dist/scripts/dip-arb/example-basic.d.ts +30 -0
  73. package/dist/scripts/dip-arb/example-basic.d.ts.map +1 -0
  74. package/dist/scripts/dip-arb/example-basic.js +222 -0
  75. package/dist/scripts/dip-arb/example-basic.js.map +1 -0
  76. package/dist/scripts/dip-arb/redeem-positions.d.ts +11 -0
  77. package/dist/scripts/dip-arb/redeem-positions.d.ts.map +1 -0
  78. package/dist/scripts/dip-arb/redeem-positions.js +201 -0
  79. package/dist/scripts/dip-arb/redeem-positions.js.map +1 -0
  80. package/dist/scripts/dip-arb/scan-markets.d.ts +6 -0
  81. package/dist/scripts/dip-arb/scan-markets.d.ts.map +1 -0
  82. package/dist/scripts/dip-arb/scan-markets.js +73 -0
  83. package/dist/scripts/dip-arb/scan-markets.js.map +1 -0
  84. package/dist/services/arbitrage-service.d.ts +3 -2
  85. package/dist/services/arbitrage-service.d.ts.map +1 -1
  86. package/dist/services/arbitrage-service.js +71 -43
  87. package/dist/services/arbitrage-service.js.map +1 -1
  88. package/dist/services/binance-service.d.ts +154 -0
  89. package/dist/services/binance-service.d.ts.map +1 -0
  90. package/dist/services/binance-service.js +266 -0
  91. package/dist/services/binance-service.js.map +1 -0
  92. package/dist/services/dip-arb-service.d.ts +209 -0
  93. package/dist/services/dip-arb-service.d.ts.map +1 -0
  94. package/dist/services/dip-arb-service.js +1602 -0
  95. package/dist/services/dip-arb-service.js.map +1 -0
  96. package/dist/services/dip-arb-types.d.ts +553 -0
  97. package/dist/services/dip-arb-types.d.ts.map +1 -0
  98. package/dist/services/dip-arb-types.js +164 -0
  99. package/dist/services/dip-arb-types.js.map +1 -0
  100. package/dist/services/market-service.d.ts +267 -8
  101. package/dist/services/market-service.d.ts.map +1 -1
  102. package/dist/services/market-service.js +771 -42
  103. package/dist/services/market-service.js.map +1 -1
  104. package/dist/services/onchain-service.d.ts +309 -0
  105. package/dist/services/onchain-service.d.ts.map +1 -0
  106. package/dist/services/onchain-service.js +417 -0
  107. package/dist/services/onchain-service.js.map +1 -0
  108. package/dist/services/realtime-service-v2.d.ts +362 -0
  109. package/dist/services/realtime-service-v2.d.ts.map +1 -0
  110. package/dist/services/realtime-service-v2.js +858 -0
  111. package/dist/services/realtime-service-v2.js.map +1 -0
  112. package/dist/services/realtime-service.d.ts +17 -17
  113. package/dist/services/realtime-service.d.ts.map +1 -1
  114. package/dist/services/realtime-service.js +91 -59
  115. package/dist/services/realtime-service.js.map +1 -1
  116. package/dist/services/smart-money-service.d.ts +352 -0
  117. package/dist/services/smart-money-service.d.ts.map +1 -0
  118. package/dist/services/smart-money-service.js +582 -0
  119. package/dist/services/smart-money-service.js.map +1 -0
  120. package/dist/services/trading-service.d.ts +177 -0
  121. package/dist/services/trading-service.d.ts.map +1 -0
  122. package/dist/services/trading-service.js +422 -0
  123. package/dist/services/trading-service.js.map +1 -0
  124. package/dist/services/wallet-service.d.ts +225 -3
  125. package/dist/services/wallet-service.d.ts.map +1 -1
  126. package/dist/services/wallet-service.js +511 -3
  127. package/dist/services/wallet-service.js.map +1 -1
  128. package/dist/src/__tests__/integration/arbitrage-service.integration.test.d.ts +12 -0
  129. package/dist/src/__tests__/integration/arbitrage-service.integration.test.d.ts.map +1 -0
  130. package/dist/src/__tests__/integration/arbitrage-service.integration.test.js +267 -0
  131. package/dist/src/__tests__/integration/arbitrage-service.integration.test.js.map +1 -0
  132. package/dist/src/__tests__/integration/bridge-client.integration.test.d.ts +11 -0
  133. package/dist/src/__tests__/integration/bridge-client.integration.test.d.ts.map +1 -0
  134. package/dist/src/__tests__/integration/bridge-client.integration.test.js +260 -0
  135. package/dist/src/__tests__/integration/bridge-client.integration.test.js.map +1 -0
  136. package/dist/src/__tests__/integration/ctf-client.integration.test.d.ts +17 -0
  137. package/dist/src/__tests__/integration/ctf-client.integration.test.d.ts.map +1 -0
  138. package/dist/src/__tests__/integration/ctf-client.integration.test.js +234 -0
  139. package/dist/src/__tests__/integration/ctf-client.integration.test.js.map +1 -0
  140. package/dist/src/__tests__/integration/data-api.integration.test.d.ts +9 -0
  141. package/dist/src/__tests__/integration/data-api.integration.test.d.ts.map +1 -0
  142. package/dist/src/__tests__/integration/data-api.integration.test.js +164 -0
  143. package/dist/src/__tests__/integration/data-api.integration.test.js.map +1 -0
  144. package/dist/src/__tests__/integration/gamma-api.integration.test.d.ts +9 -0
  145. package/dist/src/__tests__/integration/gamma-api.integration.test.d.ts.map +1 -0
  146. package/dist/src/__tests__/integration/gamma-api.integration.test.js +170 -0
  147. package/dist/src/__tests__/integration/gamma-api.integration.test.js.map +1 -0
  148. package/dist/src/__tests__/integration/market-service.integration.test.d.ts +10 -0
  149. package/dist/src/__tests__/integration/market-service.integration.test.d.ts.map +1 -0
  150. package/dist/src/__tests__/integration/market-service.integration.test.js +180 -0
  151. package/dist/src/__tests__/integration/market-service.integration.test.js.map +1 -0
  152. package/dist/src/__tests__/integration/realtime-service-v2.integration.test.d.ts +10 -0
  153. package/dist/src/__tests__/integration/realtime-service-v2.integration.test.d.ts.map +1 -0
  154. package/dist/src/__tests__/integration/realtime-service-v2.integration.test.js +307 -0
  155. package/dist/src/__tests__/integration/realtime-service-v2.integration.test.js.map +1 -0
  156. package/dist/src/__tests__/integration/trading-service.integration.test.d.ts +10 -0
  157. package/dist/src/__tests__/integration/trading-service.integration.test.d.ts.map +1 -0
  158. package/dist/src/__tests__/integration/trading-service.integration.test.js +58 -0
  159. package/dist/src/__tests__/integration/trading-service.integration.test.js.map +1 -0
  160. package/dist/src/__tests__/test-utils.d.ts +92 -0
  161. package/dist/src/__tests__/test-utils.d.ts.map +1 -0
  162. package/dist/src/__tests__/test-utils.js +143 -0
  163. package/dist/src/__tests__/test-utils.js.map +1 -0
  164. package/dist/src/clients/bridge-client.d.ts +388 -0
  165. package/dist/src/clients/bridge-client.d.ts.map +1 -0
  166. package/dist/src/clients/bridge-client.js +587 -0
  167. package/dist/src/clients/bridge-client.js.map +1 -0
  168. package/dist/src/clients/ctf-client.d.ts +475 -0
  169. package/dist/src/clients/ctf-client.d.ts.map +1 -0
  170. package/dist/src/clients/ctf-client.js +915 -0
  171. package/dist/src/clients/ctf-client.js.map +1 -0
  172. package/dist/src/clients/data-api.d.ts +452 -0
  173. package/dist/src/clients/data-api.d.ts.map +1 -0
  174. package/dist/src/clients/data-api.js +637 -0
  175. package/dist/src/clients/data-api.js.map +1 -0
  176. package/dist/src/clients/gamma-api.d.ts +421 -0
  177. package/dist/src/clients/gamma-api.d.ts.map +1 -0
  178. package/dist/src/clients/gamma-api.js +359 -0
  179. package/dist/src/clients/gamma-api.js.map +1 -0
  180. package/dist/src/clients/subgraph.d.ts +196 -0
  181. package/dist/src/clients/subgraph.d.ts.map +1 -0
  182. package/dist/src/clients/subgraph.js +332 -0
  183. package/dist/src/clients/subgraph.js.map +1 -0
  184. package/dist/src/core/cache-adapter-bridge.d.ts +36 -0
  185. package/dist/src/core/cache-adapter-bridge.d.ts.map +1 -0
  186. package/dist/src/core/cache-adapter-bridge.js +81 -0
  187. package/dist/src/core/cache-adapter-bridge.js.map +1 -0
  188. package/dist/src/core/cache.d.ts +43 -0
  189. package/dist/src/core/cache.d.ts.map +1 -0
  190. package/dist/src/core/cache.js +76 -0
  191. package/dist/src/core/cache.js.map +1 -0
  192. package/dist/src/core/errors.d.ts +39 -0
  193. package/dist/src/core/errors.d.ts.map +1 -0
  194. package/dist/src/core/errors.js +86 -0
  195. package/dist/src/core/errors.js.map +1 -0
  196. package/dist/src/core/rate-limiter.d.ts +33 -0
  197. package/dist/src/core/rate-limiter.d.ts.map +1 -0
  198. package/dist/src/core/rate-limiter.js +82 -0
  199. package/dist/src/core/rate-limiter.js.map +1 -0
  200. package/dist/src/core/types.d.ts +506 -0
  201. package/dist/src/core/types.d.ts.map +1 -0
  202. package/dist/src/core/types.js +49 -0
  203. package/dist/src/core/types.js.map +1 -0
  204. package/dist/src/core/types.test.d.ts +7 -0
  205. package/dist/src/core/types.test.d.ts.map +1 -0
  206. package/dist/src/core/types.test.js +122 -0
  207. package/dist/src/core/types.test.js.map +1 -0
  208. package/dist/src/core/unified-cache.d.ts +63 -0
  209. package/dist/src/core/unified-cache.d.ts.map +1 -0
  210. package/dist/src/core/unified-cache.js +114 -0
  211. package/dist/src/core/unified-cache.js.map +1 -0
  212. package/dist/src/index.d.ts +159 -0
  213. package/dist/src/index.d.ts.map +1 -0
  214. package/dist/src/index.js +262 -0
  215. package/dist/src/index.js.map +1 -0
  216. package/dist/src/services/arbitrage-service.d.ts +409 -0
  217. package/dist/src/services/arbitrage-service.d.ts.map +1 -0
  218. package/dist/src/services/arbitrage-service.js +1450 -0
  219. package/dist/src/services/arbitrage-service.js.map +1 -0
  220. package/dist/src/services/authorization-service.d.ts +97 -0
  221. package/dist/src/services/authorization-service.d.ts.map +1 -0
  222. package/dist/src/services/authorization-service.js +279 -0
  223. package/dist/src/services/authorization-service.js.map +1 -0
  224. package/dist/src/services/binance-service.d.ts +154 -0
  225. package/dist/src/services/binance-service.d.ts.map +1 -0
  226. package/dist/src/services/binance-service.js +266 -0
  227. package/dist/src/services/binance-service.js.map +1 -0
  228. package/dist/src/services/dip-arb-service.d.ts +245 -0
  229. package/dist/src/services/dip-arb-service.d.ts.map +1 -0
  230. package/dist/src/services/dip-arb-service.js +1865 -0
  231. package/dist/src/services/dip-arb-service.js.map +1 -0
  232. package/dist/src/services/dip-arb-types.d.ts +553 -0
  233. package/dist/src/services/dip-arb-types.d.ts.map +1 -0
  234. package/dist/src/services/dip-arb-types.js +164 -0
  235. package/dist/src/services/dip-arb-types.js.map +1 -0
  236. package/dist/src/services/market-service.d.ts +370 -0
  237. package/dist/src/services/market-service.d.ts.map +1 -0
  238. package/dist/src/services/market-service.js +1200 -0
  239. package/dist/src/services/market-service.js.map +1 -0
  240. package/dist/src/services/onchain-service.d.ts +309 -0
  241. package/dist/src/services/onchain-service.d.ts.map +1 -0
  242. package/dist/src/services/onchain-service.js +417 -0
  243. package/dist/src/services/onchain-service.js.map +1 -0
  244. package/dist/src/services/realtime-service-v2.d.ts +367 -0
  245. package/dist/src/services/realtime-service-v2.d.ts.map +1 -0
  246. package/dist/src/services/realtime-service-v2.js +876 -0
  247. package/dist/src/services/realtime-service-v2.js.map +1 -0
  248. package/dist/src/services/smart-money-service.d.ts +352 -0
  249. package/dist/src/services/smart-money-service.d.ts.map +1 -0
  250. package/dist/src/services/smart-money-service.js +582 -0
  251. package/dist/src/services/smart-money-service.js.map +1 -0
  252. package/dist/src/services/swap-service.d.ts +217 -0
  253. package/dist/src/services/swap-service.d.ts.map +1 -0
  254. package/dist/src/services/swap-service.js +695 -0
  255. package/dist/src/services/swap-service.js.map +1 -0
  256. package/dist/src/services/trading-service.d.ts +177 -0
  257. package/dist/src/services/trading-service.d.ts.map +1 -0
  258. package/dist/src/services/trading-service.js +422 -0
  259. package/dist/src/services/trading-service.js.map +1 -0
  260. package/dist/src/services/wallet-service.d.ts +316 -0
  261. package/dist/src/services/wallet-service.d.ts.map +1 -0
  262. package/dist/src/services/wallet-service.js +681 -0
  263. package/dist/src/services/wallet-service.js.map +1 -0
  264. package/dist/src/utils/price-utils.d.ts +153 -0
  265. package/dist/src/utils/price-utils.d.ts.map +1 -0
  266. package/dist/src/utils/price-utils.js +236 -0
  267. package/dist/src/utils/price-utils.js.map +1 -0
  268. package/dist/src/utils/price-utils.test.d.ts +5 -0
  269. package/dist/src/utils/price-utils.test.d.ts.map +1 -0
  270. package/dist/src/utils/price-utils.test.js +192 -0
  271. package/dist/src/utils/price-utils.test.js.map +1 -0
  272. package/dist/utils/price-utils.test.d.ts +5 -0
  273. package/dist/utils/price-utils.test.d.ts.map +1 -0
  274. package/dist/utils/price-utils.test.js +192 -0
  275. package/dist/utils/price-utils.test.js.map +1 -0
  276. package/package.json +6 -5
  277. package/README.en.md +0 -502
@@ -0,0 +1,876 @@
1
+ /**
2
+ * RealtimeService V2
3
+ *
4
+ * Comprehensive real-time data service using official @polymarket/real-time-data-client.
5
+ *
6
+ * Supports ALL available topics:
7
+ * - clob_market: price_change, agg_orderbook, last_trade_price, tick_size_change, market_created, market_resolved
8
+ * - clob_user: order, trade (requires authentication)
9
+ * - activity: trades, orders_matched
10
+ * - crypto_prices: update (BTC, ETH, etc.)
11
+ * - equity_prices: update (AAPL, etc.)
12
+ * - comments: comment_created, comment_removed, reaction_created, reaction_removed
13
+ * - rfq: request_*, quote_*
14
+ */
15
+ import { EventEmitter } from 'events';
16
+ import { RealTimeDataClient, ConnectionStatus, } from '@polymarket/real-time-data-client';
17
+ // ============================================================================
18
+ // RealtimeServiceV2 Implementation
19
+ // ============================================================================
20
+ export class RealtimeServiceV2 extends EventEmitter {
21
+ client = null;
22
+ config;
23
+ subscriptions = new Map();
24
+ subscriptionIdCounter = 0;
25
+ connected = false;
26
+ // Store subscription messages for reconnection
27
+ subscriptionMessages = new Map();
28
+ // Caches
29
+ priceCache = new Map();
30
+ bookCache = new Map();
31
+ lastTradeCache = new Map();
32
+ constructor(config = {}) {
33
+ super();
34
+ this.config = {
35
+ autoReconnect: config.autoReconnect ?? true,
36
+ pingInterval: config.pingInterval ?? 5000,
37
+ debug: config.debug ?? false,
38
+ };
39
+ }
40
+ // ============================================================================
41
+ // Connection Management
42
+ // ============================================================================
43
+ /**
44
+ * Connect to WebSocket server
45
+ */
46
+ connect() {
47
+ if (this.client) {
48
+ this.log('Already connected or connecting');
49
+ return this;
50
+ }
51
+ this.client = new RealTimeDataClient({
52
+ onConnect: this.handleConnect.bind(this),
53
+ onMessage: this.handleMessage.bind(this),
54
+ onStatusChange: this.handleStatusChange.bind(this),
55
+ autoReconnect: this.config.autoReconnect,
56
+ pingInterval: this.config.pingInterval,
57
+ });
58
+ this.client.connect();
59
+ return this;
60
+ }
61
+ /**
62
+ * Disconnect from WebSocket server
63
+ */
64
+ disconnect() {
65
+ if (this.client) {
66
+ this.client.disconnect();
67
+ this.client = null;
68
+ this.connected = false;
69
+ this.subscriptions.clear();
70
+ this.subscriptionMessages.clear(); // Clear reconnection list
71
+ }
72
+ }
73
+ /**
74
+ * Check if connected
75
+ */
76
+ isConnected() {
77
+ return this.connected;
78
+ }
79
+ // ============================================================================
80
+ // Market Data Subscriptions (clob_market)
81
+ // ============================================================================
82
+ /**
83
+ * Subscribe to market data (orderbook, prices, trades)
84
+ * @param tokenIds - Array of token IDs to subscribe to
85
+ * @param handlers - Event handlers
86
+ */
87
+ subscribeMarkets(tokenIds, handlers = {}) {
88
+ const subId = `market_${++this.subscriptionIdCounter}`;
89
+ const filterStr = JSON.stringify(tokenIds);
90
+ // Subscribe to all market data types
91
+ const subscriptions = [
92
+ { topic: 'clob_market', type: 'agg_orderbook', filters: filterStr },
93
+ { topic: 'clob_market', type: 'price_change', filters: filterStr },
94
+ { topic: 'clob_market', type: 'last_trade_price', filters: filterStr },
95
+ { topic: 'clob_market', type: 'tick_size_change', filters: filterStr },
96
+ ];
97
+ const subMsg = { subscriptions };
98
+ this.sendSubscription(subMsg);
99
+ this.subscriptionMessages.set(subId, subMsg); // Store for reconnection
100
+ // Register handlers
101
+ const orderbookHandler = (book) => {
102
+ if (tokenIds.includes(book.assetId)) {
103
+ handlers.onOrderbook?.(book);
104
+ }
105
+ };
106
+ const priceChangeHandler = (change) => {
107
+ if (tokenIds.includes(change.assetId)) {
108
+ handlers.onPriceChange?.(change);
109
+ }
110
+ };
111
+ const lastTradeHandler = (trade) => {
112
+ if (tokenIds.includes(trade.assetId)) {
113
+ handlers.onLastTrade?.(trade);
114
+ }
115
+ };
116
+ const tickSizeHandler = (change) => {
117
+ if (tokenIds.includes(change.assetId)) {
118
+ handlers.onTickSizeChange?.(change);
119
+ }
120
+ };
121
+ this.on('orderbook', orderbookHandler);
122
+ this.on('priceChange', priceChangeHandler);
123
+ this.on('lastTrade', lastTradeHandler);
124
+ this.on('tickSizeChange', tickSizeHandler);
125
+ const subscription = {
126
+ id: subId,
127
+ topic: 'clob_market',
128
+ type: '*',
129
+ tokenIds,
130
+ unsubscribe: () => {
131
+ this.off('orderbook', orderbookHandler);
132
+ this.off('priceChange', priceChangeHandler);
133
+ this.off('lastTrade', lastTradeHandler);
134
+ this.off('tickSizeChange', tickSizeHandler);
135
+ this.sendUnsubscription({ subscriptions });
136
+ this.subscriptions.delete(subId);
137
+ this.subscriptionMessages.delete(subId); // Remove from reconnection list
138
+ },
139
+ };
140
+ this.subscriptions.set(subId, subscription);
141
+ return subscription;
142
+ }
143
+ /**
144
+ * Subscribe to a single market (YES + NO tokens)
145
+ * Also emits derived price updates compatible with old API
146
+ */
147
+ subscribeMarket(yesTokenId, noTokenId, handlers = {}) {
148
+ let lastYesUpdate;
149
+ let lastNoUpdate;
150
+ const checkPairUpdate = () => {
151
+ if (lastYesUpdate && lastNoUpdate && handlers.onPairUpdate) {
152
+ handlers.onPairUpdate({
153
+ yes: lastYesUpdate,
154
+ no: lastNoUpdate,
155
+ spread: lastYesUpdate.price + lastNoUpdate.price,
156
+ });
157
+ }
158
+ };
159
+ return this.subscribeMarkets([yesTokenId, noTokenId], {
160
+ onOrderbook: (book) => {
161
+ handlers.onOrderbook?.(book);
162
+ // Convert to BookUpdate for backward compatibility
163
+ if (handlers.onBookUpdate) {
164
+ const bookUpdate = {
165
+ assetId: book.assetId,
166
+ bids: book.bids,
167
+ asks: book.asks,
168
+ timestamp: book.timestamp,
169
+ };
170
+ handlers.onBookUpdate(bookUpdate);
171
+ }
172
+ // Calculate derived price (Polymarket display logic)
173
+ const priceUpdate = this.calculateDerivedPrice(book.assetId, book);
174
+ if (priceUpdate) {
175
+ this.priceCache.set(book.assetId, priceUpdate);
176
+ if (book.assetId === yesTokenId) {
177
+ lastYesUpdate = priceUpdate;
178
+ }
179
+ else if (book.assetId === noTokenId) {
180
+ lastNoUpdate = priceUpdate;
181
+ }
182
+ handlers.onPriceUpdate?.(priceUpdate);
183
+ this.emit('priceUpdate', priceUpdate);
184
+ checkPairUpdate();
185
+ }
186
+ },
187
+ onLastTrade: (trade) => {
188
+ handlers.onLastTrade?.(trade);
189
+ this.lastTradeCache.set(trade.assetId, trade);
190
+ // Recalculate derived price with new last trade
191
+ const book = this.bookCache.get(trade.assetId);
192
+ if (book) {
193
+ const priceUpdate = this.calculateDerivedPrice(trade.assetId, book);
194
+ if (priceUpdate) {
195
+ this.priceCache.set(trade.assetId, priceUpdate);
196
+ if (trade.assetId === yesTokenId) {
197
+ lastYesUpdate = priceUpdate;
198
+ }
199
+ else if (trade.assetId === noTokenId) {
200
+ lastNoUpdate = priceUpdate;
201
+ }
202
+ handlers.onPriceUpdate?.(priceUpdate);
203
+ this.emit('priceUpdate', priceUpdate);
204
+ checkPairUpdate();
205
+ }
206
+ }
207
+ },
208
+ onPriceChange: handlers.onPriceChange,
209
+ onTickSizeChange: handlers.onTickSizeChange,
210
+ onError: handlers.onError,
211
+ });
212
+ }
213
+ /**
214
+ * Subscribe to market lifecycle events (creation, resolution)
215
+ */
216
+ subscribeMarketEvents(handlers) {
217
+ const subId = `market_event_${++this.subscriptionIdCounter}`;
218
+ const subscriptions = [
219
+ { topic: 'clob_market', type: 'market_created' },
220
+ { topic: 'clob_market', type: 'market_resolved' },
221
+ ];
222
+ this.sendSubscription({ subscriptions });
223
+ const handler = (event) => handlers.onMarketEvent?.(event);
224
+ this.on('marketEvent', handler);
225
+ const subscription = {
226
+ id: subId,
227
+ topic: 'clob_market',
228
+ type: 'lifecycle',
229
+ unsubscribe: () => {
230
+ this.off('marketEvent', handler);
231
+ this.sendUnsubscription({ subscriptions });
232
+ this.subscriptions.delete(subId);
233
+ },
234
+ };
235
+ this.subscriptions.set(subId, subscription);
236
+ return subscription;
237
+ }
238
+ // ============================================================================
239
+ // User Data Subscriptions (clob_user) - Requires Authentication
240
+ // ============================================================================
241
+ /**
242
+ * Subscribe to user order and trade events
243
+ * @param credentials - CLOB API credentials
244
+ * @param handlers - Event handlers
245
+ */
246
+ subscribeUserEvents(credentials, handlers = {}) {
247
+ const subId = `user_${++this.subscriptionIdCounter}`;
248
+ const subscriptions = [
249
+ { topic: 'clob_user', type: '*', clob_auth: credentials },
250
+ ];
251
+ this.sendSubscription({ subscriptions });
252
+ const orderHandler = (order) => handlers.onOrder?.(order);
253
+ const tradeHandler = (trade) => handlers.onTrade?.(trade);
254
+ this.on('userOrder', orderHandler);
255
+ this.on('userTrade', tradeHandler);
256
+ const subscription = {
257
+ id: subId,
258
+ topic: 'clob_user',
259
+ type: '*',
260
+ unsubscribe: () => {
261
+ this.off('userOrder', orderHandler);
262
+ this.off('userTrade', tradeHandler);
263
+ this.sendUnsubscription({ subscriptions });
264
+ this.subscriptions.delete(subId);
265
+ },
266
+ };
267
+ this.subscriptions.set(subId, subscription);
268
+ return subscription;
269
+ }
270
+ // ============================================================================
271
+ // Activity Subscriptions (trades, orders_matched)
272
+ // ============================================================================
273
+ /**
274
+ * Subscribe to trading activity for a market or event
275
+ * @param filter - Event or market slug (optional - if empty, subscribes to all activity)
276
+ * @param handlers - Event handlers
277
+ */
278
+ subscribeActivity(filter = {}, handlers = {}) {
279
+ const subId = `activity_${++this.subscriptionIdCounter}`;
280
+ // Build filter object with snake_case keys (as expected by the server)
281
+ // Only include filters if we have actual filter values
282
+ const hasFilter = filter.eventSlug || filter.marketSlug;
283
+ const filterObj = {};
284
+ if (filter.eventSlug)
285
+ filterObj.event_slug = filter.eventSlug;
286
+ if (filter.marketSlug)
287
+ filterObj.market_slug = filter.marketSlug;
288
+ // Create subscription objects - only include filters field if we have filters
289
+ const subscriptions = hasFilter
290
+ ? [
291
+ { topic: 'activity', type: 'trades', filters: JSON.stringify(filterObj) },
292
+ { topic: 'activity', type: 'orders_matched', filters: JSON.stringify(filterObj) },
293
+ ]
294
+ : [
295
+ { topic: 'activity', type: 'trades' },
296
+ { topic: 'activity', type: 'orders_matched' },
297
+ ];
298
+ this.sendSubscription({ subscriptions });
299
+ const handler = (trade) => handlers.onTrade?.(trade);
300
+ this.on('activityTrade', handler);
301
+ const subscription = {
302
+ id: subId,
303
+ topic: 'activity',
304
+ type: '*',
305
+ unsubscribe: () => {
306
+ this.off('activityTrade', handler);
307
+ this.sendUnsubscription({ subscriptions });
308
+ this.subscriptions.delete(subId);
309
+ },
310
+ };
311
+ this.subscriptions.set(subId, subscription);
312
+ return subscription;
313
+ }
314
+ /**
315
+ * Subscribe to ALL trading activity across all markets (no filtering)
316
+ * This is useful for Copy Trading - monitoring Smart Money across the platform
317
+ * @param handlers - Event handlers
318
+ */
319
+ subscribeAllActivity(handlers = {}) {
320
+ return this.subscribeActivity({}, handlers);
321
+ }
322
+ // ============================================================================
323
+ // Crypto Price Subscriptions
324
+ // ============================================================================
325
+ /**
326
+ * Subscribe to crypto price updates
327
+ * @param symbols - Array of symbols (e.g., ['BTCUSDT', 'ETHUSDT'])
328
+ * @param handlers - Event handlers
329
+ */
330
+ subscribeCryptoPrices(symbols, handlers = {}) {
331
+ const subId = `crypto_${++this.subscriptionIdCounter}`;
332
+ // Subscribe to each symbol
333
+ const subscriptions = symbols.map(symbol => ({
334
+ topic: 'crypto_prices',
335
+ type: 'update',
336
+ filters: JSON.stringify({ symbol }),
337
+ }));
338
+ this.sendSubscription({ subscriptions });
339
+ const handler = (price) => {
340
+ if (symbols.includes(price.symbol)) {
341
+ handlers.onPrice?.(price);
342
+ }
343
+ };
344
+ this.on('cryptoPrice', handler);
345
+ const subscription = {
346
+ id: subId,
347
+ topic: 'crypto_prices',
348
+ type: 'update',
349
+ unsubscribe: () => {
350
+ this.off('cryptoPrice', handler);
351
+ this.sendUnsubscription({ subscriptions });
352
+ this.subscriptions.delete(subId);
353
+ },
354
+ };
355
+ this.subscriptions.set(subId, subscription);
356
+ return subscription;
357
+ }
358
+ /**
359
+ * Subscribe to Chainlink crypto prices
360
+ * @param symbols - Array of symbols (e.g., ['ETH/USD', 'BTC/USD'])
361
+ */
362
+ subscribeCryptoChainlinkPrices(symbols, handlers = {}) {
363
+ const subId = `crypto_chainlink_${++this.subscriptionIdCounter}`;
364
+ const subscriptions = symbols.map(symbol => ({
365
+ topic: 'crypto_prices_chainlink',
366
+ type: 'update',
367
+ filters: JSON.stringify({ symbol }),
368
+ }));
369
+ const subMsg = { subscriptions };
370
+ this.sendSubscription(subMsg);
371
+ this.subscriptionMessages.set(subId, subMsg); // Store for reconnection
372
+ const handler = (price) => {
373
+ if (symbols.includes(price.symbol)) {
374
+ handlers.onPrice?.(price);
375
+ }
376
+ };
377
+ this.on('cryptoChainlinkPrice', handler);
378
+ const subscription = {
379
+ id: subId,
380
+ topic: 'crypto_prices_chainlink',
381
+ type: 'update',
382
+ unsubscribe: () => {
383
+ this.off('cryptoChainlinkPrice', handler);
384
+ this.sendUnsubscription({ subscriptions });
385
+ this.subscriptions.delete(subId);
386
+ this.subscriptionMessages.delete(subId); // Remove from reconnection list
387
+ },
388
+ };
389
+ this.subscriptions.set(subId, subscription);
390
+ return subscription;
391
+ }
392
+ // ============================================================================
393
+ // Equity Price Subscriptions
394
+ // ============================================================================
395
+ /**
396
+ * Subscribe to equity price updates
397
+ * @param symbols - Array of symbols (e.g., ['AAPL', 'GOOGL'])
398
+ * @param handlers - Event handlers
399
+ */
400
+ subscribeEquityPrices(symbols, handlers = {}) {
401
+ const subId = `equity_${++this.subscriptionIdCounter}`;
402
+ const subscriptions = symbols.map(symbol => ({
403
+ topic: 'equity_prices',
404
+ type: 'update',
405
+ filters: JSON.stringify({ symbol }),
406
+ }));
407
+ this.sendSubscription({ subscriptions });
408
+ const handler = (price) => {
409
+ if (symbols.includes(price.symbol)) {
410
+ handlers.onPrice?.(price);
411
+ }
412
+ };
413
+ this.on('equityPrice', handler);
414
+ const subscription = {
415
+ id: subId,
416
+ topic: 'equity_prices',
417
+ type: 'update',
418
+ unsubscribe: () => {
419
+ this.off('equityPrice', handler);
420
+ this.sendUnsubscription({ subscriptions });
421
+ this.subscriptions.delete(subId);
422
+ },
423
+ };
424
+ this.subscriptions.set(subId, subscription);
425
+ return subscription;
426
+ }
427
+ // ============================================================================
428
+ // Comments Subscriptions
429
+ // ============================================================================
430
+ /**
431
+ * Subscribe to comment and reaction events
432
+ */
433
+ subscribeComments(filter, handlers = {}) {
434
+ const subId = `comments_${++this.subscriptionIdCounter}`;
435
+ const filterStr = JSON.stringify({
436
+ parentEntityID: filter.parentEntityId,
437
+ parentEntityType: filter.parentEntityType,
438
+ });
439
+ const subscriptions = [
440
+ { topic: 'comments', type: 'comment_created', filters: filterStr },
441
+ { topic: 'comments', type: 'comment_removed', filters: filterStr },
442
+ { topic: 'comments', type: 'reaction_created', filters: filterStr },
443
+ { topic: 'comments', type: 'reaction_removed', filters: filterStr },
444
+ ];
445
+ this.sendSubscription({ subscriptions });
446
+ const commentHandler = (comment) => handlers.onComment?.(comment);
447
+ const reactionHandler = (reaction) => handlers.onReaction?.(reaction);
448
+ this.on('comment', commentHandler);
449
+ this.on('reaction', reactionHandler);
450
+ const subscription = {
451
+ id: subId,
452
+ topic: 'comments',
453
+ type: '*',
454
+ unsubscribe: () => {
455
+ this.off('comment', commentHandler);
456
+ this.off('reaction', reactionHandler);
457
+ this.sendUnsubscription({ subscriptions });
458
+ this.subscriptions.delete(subId);
459
+ },
460
+ };
461
+ this.subscriptions.set(subId, subscription);
462
+ return subscription;
463
+ }
464
+ // ============================================================================
465
+ // RFQ Subscriptions
466
+ // ============================================================================
467
+ /**
468
+ * Subscribe to RFQ (Request for Quote) events
469
+ */
470
+ subscribeRFQ(handlers = {}) {
471
+ const subId = `rfq_${++this.subscriptionIdCounter}`;
472
+ const subscriptions = [
473
+ { topic: 'rfq', type: 'request_created' },
474
+ { topic: 'rfq', type: 'request_edited' },
475
+ { topic: 'rfq', type: 'request_canceled' },
476
+ { topic: 'rfq', type: 'request_expired' },
477
+ { topic: 'rfq', type: 'quote_created' },
478
+ { topic: 'rfq', type: 'quote_edited' },
479
+ { topic: 'rfq', type: 'quote_canceled' },
480
+ { topic: 'rfq', type: 'quote_expired' },
481
+ ];
482
+ this.sendSubscription({ subscriptions });
483
+ const requestHandler = (request) => handlers.onRequest?.(request);
484
+ const quoteHandler = (quote) => handlers.onQuote?.(quote);
485
+ this.on('rfqRequest', requestHandler);
486
+ this.on('rfqQuote', quoteHandler);
487
+ const subscription = {
488
+ id: subId,
489
+ topic: 'rfq',
490
+ type: '*',
491
+ unsubscribe: () => {
492
+ this.off('rfqRequest', requestHandler);
493
+ this.off('rfqQuote', quoteHandler);
494
+ this.sendUnsubscription({ subscriptions });
495
+ this.subscriptions.delete(subId);
496
+ },
497
+ };
498
+ this.subscriptions.set(subId, subscription);
499
+ return subscription;
500
+ }
501
+ // ============================================================================
502
+ // Cache Access
503
+ // ============================================================================
504
+ /**
505
+ * Get cached derived price for an asset
506
+ */
507
+ getPrice(assetId) {
508
+ return this.priceCache.get(assetId);
509
+ }
510
+ /**
511
+ * Get all cached prices
512
+ */
513
+ getAllPrices() {
514
+ return new Map(this.priceCache);
515
+ }
516
+ /**
517
+ * Get cached orderbook for an asset
518
+ */
519
+ getBook(assetId) {
520
+ return this.bookCache.get(assetId);
521
+ }
522
+ /**
523
+ * Get cached last trade for an asset
524
+ */
525
+ getLastTrade(assetId) {
526
+ return this.lastTradeCache.get(assetId);
527
+ }
528
+ // ============================================================================
529
+ // Subscription Management
530
+ // ============================================================================
531
+ /**
532
+ * Get all active subscriptions
533
+ */
534
+ getActiveSubscriptions() {
535
+ return Array.from(this.subscriptions.values());
536
+ }
537
+ /**
538
+ * Unsubscribe from all
539
+ */
540
+ unsubscribeAll() {
541
+ for (const sub of this.subscriptions.values()) {
542
+ sub.unsubscribe();
543
+ }
544
+ this.subscriptions.clear();
545
+ this.subscriptionMessages.clear(); // Clear reconnection list
546
+ }
547
+ // ============================================================================
548
+ // Private Methods
549
+ // ============================================================================
550
+ handleConnect(client) {
551
+ this.connected = true;
552
+ this.log('Connected to WebSocket server');
553
+ // Re-subscribe to all active subscriptions on reconnect
554
+ if (this.subscriptionMessages.size > 0) {
555
+ this.log(`Re-subscribing to ${this.subscriptionMessages.size} subscriptions...`);
556
+ for (const [subId, msg] of this.subscriptionMessages) {
557
+ this.log(`Re-subscribing: ${subId}`);
558
+ this.client?.subscribe(msg);
559
+ }
560
+ }
561
+ this.emit('connected');
562
+ }
563
+ handleStatusChange(status) {
564
+ this.log(`Connection status: ${status}`);
565
+ if (status === ConnectionStatus.DISCONNECTED) {
566
+ this.connected = false;
567
+ this.emit('disconnected');
568
+ }
569
+ else if (status === ConnectionStatus.CONNECTED) {
570
+ this.connected = true;
571
+ }
572
+ this.emit('statusChange', status);
573
+ }
574
+ handleMessage(client, message) {
575
+ this.log(`Received: ${message.topic}:${message.type}`);
576
+ const payload = message.payload;
577
+ switch (message.topic) {
578
+ case 'clob_market':
579
+ this.handleMarketMessage(message.type, payload, message.timestamp);
580
+ break;
581
+ case 'clob_user':
582
+ this.handleUserMessage(message.type, payload, message.timestamp);
583
+ break;
584
+ case 'activity':
585
+ this.handleActivityMessage(message.type, payload, message.timestamp);
586
+ break;
587
+ case 'crypto_prices':
588
+ this.handleCryptoPriceMessage(payload, message.timestamp);
589
+ break;
590
+ case 'crypto_prices_chainlink':
591
+ this.handleCryptoChainlinkPriceMessage(payload, message.timestamp);
592
+ break;
593
+ case 'equity_prices':
594
+ this.handleEquityPriceMessage(payload, message.timestamp);
595
+ break;
596
+ case 'comments':
597
+ this.handleCommentMessage(message.type, payload, message.timestamp);
598
+ break;
599
+ case 'rfq':
600
+ this.handleRFQMessage(message.type, payload, message.timestamp);
601
+ break;
602
+ default:
603
+ this.log(`Unknown topic: ${message.topic}`);
604
+ }
605
+ }
606
+ handleMarketMessage(type, payload, timestamp) {
607
+ switch (type) {
608
+ case 'agg_orderbook': {
609
+ const book = this.parseOrderbook(payload, timestamp);
610
+ this.bookCache.set(book.assetId, book);
611
+ this.emit('orderbook', book);
612
+ break;
613
+ }
614
+ case 'price_change': {
615
+ const change = this.parsePriceChange(payload, timestamp);
616
+ this.emit('priceChange', change);
617
+ break;
618
+ }
619
+ case 'last_trade_price': {
620
+ const trade = this.parseLastTrade(payload, timestamp);
621
+ this.lastTradeCache.set(trade.assetId, trade);
622
+ this.emit('lastTrade', trade);
623
+ break;
624
+ }
625
+ case 'tick_size_change': {
626
+ const change = this.parseTickSizeChange(payload, timestamp);
627
+ this.emit('tickSizeChange', change);
628
+ break;
629
+ }
630
+ case 'market_created':
631
+ case 'market_resolved': {
632
+ const event = {
633
+ conditionId: payload.condition_id || '',
634
+ type: type === 'market_created' ? 'created' : 'resolved',
635
+ data: payload,
636
+ timestamp,
637
+ };
638
+ this.emit('marketEvent', event);
639
+ break;
640
+ }
641
+ }
642
+ }
643
+ handleUserMessage(type, payload, timestamp) {
644
+ if (type === 'order') {
645
+ const order = {
646
+ orderId: payload.order_id || '',
647
+ market: payload.market || '',
648
+ asset: payload.asset || '',
649
+ side: payload.side,
650
+ price: Number(payload.price) || 0,
651
+ originalSize: Number(payload.original_size) || 0,
652
+ matchedSize: Number(payload.matched_size) || 0,
653
+ eventType: payload.event_type,
654
+ timestamp,
655
+ };
656
+ this.emit('userOrder', order);
657
+ }
658
+ else if (type === 'trade') {
659
+ const trade = {
660
+ tradeId: payload.trade_id || '',
661
+ market: payload.market || '',
662
+ outcome: payload.outcome || '',
663
+ price: Number(payload.price) || 0,
664
+ size: Number(payload.size) || 0,
665
+ side: payload.side,
666
+ status: payload.status,
667
+ timestamp,
668
+ transactionHash: payload.transaction_hash,
669
+ };
670
+ this.emit('userTrade', trade);
671
+ }
672
+ }
673
+ handleActivityMessage(type, payload, timestamp) {
674
+ const trade = {
675
+ asset: payload.asset || '',
676
+ conditionId: payload.conditionId || '',
677
+ eventSlug: payload.eventSlug || '',
678
+ marketSlug: payload.slug || '',
679
+ outcome: payload.outcome || '',
680
+ price: Number(payload.price) || 0,
681
+ side: payload.side,
682
+ size: Number(payload.size) || 0,
683
+ timestamp: this.normalizeTimestamp(payload.timestamp) || timestamp,
684
+ transactionHash: payload.transactionHash || '',
685
+ trader: {
686
+ name: payload.name,
687
+ address: payload.proxyWallet,
688
+ },
689
+ };
690
+ this.emit('activityTrade', trade);
691
+ }
692
+ handleCryptoPriceMessage(payload, timestamp) {
693
+ const price = {
694
+ symbol: payload.symbol || '',
695
+ price: Number(payload.value) || 0,
696
+ timestamp: this.normalizeTimestamp(payload.timestamp) || timestamp,
697
+ };
698
+ this.emit('cryptoPrice', price);
699
+ }
700
+ handleCryptoChainlinkPriceMessage(payload, timestamp) {
701
+ const price = {
702
+ symbol: payload.symbol || '',
703
+ price: Number(payload.value) || 0,
704
+ timestamp: this.normalizeTimestamp(payload.timestamp) || timestamp,
705
+ };
706
+ this.emit('cryptoChainlinkPrice', price);
707
+ }
708
+ handleEquityPriceMessage(payload, timestamp) {
709
+ const price = {
710
+ symbol: payload.symbol || '',
711
+ price: Number(payload.value) || 0,
712
+ timestamp: this.normalizeTimestamp(payload.timestamp) || timestamp,
713
+ };
714
+ this.emit('equityPrice', price);
715
+ }
716
+ handleCommentMessage(type, payload, timestamp) {
717
+ if (type.includes('comment')) {
718
+ const comment = {
719
+ id: payload.id || '',
720
+ parentEntityId: payload.parentEntityID || 0,
721
+ parentEntityType: payload.parentEntityType,
722
+ content: payload.content,
723
+ author: payload.author,
724
+ timestamp,
725
+ };
726
+ this.emit('comment', comment);
727
+ }
728
+ else if (type.includes('reaction')) {
729
+ const reaction = {
730
+ id: payload.id || '',
731
+ commentId: payload.commentId || '',
732
+ type: payload.type || '',
733
+ author: payload.author,
734
+ timestamp,
735
+ };
736
+ this.emit('reaction', reaction);
737
+ }
738
+ }
739
+ handleRFQMessage(type, payload, timestamp) {
740
+ if (type.startsWith('request_')) {
741
+ const status = type.replace('request_', '');
742
+ const request = {
743
+ id: payload.id || '',
744
+ market: payload.market || '',
745
+ side: payload.side,
746
+ size: Number(payload.size) || 0,
747
+ status,
748
+ timestamp,
749
+ };
750
+ this.emit('rfqRequest', request);
751
+ }
752
+ else if (type.startsWith('quote_')) {
753
+ const status = type.replace('quote_', '');
754
+ const quote = {
755
+ id: payload.id || '',
756
+ requestId: payload.request_id || '',
757
+ price: Number(payload.price) || 0,
758
+ size: Number(payload.size) || 0,
759
+ status,
760
+ timestamp,
761
+ };
762
+ this.emit('rfqQuote', quote);
763
+ }
764
+ }
765
+ // Parsers
766
+ parseOrderbook(payload, timestamp) {
767
+ const bidsRaw = payload.bids || [];
768
+ const asksRaw = payload.asks || [];
769
+ // Sort bids descending, asks ascending
770
+ const bids = bidsRaw
771
+ .map(l => ({ price: parseFloat(l.price), size: parseFloat(l.size) }))
772
+ .sort((a, b) => b.price - a.price);
773
+ const asks = asksRaw
774
+ .map(l => ({ price: parseFloat(l.price), size: parseFloat(l.size) }))
775
+ .sort((a, b) => a.price - b.price);
776
+ const tokenId = payload.asset_id || '';
777
+ return {
778
+ tokenId,
779
+ assetId: tokenId, // Backward compatibility
780
+ market: payload.market || '',
781
+ bids,
782
+ asks,
783
+ timestamp: this.normalizeTimestamp(payload.timestamp) || timestamp,
784
+ tickSize: payload.tick_size || '0.01',
785
+ minOrderSize: payload.min_order_size || '1',
786
+ hash: payload.hash || '',
787
+ };
788
+ }
789
+ parsePriceChange(payload, timestamp) {
790
+ const changes = payload.price_changes || [];
791
+ return {
792
+ assetId: payload.asset_id || '',
793
+ changes,
794
+ timestamp,
795
+ };
796
+ }
797
+ parseLastTrade(payload, timestamp) {
798
+ return {
799
+ assetId: payload.asset_id || '',
800
+ price: parseFloat(payload.price) || 0,
801
+ side: payload.side || 'BUY',
802
+ size: parseFloat(payload.size) || 0,
803
+ timestamp: this.normalizeTimestamp(payload.timestamp) || timestamp,
804
+ };
805
+ }
806
+ parseTickSizeChange(payload, timestamp) {
807
+ return {
808
+ assetId: payload.asset_id || '',
809
+ oldTickSize: payload.old_tick_size || '',
810
+ newTickSize: payload.new_tick_size || '',
811
+ timestamp,
812
+ };
813
+ }
814
+ /**
815
+ * Calculate derived price using Polymarket's display logic:
816
+ * - If spread <= 0.10: use midpoint
817
+ * - If spread > 0.10: use last trade price
818
+ */
819
+ calculateDerivedPrice(assetId, book) {
820
+ if (book.bids.length === 0 || book.asks.length === 0) {
821
+ return null;
822
+ }
823
+ const bestBid = book.bids[0].price;
824
+ const bestAsk = book.asks[0].price;
825
+ const spread = bestAsk - bestBid;
826
+ const midpoint = (bestBid + bestAsk) / 2;
827
+ const lastTrade = this.lastTradeCache.get(assetId);
828
+ const lastTradePrice = lastTrade?.price ?? midpoint;
829
+ // Polymarket display logic
830
+ const displayPrice = spread <= 0.10 ? midpoint : lastTradePrice;
831
+ return {
832
+ assetId,
833
+ price: displayPrice,
834
+ midpoint,
835
+ spread,
836
+ timestamp: book.timestamp,
837
+ };
838
+ }
839
+ sendSubscription(msg) {
840
+ if (this.client && this.connected) {
841
+ this.client.subscribe(msg);
842
+ }
843
+ else {
844
+ this.log('Cannot subscribe: not connected');
845
+ }
846
+ }
847
+ sendUnsubscription(msg) {
848
+ if (this.client && this.connected) {
849
+ this.client.unsubscribe(msg);
850
+ }
851
+ }
852
+ log(message) {
853
+ if (this.config.debug) {
854
+ console.log(`[RealtimeService] ${message}`);
855
+ }
856
+ }
857
+ /**
858
+ * Normalize timestamp to milliseconds
859
+ * Polymarket WebSocket returns timestamps in seconds, need to convert to milliseconds
860
+ */
861
+ normalizeTimestamp(ts) {
862
+ if (typeof ts === 'string') {
863
+ const parsed = parseInt(ts, 10);
864
+ if (isNaN(parsed))
865
+ return Date.now();
866
+ // If timestamp is in seconds (< 1e12), convert to milliseconds
867
+ return parsed < 1e12 ? parsed * 1000 : parsed;
868
+ }
869
+ if (typeof ts === 'number') {
870
+ // If timestamp is in seconds (< 1e12), convert to milliseconds
871
+ return ts < 1e12 ? ts * 1000 : ts;
872
+ }
873
+ return Date.now();
874
+ }
875
+ }
876
+ //# sourceMappingURL=realtime-service-v2.js.map