@catalyst-team/poly-sdk 0.2.0 → 0.3.0

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