@catalyst-team/poly-sdk 0.1.1 β 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.
- package/README.en.md +538 -0
- package/README.md +3 -0
- package/docs/arb/test-plan.md +387 -0
- package/docs/arb/test-results.md +336 -0
- package/package.json +1 -1
- package/scripts/arb-tests/01-unit-tests.ts +495 -0
- package/scripts/arb-tests/02-integration-tests.ts +412 -0
- package/scripts/arb-tests/03-e2e-tests.ts +503 -0
- package/scripts/arb-tests/README.md +109 -0
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
|
+
[](package.json)
|
|
6
|
+
[](#testing)
|
|
7
|
+
[](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
|
package/README.md
CHANGED