@pear-protocol/hyperliquid 0.1.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.md +615 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7849 -0
- package/dist/index.d.ts +7849 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
# @pear-protocol/hyperliquid
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for building on Pear Protocol — pair trading on Hyperliquid.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @pear-protocol/hyperliquid
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { PearSdk } from '@pear-protocol/hyperliquid';
|
|
11
|
+
const sdk = new PearSdk();
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Market Data
|
|
17
|
+
|
|
18
|
+
### Instruments
|
|
19
|
+
|
|
20
|
+
Real-time prices, funding rates, and leverage for every listed asset.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const unsub = sdk.market.instrument((instruments) => {
|
|
24
|
+
for (const token of instruments) {
|
|
25
|
+
console.log(token.assetName, token.markPrice, token.funding);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Field | Type | Description |
|
|
31
|
+
|-------|------|-------------|
|
|
32
|
+
| `assetName` | `string` | Symbol (`"BTC"`, `"ETH"`) |
|
|
33
|
+
| `markPrice` | `number` | Mark price |
|
|
34
|
+
| `oraclePrice` | `number` | Oracle price |
|
|
35
|
+
| `prevDayPrice` | `number` | Previous day price |
|
|
36
|
+
| `priceChange24hPercent` | `number` | 24h change % |
|
|
37
|
+
| `funding` | `number` | Funding rate |
|
|
38
|
+
| `maxLeverage` | `number` | Max leverage |
|
|
39
|
+
|
|
40
|
+
### Basket Snapshot
|
|
41
|
+
|
|
42
|
+
Real-time computed metrics for any long/short basket. Weights are decimals (0-1), and the sum of all long + short weights must equal 1.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const unsub = sdk.market.basket(
|
|
46
|
+
[{ symbol: 'BTC', weight: 0.25 }, { symbol: 'ETH', weight: 0.25 }],
|
|
47
|
+
[{ symbol: 'SOL', weight: 0.5 }],
|
|
48
|
+
(snapshot) => {
|
|
49
|
+
snapshot.weightedRatio; // geometric mean ratio
|
|
50
|
+
snapshot.priceRatio; // long/short price (1L:1S only, else null)
|
|
51
|
+
snapshot.price; // single-asset price (1L or 1S only, else null)
|
|
52
|
+
snapshot.weightedRatio24hAgo; // weightedRatio 24h ago
|
|
53
|
+
snapshot.priceRatio24hAgo; // priceRatio 24h ago (1L:1S only, else null)
|
|
54
|
+
snapshot.price24hAgo; // price 24h ago (1L or 1S only, else null)
|
|
55
|
+
snapshot.netFundingRate; // weighted net funding in decimal form (e.g. 0.01 for 1%)
|
|
56
|
+
snapshot.maxLeverage; // min across all assets
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Ideas (Picks)
|
|
62
|
+
|
|
63
|
+
Pre-curated trading baskets streamed via WebSocket. Includes actives, watchlist (when authenticated), and AI-generated pairs.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const unsub = sdk.market.picks((baskets) => {
|
|
67
|
+
for (const b of baskets) {
|
|
68
|
+
b.category; // 'active' | 'watchlist' | 'ai-picks'
|
|
69
|
+
b.name; // optional display name
|
|
70
|
+
b.longTokens; // TokenSelection[] (symbol + weight)
|
|
71
|
+
b.shortTokens;
|
|
72
|
+
b.weightedRatio; // computed from live prices
|
|
73
|
+
b.netFundingRate;
|
|
74
|
+
b.maxLeverage;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Filter by category on the client:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const aiPicks = baskets.filter((b) => b.category === 'ai-picks');
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Chart
|
|
86
|
+
|
|
87
|
+
Historical and real-time OHLCV candle data for any basket or individual asset.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const { chart } = sdk.market;
|
|
91
|
+
|
|
92
|
+
// 1. Configure tokens
|
|
93
|
+
chart.setTokens(
|
|
94
|
+
[{ symbol: 'BTC', weight: 0.25 }, { symbol: 'ETH', weight: 0.25 }],
|
|
95
|
+
[{ symbol: 'SOL', weight: 0.5 }],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// 2. Set candle interval (default: '1h')
|
|
99
|
+
chart.setCandleInterval('15m');
|
|
100
|
+
|
|
101
|
+
// 3. Fetch historical bars (requires chart type)
|
|
102
|
+
const bars = await chart.getBars('weighted-ratio', startMs, endMs);
|
|
103
|
+
const assetBars = await chart.getAssetBars('BTC', startMs, endMs);
|
|
104
|
+
|
|
105
|
+
// 4. Real-time basket updates
|
|
106
|
+
const subId = chart.subscribeRealtimeBars('weighted-ratio', (bar) => {
|
|
107
|
+
console.log(bar.time, bar.close);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// 5. Real-time single-asset updates
|
|
111
|
+
const assetSubId = chart.subscribeRealtimeAssetBars('BTC', (bar) => {
|
|
112
|
+
console.log(bar.time, bar.close);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// 6. Unsubscribe
|
|
116
|
+
chart.unsubscribeRealtimeBars(subId);
|
|
117
|
+
chart.unsubscribeRealtimeBars(assetSubId);
|
|
118
|
+
|
|
119
|
+
// 7. Get earliest data boundary (useful for "no data" pagination)
|
|
120
|
+
const boundary = chart.getEffectiveDataBoundary(); // ms timestamp | null
|
|
121
|
+
|
|
122
|
+
// 8. Clear cached data (call after changing tokens or interval)
|
|
123
|
+
chart.clearCache();
|
|
124
|
+
|
|
125
|
+
// 9. Cleanup
|
|
126
|
+
chart.destroy();
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Chart Types
|
|
130
|
+
|
|
131
|
+
| Type | Description |
|
|
132
|
+
|------|-------------|
|
|
133
|
+
| `'weighted-ratio'` | Geometric mean of weighted long basket / short basket prices |
|
|
134
|
+
| `'price-ratio'` | Long / short price ratio (1L:1S pairs only, else empty) |
|
|
135
|
+
| `'performance'` | Individual asset performance from a baseline — use with overlays |
|
|
136
|
+
| `'price'` | Single-asset price |
|
|
137
|
+
|
|
138
|
+
#### Candle Intervals
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
type CandleInterval =
|
|
142
|
+
| '1m' | '3m' | '5m' | '15m' | '30m'
|
|
143
|
+
| '1h' | '2h' | '4h' | '8h' | '12h'
|
|
144
|
+
| '1d' | '3d' | '1w' | '1M';
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Bar Shape
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
interface Bar {
|
|
151
|
+
time: number; // ms timestamp
|
|
152
|
+
open: number;
|
|
153
|
+
high: number;
|
|
154
|
+
low: number;
|
|
155
|
+
close: number;
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### TradingView Datafeed Integration
|
|
160
|
+
|
|
161
|
+
The SDK chart plugs into TradingView's `IBasicDataFeed`. Two key methods to wire up:
|
|
162
|
+
|
|
163
|
+
#### `getBars` — Historical Data
|
|
164
|
+
|
|
165
|
+
Convert TradingView's resolution to a `CandleInterval`, set it on the chart, then fetch bars. TradingView passes timestamps in seconds — multiply by 1000 for the SDK.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
// Map TradingView resolution strings to SDK intervals:
|
|
169
|
+
// '1' → '1m', '5' → '5m', '60' → '1h', '240' → '4h', '1D' → '1d', etc.
|
|
170
|
+
|
|
171
|
+
const sdkInterval = tvResolutionToSdkInterval(resolution);
|
|
172
|
+
chart.setCandleInterval(sdkInterval);
|
|
173
|
+
|
|
174
|
+
const startMs = periodParams.from * 1000;
|
|
175
|
+
const endMs = periodParams.to * 1000;
|
|
176
|
+
|
|
177
|
+
// For basket symbols → chart.getBars(chartType, startMs, endMs)
|
|
178
|
+
// For individual assets (e.g. 'LONG:BTC') → chart.getAssetBars('BTC', startMs, endMs)
|
|
179
|
+
const bars = await chart.getBars('weighted-ratio', startMs, endMs);
|
|
180
|
+
|
|
181
|
+
// When no data, use getEffectiveDataBoundary() to tell TradingView where data starts
|
|
182
|
+
if (bars.length === 0) {
|
|
183
|
+
const boundary = chart.getEffectiveDataBoundary();
|
|
184
|
+
onResult([], { noData: true, nextTime: boundary ? boundary / 1000 : undefined });
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### `subscribeBars` — Real-time Updates
|
|
189
|
+
|
|
190
|
+
Subscribe to live bar updates. Map each TradingView `listenerGuid` to an SDK subscription ID for cleanup.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
// For basket symbols:
|
|
194
|
+
const subId = chart.subscribeRealtimeBars('weighted-ratio', (bar) => {
|
|
195
|
+
onTick({ time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close });
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// For individual assets (used in 'performance' overlays):
|
|
199
|
+
const subId = chart.subscribeRealtimeAssetBars('BTC', (bar) => {
|
|
200
|
+
onTick({ time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close });
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Unsubscribe:
|
|
204
|
+
chart.unsubscribeRealtimeBars(subId);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 2. Authentication
|
|
210
|
+
|
|
211
|
+
EIP-712 signature-based login. Tokens auto-refresh on 401.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
// 1. Get message to sign
|
|
215
|
+
const { data: msg } = await sdk.api.public.auth.getEIP712Message(address, clientId, chainId);
|
|
216
|
+
|
|
217
|
+
// 2. Sign with wallet (e.g. wagmi signTypedData)
|
|
218
|
+
const signature = await signTypedData({ ... });
|
|
219
|
+
|
|
220
|
+
// 3. Login
|
|
221
|
+
await sdk.api.public.auth.login({
|
|
222
|
+
address,
|
|
223
|
+
clientId: CLIENT_ID,
|
|
224
|
+
details: { signature, timestamp: eip712.timestamp, chainId },
|
|
225
|
+
method: 'eip712',
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// 4. Set address for WebSocket subscriptions
|
|
229
|
+
sdk.setAddress(address);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Token persistence:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
// Restore from storage
|
|
236
|
+
sdk.api.setTokens(savedAccessToken, savedRefreshToken);
|
|
237
|
+
|
|
238
|
+
// Read current tokens
|
|
239
|
+
const accessToken = sdk.api.getAccessToken();
|
|
240
|
+
const refreshToken = sdk.api.getRefreshToken();
|
|
241
|
+
|
|
242
|
+
// Listen for refreshes
|
|
243
|
+
sdk.api.onTokenRefreshed = (access, refresh) => {
|
|
244
|
+
localStorage.setItem('tokens', JSON.stringify({ access, refresh }));
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
sdk.api.onLogout = () => {
|
|
248
|
+
localStorage.removeItem('tokens');
|
|
249
|
+
};
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Approval Checks
|
|
253
|
+
|
|
254
|
+
Before trading, two Hyperliquid approvals are required:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
const builderOk = await sdk.user.isBuilderApproved();
|
|
258
|
+
const agentOk = await sdk.user.isAgentApproved();
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Builder fee approval** — if `isBuilderApproved()` returns `false`, the user must send an `approveBuilderFee` action to the Hyperliquid exchange endpoint ([docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#approve-a-builder-fee)):
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
// Sign and send via Hyperliquid exchange API
|
|
265
|
+
{
|
|
266
|
+
"type": "approveBuilderFee",
|
|
267
|
+
"builder": "0xA47D4d99191db54A4829cdf3de2417E527c3b042",
|
|
268
|
+
"maxFeeRate": "0.06%"
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Agent wallet approval** — if `isAgentApproved()` returns `false`, create and approve via:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
await sdk.api.private.agentWallet.create();
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 3. Trading
|
|
281
|
+
|
|
282
|
+
### Open a Position
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
await sdk.api.private.positions.create({
|
|
286
|
+
usdValue: 1000,
|
|
287
|
+
leverage: 5,
|
|
288
|
+
longAssets: [{ asset: 'BTC', weight: 0.25 }, { asset: 'ETH', weight: 0.25 }],
|
|
289
|
+
shortAssets: [{ asset: 'SOL', weight: 0.5 }],
|
|
290
|
+
executionType: 'MARKET',
|
|
291
|
+
slippage: 0.02,
|
|
292
|
+
takeProfit: { type: 'PERCENTAGE', value: 10 },
|
|
293
|
+
stopLoss: { type: 'PERCENTAGE', value: -5 },
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Close
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
await sdk.api.private.positions.close(positionId, { executionType: 'MARKET' });
|
|
301
|
+
await sdk.api.private.positions.closeAll({ executionType: 'MARKET' });
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Adjust Size & Leverage
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
await sdk.api.private.positions.adjust(positionId, {
|
|
308
|
+
adjustmentType: 'INCREASE',
|
|
309
|
+
usdValue: 500,
|
|
310
|
+
executionType: 'MARKET',
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
await sdk.api.private.positions.adjustLeverage(positionId, {
|
|
314
|
+
leverage: 10,
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Update TP/SL
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
await sdk.api.private.positions.updateRiskParameters(positionId, {
|
|
322
|
+
takeProfit: { type: 'PERCENTAGE', value: 15 },
|
|
323
|
+
stopLoss: { type: 'PERCENTAGE', value: -8, trailingStop: true },
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Rebalance Weights
|
|
328
|
+
|
|
329
|
+
Preview then execute weight changes on an existing position:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
const { data: plan } = await sdk.api.private.positions.planRebalance(positionId, {
|
|
333
|
+
targetWeights: { BTC: 0.3, ETH: 0.2, SOL: 0.5 },
|
|
334
|
+
});
|
|
335
|
+
// plan.assets — what will be traded
|
|
336
|
+
// plan.skippedAssets — what can't be changed and why
|
|
337
|
+
|
|
338
|
+
await sdk.api.private.positions.rebalance(positionId, {
|
|
339
|
+
targetWeights: { BTC: 0.3, ETH: 0.2, SOL: 0.5 },
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Adjust (Advanced)
|
|
344
|
+
|
|
345
|
+
Set target absolute sizes per asset on an existing position:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
await sdk.api.private.positions.adjustAdvance(positionId, [{
|
|
349
|
+
longAssets: [{ asset: 'BTC', size: 0.5 }, { asset: 'ETH', size: 2.0 }],
|
|
350
|
+
shortAssets: [{ asset: 'SOL', size: 10.0 }],
|
|
351
|
+
}]);
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### TWAP Orders
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
await sdk.api.private.positions.create({
|
|
358
|
+
usdValue: 5000,
|
|
359
|
+
leverage: 3,
|
|
360
|
+
longAssets: [{ asset: 'BTC', weight: 0.5 }],
|
|
361
|
+
shortAssets: [{ asset: 'ETH', weight: 0.5 }],
|
|
362
|
+
executionType: 'TWAP',
|
|
363
|
+
twapDuration: 3600,
|
|
364
|
+
twapInterval: 300,
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Spot Orders
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
await sdk.api.private.orders.spot({
|
|
372
|
+
coin: 'HYPE',
|
|
373
|
+
isBuy: true,
|
|
374
|
+
sz: 100,
|
|
375
|
+
limitPx: null,
|
|
376
|
+
});
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Cancel Orders
|
|
380
|
+
|
|
381
|
+
```ts
|
|
382
|
+
await sdk.api.private.orders.cancel(orderId);
|
|
383
|
+
await sdk.api.private.orders.cancelTwap(orderId);
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 4. Tracking
|
|
389
|
+
|
|
390
|
+
All subscriptions return an unsubscribe function. Require `sdk.setAddress()` first.
|
|
391
|
+
|
|
392
|
+
### Positions
|
|
393
|
+
|
|
394
|
+
Enriched with real-time mark prices from Hyperliquid.
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
const unsub = sdk.user.positions((positions) => {
|
|
398
|
+
for (const pos of positions) {
|
|
399
|
+
pos.positionId;
|
|
400
|
+
pos.entryRatio;
|
|
401
|
+
pos.markRatio;
|
|
402
|
+
pos.unrealizedPnl;
|
|
403
|
+
pos.unrealizedPnlPercentage;
|
|
404
|
+
pos.marginUsed;
|
|
405
|
+
pos.positionValue;
|
|
406
|
+
pos.stopLoss;
|
|
407
|
+
pos.takeProfit;
|
|
408
|
+
|
|
409
|
+
for (const asset of [...pos.longAssets, ...pos.shortAssets]) {
|
|
410
|
+
asset.coin;
|
|
411
|
+
asset.entryPrice;
|
|
412
|
+
asset.actualSize;
|
|
413
|
+
asset.leverage;
|
|
414
|
+
asset.unrealizedPnl;
|
|
415
|
+
asset.currentWeight;
|
|
416
|
+
asset.fundingPaid;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Orders
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
sdk.user.orders((orders) => {
|
|
426
|
+
for (const ord of orders) {
|
|
427
|
+
ord.orderId;
|
|
428
|
+
ord.orderType; // 'MARKET' | 'TRIGGER' | 'TWAP' | 'TP' | 'SL'
|
|
429
|
+
ord.status; // 'OPEN' | 'PROCESSING' | 'EXECUTED'
|
|
430
|
+
ord.longAssets;
|
|
431
|
+
ord.shortAssets;
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Account & History
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
sdk.user.accountSummary((summary) => { ... });
|
|
440
|
+
sdk.user.tradeHistories((histories) => { ... });
|
|
441
|
+
sdk.user.twapDetails((twaps) => { ... });
|
|
442
|
+
sdk.user.notifications((notifications) => {
|
|
443
|
+
for (const n of notifications) {
|
|
444
|
+
n.category; // 'TP_ORDER_FILLED' | 'POSITION_LIQUIDATED' | ...
|
|
445
|
+
n.parameters; // typed per category
|
|
446
|
+
n.is_read;
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Fills Sync
|
|
452
|
+
|
|
453
|
+
Automatic. Syncs on first connection and on every `userFills` WebSocket event from Hyperliquid. No manual call needed.
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## 5. REST API Reference
|
|
458
|
+
|
|
459
|
+
### Public (no auth)
|
|
460
|
+
|
|
461
|
+
```ts
|
|
462
|
+
await sdk.api.public.markets.list({ searchText: 'BTC', sort: 'volume' });
|
|
463
|
+
await sdk.api.public.markets.active();
|
|
464
|
+
await sdk.api.public.fills.list({ address: '0x...', assetName: 'BTC' });
|
|
465
|
+
await sdk.api.public.stats.addressStats('0xabc,0xdef');
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Private (requires auth)
|
|
469
|
+
|
|
470
|
+
```ts
|
|
471
|
+
// Account
|
|
472
|
+
await sdk.api.private.accounts.summary();
|
|
473
|
+
await sdk.api.private.portfolio.get();
|
|
474
|
+
|
|
475
|
+
// Agent wallet
|
|
476
|
+
await sdk.api.private.agentWallet.get();
|
|
477
|
+
await sdk.api.private.agentWallet.create();
|
|
478
|
+
|
|
479
|
+
// API keys
|
|
480
|
+
await sdk.api.private.apiKeys.list();
|
|
481
|
+
await sdk.api.private.apiKeys.create({ name: 'Bot Key' });
|
|
482
|
+
|
|
483
|
+
// Positions — see Trading section above
|
|
484
|
+
await sdk.api.private.positions.list();
|
|
485
|
+
await sdk.api.private.positions.create({ ... });
|
|
486
|
+
await sdk.api.private.positions.close(id, { ... });
|
|
487
|
+
await sdk.api.private.positions.closeAll({ ... });
|
|
488
|
+
await sdk.api.private.positions.adjust(id, { ... });
|
|
489
|
+
await sdk.api.private.positions.adjustAdvance(id, [{ longAssets: [{ asset, size }], shortAssets: [{ asset, size }] }]);
|
|
490
|
+
await sdk.api.private.positions.adjustLeverage(id, { ... });
|
|
491
|
+
await sdk.api.private.positions.updateRiskParameters(id, { ... });
|
|
492
|
+
await sdk.api.private.positions.planRebalance(id, { targetWeights });
|
|
493
|
+
await sdk.api.private.positions.rebalance(id, { targetWeights });
|
|
494
|
+
|
|
495
|
+
// Orders
|
|
496
|
+
await sdk.api.private.orders.list({ page: 1, limit: 50, status: 'EXECUTED' });
|
|
497
|
+
await sdk.api.private.orders.open();
|
|
498
|
+
await sdk.api.private.orders.twap();
|
|
499
|
+
await sdk.api.private.orders.cancel(orderId);
|
|
500
|
+
await sdk.api.private.orders.cancelTwap(orderId);
|
|
501
|
+
await sdk.api.private.orders.spot({ ... });
|
|
502
|
+
await sdk.api.private.orders.triggers({ category: 'TP' });
|
|
503
|
+
await sdk.api.private.orders.kalshiTriggers({ category: 'active', search: '', cursor: '', pageSize: 20 });
|
|
504
|
+
|
|
505
|
+
// History
|
|
506
|
+
await sdk.api.private.tradeHistory.list({ limit: 100 });
|
|
507
|
+
await sdk.api.private.notifications.list({ limit: 50 });
|
|
508
|
+
await sdk.api.private.notifications.markRead({ ids: ['...'] });
|
|
509
|
+
|
|
510
|
+
// Watchlist
|
|
511
|
+
await sdk.api.private.watchlist.get();
|
|
512
|
+
await sdk.api.private.watchlist.toggle({ longAssets: [...], shortAssets: [...] });
|
|
513
|
+
|
|
514
|
+
// Sync
|
|
515
|
+
await sdk.api.private.sync.fills({ user, fills, assetPositions });
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## 6. Error Handling
|
|
521
|
+
|
|
522
|
+
All API methods throw `PearApiError` on failure. Each error carries a machine-readable `code`, HTTP `statusCode`, and human-readable `message`.
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
import { PearApiError, PearErrorCode } from '@pear-protocol/hyperliquid';
|
|
526
|
+
|
|
527
|
+
try {
|
|
528
|
+
await sdk.api.private.positions.close(positionId, { executionType: 'MARKET' });
|
|
529
|
+
} catch (err) {
|
|
530
|
+
if (err instanceof PearApiError) {
|
|
531
|
+
err.statusCode; // 404
|
|
532
|
+
err.code; // 'POSITION_NOT_FOUND'
|
|
533
|
+
err.message; // 'Position abc123 not found or already closed'
|
|
534
|
+
err.originalError; // raw AxiosError (for headers, response body, etc.)
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Handling by Status Code
|
|
540
|
+
|
|
541
|
+
```ts
|
|
542
|
+
try {
|
|
543
|
+
await sdk.api.private.positions.create({ ... });
|
|
544
|
+
} catch (err) {
|
|
545
|
+
if (!(err instanceof PearApiError)) throw err;
|
|
546
|
+
|
|
547
|
+
switch (err.statusCode) {
|
|
548
|
+
case 400: showValidationError(err.message); break;
|
|
549
|
+
case 404: showNotFound(err.message); break;
|
|
550
|
+
case 408: retryAfterDelay(); break; // ACTIVE_TRADE_TIMEOUT
|
|
551
|
+
case 409: showConflict(err.message); break;
|
|
552
|
+
case 429: backoff(); break;
|
|
553
|
+
default: showGenericError(); break;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### Handling by Error Code
|
|
559
|
+
|
|
560
|
+
```ts
|
|
561
|
+
if (err instanceof PearApiError && err.code === PearErrorCode.ACTIVE_TRADE_TIMEOUT) {
|
|
562
|
+
// Another trade is in progress, retry shortly
|
|
563
|
+
await sleep(2000);
|
|
564
|
+
return retry();
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### Network Errors
|
|
569
|
+
|
|
570
|
+
When the server is unreachable, `PearApiError` is thrown with `statusCode: 0` and `code: 'NETWORK_ERROR'`.
|
|
571
|
+
|
|
572
|
+
```ts
|
|
573
|
+
if (err instanceof PearApiError && err.statusCode === 0) {
|
|
574
|
+
showOfflineMessage();
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### Error Codes Reference
|
|
579
|
+
|
|
580
|
+
| Code | Status | When |
|
|
581
|
+
|------|--------|------|
|
|
582
|
+
| `POSITION_NOT_FOUND` | 404 | Position doesn't exist or already closed |
|
|
583
|
+
| `POSITION_NOT_OPEN` | 400 | Position exists but isn't open |
|
|
584
|
+
| `POSITION_UNAUTHORIZED` | 403 | Position belongs to another user |
|
|
585
|
+
| `ORDER_NOT_FOUND` | 404 | Order doesn't exist |
|
|
586
|
+
| `INVALID_ORDER_STATUS` | 400 | Order can't be cancelled in current status |
|
|
587
|
+
| `INVALID_ORDER_TYPE` | 400 | Wrong order type for the endpoint |
|
|
588
|
+
| `ACTIVE_TRADE_TIMEOUT` | 408 | Another trade is still processing |
|
|
589
|
+
| `UNSUPPORTED_EXECUTION_TYPE` | 400 | Execution type not supported for this action |
|
|
590
|
+
| `DUPLICATE_TRIGGER` | 409 | Position already has an active trigger |
|
|
591
|
+
| `INVALID_ADDRESS` | 400 | Malformed wallet address |
|
|
592
|
+
| `MISSING_REQUIRED_FIELD` | 400 | Required parameter missing |
|
|
593
|
+
| `INVALID_FIELD_VALUE` | 400 | Parameter value is invalid |
|
|
594
|
+
| `UNSUPPORTED_TRIGGER_TYPE` | 400 | Trigger type not supported |
|
|
595
|
+
| `INVALID_POSITION_STRUCTURE` | 400 | Position assets don't match trigger requirements |
|
|
596
|
+
| `INVALID_LADDER_CONFIG` | 400 | Missing or invalid ladder configuration |
|
|
597
|
+
| `TWAP_DURATION_REQUIRED` | 400 | TWAP order missing duration |
|
|
598
|
+
| `TWAP_INSUFFICIENT_VALUE` | 400 | Asset value too low for TWAP chunking |
|
|
599
|
+
| `INVALID_RISK_PARAMETERS` | 400 | Can't update TP/SL for this position |
|
|
600
|
+
| `HL_CANCEL_FAILED` | 500 | Failed to cancel order on Hyperliquid |
|
|
601
|
+
| `LEVERAGE_CONFIG_FAILED` | 400 | Leverage configuration failed |
|
|
602
|
+
| `VAULT_WALLET_NOT_FOUND` | 404 | Vault wallet doesn't exist |
|
|
603
|
+
| `VAULT_UNAUTHORIZED` | 403 | Vault doesn't belong to this user |
|
|
604
|
+
| `VAULT_UNSUPPORTED_TOKEN` | 400 | Token not supported for this vault operation |
|
|
605
|
+
| `INTERNAL_ERROR` | 500 | Unexpected server error |
|
|
606
|
+
| `NETWORK_ERROR` | 0 | Server unreachable |
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## 7. Cleanup
|
|
611
|
+
|
|
612
|
+
```ts
|
|
613
|
+
sdk.destroy(); // closes all WebSocket connections and subscriptions
|
|
614
|
+
```
|
|
615
|
+
|