@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,681 @@
1
+ /**
2
+ * Wallet Service
3
+ *
4
+ * Provides smart money analysis features:
5
+ * - Wallet profile analysis
6
+ * - Position tracking
7
+ * - Activity monitoring
8
+ * - Sell detection for follow wallet strategy
9
+ * - Time-based leaderboard and wallet stats
10
+ */
11
+ import { CACHE_TTL } from '../core/unified-cache.js';
12
+ /**
13
+ * Collateral Asset ID (USDC)
14
+ * In Polymarket orderbook, "0" represents the collateral token (USDC)
15
+ */
16
+ const COLLATERAL_ASSET_ID = '0';
17
+ /**
18
+ * Check if an asset ID is collateral (USDC) or outcome token
19
+ */
20
+ function isCollateralAsset(assetId) {
21
+ return assetId === COLLATERAL_ASSET_ID;
22
+ }
23
+ export class WalletService {
24
+ dataApi;
25
+ subgraph;
26
+ cache;
27
+ constructor(dataApi, subgraph, cache) {
28
+ this.dataApi = dataApi;
29
+ this.subgraph = subgraph;
30
+ this.cache = cache;
31
+ }
32
+ // ===== Time Period Helpers =====
33
+ /**
34
+ * Get start timestamp for a time period
35
+ */
36
+ getPeriodStartTime(period) {
37
+ const now = Math.floor(Date.now() / 1000);
38
+ switch (period) {
39
+ case 'day':
40
+ return now - 24 * 60 * 60;
41
+ case 'week':
42
+ return now - 7 * 24 * 60 * 60;
43
+ case 'month':
44
+ return now - 30 * 24 * 60 * 60;
45
+ case 'all':
46
+ return 0;
47
+ }
48
+ }
49
+ // ===== Wallet Analysis =====
50
+ /**
51
+ * Get comprehensive wallet profile with PnL analysis
52
+ */
53
+ async getWalletProfile(address) {
54
+ const [positions, activities] = await Promise.all([
55
+ this.dataApi.getPositions(address),
56
+ this.dataApi.getActivity(address, { limit: 100 }),
57
+ ]);
58
+ const totalPnL = positions.reduce((sum, p) => sum + (p.cashPnl || 0), 0);
59
+ const realizedPnL = positions.reduce((sum, p) => sum + (p.realizedPnl || 0), 0);
60
+ const unrealizedPnL = totalPnL - realizedPnL;
61
+ const avgPercentPnL = positions.length > 0
62
+ ? positions.reduce((sum, p) => sum + (p.percentPnl || 0), 0) / positions.length
63
+ : 0;
64
+ const lastActivity = activities[0];
65
+ return {
66
+ address,
67
+ totalPnL,
68
+ realizedPnL,
69
+ unrealizedPnL,
70
+ avgPercentPnL,
71
+ positionCount: positions.length,
72
+ tradeCount: activities.filter((a) => a.type === 'TRADE').length,
73
+ smartScore: this.calculateSmartScore(positions, activities),
74
+ lastActiveAt: lastActivity ? new Date(lastActivity.timestamp) : new Date(0),
75
+ };
76
+ }
77
+ /**
78
+ * Get positions for a wallet
79
+ */
80
+ async getWalletPositions(address) {
81
+ return this.dataApi.getPositions(address);
82
+ }
83
+ /**
84
+ * Get positions for a specific market
85
+ */
86
+ async getPositionsForMarket(address, conditionId) {
87
+ const positions = await this.dataApi.getPositions(address);
88
+ return positions.filter((p) => p.conditionId === conditionId);
89
+ }
90
+ /**
91
+ * Get wallet activity with summary
92
+ *
93
+ * @param address - Wallet address
94
+ * @param options - Activity query options (limit, start, end, type, side, market, fetchAll)
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * // Get recent 100 activities
99
+ * const activity = await walletService.getWalletActivity(address);
100
+ *
101
+ * // Get activities from past week
102
+ * const oneWeekAgo = Math.floor(Date.now() / 1000) - 7 * 24 * 60 * 60;
103
+ * const weekActivity = await walletService.getWalletActivity(address, { start: oneWeekAgo });
104
+ *
105
+ * // Get all activities (auto-pagination, up to API limit)
106
+ * const allActivity = await walletService.getWalletActivity(address, { fetchAll: true });
107
+ * ```
108
+ */
109
+ async getWalletActivity(address, options = 100) {
110
+ // Support legacy signature: getWalletActivity(address, limit)
111
+ const opts = typeof options === 'number' ? { limit: options } : options;
112
+ let activities;
113
+ if (opts.fetchAll) {
114
+ // Use getAllActivity for complete history with auto-pagination
115
+ activities = await this.dataApi.getAllActivity(address, {
116
+ start: opts.start,
117
+ end: opts.end,
118
+ type: opts.type,
119
+ side: opts.side,
120
+ market: opts.market ? [opts.market] : undefined,
121
+ }, opts.maxItems || 10000);
122
+ }
123
+ else {
124
+ // Use getActivity for single page
125
+ activities = await this.dataApi.getActivity(address, {
126
+ limit: opts.limit || 100,
127
+ offset: opts.offset,
128
+ start: opts.start,
129
+ end: opts.end,
130
+ type: opts.type,
131
+ side: opts.side,
132
+ market: opts.market ? [opts.market] : undefined,
133
+ });
134
+ }
135
+ const buys = activities.filter((a) => a.side === 'BUY');
136
+ const sells = activities.filter((a) => a.side === 'SELL');
137
+ // Calculate time range
138
+ let timeRange;
139
+ if (activities.length > 0) {
140
+ const timestamps = activities.map((a) => a.timestamp);
141
+ timeRange = {
142
+ earliest: new Date(Math.min(...timestamps)),
143
+ latest: new Date(Math.max(...timestamps)),
144
+ };
145
+ }
146
+ return {
147
+ address,
148
+ activities,
149
+ summary: {
150
+ totalBuys: buys.length,
151
+ totalSells: sells.length,
152
+ buyVolume: buys.reduce((sum, a) => sum + (a.usdcSize || 0), 0),
153
+ sellVolume: sells.reduce((sum, a) => sum + (a.usdcSize || 0), 0),
154
+ activeMarkets: [...new Set(activities.map((a) => a.conditionId))],
155
+ },
156
+ timeRange,
157
+ };
158
+ }
159
+ // ===== Wallet Discovery =====
160
+ /**
161
+ * Get leaderboard
162
+ */
163
+ async getLeaderboard(page = 0, pageSize = 50) {
164
+ return this.dataApi.getLeaderboard({ limit: pageSize, offset: page * pageSize });
165
+ }
166
+ /**
167
+ * Get top traders from leaderboard
168
+ */
169
+ async getTopTraders(limit = 10) {
170
+ const leaderboard = await this.dataApi.getLeaderboard({ limit });
171
+ return leaderboard.entries;
172
+ }
173
+ // ===== Time-Based Leaderboard =====
174
+ /**
175
+ * Get leaderboard by time period using official Polymarket API
176
+ *
177
+ * Uses the official Data API which supports time period filtering.
178
+ * This is the recommended method as it uses server-side calculations.
179
+ *
180
+ * @param period - Time period: 'day', 'week', 'month', or 'all'
181
+ * @param limit - Maximum entries to return (default: 50, max: 50)
182
+ * @param sortBy - Sort criteria: 'volume' or 'pnl' (default: 'pnl')
183
+ * @param category - Market category filter (default: 'OVERALL')
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * // Get top traders of the week by PnL
188
+ * const weeklyByPnl = await walletService.getLeaderboardByPeriod('week', 20, 'pnl');
189
+ *
190
+ * // Get top traders by volume
191
+ * const weeklyByVolume = await walletService.getLeaderboardByPeriod('week', 20, 'volume');
192
+ *
193
+ * // Get politics category leaderboard
194
+ * const politics = await walletService.getLeaderboardByPeriod('week', 20, 'pnl', 'POLITICS');
195
+ * ```
196
+ */
197
+ async getLeaderboardByPeriod(period, limit = 50, sortBy = 'pnl', category = 'OVERALL', offset = 0) {
198
+ // Map lowercase period to API's uppercase format
199
+ const timePeriodMap = {
200
+ day: 'DAY',
201
+ week: 'WEEK',
202
+ month: 'MONTH',
203
+ all: 'ALL',
204
+ };
205
+ // Map sortBy to API's orderBy format
206
+ const orderByMap = {
207
+ volume: 'VOL',
208
+ pnl: 'PNL',
209
+ };
210
+ const timePeriod = timePeriodMap[period];
211
+ const orderBy = orderByMap[sortBy];
212
+ // Use official API
213
+ const result = await this.dataApi.getLeaderboard({
214
+ timePeriod,
215
+ orderBy,
216
+ category,
217
+ limit,
218
+ offset,
219
+ });
220
+ // Map to PeriodLeaderboardEntry format
221
+ return result.entries.map((entry, index) => ({
222
+ address: entry.address,
223
+ rank: entry.rank || index + 1,
224
+ volume: entry.volume,
225
+ pnl: entry.pnl,
226
+ // API provides combined PnL, set as total
227
+ totalPnl: entry.pnl,
228
+ realizedPnl: entry.pnl, // API doesn't separate realized/unrealized
229
+ unrealizedPnl: 0,
230
+ // API doesn't provide these breakdowns
231
+ tradeCount: entry.trades || 0,
232
+ buyCount: 0,
233
+ sellCount: 0,
234
+ buyVolume: 0,
235
+ sellVolume: 0,
236
+ makerVolume: 0,
237
+ takerVolume: 0,
238
+ // User profile
239
+ userName: entry.userName,
240
+ profileImage: entry.profileImage,
241
+ }));
242
+ }
243
+ /**
244
+ * Get a specific user's PnL and ranking for a time period
245
+ *
246
+ * Uses the official Data API's user filter to get a single user's stats.
247
+ *
248
+ * @param address - User's wallet address
249
+ * @param period - Time period: 'day', 'week', 'month', or 'all'
250
+ * @param category - Market category filter (default: 'OVERALL')
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * // Get user's weekly PnL
255
+ * const stats = await walletService.getUserPeriodPnl(address, 'week');
256
+ * console.log(`Rank: #${stats.rank}, PnL: $${stats.pnl}`);
257
+ *
258
+ * // Get user's monthly PnL in politics category
259
+ * const politicsStats = await walletService.getUserPeriodPnl(address, 'month', 'POLITICS');
260
+ * ```
261
+ */
262
+ async getUserPeriodPnl(address, period, category = 'OVERALL') {
263
+ // Map lowercase period to API's uppercase format
264
+ const timePeriodMap = {
265
+ day: 'DAY',
266
+ week: 'WEEK',
267
+ month: 'MONTH',
268
+ all: 'ALL',
269
+ };
270
+ const timePeriod = timePeriodMap[period];
271
+ // Use official API with user filter
272
+ const result = await this.dataApi.getLeaderboard({
273
+ timePeriod,
274
+ orderBy: 'PNL',
275
+ category,
276
+ user: address,
277
+ limit: 1,
278
+ });
279
+ if (result.entries.length === 0) {
280
+ return null;
281
+ }
282
+ const entry = result.entries[0];
283
+ return {
284
+ address: entry.address,
285
+ rank: entry.rank || 0,
286
+ volume: entry.volume,
287
+ pnl: entry.pnl,
288
+ totalPnl: entry.pnl,
289
+ realizedPnl: entry.pnl,
290
+ unrealizedPnl: 0,
291
+ tradeCount: entry.trades || 0,
292
+ buyCount: 0,
293
+ sellCount: 0,
294
+ buyVolume: 0,
295
+ sellVolume: 0,
296
+ makerVolume: 0,
297
+ takerVolume: 0,
298
+ userName: entry.userName,
299
+ profileImage: entry.profileImage,
300
+ };
301
+ }
302
+ /**
303
+ * Get wallet stats for a specific time period
304
+ *
305
+ * Combines data from Orderbook Subgraph (trades) and Activity Subgraph (splits/merges/redemptions)
306
+ *
307
+ * @param address - Wallet address
308
+ * @param period - Time period: 'day', 'week', 'month', or 'all'
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * // Get wallet's monthly stats
313
+ * const stats = await walletService.getWalletStatsByPeriod(address, 'month');
314
+ * console.log(`Monthly volume: $${stats.volume}`);
315
+ * ```
316
+ */
317
+ async getWalletStatsByPeriod(address, period) {
318
+ const startTime = this.getPeriodStartTime(period);
319
+ const endTime = Math.floor(Date.now() / 1000);
320
+ const cacheKey = `wallet:stats:${address}:${period}`;
321
+ return this.cache.getOrSet(cacheKey, CACHE_TTL.ACTIVITY, async () => {
322
+ // Fetch data in parallel
323
+ const [makerFills, takerFills, splits, merges, redemptions] = await Promise.all([
324
+ this.subgraph.getMakerFills(address, {
325
+ first: 1000,
326
+ where: { timestamp_gte: String(startTime) },
327
+ }),
328
+ this.subgraph.getTakerFills(address, {
329
+ first: 1000,
330
+ where: { timestamp_gte: String(startTime) },
331
+ }),
332
+ this.subgraph.getSplits(address, {
333
+ first: 500,
334
+ where: { timestamp_gte: String(startTime) },
335
+ }),
336
+ this.subgraph.getMerges(address, {
337
+ first: 500,
338
+ where: { timestamp_gte: String(startTime) },
339
+ }),
340
+ this.subgraph.getRedemptions(address, {
341
+ first: 500,
342
+ where: { timestamp_gte: String(startTime) },
343
+ }),
344
+ ]);
345
+ // Calculate volumes (amounts are in micro-units, divide by 1e6 for USDC)
346
+ const makerVolume = makerFills.reduce((sum, f) => sum + Number(f.makerAmountFilled) / 1e6, 0);
347
+ const takerVolume = takerFills.reduce((sum, f) => sum + Number(f.takerAmountFilled) / 1e6, 0);
348
+ const redemptionPayout = redemptions.reduce((sum, r) => sum + Number(r.payout) / 1e6, 0);
349
+ return {
350
+ address,
351
+ period,
352
+ startTime,
353
+ endTime,
354
+ volume: makerVolume + takerVolume,
355
+ tradeCount: makerFills.length + takerFills.length,
356
+ makerVolume,
357
+ takerVolume,
358
+ makerCount: makerFills.length,
359
+ takerCount: takerFills.length,
360
+ splitCount: splits.length,
361
+ mergeCount: merges.length,
362
+ redemptionCount: redemptions.length,
363
+ redemptionPayout,
364
+ };
365
+ });
366
+ }
367
+ /**
368
+ * Fetch all order filled events in a time period with pagination
369
+ */
370
+ async fetchAllFillsInPeriod(startTime, maxItems = 5000) {
371
+ const allFills = [];
372
+ let skip = 0;
373
+ const first = 1000;
374
+ while (allFills.length < maxItems) {
375
+ const fills = await this.subgraph.getOrderFilledEvents({
376
+ first,
377
+ skip,
378
+ where: startTime > 0 ? { timestamp_gte: String(startTime) } : undefined,
379
+ orderBy: 'timestamp',
380
+ orderDirection: 'desc',
381
+ });
382
+ if (fills.length === 0)
383
+ break;
384
+ allFills.push(...fills);
385
+ skip += first;
386
+ // Break if we got less than requested (no more data)
387
+ if (fills.length < first)
388
+ break;
389
+ }
390
+ return allFills.slice(0, maxItems);
391
+ }
392
+ /**
393
+ * Parse OrderFilledEvent into user trades
394
+ *
395
+ * OrderFilledEvent 结构:
396
+ * - makerAssetId = "0" (USDC): Maker 卖 USDC = BUY outcome token
397
+ * - makerAssetId = 长数字: Maker 卖 outcome token = SELL
398
+ */
399
+ parseOrderFilledEvent(fill) {
400
+ const timestamp = Number(fill.timestamp);
401
+ const makerSellsCollateral = isCollateralAsset(fill.makerAssetId);
402
+ if (makerSellsCollateral) {
403
+ // Maker: 卖 USDC, 买 Outcome Token = BUY
404
+ // Taker: 卖 Outcome Token, 买 USDC = SELL
405
+ const usdcAmount = Number(fill.makerAmountFilled) / 1e6;
406
+ const tokenAmount = Number(fill.takerAmountFilled) / 1e6;
407
+ const price = tokenAmount > 0 ? usdcAmount / tokenAmount : 0;
408
+ const tokenId = fill.takerAssetId;
409
+ return [
410
+ {
411
+ timestamp,
412
+ user: fill.maker.toLowerCase(),
413
+ role: 'maker',
414
+ side: 'BUY',
415
+ tokenId,
416
+ tokenAmount,
417
+ usdcAmount,
418
+ price,
419
+ },
420
+ {
421
+ timestamp,
422
+ user: fill.taker.toLowerCase(),
423
+ role: 'taker',
424
+ side: 'SELL',
425
+ tokenId,
426
+ tokenAmount,
427
+ usdcAmount,
428
+ price,
429
+ },
430
+ ];
431
+ }
432
+ else {
433
+ // Maker: 卖 Outcome Token, 买 USDC = SELL
434
+ // Taker: 卖 USDC, 买 Outcome Token = BUY
435
+ const tokenAmount = Number(fill.makerAmountFilled) / 1e6;
436
+ const usdcAmount = Number(fill.takerAmountFilled) / 1e6;
437
+ const price = tokenAmount > 0 ? usdcAmount / tokenAmount : 0;
438
+ const tokenId = fill.makerAssetId;
439
+ return [
440
+ {
441
+ timestamp,
442
+ user: fill.maker.toLowerCase(),
443
+ role: 'maker',
444
+ side: 'SELL',
445
+ tokenId,
446
+ tokenAmount,
447
+ usdcAmount,
448
+ price,
449
+ },
450
+ {
451
+ timestamp,
452
+ user: fill.taker.toLowerCase(),
453
+ role: 'taker',
454
+ side: 'BUY',
455
+ tokenId,
456
+ tokenAmount,
457
+ usdcAmount,
458
+ price,
459
+ },
460
+ ];
461
+ }
462
+ }
463
+ /**
464
+ * Update position with a trade and calculate realized PnL
465
+ *
466
+ * 买入: 增加持仓,更新平均成本
467
+ * 卖出: 减少持仓,计算已实现盈亏 = 卖出收入 - 成本基础
468
+ */
469
+ updatePositionWithTrade(position, trade) {
470
+ if (trade.side === 'BUY') {
471
+ // 买入:增加持仓,更新平均成本
472
+ const newAmount = position.amount + trade.tokenAmount;
473
+ const newTotalCost = position.totalCost + trade.usdcAmount;
474
+ const newAvgCost = newAmount > 0 ? newTotalCost / newAmount : 0;
475
+ return {
476
+ ...position,
477
+ amount: newAmount,
478
+ totalCost: newTotalCost,
479
+ avgCost: newAvgCost,
480
+ };
481
+ }
482
+ else {
483
+ // 卖出:减少持仓,计算已实现盈亏
484
+ const sellAmount = Math.min(trade.tokenAmount, position.amount);
485
+ const costBasis = sellAmount * position.avgCost;
486
+ const revenue = (sellAmount / trade.tokenAmount) * trade.usdcAmount;
487
+ const realizedPnl = revenue - costBasis;
488
+ const newAmount = position.amount - sellAmount;
489
+ const newTotalCost = newAmount > 0 ? newAmount * position.avgCost : 0;
490
+ return {
491
+ ...position,
492
+ amount: newAmount,
493
+ totalCost: newTotalCost,
494
+ realizedPnl: position.realizedPnl + realizedPnl,
495
+ };
496
+ }
497
+ }
498
+ /**
499
+ * Calculate unrealized PnL for a position
500
+ *
501
+ * 未实现盈亏 = 当前市值 - 总成本
502
+ * 假设当前价格约等于最后交易价格 (简化处理)
503
+ */
504
+ calculateUnrealizedPnl(position, lastPrice) {
505
+ if (position.amount <= 0)
506
+ return 0;
507
+ const currentValue = position.amount * lastPrice;
508
+ return currentValue - position.totalCost;
509
+ }
510
+ /**
511
+ * Aggregate user statistics with PnL calculation from order filled events
512
+ *
513
+ * 核心计算流程:
514
+ * 1. 解析 OrderFilledEvent 为标准化的 ParsedTrade
515
+ * 2. 按时间顺序处理每笔交易
516
+ * 3. 追踪每个用户每个 token 的持仓和平均成本
517
+ * 4. 卖出时计算已实现盈亏
518
+ * 5. 期末计算未实现盈亏
519
+ */
520
+ aggregateUserStatsWithPnl(fills) {
521
+ const userStats = new Map();
522
+ const lastPrices = new Map(); // 记录每个 token 的最后交易价格
523
+ // 初始化用户统计
524
+ const getOrCreateUserStats = (address) => {
525
+ let stats = userStats.get(address);
526
+ if (!stats) {
527
+ stats = {
528
+ address,
529
+ volume: 0,
530
+ tradeCount: 0,
531
+ buyCount: 0,
532
+ sellCount: 0,
533
+ buyVolume: 0,
534
+ sellVolume: 0,
535
+ realizedPnl: 0,
536
+ unrealizedPnl: 0,
537
+ positions: new Map(),
538
+ };
539
+ userStats.set(address, stats);
540
+ }
541
+ return stats;
542
+ };
543
+ // 解析所有交易
544
+ const allTrades = [];
545
+ for (const fill of fills) {
546
+ const trades = this.parseOrderFilledEvent(fill);
547
+ allTrades.push(...trades);
548
+ }
549
+ // 按时间排序 (升序,确保正确计算成本基础)
550
+ allTrades.sort((a, b) => a.timestamp - b.timestamp);
551
+ // 处理每笔交易
552
+ for (const trade of allTrades) {
553
+ const stats = getOrCreateUserStats(trade.user);
554
+ // 更新交易统计
555
+ stats.volume += trade.usdcAmount;
556
+ stats.tradeCount += 1;
557
+ if (trade.side === 'BUY') {
558
+ stats.buyCount += 1;
559
+ stats.buyVolume += trade.usdcAmount;
560
+ }
561
+ else {
562
+ stats.sellCount += 1;
563
+ stats.sellVolume += trade.usdcAmount;
564
+ }
565
+ // 更新持仓和计算已实现 PnL
566
+ let position = stats.positions.get(trade.tokenId);
567
+ if (!position) {
568
+ position = {
569
+ tokenId: trade.tokenId,
570
+ amount: 0,
571
+ avgCost: 0,
572
+ totalCost: 0,
573
+ realizedPnl: 0,
574
+ };
575
+ }
576
+ const oldRealizedPnl = position.realizedPnl;
577
+ position = this.updatePositionWithTrade(position, trade);
578
+ stats.positions.set(trade.tokenId, position);
579
+ // 累加已实现 PnL
580
+ stats.realizedPnl += position.realizedPnl - oldRealizedPnl;
581
+ // 记录最后价格
582
+ if (trade.price > 0) {
583
+ lastPrices.set(trade.tokenId, trade.price);
584
+ }
585
+ }
586
+ // 计算每个用户的未实现 PnL
587
+ for (const [, stats] of userStats) {
588
+ let totalUnrealizedPnl = 0;
589
+ for (const [tokenId, position] of stats.positions) {
590
+ if (position.amount > 0) {
591
+ const lastPrice = lastPrices.get(tokenId) || position.avgCost;
592
+ totalUnrealizedPnl += this.calculateUnrealizedPnl(position, lastPrice);
593
+ }
594
+ }
595
+ stats.unrealizedPnl = totalUnrealizedPnl;
596
+ }
597
+ return userStats;
598
+ }
599
+ /**
600
+ * Discover active wallets from recent trades
601
+ */
602
+ async discoverActiveWallets(limit = 100) {
603
+ const trades = await this.dataApi.getTrades({ limit: 1000 });
604
+ // Count trades per wallet
605
+ const walletCounts = new Map();
606
+ for (const trade of trades) {
607
+ if (trade.proxyWallet) {
608
+ walletCounts.set(trade.proxyWallet, (walletCounts.get(trade.proxyWallet) || 0) + 1);
609
+ }
610
+ }
611
+ // Sort by trade count
612
+ return [...walletCounts.entries()]
613
+ .sort((a, b) => b[1] - a[1])
614
+ .slice(0, limit)
615
+ .map(([address, tradeCount]) => ({ address, tradeCount }));
616
+ }
617
+ // ===== Sell Detection (Follow Wallet Strategy) =====
618
+ /**
619
+ * Detect sell activity for a wallet in a specific market
620
+ */
621
+ async detectSellActivity(address, conditionId, sinceTimestamp, peakValue) {
622
+ const activities = await this.dataApi.getActivity(address, { limit: 200, type: 'TRADE' });
623
+ const sellTransactions = activities.filter((a) => a.conditionId === conditionId && a.side === 'SELL' && a.timestamp >= sinceTimestamp);
624
+ const totalSellAmount = sellTransactions.reduce((sum, a) => sum + (a.usdcSize || a.size * a.price), 0);
625
+ // Calculate sell ratio if peak value is provided
626
+ const sellRatio = peakValue && peakValue > 0 ? totalSellAmount / peakValue : 0;
627
+ return {
628
+ totalSellAmount,
629
+ sellTransactions,
630
+ sellRatio,
631
+ shouldExit: sellRatio >= 0.3, // 30% threshold for exit signal
632
+ };
633
+ }
634
+ /**
635
+ * Track sell ratio for multiple wallets (aggregated)
636
+ */
637
+ async trackGroupSellRatio(addresses, conditionId, peakTotalValue, sinceTimestamp) {
638
+ const walletSells = [];
639
+ let cumulativeSellAmount = 0;
640
+ for (const address of addresses) {
641
+ const sellData = await this.detectSellActivity(address, conditionId, sinceTimestamp);
642
+ walletSells.push({ address, sellAmount: sellData.totalSellAmount });
643
+ cumulativeSellAmount += sellData.totalSellAmount;
644
+ }
645
+ const sellRatio = peakTotalValue > 0 ? cumulativeSellAmount / peakTotalValue : 0;
646
+ return {
647
+ cumulativeSellAmount,
648
+ sellRatio,
649
+ shouldExit: sellRatio >= 0.3,
650
+ walletSells,
651
+ };
652
+ }
653
+ // ===== Smart Score Calculation =====
654
+ calculateSmartScore(positions, activities) {
655
+ // Weights: PnL 40%, Win Rate 30%, Consistency 20%, Activity 10%
656
+ // PnL Score (0-40)
657
+ const avgPnL = positions.length > 0
658
+ ? positions.reduce((sum, p) => sum + (p.percentPnl || 0), 0) / positions.length
659
+ : 0;
660
+ const pnlScore = Math.min(40, Math.max(0, ((avgPnL + 50) / 100) * 40));
661
+ // Win Rate Score (0-30)
662
+ const winningPositions = positions.filter((p) => (p.cashPnl || 0) > 0).length;
663
+ const winRate = positions.length > 0 ? winningPositions / positions.length : 0;
664
+ const winRateScore = winRate * 30;
665
+ // Consistency Score (0-20)
666
+ const pnlValues = positions.map((p) => p.percentPnl || 0);
667
+ const variance = this.calculateVariance(pnlValues);
668
+ const consistencyScore = Math.max(0, 20 - variance / 10);
669
+ // Activity Score (0-10)
670
+ const recentTrades = activities.filter((a) => a.timestamp > Date.now() - 7 * 24 * 60 * 60 * 1000).length;
671
+ const activityScore = Math.min(10, (recentTrades / 5) * 10);
672
+ return Math.round(pnlScore + winRateScore + consistencyScore + activityScore);
673
+ }
674
+ calculateVariance(values) {
675
+ if (values.length === 0)
676
+ return 0;
677
+ const mean = values.reduce((a, b) => a + b, 0) / values.length;
678
+ return values.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / values.length;
679
+ }
680
+ }
681
+ //# sourceMappingURL=wallet-service.js.map