@catalyst-team/poly-sdk 0.1.0 β†’ 0.2.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 (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +538 -0
  3. package/README.md +357 -78
  4. package/docs/00-design.md +1 -1
  5. package/docs/02-API.md +21 -21
  6. package/docs/arb/test-plan.md +387 -0
  7. package/docs/arb/test-results.md +336 -0
  8. package/docs/arbitrage.md +754 -0
  9. package/docs/reports/smart-money-analysis-2025-12-23-cn.md +840 -0
  10. package/examples/13-arbitrage-service.ts +211 -0
  11. package/examples/README.md +1 -1
  12. package/package.json +19 -19
  13. package/scripts/arb/faze-bo3-arb.ts +385 -0
  14. package/scripts/arb/settle-position.ts +190 -0
  15. package/scripts/arb/token-rebalancer.ts +420 -0
  16. package/scripts/arb-tests/01-unit-tests.ts +495 -0
  17. package/scripts/arb-tests/02-integration-tests.ts +412 -0
  18. package/scripts/arb-tests/03-e2e-tests.ts +503 -0
  19. package/scripts/arb-tests/README.md +109 -0
  20. package/scripts/verify/verify-all-apis.ts +1 -1
  21. package/src/clients/clob-api.ts +1 -1
  22. package/src/clients/gamma-api.ts +1 -1
  23. package/src/core/cache-adapter-bridge.ts +1 -1
  24. package/src/core/types.ts +3 -3
  25. package/src/core/unified-cache.ts +1 -1
  26. package/src/index.ts +25 -19
  27. package/src/services/arbitrage-service.ts +1807 -0
  28. package/.env +0 -0
  29. package/dist/__tests__/clob-api.test.d.ts +0 -5
  30. package/dist/__tests__/clob-api.test.d.ts.map +0 -1
  31. package/dist/__tests__/clob-api.test.js +0 -240
  32. package/dist/__tests__/clob-api.test.js.map +0 -1
  33. package/dist/__tests__/integration/bridge-client.integration.test.d.ts +0 -11
  34. package/dist/__tests__/integration/bridge-client.integration.test.d.ts.map +0 -1
  35. package/dist/__tests__/integration/bridge-client.integration.test.js +0 -260
  36. package/dist/__tests__/integration/bridge-client.integration.test.js.map +0 -1
  37. package/dist/__tests__/integration/clob-api.integration.test.d.ts +0 -13
  38. package/dist/__tests__/integration/clob-api.integration.test.d.ts.map +0 -1
  39. package/dist/__tests__/integration/clob-api.integration.test.js +0 -170
  40. package/dist/__tests__/integration/clob-api.integration.test.js.map +0 -1
  41. package/dist/__tests__/integration/ctf-client.integration.test.d.ts +0 -17
  42. package/dist/__tests__/integration/ctf-client.integration.test.d.ts.map +0 -1
  43. package/dist/__tests__/integration/ctf-client.integration.test.js +0 -234
  44. package/dist/__tests__/integration/ctf-client.integration.test.js.map +0 -1
  45. package/dist/__tests__/integration/data-api.integration.test.d.ts +0 -9
  46. package/dist/__tests__/integration/data-api.integration.test.d.ts.map +0 -1
  47. package/dist/__tests__/integration/data-api.integration.test.js +0 -161
  48. package/dist/__tests__/integration/data-api.integration.test.js.map +0 -1
  49. package/dist/__tests__/integration/gamma-api.integration.test.d.ts +0 -9
  50. package/dist/__tests__/integration/gamma-api.integration.test.d.ts.map +0 -1
  51. package/dist/__tests__/integration/gamma-api.integration.test.js +0 -170
  52. package/dist/__tests__/integration/gamma-api.integration.test.js.map +0 -1
  53. package/dist/__tests__/test-utils.d.ts +0 -92
  54. package/dist/__tests__/test-utils.d.ts.map +0 -1
  55. package/dist/__tests__/test-utils.js +0 -143
  56. package/dist/__tests__/test-utils.js.map +0 -1
  57. package/dist/clients/bridge-client.d.ts +0 -388
  58. package/dist/clients/bridge-client.d.ts.map +0 -1
  59. package/dist/clients/bridge-client.js +0 -587
  60. package/dist/clients/bridge-client.js.map +0 -1
  61. package/dist/clients/clob-api.d.ts +0 -318
  62. package/dist/clients/clob-api.d.ts.map +0 -1
  63. package/dist/clients/clob-api.js +0 -388
  64. package/dist/clients/clob-api.js.map +0 -1
  65. package/dist/clients/ctf-client.d.ts +0 -473
  66. package/dist/clients/ctf-client.d.ts.map +0 -1
  67. package/dist/clients/ctf-client.js +0 -915
  68. package/dist/clients/ctf-client.js.map +0 -1
  69. package/dist/clients/data-api.d.ts +0 -134
  70. package/dist/clients/data-api.d.ts.map +0 -1
  71. package/dist/clients/data-api.js +0 -265
  72. package/dist/clients/data-api.js.map +0 -1
  73. package/dist/clients/gamma-api.d.ts +0 -401
  74. package/dist/clients/gamma-api.d.ts.map +0 -1
  75. package/dist/clients/gamma-api.js +0 -352
  76. package/dist/clients/gamma-api.js.map +0 -1
  77. package/dist/clients/trading-client.d.ts +0 -252
  78. package/dist/clients/trading-client.d.ts.map +0 -1
  79. package/dist/clients/trading-client.js +0 -543
  80. package/dist/clients/trading-client.js.map +0 -1
  81. package/dist/clients/websocket-manager.d.ts +0 -100
  82. package/dist/clients/websocket-manager.d.ts.map +0 -1
  83. package/dist/clients/websocket-manager.js +0 -193
  84. package/dist/clients/websocket-manager.js.map +0 -1
  85. package/dist/core/cache-adapter-bridge.d.ts +0 -36
  86. package/dist/core/cache-adapter-bridge.d.ts.map +0 -1
  87. package/dist/core/cache-adapter-bridge.js +0 -81
  88. package/dist/core/cache-adapter-bridge.js.map +0 -1
  89. package/dist/core/cache.d.ts +0 -40
  90. package/dist/core/cache.d.ts.map +0 -1
  91. package/dist/core/cache.js +0 -71
  92. package/dist/core/cache.js.map +0 -1
  93. package/dist/core/errors.d.ts +0 -38
  94. package/dist/core/errors.d.ts.map +0 -1
  95. package/dist/core/errors.js +0 -84
  96. package/dist/core/errors.js.map +0 -1
  97. package/dist/core/rate-limiter.d.ts +0 -31
  98. package/dist/core/rate-limiter.d.ts.map +0 -1
  99. package/dist/core/rate-limiter.js +0 -70
  100. package/dist/core/rate-limiter.js.map +0 -1
  101. package/dist/core/types.d.ts +0 -314
  102. package/dist/core/types.d.ts.map +0 -1
  103. package/dist/core/types.js +0 -19
  104. package/dist/core/types.js.map +0 -1
  105. package/dist/core/unified-cache.d.ts +0 -63
  106. package/dist/core/unified-cache.d.ts.map +0 -1
  107. package/dist/core/unified-cache.js +0 -114
  108. package/dist/core/unified-cache.js.map +0 -1
  109. package/dist/index.d.ts +0 -94
  110. package/dist/index.d.ts.map +0 -1
  111. package/dist/index.js +0 -258
  112. package/dist/index.js.map +0 -1
  113. package/dist/mcp/errors.d.ts +0 -33
  114. package/dist/mcp/errors.d.ts.map +0 -1
  115. package/dist/mcp/errors.js +0 -86
  116. package/dist/mcp/errors.js.map +0 -1
  117. package/dist/mcp/index.d.ts +0 -62
  118. package/dist/mcp/index.d.ts.map +0 -1
  119. package/dist/mcp/index.js +0 -173
  120. package/dist/mcp/index.js.map +0 -1
  121. package/dist/mcp/server.d.ts +0 -17
  122. package/dist/mcp/server.d.ts.map +0 -1
  123. package/dist/mcp/server.js +0 -155
  124. package/dist/mcp/server.js.map +0 -1
  125. package/dist/mcp/tools/guide.d.ts +0 -12
  126. package/dist/mcp/tools/guide.d.ts.map +0 -1
  127. package/dist/mcp/tools/guide.js +0 -801
  128. package/dist/mcp/tools/guide.js.map +0 -1
  129. package/dist/mcp/tools/index.d.ts +0 -11
  130. package/dist/mcp/tools/index.d.ts.map +0 -1
  131. package/dist/mcp/tools/index.js +0 -27
  132. package/dist/mcp/tools/index.js.map +0 -1
  133. package/dist/mcp/tools/market.d.ts +0 -11
  134. package/dist/mcp/tools/market.d.ts.map +0 -1
  135. package/dist/mcp/tools/market.js +0 -314
  136. package/dist/mcp/tools/market.js.map +0 -1
  137. package/dist/mcp/tools/order.d.ts +0 -10
  138. package/dist/mcp/tools/order.d.ts.map +0 -1
  139. package/dist/mcp/tools/order.js +0 -258
  140. package/dist/mcp/tools/order.js.map +0 -1
  141. package/dist/mcp/tools/trade.d.ts +0 -38
  142. package/dist/mcp/tools/trade.d.ts.map +0 -1
  143. package/dist/mcp/tools/trade.js +0 -314
  144. package/dist/mcp/tools/trade.js.map +0 -1
  145. package/dist/mcp/tools/trader.d.ts +0 -11
  146. package/dist/mcp/tools/trader.d.ts.map +0 -1
  147. package/dist/mcp/tools/trader.js +0 -277
  148. package/dist/mcp/tools/trader.js.map +0 -1
  149. package/dist/mcp/tools/wallet.d.ts +0 -274
  150. package/dist/mcp/tools/wallet.d.ts.map +0 -1
  151. package/dist/mcp/tools/wallet.js +0 -579
  152. package/dist/mcp/tools/wallet.js.map +0 -1
  153. package/dist/mcp/types.d.ts +0 -413
  154. package/dist/mcp/types.d.ts.map +0 -1
  155. package/dist/mcp/types.js +0 -5
  156. package/dist/mcp/types.js.map +0 -1
  157. package/dist/services/authorization-service.d.ts +0 -97
  158. package/dist/services/authorization-service.d.ts.map +0 -1
  159. package/dist/services/authorization-service.js +0 -279
  160. package/dist/services/authorization-service.js.map +0 -1
  161. package/dist/services/market-service.d.ts +0 -108
  162. package/dist/services/market-service.d.ts.map +0 -1
  163. package/dist/services/market-service.js +0 -458
  164. package/dist/services/market-service.js.map +0 -1
  165. package/dist/services/realtime-service.d.ts +0 -82
  166. package/dist/services/realtime-service.d.ts.map +0 -1
  167. package/dist/services/realtime-service.js +0 -150
  168. package/dist/services/realtime-service.js.map +0 -1
  169. package/dist/services/swap-service.d.ts +0 -217
  170. package/dist/services/swap-service.d.ts.map +0 -1
  171. package/dist/services/swap-service.js +0 -695
  172. package/dist/services/swap-service.js.map +0 -1
  173. package/dist/services/wallet-service.d.ts +0 -94
  174. package/dist/services/wallet-service.d.ts.map +0 -1
  175. package/dist/services/wallet-service.js +0 -173
  176. package/dist/services/wallet-service.js.map +0 -1
  177. package/dist/utils/price-utils.d.ts +0 -153
  178. package/dist/utils/price-utils.d.ts.map +0 -1
  179. package/dist/utils/price-utils.js +0 -236
  180. package/dist/utils/price-utils.js.map +0 -1
  181. package/docs/01-mcp.md +0 -2041
  182. package/docs/e2e/01-trader-tools.md +0 -159
  183. package/docs/e2e/02-market-tools.md +0 -180
  184. package/docs/e2e/03-order-tools.md +0 -166
  185. package/docs/e2e/04-wallet-tools.md +0 -224
  186. package/docs/e2e/05-trading-tools.md +0 -327
  187. package/docs/e2e/06-integration-scenarios.md +0 -481
  188. package/docs/e2e/coordinator.md +0 -376
  189. package/scripts/truth.md +0 -440
  190. package/src/mcp/README.md +0 -380
  191. package/src/mcp/errors.ts +0 -124
  192. package/src/mcp/index.ts +0 -309
  193. package/src/mcp/server.ts +0 -183
  194. package/src/mcp/tools/guide.ts +0 -821
  195. package/src/mcp/tools/index.ts +0 -73
  196. package/src/mcp/tools/market.ts +0 -363
  197. package/src/mcp/tools/order.ts +0 -326
  198. package/src/mcp/tools/trade.ts +0 -417
  199. package/src/mcp/tools/trader.ts +0 -322
  200. package/src/mcp/tools/wallet.ts +0 -683
  201. package/src/mcp/types.ts +0 -472
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,538 @@
1
+ # @catalyst-team/poly-sdk
2
+
3
+ Unified TypeScript SDK for Polymarket - prediction markets trading, arbitrage detection, smart money analysis, and comprehensive market data.
4
+
5
+ [![Version](https://img.shields.io/badge/version-0.2.0-blue.svg)](package.json)
6
+ [![Tests](https://img.shields.io/badge/tests-43%2F43%20passing-brightgreen.svg)](#testing)
7
+ [![License](https://img.shields.io/badge/license-Private-red.svg)](LICENSE)
8
+
9
+ **Builder**: [@hhhx402](https://x.com/hhhx402)
10
+ **Project**: [Catalyst.fun](https://x.com/catalystdotfun)
11
+
12
+ ## Features
13
+
14
+ - πŸ”„ **Real-time Arbitrage Detection**: WebSocket monitoring with automatic execution
15
+ - πŸ“Š **Smart Money Analysis**: Track top traders and their strategies
16
+ - πŸ’± **Trading Integration**: Place orders with GTC/GTD/FOK/FAK support
17
+ - πŸ” **On-Chain Operations**: Split, merge, and redeem CTF tokens
18
+ - πŸŒ‰ **Cross-Chain Bridge**: Deposit from Ethereum, Solana, Bitcoin
19
+ - πŸ’° **DEX Swaps**: Convert tokens on Polygon using QuickSwap V3
20
+ - πŸ“ˆ **Market Analytics**: K-lines, signals, and volume analysis
21
+ - βœ… **Comprehensive Testing**: 100% test coverage (43/43 tests passing)
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pnpm add @catalyst-team/poly-sdk
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { PolymarketSDK } from '@catalyst-team/poly-sdk';
33
+
34
+ const sdk = new PolymarketSDK();
35
+
36
+ // Get market by slug or condition ID
37
+ const market = await sdk.getMarket('will-trump-win-2024');
38
+ console.log(market.tokens.yes.price); // 0.65
39
+
40
+ // Get processed orderbook with analytics
41
+ const orderbook = await sdk.getOrderbook(market.conditionId);
42
+ console.log(orderbook.summary.longArbProfit); // Arbitrage opportunity
43
+
44
+ // Detect arbitrage
45
+ const arb = await sdk.detectArbitrage(market.conditionId);
46
+ if (arb) {
47
+ console.log(`${arb.type} arb: ${arb.profit * 100}% profit`);
48
+ }
49
+ ```
50
+
51
+ ## Architecture
52
+
53
+ ```
54
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
55
+ β”‚ PolymarketSDK β”‚
56
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
57
+ β”‚ Layer 3: Services β”‚
58
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
59
+ β”‚ β”‚WalletServiceβ”‚ β”‚MarketServiceβ”‚ β”‚RealtimeServiceβ”‚ β”‚ AuthorizationService β”‚β”‚
60
+ β”‚ β”‚ - profiles β”‚ β”‚ - K-Lines β”‚ β”‚- subscriptionsβ”‚ β”‚ - ERC20 approvals β”‚β”‚
61
+ β”‚ β”‚ - sell det. β”‚ β”‚ - signals β”‚ β”‚- price cache β”‚ β”‚ - ERC1155 approvals β”‚β”‚
62
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
63
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
64
+ β”‚ β”‚ ArbitrageService: Real-time arbitrage detection, rebalancer, settlement β”‚ β”‚
65
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
66
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
67
+ β”‚ β”‚ SwapService: DEX swaps on Polygon (QuickSwap V3, USDC/USDC.e conversion)β”‚ β”‚
68
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
69
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
70
+ β”‚ Layer 2: API Clients β”‚
71
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
72
+ β”‚ β”‚ DataAPI β”‚ β”‚ GammaAPI β”‚ β”‚ CLOB API β”‚ β”‚ WebSocket β”‚ β”‚ BridgeClient β”‚ β”‚
73
+ β”‚ β”‚positions β”‚ β”‚ markets β”‚ β”‚ orderbookβ”‚ β”‚ real-time β”‚ β”‚ cross-chain β”‚ β”‚
74
+ β”‚ β”‚ trades β”‚ β”‚ events β”‚ β”‚ trading β”‚ β”‚ prices β”‚ β”‚ deposits β”‚ β”‚
75
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
76
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
77
+ β”‚ β”‚ TradingClient: Order execution β”‚ β”‚ CTFClient: On-chain operations β”‚ β”‚
78
+ β”‚ β”‚ GTC/GTD/FOK/FAK, rewards, balances β”‚ β”‚ Split / Merge / Redeem tokens β”‚ β”‚
79
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
80
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
81
+ β”‚ Layer 1: Infrastructure β”‚
82
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
83
+ β”‚ β”‚RateLimiter β”‚ β”‚ Cache β”‚ β”‚ Errors β”‚ β”‚ Types β”‚ β”‚ Price Utils β”‚ β”‚
84
+ β”‚ β”‚per-API β”‚ β”‚TTL-basedβ”‚ β”‚ retry β”‚ β”‚ unified β”‚ β”‚ arb detect β”‚ β”‚
85
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
86
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
87
+ ```
88
+
89
+ ## Core Concepts
90
+
91
+ ### Understanding Polymarket's Mirror Orderbook
92
+
93
+ ⚠️ **CRITICAL: Polymarket's orderbook has a mirror property that's easy to overlook:**
94
+
95
+ ```
96
+ Buy YES @ P = Sell NO @ (1-P)
97
+ ```
98
+
99
+ This means **the same order appears in both orderbooks**. For example, a "Sell NO @ 0.50" order will simultaneously appear as "Buy YES @ 0.50" in the YES orderbook.
100
+
101
+ **Common Mistake:**
102
+ ```typescript
103
+ // ❌ WRONG: Simple addition double-counts mirror orders
104
+ const askSum = YES.ask + NO.ask; // β‰ˆ 1.998-1.999, not β‰ˆ 1.0
105
+ const bidSum = YES.bid + NO.bid; // β‰ˆ 0.001-0.002, not β‰ˆ 1.0
106
+ ```
107
+
108
+ **Correct Approach: Use Effective Prices**
109
+ ```typescript
110
+ import { getEffectivePrices, checkArbitrage } from '@catalyst-team/poly-sdk';
111
+
112
+ // Calculate optimal prices considering mirrors
113
+ const effective = getEffectivePrices(yesAsk, yesBid, noAsk, noBid);
114
+
115
+ // effective.effectiveBuyYes = min(YES.ask, 1 - NO.bid)
116
+ // effective.effectiveBuyNo = min(NO.ask, 1 - YES.bid)
117
+ // effective.effectiveSellYes = max(YES.bid, 1 - NO.ask)
118
+ // effective.effectiveSellNo = max(NO.bid, 1 - YES.ask)
119
+
120
+ // Detect arbitrage using effective prices
121
+ const arb = checkArbitrage(yesAsk, noAsk, yesBid, noBid);
122
+ if (arb) {
123
+ console.log(`${arb.type} arb: ${(arb.profit * 100).toFixed(2)}% profit`);
124
+ console.log(arb.description);
125
+ }
126
+ ```
127
+
128
+ See detailed documentation: [docs/01-polymarket-orderbook-arbitrage.md](docs/01-polymarket-orderbook-arbitrage.md)
129
+
130
+ ### ArbitrageService - Automated Trading
131
+
132
+ Real-time arbitrage detection and execution with market scanning, auto-rebalancing, and smart position clearing.
133
+
134
+ ```
135
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
136
+ β”‚ Core Features β”‚
137
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
138
+ β”‚ β€’ scanMarkets() - Scan markets for arbitrage opportunities β”‚
139
+ β”‚ β€’ start(market) - Start real-time monitoring + auto-execution β”‚
140
+ β”‚ β€’ clearPositions() - Smart clearing (sell active, redeem resolved) β”‚
141
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
142
+ β”‚ Auto-Rebalancer β”‚
143
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
144
+ β”‚ Arbitrage requires USDC + YES/NO tokens. Rebalancer maintains optimal mix: β”‚
145
+ β”‚ β€’ USDC < 20% β†’ Auto Merge (YES+NO β†’ USDC) β”‚
146
+ β”‚ β€’ USDC > 80% β†’ Auto Split (USDC β†’ YES+NO) β”‚
147
+ β”‚ β€’ Cooldown: 30s between actions, 10s check interval β”‚
148
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
149
+ β”‚ Partial Fill Protection β”‚
150
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
151
+ β”‚ Arbitrage needs both YES and NO, but orders may partially fill: β”‚
152
+ β”‚ β€’ sizeSafetyFactor=0.8 β†’ Use only 80% of orderbook depth β”‚
153
+ β”‚ β€’ autoFixImbalance=true β†’ Auto-sell excess if one side fails β”‚
154
+ β”‚ β€’ imbalanceThreshold=5 β†’ Trigger fix when YES-NO diff > $5 β”‚
155
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
156
+ ```
157
+
158
+ **Complete Workflow:**
159
+
160
+ ```typescript
161
+ import { ArbitrageService } from '@catalyst-team/poly-sdk';
162
+
163
+ const arbService = new ArbitrageService({
164
+ privateKey: process.env.POLY_PRIVKEY,
165
+ profitThreshold: 0.005, // 0.5% minimum profit
166
+ minTradeSize: 5, // $5 minimum
167
+ maxTradeSize: 100, // $100 maximum
168
+ autoExecute: true, // Auto-execute opportunities
169
+
170
+ // Rebalancer config
171
+ enableRebalancer: true, // Auto-rebalance position
172
+ minUsdcRatio: 0.2, // Min 20% USDC (Split if below)
173
+ maxUsdcRatio: 0.8, // Max 80% USDC (Merge if above)
174
+ targetUsdcRatio: 0.5, // Target 50% when rebalancing
175
+ imbalanceThreshold: 5, // Max YES-NO difference before fix
176
+ rebalanceInterval: 10000, // Check every 10s
177
+ rebalanceCooldown: 30000, // Min 30s between actions
178
+
179
+ // Execution safety (prevents YES β‰  NO from partial fills)
180
+ sizeSafetyFactor: 0.8, // Use 80% of orderbook depth
181
+ autoFixImbalance: true, // Auto-sell excess if one side fails
182
+ });
183
+
184
+ // Listen for events
185
+ arbService.on('opportunity', (opp) => {
186
+ console.log(`${opp.type.toUpperCase()} ARB: ${opp.profitPercent.toFixed(2)}%`);
187
+ });
188
+
189
+ arbService.on('execution', (result) => {
190
+ if (result.success) {
191
+ console.log(`βœ… Executed: $${result.profit.toFixed(2)} profit`);
192
+ }
193
+ });
194
+
195
+ // ========== Step 1: Scan Markets ==========
196
+ const results = await arbService.scanMarkets({ minVolume24h: 5000 }, 0.005);
197
+ console.log(`Found ${results.filter(r => r.arbType !== 'none').length} opportunities`);
198
+
199
+ // Or use one-click scan + start best market
200
+ const best = await arbService.findAndStart(0.005);
201
+ if (!best) {
202
+ console.log('No arbitrage opportunities found');
203
+ process.exit(0);
204
+ }
205
+ console.log(`🎯 Started: ${best.market.name} (+${best.profitPercent.toFixed(2)}%)`);
206
+
207
+ // ========== Step 2: Run Arbitrage ==========
208
+ // Service now auto-monitors and executes...
209
+ await new Promise(resolve => setTimeout(resolve, 60 * 60 * 1000)); // 1 hour
210
+
211
+ // ========== Step 3: Stop & Clear Positions ==========
212
+ await arbService.stop();
213
+ console.log('Stats:', arbService.getStats());
214
+
215
+ // Smart clearing: merge+sell active markets, redeem resolved markets
216
+ const clearResult = await arbService.clearPositions(best.market, true);
217
+ console.log(`βœ… Recovered: $${clearResult.totalUsdcRecovered.toFixed(2)}`);
218
+ ```
219
+
220
+ ## Testing
221
+
222
+ This package includes comprehensive testing infrastructure with **100% test coverage**:
223
+
224
+ ```bash
225
+ # Run all tests
226
+ pnpm test:unit # 27 unit tests
227
+ pnpm test:integration # 10 integration tests
228
+ pnpm test:e2e # 6 E2E tests (requires funded wallet)
229
+ ```
230
+
231
+ ### Test Coverage
232
+
233
+ | Test Level | Tests | Coverage | Description |
234
+ |------------|-------|----------|-------------|
235
+ | **Unit Tests** | 27/27 βœ… | Mirror orderbook calculations, arbitrage detection |
236
+ | **Integration Tests** | 10/10 βœ… | WebSocket real-time monitoring, market scanning |
237
+ | **E2E Tests** | 6/6 βœ… | On-chain CTF operations on Polygon mainnet |
238
+
239
+ All tests are located in `scripts/arb-tests/`:
240
+ - `01-unit-tests.ts` - Price utilities and arbitrage logic
241
+ - `02-integration-tests.ts` - API integration and WebSocket
242
+ - `03-e2e-tests.ts` - Real blockchain transactions (Split/Merge)
243
+
244
+ See [docs/arb/test-results.md](docs/arb/test-results.md) for detailed test reports.
245
+
246
+ ## API Clients
247
+
248
+ ### TradingClient - Order Execution
249
+
250
+ ```typescript
251
+ import { TradingClient, RateLimiter } from '@catalyst-team/poly-sdk';
252
+
253
+ const tradingClient = new TradingClient(new RateLimiter(), {
254
+ privateKey: process.env.POLYMARKET_PRIVATE_KEY!,
255
+ });
256
+
257
+ await tradingClient.initialize();
258
+
259
+ // GTC Limit Order (stays until filled or cancelled)
260
+ const order = await tradingClient.createOrder({
261
+ tokenId: yesTokenId,
262
+ side: 'BUY',
263
+ price: 0.45,
264
+ size: 10,
265
+ orderType: 'GTC',
266
+ });
267
+
268
+ // FOK Market Order (fill entirely or cancel)
269
+ const marketOrder = await tradingClient.createMarketOrder({
270
+ tokenId: yesTokenId,
271
+ side: 'BUY',
272
+ amount: 10, // $10 USDC
273
+ orderType: 'FOK',
274
+ });
275
+
276
+ // Order management
277
+ const openOrders = await tradingClient.getOpenOrders();
278
+ await tradingClient.cancelOrder(orderId);
279
+ ```
280
+
281
+ ### CTFClient - On-Chain Token Operations
282
+
283
+ The CTF (Conditional Token Framework) client enables on-chain operations for Polymarket's conditional tokens.
284
+
285
+ ```
286
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
287
+ β”‚ CTF Operations Quick Reference β”‚
288
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
289
+ β”‚ Operation β”‚ Function β”‚ Typical Use Case β”‚
290
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
291
+ β”‚ Split β”‚ USDC β†’ YES + NO β”‚ Market making: create token inventoryβ”‚
292
+ β”‚ Merge β”‚ YES + NO β†’ USDC β”‚ Arbitrage: buy both and merge β”‚
293
+ β”‚ Redeem β”‚ Winning token β†’ USDC β”‚ Settlement: claim winnings β”‚
294
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
295
+ ```
296
+
297
+ ```typescript
298
+ import { CTFClient } from '@catalyst-team/poly-sdk';
299
+
300
+ const ctf = new CTFClient({
301
+ privateKey: process.env.POLYMARKET_PRIVATE_KEY!,
302
+ rpcUrl: 'https://polygon-rpc.com',
303
+ });
304
+
305
+ // Split: USDC β†’ YES + NO tokens
306
+ const splitResult = await ctf.split(conditionId, '100');
307
+ console.log(`Created ${splitResult.yesTokens} YES + ${splitResult.noTokens} NO`);
308
+
309
+ // Merge: YES + NO β†’ USDC
310
+ const tokenIds = {
311
+ yesTokenId: market.tokens[0].tokenId,
312
+ noTokenId: market.tokens[1].tokenId,
313
+ };
314
+ const mergeResult = await ctf.mergeByTokenIds(conditionId, tokenIds, '100');
315
+ console.log(`Received ${mergeResult.usdcReceived} USDC`);
316
+
317
+ // Redeem: Winning tokens β†’ USDC
318
+ const redeemResult = await ctf.redeemByTokenIds(conditionId, tokenIds);
319
+ console.log(`Redeemed ${redeemResult.tokensRedeemed} tokens`);
320
+ ```
321
+
322
+ ⚠️ **Important: Use `mergeByTokenIds()` and `redeemByTokenIds()` for Polymarket markets**
323
+
324
+ Polymarket uses custom token IDs that differ from standard CTF position IDs. Always use the `*ByTokenIds` methods with token IDs from CLOB API.
325
+
326
+ ### SwapService - DEX Swaps on Polygon
327
+
328
+ Swap tokens on Polygon using QuickSwap V3. Essential for converting to USDC.e for CTF operations.
329
+
330
+ ⚠️ **USDC vs USDC.e for Polymarket CTF**
331
+
332
+ | Token | Address | Polymarket CTF |
333
+ |-------|---------|----------------|
334
+ | USDC.e | `0x2791...` | βœ… **Required** |
335
+ | USDC (Native) | `0x3c49...` | ❌ Not accepted |
336
+
337
+ ```typescript
338
+ import { SwapService, POLYGON_TOKENS } from '@catalyst-team/poly-sdk';
339
+
340
+ const swapService = new SwapService(signer);
341
+
342
+ // Swap native USDC to USDC.e for CTF operations
343
+ const swapResult = await swapService.swap('USDC', 'USDC_E', '100');
344
+ console.log(`Swapped: ${swapResult.amountOut} USDC.e`);
345
+
346
+ // Swap MATIC to USDC.e
347
+ const maticSwap = await swapService.swap('MATIC', 'USDC_E', '50');
348
+
349
+ // Get quote before swapping
350
+ const quote = await swapService.getQuote('WETH', 'USDC_E', '0.1');
351
+ console.log(`Expected output: ${quote.estimatedAmountOut} USDC.e`);
352
+ ```
353
+
354
+ ### WalletService - Smart Money Analysis
355
+
356
+ ```typescript
357
+ // Get top traders
358
+ const traders = await sdk.wallets.getTopTraders(10);
359
+
360
+ // Get wallet profile with smart score
361
+ const profile = await sdk.wallets.getWalletProfile('0x...');
362
+ console.log(profile.smartScore); // 0-100
363
+
364
+ // Detect sell activity (for follow-wallet strategy)
365
+ const sellResult = await sdk.wallets.detectSellActivity(
366
+ '0x...',
367
+ conditionId,
368
+ Date.now() - 24 * 60 * 60 * 1000
369
+ );
370
+ if (sellResult.isSelling) {
371
+ console.log(`Sold ${sellResult.percentageSold}%`);
372
+ }
373
+ ```
374
+
375
+ ### MarketService - K-Lines and Signals
376
+
377
+ ```typescript
378
+ // Get K-Line candles
379
+ const klines = await sdk.markets.getKLines(conditionId, '1h', { limit: 100 });
380
+
381
+ // Get dual K-Lines (YES + NO) with spread analysis
382
+ const dual = await sdk.markets.getDualKLines(conditionId, '1h');
383
+
384
+ // Historical spread (from trade close prices) - for backtesting
385
+ for (const point of dual.spreadAnalysis) {
386
+ console.log(`${point.timestamp}: sum=${point.priceSum}, spread=${point.priceSpread}`);
387
+ if (point.arbOpportunity) {
388
+ console.log(` Historical ${point.arbOpportunity} signal`);
389
+ }
390
+ }
391
+
392
+ // Real-time spread (from orderbook) - for live trading
393
+ if (dual.realtimeSpread) {
394
+ const rt = dual.realtimeSpread;
395
+ if (rt.arbOpportunity) {
396
+ console.log(`🎯 ${rt.arbOpportunity} ARB: ${rt.arbProfitPercent.toFixed(2)}%`);
397
+ }
398
+ }
399
+ ```
400
+
401
+ #### Spread Analysis - Two Approaches
402
+
403
+ ```
404
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
405
+ β”‚ spreadAnalysis (Historical) β”‚ realtimeSpread (Live Trading) β”‚
406
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
407
+ β”‚ Data Source: Trade close prices β”‚ Data Source: Orderbook bid/ask β”‚
408
+ β”‚ YES_close + NO_close β”‚ Uses effective prices (mirrors) β”‚
409
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
410
+ β”‚ βœ… Can build historical chart β”‚ ❌ No historical data available* β”‚
411
+ β”‚ βœ… Polymarket keeps trade historyβ”‚ ❌ Polymarket doesn't keep snapshotsβ”‚
412
+ β”‚ βœ… Good for backtesting β”‚ βœ… Good for live trading β”‚
413
+ β”‚ ⚠️ Arb signals are approximate β”‚ βœ… Arb profit calculation accurate β”‚
414
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
415
+
416
+ * To build historical real-time spread, you must store orderbook snapshots yourself
417
+ See: apps/api/src/services/spread-sampler.ts
418
+ ```
419
+
420
+ ### RealtimeService - WebSocket Subscriptions
421
+
422
+ ⚠️ **Important: Orderbook Auto-Sorting**
423
+
424
+ Polymarket CLOB API returns orderbooks in reverse order from standard expectations:
425
+ - **bids**: Ascending (lowest first = worst price)
426
+ - **asks**: Descending (highest first = worst price)
427
+
428
+ Our SDK **automatically normalizes** orderbook data:
429
+ - **bids**: Descending (highest first = best bid)
430
+ - **asks**: Ascending (lowest first = best ask)
431
+
432
+ This means you can safely use `bids[0]` and `asks[0]` to get best prices:
433
+
434
+ ```typescript
435
+ const book = await sdk.clobApi.getOrderbook(conditionId);
436
+ const bestBid = book.bids[0]?.price; // βœ… Highest bid (best)
437
+ const bestAsk = book.asks[0]?.price; // βœ… Lowest ask (best)
438
+
439
+ // WebSocket updates are also auto-sorted
440
+ wsManager.on('bookUpdate', (update) => {
441
+ const bestBid = update.bids[0]?.price; // βœ… Already sorted
442
+ const bestAsk = update.asks[0]?.price; // βœ… Already sorted
443
+ });
444
+ ```
445
+
446
+ ## Examples
447
+
448
+ | Example | Description | Command |
449
+ |---------|-------------|---------|
450
+ | [Basic Usage](examples/01-basic-usage.ts) | Get markets, orderbooks, detect arbitrage | `pnpm example:basic` |
451
+ | [Smart Money](examples/02-smart-money.ts) | Top traders, wallet profiles, smart scores | `pnpm example:smart-money` |
452
+ | [Market Analysis](examples/03-market-analysis.ts) | Market signals, volume analysis | `pnpm example:market-analysis` |
453
+ | [K-Line Aggregation](examples/04-kline-aggregation.ts) | Build OHLCV candles from trades | `pnpm example:kline` |
454
+ | [Follow Wallet](examples/05-follow-wallet-strategy.ts) | Track smart money positions, detect exits | `pnpm example:follow-wallet` |
455
+ | [Services Demo](examples/06-services-demo.ts) | All SDK services in action | `pnpm example:services` |
456
+ | [Realtime WebSocket](examples/07-realtime-websocket.ts) | Live price feeds, orderbook updates | `pnpm example:realtime` |
457
+ | [Trading Orders](examples/08-trading-orders.ts) | GTC, GTD, FOK, FAK order types | `pnpm example:trading` |
458
+ | [Rewards Tracking](examples/09-rewards-tracking.ts) | Market maker incentives, earnings | `pnpm example:rewards` |
459
+ | [CTF Operations](examples/10-ctf-operations.ts) | Split, merge, redeem tokens | `pnpm example:ctf` |
460
+ | [Live Arbitrage Scan](examples/11-live-arbitrage-scan.ts) | Scan real markets for opportunities | `pnpm example:live-arb` |
461
+
462
+ ## Error Handling
463
+
464
+ ```typescript
465
+ import { PolymarketError, ErrorCode, withRetry } from '@catalyst-team/poly-sdk';
466
+
467
+ try {
468
+ const market = await sdk.getMarket('invalid-slug');
469
+ } catch (error) {
470
+ if (error instanceof PolymarketError) {
471
+ if (error.code === ErrorCode.MARKET_NOT_FOUND) {
472
+ console.log('Market not found');
473
+ } else if (error.code === ErrorCode.RATE_LIMITED) {
474
+ console.log('Rate limited, retry later');
475
+ }
476
+ }
477
+ }
478
+
479
+ // Auto-retry with exponential backoff
480
+ const result = await withRetry(() => sdk.getMarket(slug), {
481
+ maxRetries: 3,
482
+ baseDelay: 1000,
483
+ });
484
+ ```
485
+
486
+ ## Rate Limiting
487
+
488
+ Built-in rate limiting per API type:
489
+ - Data API: 10 req/sec
490
+ - Gamma API: 10 req/sec
491
+ - CLOB API: 5 req/sec
492
+
493
+ ```typescript
494
+ import { RateLimiter, ApiType } from '@catalyst-team/poly-sdk';
495
+
496
+ // Custom rate limiter
497
+ const limiter = new RateLimiter({
498
+ [ApiType.DATA]: { maxConcurrent: 5, minTime: 200 },
499
+ [ApiType.GAMMA]: { maxConcurrent: 5, minTime: 200 },
500
+ [ApiType.CLOB]: { maxConcurrent: 2, minTime: 500 },
501
+ });
502
+ ```
503
+
504
+ ## Documentation
505
+
506
+ - [Orderbook & Arbitrage Guide](docs/01-polymarket-orderbook-arbitrage.md) - Understanding mirror orders
507
+ - [Test Plan](docs/arb/test-plan.md) - Testing strategy and approach
508
+ - [Test Results](docs/arb/test-results.md) - Detailed test coverage report
509
+ - [Test Scripts README](scripts/arb-tests/README.md) - Running tests
510
+
511
+ ## Dependencies
512
+
513
+ - `@nevuamarkets/poly-websockets` - WebSocket client
514
+ - `bottleneck` - Rate limiting
515
+ - `ethers` - Blockchain interactions
516
+
517
+ ## License
518
+
519
+ Private
520
+
521
+ ## Changelog
522
+
523
+ ### v0.2.0 (2024-12-24)
524
+
525
+ - βœ… **Complete test coverage**: 43/43 tests passing (100%)
526
+ - 27 unit tests for arbitrage detection logic
527
+ - 10 integration tests for WebSocket and market scanning
528
+ - 6 E2E tests with real on-chain transactions
529
+ - πŸ› Fixed WebSocket integration tests (listen to `orderbookUpdate` events)
530
+ - πŸ“Š Smart market selection based on volume and orderbook depth
531
+ - πŸ“ Comprehensive test documentation and reports
532
+ - πŸ”§ Test infrastructure for ArbitrageService verification
533
+
534
+ ### v0.1.1
535
+
536
+ - Initial arbitrage service implementation
537
+ - CTF operations support
538
+ - Real-time WebSocket monitoring