@lynx-crypto/kraken-api 1.1.0 → 1.1.1
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 +119 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,15 +69,15 @@ Bulk workflow:
|
|
|
69
69
|
|
|
70
70
|
Using Yarn:
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
```sh
|
|
73
73
|
yarn add @lynx-crypto/kraken-api
|
|
74
|
-
|
|
74
|
+
```
|
|
75
75
|
|
|
76
76
|
Using npm:
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
```sh
|
|
79
79
|
npm install @lynx-crypto/kraken-api
|
|
80
|
-
|
|
80
|
+
```
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
@@ -96,21 +96,21 @@ npm install @lynx-crypto/kraken-api
|
|
|
96
96
|
|
|
97
97
|
## Environment Variables (for private usage)
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
```bash
|
|
100
100
|
KRAKEN_API_KEY="your_api_key"
|
|
101
101
|
KRAKEN_API_SECRET="your_api_secret"
|
|
102
|
-
|
|
102
|
+
```
|
|
103
103
|
|
|
104
104
|
## Environment Variables (optional, for bulk downloads)
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
```bash
|
|
107
107
|
|
|
108
108
|
# If set, bulk downloads can be performed via the Google Drive API.
|
|
109
109
|
|
|
110
110
|
# If not set, ZIPs can be downloaded manually and placed into the expected folders.
|
|
111
111
|
|
|
112
112
|
LYNX_CRYPTO_KRAKEN_API_GOOGLE_DRIVE_API_KEY="your_google_drive_api_key"
|
|
113
|
-
|
|
113
|
+
```
|
|
114
114
|
|
|
115
115
|
---
|
|
116
116
|
|
|
@@ -154,52 +154,52 @@ All public request/response and stream payloads are exported as TypeScript types
|
|
|
154
154
|
|
|
155
155
|
### Public REST example (no authentication)
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
```ts
|
|
158
158
|
import { KrakenSpotRestClient } from '@lynx-crypto/kraken-api';
|
|
159
159
|
|
|
160
160
|
async function main() {
|
|
161
|
-
const kraken = new KrakenSpotRestClient({
|
|
162
|
-
userAgent: 'example-app/1.0.0',
|
|
163
|
-
});
|
|
161
|
+
const kraken = new KrakenSpotRestClient({
|
|
162
|
+
userAgent: 'example-app/1.0.0',
|
|
163
|
+
});
|
|
164
164
|
|
|
165
|
-
const time = await kraken.marketData.getServerTime();
|
|
166
|
-
console.log('Kraken time:', time.rfc1123);
|
|
165
|
+
const time = await kraken.marketData.getServerTime();
|
|
166
|
+
console.log('Kraken time:', time.rfc1123);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
main().catch((err) => {
|
|
170
|
-
console.error(err);
|
|
171
|
-
process.exitCode = 1;
|
|
170
|
+
console.error(err);
|
|
171
|
+
process.exitCode = 1;
|
|
172
172
|
});
|
|
173
|
-
|
|
173
|
+
```
|
|
174
174
|
|
|
175
175
|
### Private REST example (authenticated)
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
```ts
|
|
178
178
|
import 'dotenv/config';
|
|
179
179
|
import { KrakenSpotRestClient } from '@lynx-crypto/kraken-api';
|
|
180
180
|
|
|
181
181
|
function requireEnv(name: string): string {
|
|
182
|
-
const v = process.env[name];
|
|
183
|
-
if (!v) throw new Error(`Missing env var: ${name}`);
|
|
184
|
-
return v;
|
|
182
|
+
const v = process.env[name];
|
|
183
|
+
if (!v) throw new Error(`Missing env var: ${name}`);
|
|
184
|
+
return v;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
async function main() {
|
|
188
|
-
const kraken = new KrakenSpotRestClient({
|
|
189
|
-
userAgent: 'example-app/1.0.0',
|
|
190
|
-
apiKey: requireEnv('KRAKEN_API_KEY'),
|
|
191
|
-
apiSecret: requireEnv('KRAKEN_API_SECRET'),
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const balances = await kraken.accountData.getAccountBalance();
|
|
195
|
-
console.log('Asset count:', Object.keys(balances).length);
|
|
188
|
+
const kraken = new KrakenSpotRestClient({
|
|
189
|
+
userAgent: 'example-app/1.0.0',
|
|
190
|
+
apiKey: requireEnv('KRAKEN_API_KEY'),
|
|
191
|
+
apiSecret: requireEnv('KRAKEN_API_SECRET'),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const balances = await kraken.accountData.getAccountBalance();
|
|
195
|
+
console.log('Asset count:', Object.keys(balances).length);
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
main().catch((err) => {
|
|
199
|
-
console.error(err);
|
|
200
|
-
process.exitCode = 1;
|
|
199
|
+
console.error(err);
|
|
200
|
+
process.exitCode = 1;
|
|
201
201
|
});
|
|
202
|
-
|
|
202
|
+
```
|
|
203
203
|
|
|
204
204
|
---
|
|
205
205
|
|
|
@@ -207,95 +207,95 @@ process.exitCode = 1;
|
|
|
207
207
|
|
|
208
208
|
### Public WebSocket (market data)
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
```ts
|
|
211
211
|
import { KrakenSpotWebsocketV2Client } from '@lynx-crypto/kraken-api';
|
|
212
212
|
|
|
213
213
|
async function main() {
|
|
214
|
-
const wsClient = new KrakenSpotWebsocketV2Client({
|
|
215
|
-
autoReconnect: false,
|
|
216
|
-
});
|
|
214
|
+
const wsClient = new KrakenSpotWebsocketV2Client({
|
|
215
|
+
autoReconnect: false,
|
|
216
|
+
});
|
|
217
217
|
|
|
218
|
-
await wsClient.publicConnection.connect();
|
|
218
|
+
await wsClient.publicConnection.connect();
|
|
219
219
|
|
|
220
|
-
const ack = await wsClient.marketData.subscribeTrade(
|
|
221
|
-
{ symbol: ['BTC/USD'], snapshot: true },
|
|
222
|
-
{ reqId: 1 },
|
|
223
|
-
);
|
|
220
|
+
const ack = await wsClient.marketData.subscribeTrade(
|
|
221
|
+
{ symbol: ['BTC/USD'], snapshot: true },
|
|
222
|
+
{ reqId: 1 },
|
|
223
|
+
);
|
|
224
224
|
|
|
225
|
-
if (!ack.success) {
|
|
226
|
-
throw new Error(`Subscribe failed: ${ack.error}`);
|
|
227
|
-
}
|
|
225
|
+
if (!ack.success) {
|
|
226
|
+
throw new Error(`Subscribe failed: ${ack.error}`);
|
|
227
|
+
}
|
|
228
228
|
|
|
229
|
-
const unsubscribe = wsClient.publicConnection.addMessageHandler((msg) => {
|
|
230
|
-
console.log(JSON.stringify(msg));
|
|
231
|
-
});
|
|
229
|
+
const unsubscribe = wsClient.publicConnection.addMessageHandler((msg) => {
|
|
230
|
+
console.log(JSON.stringify(msg));
|
|
231
|
+
});
|
|
232
232
|
|
|
233
|
-
setTimeout(() => {
|
|
234
|
-
unsubscribe();
|
|
235
|
-
wsClient.publicConnection.close(1000, 'example done');
|
|
236
|
-
}, 20_000);
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
unsubscribe();
|
|
235
|
+
wsClient.publicConnection.close(1000, 'example done');
|
|
236
|
+
}, 20_000);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
main().catch((err) => {
|
|
240
|
-
console.error(err);
|
|
241
|
-
process.exitCode = 1;
|
|
240
|
+
console.error(err);
|
|
241
|
+
process.exitCode = 1;
|
|
242
242
|
});
|
|
243
|
-
|
|
243
|
+
```
|
|
244
244
|
|
|
245
245
|
### Private WebSocket (balances / executions)
|
|
246
246
|
|
|
247
247
|
Authenticated WebSocket usage requires a token obtained via REST.
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
```ts
|
|
250
250
|
import 'dotenv/config';
|
|
251
251
|
import {
|
|
252
|
-
KrakenSpotRestClient,
|
|
253
|
-
KrakenSpotWebsocketV2Client,
|
|
252
|
+
KrakenSpotRestClient,
|
|
253
|
+
KrakenSpotWebsocketV2Client,
|
|
254
254
|
} from '@lynx-crypto/kraken-api';
|
|
255
255
|
|
|
256
256
|
function requireEnv(name: string): string {
|
|
257
|
-
const v = process.env[name];
|
|
258
|
-
if (!v) throw new Error(`Missing env var: ${name}`);
|
|
259
|
-
return v;
|
|
257
|
+
const v = process.env[name];
|
|
258
|
+
if (!v) throw new Error(`Missing env var: ${name}`);
|
|
259
|
+
return v;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
async function main() {
|
|
263
|
-
const rest = new KrakenSpotRestClient({
|
|
264
|
-
userAgent: 'example-app/1.0.0',
|
|
265
|
-
apiKey: requireEnv('KRAKEN_API_KEY'),
|
|
266
|
-
apiSecret: requireEnv('KRAKEN_API_SECRET'),
|
|
267
|
-
});
|
|
263
|
+
const rest = new KrakenSpotRestClient({
|
|
264
|
+
userAgent: 'example-app/1.0.0',
|
|
265
|
+
apiKey: requireEnv('KRAKEN_API_KEY'),
|
|
266
|
+
apiSecret: requireEnv('KRAKEN_API_SECRET'),
|
|
267
|
+
});
|
|
268
268
|
|
|
269
|
-
const { token } = await rest.trading.getWebSocketsToken();
|
|
269
|
+
const { token } = await rest.trading.getWebSocketsToken();
|
|
270
270
|
|
|
271
|
-
const wsClient = new KrakenSpotWebsocketV2Client({
|
|
272
|
-
authToken: token,
|
|
273
|
-
autoReconnect: false,
|
|
274
|
-
});
|
|
271
|
+
const wsClient = new KrakenSpotWebsocketV2Client({
|
|
272
|
+
authToken: token,
|
|
273
|
+
autoReconnect: false,
|
|
274
|
+
});
|
|
275
275
|
|
|
276
|
-
await wsClient.privateConnection.connect();
|
|
276
|
+
await wsClient.privateConnection.connect();
|
|
277
277
|
|
|
278
|
-
const ack = await wsClient.userData.subscribeBalances({}, { reqId: 10 });
|
|
278
|
+
const ack = await wsClient.userData.subscribeBalances({}, { reqId: 10 });
|
|
279
279
|
|
|
280
|
-
if (!ack.success) {
|
|
281
|
-
throw new Error(`Subscribe failed: ${ack.error}`);
|
|
282
|
-
}
|
|
280
|
+
if (!ack.success) {
|
|
281
|
+
throw new Error(`Subscribe failed: ${ack.error}`);
|
|
282
|
+
}
|
|
283
283
|
|
|
284
|
-
const off = wsClient.privateConnection.addMessageHandler((msg) => {
|
|
285
|
-
console.log(JSON.stringify(msg));
|
|
286
|
-
});
|
|
284
|
+
const off = wsClient.privateConnection.addMessageHandler((msg) => {
|
|
285
|
+
console.log(JSON.stringify(msg));
|
|
286
|
+
});
|
|
287
287
|
|
|
288
|
-
setTimeout(() => {
|
|
289
|
-
off();
|
|
290
|
-
wsClient.privateConnection.close(1000, 'example done');
|
|
291
|
-
}, 30_000);
|
|
288
|
+
setTimeout(() => {
|
|
289
|
+
off();
|
|
290
|
+
wsClient.privateConnection.close(1000, 'example done');
|
|
291
|
+
}, 30_000);
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
main().catch((err) => {
|
|
295
|
-
console.error(err);
|
|
296
|
-
process.exitCode = 1;
|
|
295
|
+
console.error(err);
|
|
296
|
+
process.exitCode = 1;
|
|
297
297
|
});
|
|
298
|
-
|
|
298
|
+
```
|
|
299
299
|
|
|
300
300
|
---
|
|
301
301
|
|
|
@@ -312,7 +312,7 @@ Bulk data is managed by `KrakenBulkClient`. It supports:
|
|
|
312
312
|
|
|
313
313
|
A `storageDir` is provided when constructing the client (examples use `.bulk-data`). The library stores ZIPs and extracted CSVs under dataset-specific folders:
|
|
314
314
|
|
|
315
|
-
|
|
315
|
+
```txt
|
|
316
316
|
<storageDir>/
|
|
317
317
|
ohlcvt/
|
|
318
318
|
zips/
|
|
@@ -328,7 +328,7 @@ quarterly/<YYYYQn>.zip
|
|
|
328
328
|
extracted/
|
|
329
329
|
complete/
|
|
330
330
|
quarterly/<YYYYQn>/
|
|
331
|
-
|
|
331
|
+
```
|
|
332
332
|
|
|
333
333
|
Recommended:
|
|
334
334
|
|
|
@@ -362,27 +362,27 @@ This library supports Kraken-style rate limiting with optional automatic retries
|
|
|
362
362
|
|
|
363
363
|
Example:
|
|
364
364
|
|
|
365
|
-
|
|
365
|
+
```ts
|
|
366
366
|
const kraken = new KrakenSpotRestClient({
|
|
367
|
-
apiKey: process.env.KRAKEN_API_KEY,
|
|
368
|
-
apiSecret: process.env.KRAKEN_API_SECRET,
|
|
369
|
-
rateLimit: {
|
|
370
|
-
mode: 'auto',
|
|
371
|
-
tier: 'starter',
|
|
372
|
-
retryOnRateLimit: true,
|
|
373
|
-
maxRetries: 5,
|
|
374
|
-
// restCostFn: (path) => (path.includes("Ledgers") ? 2 : 1),
|
|
375
|
-
},
|
|
367
|
+
apiKey: process.env.KRAKEN_API_KEY,
|
|
368
|
+
apiSecret: process.env.KRAKEN_API_SECRET,
|
|
369
|
+
rateLimit: {
|
|
370
|
+
mode: 'auto',
|
|
371
|
+
tier: 'starter',
|
|
372
|
+
retryOnRateLimit: true,
|
|
373
|
+
maxRetries: 5,
|
|
374
|
+
// restCostFn: (path) => (path.includes("Ledgers") ? 2 : 1),
|
|
375
|
+
},
|
|
376
376
|
});
|
|
377
|
-
|
|
377
|
+
```
|
|
378
378
|
|
|
379
379
|
Disable built-in throttling:
|
|
380
380
|
|
|
381
|
-
|
|
381
|
+
```ts
|
|
382
382
|
rateLimit: {
|
|
383
383
|
mode: 'off',
|
|
384
384
|
}
|
|
385
|
-
|
|
385
|
+
```
|
|
386
386
|
|
|
387
387
|
### Redis rate limiting (multi-process / multi-container)
|
|
388
388
|
|
|
@@ -392,7 +392,7 @@ For cross-process coordination, you can use the Redis-backed token bucket limite
|
|
|
392
392
|
|
|
393
393
|
Example (you provide the Redis client + EVAL wrapper):
|
|
394
394
|
|
|
395
|
-
|
|
395
|
+
```ts
|
|
396
396
|
import { KrakenSpotRestClient } from '@lynx-crypto/kraken-api';
|
|
397
397
|
import { RedisTokenBucketLimiter } from '@lynx-crypto/kraken-api/base/redisRateLimit';
|
|
398
398
|
|
|
@@ -400,26 +400,26 @@ import { RedisTokenBucketLimiter } from '@lynx-crypto/kraken-api/base/redisRateL
|
|
|
400
400
|
// - 0 means "proceed now"
|
|
401
401
|
// - >0 means "wait this many ms then retry"
|
|
402
402
|
const evalRedis = async (
|
|
403
|
-
key: string,
|
|
404
|
-
maxCounter: number,
|
|
405
|
-
decayPerSec: number,
|
|
406
|
-
cost: number,
|
|
407
|
-
ttlSeconds: number,
|
|
408
|
-
minWaitMs: number,
|
|
403
|
+
key: string,
|
|
404
|
+
maxCounter: number,
|
|
405
|
+
decayPerSec: number,
|
|
406
|
+
cost: number,
|
|
407
|
+
ttlSeconds: number,
|
|
408
|
+
minWaitMs: number,
|
|
409
409
|
): Promise<number> => {
|
|
410
|
-
// Example shape (pseudo-code):
|
|
411
|
-
// return await redis.eval(luaScript, { keys: [key], arguments: [maxCounter, decayPerSec, cost, ttlSeconds, minWaitMs] });
|
|
412
|
-
return 0;
|
|
410
|
+
// Example shape (pseudo-code):
|
|
411
|
+
// return await redis.eval(luaScript, { keys: [key], arguments: [maxCounter, decayPerSec, cost, ttlSeconds, minWaitMs] });
|
|
412
|
+
return 0;
|
|
413
413
|
};
|
|
414
414
|
|
|
415
415
|
const kraken = new KrakenSpotRestClient({
|
|
416
|
-
apiKey: process.env.KRAKEN_API_KEY,
|
|
417
|
-
apiSecret: process.env.KRAKEN_API_SECRET,
|
|
418
|
-
rateLimit: {
|
|
419
|
-
mode: 'auto',
|
|
420
|
-
tier: 'starter',
|
|
421
|
-
retryOnRateLimit: true,
|
|
422
|
-
maxRetries: 5,
|
|
416
|
+
apiKey: process.env.KRAKEN_API_KEY,
|
|
417
|
+
apiSecret: process.env.KRAKEN_API_SECRET,
|
|
418
|
+
rateLimit: {
|
|
419
|
+
mode: 'auto',
|
|
420
|
+
tier: 'starter',
|
|
421
|
+
retryOnRateLimit: true,
|
|
422
|
+
maxRetries: 5,
|
|
423
423
|
|
|
424
424
|
// Cross-process limiter (Redis):
|
|
425
425
|
redis: {
|
|
@@ -432,10 +432,9 @@ maxRetries: 5,
|
|
|
432
432
|
evalRedis,
|
|
433
433
|
}),
|
|
434
434
|
},
|
|
435
|
-
|
|
436
|
-
},
|
|
435
|
+
},
|
|
437
436
|
});
|
|
438
|
-
|
|
437
|
+
```
|
|
439
438
|
|
|
440
439
|
Notes:
|
|
441
440
|
|