pmxtjs 0.3.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.
- package/API_REFERENCE.md +230 -1
- package/dist/BaseExchange.d.ts +36 -2
- package/dist/BaseExchange.js +43 -1
- package/dist/exchanges/kalshi/auth.d.ts +23 -0
- package/dist/exchanges/kalshi/auth.js +99 -0
- package/dist/exchanges/kalshi/fetchMarkets.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchMarkets.js +84 -0
- package/dist/exchanges/kalshi/fetchOHLCV.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchOHLCV.js +78 -0
- package/dist/exchanges/kalshi/fetchOrderBook.d.ts +2 -0
- package/dist/exchanges/kalshi/fetchOrderBook.js +32 -0
- package/dist/exchanges/kalshi/fetchTrades.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchTrades.js +31 -0
- package/dist/exchanges/kalshi/getMarketsBySlug.d.ts +7 -0
- package/dist/exchanges/kalshi/getMarketsBySlug.js +46 -0
- package/dist/exchanges/kalshi/index.d.ts +21 -0
- package/dist/exchanges/kalshi/index.js +273 -0
- package/dist/exchanges/kalshi/kalshi.test.d.ts +1 -0
- package/dist/exchanges/kalshi/kalshi.test.js +309 -0
- package/dist/exchanges/kalshi/searchMarkets.d.ts +3 -0
- package/dist/exchanges/kalshi/searchMarkets.js +28 -0
- package/dist/exchanges/kalshi/utils.d.ts +4 -0
- package/dist/exchanges/kalshi/utils.js +70 -0
- package/dist/exchanges/polymarket/auth.d.ts +32 -0
- package/dist/exchanges/polymarket/auth.js +98 -0
- package/dist/exchanges/polymarket/fetchMarkets.d.ts +3 -0
- package/dist/exchanges/polymarket/fetchMarkets.js +75 -0
- package/dist/exchanges/polymarket/fetchOHLCV.d.ts +7 -0
- package/dist/exchanges/polymarket/fetchOHLCV.js +73 -0
- package/dist/exchanges/polymarket/fetchOrderBook.d.ts +6 -0
- package/dist/exchanges/polymarket/fetchOrderBook.js +38 -0
- package/dist/exchanges/polymarket/fetchPositions.d.ts +2 -0
- package/dist/exchanges/polymarket/fetchPositions.js +27 -0
- package/dist/exchanges/polymarket/fetchTrades.d.ts +11 -0
- package/dist/exchanges/polymarket/fetchTrades.js +59 -0
- package/dist/exchanges/polymarket/getMarketsBySlug.d.ts +7 -0
- package/dist/exchanges/polymarket/getMarketsBySlug.js +39 -0
- package/dist/exchanges/polymarket/index.d.ts +23 -0
- package/dist/exchanges/polymarket/index.js +216 -0
- package/dist/exchanges/polymarket/searchMarkets.d.ts +3 -0
- package/dist/exchanges/polymarket/searchMarkets.js +35 -0
- package/dist/exchanges/polymarket/utils.d.ts +7 -0
- package/dist/exchanges/polymarket/utils.js +95 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -8
- package/dist/types.d.ts +38 -0
- package/package.json +5 -1
- package/readme.md +37 -3
package/API_REFERENCE.md
CHANGED
|
@@ -123,7 +123,7 @@ const trades = await kalshi.fetchTrades('FED-25JAN', {
|
|
|
123
123
|
### `MarketOutcome`
|
|
124
124
|
```typescript
|
|
125
125
|
{
|
|
126
|
-
id: string; //
|
|
126
|
+
id: string; // Use this for fetchOHLCV/fetchOrderBook/fetchTrades
|
|
127
127
|
// Polymarket: CLOB Token ID
|
|
128
128
|
// Kalshi: Market Ticker
|
|
129
129
|
label: string; // "Trump", "Yes", etc.
|
|
@@ -243,12 +243,241 @@ import pmxt, {
|
|
|
243
243
|
|
|
244
244
|
---
|
|
245
245
|
|
|
246
|
+
## Authentication & Trading
|
|
247
|
+
|
|
248
|
+
Both Polymarket and Kalshi support authenticated trading operations. You must provide credentials when initializing the exchange.
|
|
249
|
+
|
|
250
|
+
### Polymarket Authentication
|
|
251
|
+
|
|
252
|
+
Requires your **Polygon Private Key**. See [Setup Guide](docs/SETUP_POLYMARKET.md) for details.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import pmxt from 'pmxtjs';
|
|
256
|
+
|
|
257
|
+
const polymarket = new pmxt.Polymarket({
|
|
258
|
+
privateKey: process.env.POLYMARKET_PRIVATE_KEY
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Kalshi Authentication
|
|
263
|
+
|
|
264
|
+
Requires **API Key** and **Private Key**.
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import pmxt from 'pmxtjs';
|
|
268
|
+
|
|
269
|
+
const kalshi = new pmxt.Kalshi({
|
|
270
|
+
apiKey: process.env.KALSHI_API_KEY,
|
|
271
|
+
privateKey: process.env.KALSHI_PRIVATE_KEY
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Account Methods
|
|
278
|
+
|
|
279
|
+
### `fetchBalance()`
|
|
280
|
+
Get your account balance.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const balances = await polymarket.fetchBalance();
|
|
284
|
+
console.log(balances);
|
|
285
|
+
// [{ currency: 'USDC', total: 1000, available: 950, locked: 50 }]
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Returns**: `Balance[]`
|
|
289
|
+
```typescript
|
|
290
|
+
interface Balance {
|
|
291
|
+
currency: string; // e.g., 'USDC'
|
|
292
|
+
total: number; // Total balance
|
|
293
|
+
available: number; // Available for trading
|
|
294
|
+
locked: number; // Locked in open orders
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### `fetchPositions()`
|
|
299
|
+
Get your current positions across all markets.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
const positions = await kalshi.fetchPositions();
|
|
303
|
+
positions.forEach(pos => {
|
|
304
|
+
console.log(`${pos.outcomeLabel}: ${pos.size} @ $${pos.entryPrice}`);
|
|
305
|
+
console.log(`Unrealized P&L: $${pos.unrealizedPnL}`);
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Returns**: `Position[]`
|
|
310
|
+
```typescript
|
|
311
|
+
interface Position {
|
|
312
|
+
marketId: string;
|
|
313
|
+
outcomeId: string;
|
|
314
|
+
outcomeLabel: string;
|
|
315
|
+
size: number; // Positive for long, negative for short
|
|
316
|
+
entryPrice: number;
|
|
317
|
+
currentPrice: number;
|
|
318
|
+
unrealizedPnL: number;
|
|
319
|
+
realizedPnL?: number;
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Trading Methods
|
|
326
|
+
|
|
327
|
+
### `createOrder(params)`
|
|
328
|
+
Place a new order (market or limit).
|
|
329
|
+
|
|
330
|
+
**Limit Order Example**:
|
|
331
|
+
```typescript
|
|
332
|
+
const order = await polymarket.createOrder({
|
|
333
|
+
marketId: '663583',
|
|
334
|
+
outcomeId: '10991849228756847439673778874175365458450913336396982752046655649803657501964',
|
|
335
|
+
side: 'buy',
|
|
336
|
+
type: 'limit',
|
|
337
|
+
amount: 10, // Number of contracts
|
|
338
|
+
price: 0.55 // Required for limit orders (0.0-1.0)
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
console.log(`Order ${order.id}: ${order.status}`);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Market Order Example**:
|
|
345
|
+
```typescript
|
|
346
|
+
const order = await kalshi.createOrder({
|
|
347
|
+
marketId: 'FED-25JAN',
|
|
348
|
+
outcomeId: 'FED-25JAN-YES',
|
|
349
|
+
side: 'sell',
|
|
350
|
+
type: 'market',
|
|
351
|
+
amount: 5 // Price not needed for market orders
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Parameters**: `CreateOrderParams`
|
|
356
|
+
```typescript
|
|
357
|
+
interface CreateOrderParams {
|
|
358
|
+
marketId: string;
|
|
359
|
+
outcomeId: string; // Use outcome.id from market data
|
|
360
|
+
side: 'buy' | 'sell';
|
|
361
|
+
type: 'market' | 'limit';
|
|
362
|
+
amount: number; // Number of contracts/shares
|
|
363
|
+
price?: number; // Required for limit orders (0.0-1.0)
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Returns**: `Order`
|
|
368
|
+
```typescript
|
|
369
|
+
interface Order {
|
|
370
|
+
id: string;
|
|
371
|
+
marketId: string;
|
|
372
|
+
outcomeId: string;
|
|
373
|
+
side: 'buy' | 'sell';
|
|
374
|
+
type: 'market' | 'limit';
|
|
375
|
+
price?: number;
|
|
376
|
+
amount: number;
|
|
377
|
+
status: 'pending' | 'open' | 'filled' | 'cancelled' | 'rejected';
|
|
378
|
+
filled: number; // Amount filled so far
|
|
379
|
+
remaining: number; // Amount remaining
|
|
380
|
+
timestamp: number;
|
|
381
|
+
fee?: number;
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### `cancelOrder(orderId)`
|
|
386
|
+
Cancel an open order.
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
const cancelledOrder = await polymarket.cancelOrder('order-123');
|
|
390
|
+
console.log(cancelledOrder.status); // 'cancelled'
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Returns**: `Order` (with updated status)
|
|
394
|
+
|
|
395
|
+
### `fetchOrder(orderId)`
|
|
396
|
+
Get details of a specific order.
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
const order = await kalshi.fetchOrder('order-456');
|
|
400
|
+
console.log(`Filled: ${order.filled}/${order.amount}`);
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Returns**: `Order`
|
|
404
|
+
|
|
405
|
+
### `fetchOpenOrders(marketId?)`
|
|
406
|
+
Get all open orders, optionally filtered by market.
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
// All open orders
|
|
410
|
+
const allOrders = await polymarket.fetchOpenOrders();
|
|
411
|
+
|
|
412
|
+
// Open orders for specific market
|
|
413
|
+
const marketOrders = await kalshi.fetchOpenOrders('FED-25JAN');
|
|
414
|
+
|
|
415
|
+
allOrders.forEach(order => {
|
|
416
|
+
console.log(`${order.side} ${order.amount} @ ${order.price}`);
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Returns**: `Order[]`
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Complete Trading Workflow
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
import pmxt from 'pmxtjs';
|
|
428
|
+
|
|
429
|
+
const exchange = new pmxt.Polymarket({
|
|
430
|
+
privateKey: process.env.POLYMARKET_PRIVATE_KEY
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// 1. Check balance
|
|
434
|
+
const [balance] = await exchange.fetchBalance();
|
|
435
|
+
console.log(`Available: $${balance.available}`);
|
|
436
|
+
|
|
437
|
+
// 2. Search for a market
|
|
438
|
+
const markets = await exchange.searchMarkets('Trump');
|
|
439
|
+
const market = markets[0];
|
|
440
|
+
const outcome = market.outcomes[0];
|
|
441
|
+
|
|
442
|
+
// 3. Place a limit order
|
|
443
|
+
const order = await exchange.createOrder({
|
|
444
|
+
marketId: market.id,
|
|
445
|
+
outcomeId: outcome.id,
|
|
446
|
+
side: 'buy',
|
|
447
|
+
type: 'limit',
|
|
448
|
+
amount: 10,
|
|
449
|
+
price: 0.50
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
console.log(`Order placed: ${order.id}`);
|
|
453
|
+
|
|
454
|
+
// 4. Check order status
|
|
455
|
+
const updatedOrder = await exchange.fetchOrder(order.id);
|
|
456
|
+
console.log(`Status: ${updatedOrder.status}`);
|
|
457
|
+
console.log(`Filled: ${updatedOrder.filled}/${updatedOrder.amount}`);
|
|
458
|
+
|
|
459
|
+
// 5. Cancel if needed
|
|
460
|
+
if (updatedOrder.status === 'open') {
|
|
461
|
+
await exchange.cancelOrder(order.id);
|
|
462
|
+
console.log('Order cancelled');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// 6. Check positions
|
|
466
|
+
const positions = await exchange.fetchPositions();
|
|
467
|
+
positions.forEach(pos => {
|
|
468
|
+
console.log(`${pos.outcomeLabel}: ${pos.unrealizedPnL > 0 ? '+' : ''}$${pos.unrealizedPnL.toFixed(2)}`);
|
|
469
|
+
});
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
246
474
|
## Quick Reference
|
|
247
475
|
|
|
248
476
|
- **Prices**: Always 0.0-1.0 (multiply by 100 for %)
|
|
249
477
|
- **Timestamps**: Unix milliseconds
|
|
250
478
|
- **Volumes**: USD
|
|
251
479
|
- **IDs**: Use `outcome.id` for deep-dive methods, not `market.id`
|
|
480
|
+
- **Authentication**: Required for all trading and account methods
|
|
252
481
|
|
|
253
482
|
For more examples, see [`examples/`](examples/).
|
|
254
483
|
|
package/dist/BaseExchange.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UnifiedMarket, PriceCandle, CandleInterval, OrderBook, Trade } from './types';
|
|
1
|
+
import { UnifiedMarket, PriceCandle, CandleInterval, OrderBook, Trade, Order, Position, Balance, CreateOrderParams } from './types';
|
|
2
2
|
export interface MarketFilterParams {
|
|
3
3
|
limit?: number;
|
|
4
4
|
offset?: number;
|
|
@@ -11,7 +11,17 @@ export interface HistoryFilterParams {
|
|
|
11
11
|
end?: Date;
|
|
12
12
|
limit?: number;
|
|
13
13
|
}
|
|
14
|
+
export interface ExchangeCredentials {
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
apiSecret?: string;
|
|
17
|
+
passphrase?: string;
|
|
18
|
+
privateKey?: string;
|
|
19
|
+
signatureType?: number;
|
|
20
|
+
funderAddress?: string;
|
|
21
|
+
}
|
|
14
22
|
export declare abstract class PredictionMarketExchange {
|
|
23
|
+
protected credentials?: ExchangeCredentials;
|
|
24
|
+
constructor(credentials?: ExchangeCredentials);
|
|
15
25
|
abstract get name(): string;
|
|
16
26
|
/**
|
|
17
27
|
* Fetch all relevant markets from the source.
|
|
@@ -34,7 +44,31 @@ export declare abstract class PredictionMarketExchange {
|
|
|
34
44
|
fetchOrderBook(id: string): Promise<OrderBook>;
|
|
35
45
|
/**
|
|
36
46
|
* Fetch raw trade history.
|
|
37
|
-
* Useful for generating synthetic OHLCV candles if the exchange doesn't provide them natively.
|
|
38
47
|
*/
|
|
39
48
|
fetchTrades(id: string, params: HistoryFilterParams): Promise<Trade[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Place a new order.
|
|
51
|
+
*/
|
|
52
|
+
createOrder(params: CreateOrderParams): Promise<Order>;
|
|
53
|
+
/**
|
|
54
|
+
* Cancel an existing order.
|
|
55
|
+
*/
|
|
56
|
+
cancelOrder(orderId: string): Promise<Order>;
|
|
57
|
+
/**
|
|
58
|
+
* Fetch a specific order by ID.
|
|
59
|
+
*/
|
|
60
|
+
fetchOrder(orderId: string): Promise<Order>;
|
|
61
|
+
/**
|
|
62
|
+
* Fetch all open orders.
|
|
63
|
+
* @param marketId - Optional filter by market.
|
|
64
|
+
*/
|
|
65
|
+
fetchOpenOrders(marketId?: string): Promise<Order[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Fetch current user positions.
|
|
68
|
+
*/
|
|
69
|
+
fetchPositions(): Promise<Position[]>;
|
|
70
|
+
/**
|
|
71
|
+
* Fetch account balances.
|
|
72
|
+
*/
|
|
73
|
+
fetchBalance(): Promise<Balance[]>;
|
|
40
74
|
}
|
package/dist/BaseExchange.js
CHANGED
|
@@ -5,6 +5,9 @@ exports.PredictionMarketExchange = void 0;
|
|
|
5
5
|
// Base Exchange Class
|
|
6
6
|
// ----------------------------------------------------------------------------
|
|
7
7
|
class PredictionMarketExchange {
|
|
8
|
+
constructor(credentials) {
|
|
9
|
+
this.credentials = credentials;
|
|
10
|
+
}
|
|
8
11
|
/**
|
|
9
12
|
* Fetch historical price data for a specific market outcome.
|
|
10
13
|
* @param id - The Outcome ID (MarketOutcome.id). This should be the ID of the specific tradeable asset.
|
|
@@ -21,10 +24,49 @@ class PredictionMarketExchange {
|
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
23
26
|
* Fetch raw trade history.
|
|
24
|
-
* Useful for generating synthetic OHLCV candles if the exchange doesn't provide them natively.
|
|
25
27
|
*/
|
|
26
28
|
async fetchTrades(id, params) {
|
|
27
29
|
throw new Error("Method fetchTrades not implemented.");
|
|
28
30
|
}
|
|
31
|
+
// ----------------------------------------------------------------------------
|
|
32
|
+
// Trading Methods
|
|
33
|
+
// ----------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* Place a new order.
|
|
36
|
+
*/
|
|
37
|
+
async createOrder(params) {
|
|
38
|
+
throw new Error("Method createOrder not implemented.");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Cancel an existing order.
|
|
42
|
+
*/
|
|
43
|
+
async cancelOrder(orderId) {
|
|
44
|
+
throw new Error("Method cancelOrder not implemented.");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fetch a specific order by ID.
|
|
48
|
+
*/
|
|
49
|
+
async fetchOrder(orderId) {
|
|
50
|
+
throw new Error("Method fetchOrder not implemented.");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Fetch all open orders.
|
|
54
|
+
* @param marketId - Optional filter by market.
|
|
55
|
+
*/
|
|
56
|
+
async fetchOpenOrders(marketId) {
|
|
57
|
+
throw new Error("Method fetchOpenOrders not implemented.");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Fetch current user positions.
|
|
61
|
+
*/
|
|
62
|
+
async fetchPositions() {
|
|
63
|
+
throw new Error("Method fetchPositions not implemented.");
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Fetch account balances.
|
|
67
|
+
*/
|
|
68
|
+
async fetchBalance() {
|
|
69
|
+
throw new Error("Method fetchBalance not implemented.");
|
|
70
|
+
}
|
|
29
71
|
}
|
|
30
72
|
exports.PredictionMarketExchange = PredictionMarketExchange;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ExchangeCredentials } from '../../BaseExchange';
|
|
2
|
+
/**
|
|
3
|
+
* Manages Kalshi authentication using RSA-PSS signatures.
|
|
4
|
+
* Reference: https://docs.kalshi.com/getting_started/quick_start_authenticated_requests
|
|
5
|
+
*/
|
|
6
|
+
export declare class KalshiAuth {
|
|
7
|
+
private credentials;
|
|
8
|
+
constructor(credentials: ExchangeCredentials);
|
|
9
|
+
private validateCredentials;
|
|
10
|
+
/**
|
|
11
|
+
* Generates the required headers for an authenticated request.
|
|
12
|
+
*
|
|
13
|
+
* @param method The HTTP method (e.g., "GET", "POST").
|
|
14
|
+
* @param path The request path (e.g., "/trade-api/v2/portfolio/orders").
|
|
15
|
+
* @returns An object containing the authentication headers.
|
|
16
|
+
*/
|
|
17
|
+
getHeaders(method: string, path: string): Record<string, string>;
|
|
18
|
+
/**
|
|
19
|
+
* Signs the request using RSA-PSS.
|
|
20
|
+
* The message to sign is: timestamp + method + path
|
|
21
|
+
*/
|
|
22
|
+
private signRequest;
|
|
23
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.KalshiAuth = void 0;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
/**
|
|
39
|
+
* Manages Kalshi authentication using RSA-PSS signatures.
|
|
40
|
+
* Reference: https://docs.kalshi.com/getting_started/quick_start_authenticated_requests
|
|
41
|
+
*/
|
|
42
|
+
class KalshiAuth {
|
|
43
|
+
constructor(credentials) {
|
|
44
|
+
this.credentials = credentials;
|
|
45
|
+
this.validateCredentials();
|
|
46
|
+
}
|
|
47
|
+
validateCredentials() {
|
|
48
|
+
if (!this.credentials.apiKey) {
|
|
49
|
+
throw new Error('Kalshi requires an apiKey (Key ID) for authentication');
|
|
50
|
+
}
|
|
51
|
+
if (!this.credentials.privateKey) {
|
|
52
|
+
throw new Error('Kalshi requires a privateKey (RSA Private Key) for authentication');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Generates the required headers for an authenticated request.
|
|
57
|
+
*
|
|
58
|
+
* @param method The HTTP method (e.g., "GET", "POST").
|
|
59
|
+
* @param path The request path (e.g., "/trade-api/v2/portfolio/orders").
|
|
60
|
+
* @returns An object containing the authentication headers.
|
|
61
|
+
*/
|
|
62
|
+
getHeaders(method, path) {
|
|
63
|
+
const timestamp = Date.now().toString();
|
|
64
|
+
const signature = this.signRequest(timestamp, method, path);
|
|
65
|
+
return {
|
|
66
|
+
'KALSHI-ACCESS-KEY': this.credentials.apiKey,
|
|
67
|
+
'KALSHI-ACCESS-TIMESTAMP': timestamp,
|
|
68
|
+
'KALSHI-ACCESS-SIGNATURE': signature,
|
|
69
|
+
'Content-Type': 'application/json'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Signs the request using RSA-PSS.
|
|
74
|
+
* The message to sign is: timestamp + method + path
|
|
75
|
+
*/
|
|
76
|
+
signRequest(timestamp, method, path) {
|
|
77
|
+
const payload = `${timestamp}${method}${path}`;
|
|
78
|
+
try {
|
|
79
|
+
const signer = crypto.createSign('SHA256');
|
|
80
|
+
signer.update(payload);
|
|
81
|
+
// Allow input of private key in both raw string or PEM format
|
|
82
|
+
// If it's a raw key without headers, accessing it might be tricky with implicit types,
|
|
83
|
+
// but standard PEM is best. We assume the user provides a valid PEM.
|
|
84
|
+
const privateKey = this.credentials.privateKey;
|
|
85
|
+
// Kalshi uses RSA-PSS for signing
|
|
86
|
+
const signature = signer.sign({
|
|
87
|
+
key: privateKey,
|
|
88
|
+
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
|
|
89
|
+
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST
|
|
90
|
+
}, 'base64');
|
|
91
|
+
return signature;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.error('Error signing Kalshi request:', error);
|
|
95
|
+
throw new Error(`Failed to sign Kalshi request: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.KalshiAuth = KalshiAuth;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchMarkets = fetchMarkets;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
async function fetchActiveEvents(targetMarketCount) {
|
|
10
|
+
let allEvents = [];
|
|
11
|
+
let totalMarketCount = 0;
|
|
12
|
+
let cursor = null;
|
|
13
|
+
let page = 0;
|
|
14
|
+
// Note: Kalshi API uses cursor-based pagination which requires sequential fetching.
|
|
15
|
+
// We cannot parallelize requests for a single list because we need the cursor from page N to fetch page N+1.
|
|
16
|
+
// To optimize, we use the maximum allowed limit (200) and fetch until exhaustion.
|
|
17
|
+
const MAX_PAGES = 1000; // Safety cap against infinite loops
|
|
18
|
+
const BATCH_SIZE = 200; // Max limit per Kalshi API docs
|
|
19
|
+
do {
|
|
20
|
+
try {
|
|
21
|
+
// console.log(`Fetching Kalshi page ${page + 1}...`);
|
|
22
|
+
const queryParams = {
|
|
23
|
+
limit: BATCH_SIZE,
|
|
24
|
+
with_nested_markets: true,
|
|
25
|
+
status: 'open' // Filter to open markets to improve relevance and speed
|
|
26
|
+
};
|
|
27
|
+
if (cursor)
|
|
28
|
+
queryParams.cursor = cursor;
|
|
29
|
+
const response = await axios_1.default.get(utils_1.KALSHI_API_URL, { params: queryParams });
|
|
30
|
+
const events = response.data.events || [];
|
|
31
|
+
if (events.length === 0)
|
|
32
|
+
break;
|
|
33
|
+
allEvents = allEvents.concat(events);
|
|
34
|
+
// Count markets in this batch for early termination
|
|
35
|
+
if (targetMarketCount) {
|
|
36
|
+
for (const event of events) {
|
|
37
|
+
totalMarketCount += (event.markets || []).length;
|
|
38
|
+
}
|
|
39
|
+
// Early termination: if we have enough markets, stop fetching
|
|
40
|
+
if (totalMarketCount >= targetMarketCount * 2) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
cursor = response.data.cursor;
|
|
45
|
+
page++;
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
console.error(`Error fetching Kalshi page ${page}:`, e);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
} while (cursor && page < MAX_PAGES);
|
|
52
|
+
return allEvents;
|
|
53
|
+
}
|
|
54
|
+
async function fetchMarkets(params) {
|
|
55
|
+
const limit = params?.limit || 50;
|
|
56
|
+
try {
|
|
57
|
+
// Fetch active events with nested markets
|
|
58
|
+
// For small limits, we can optimize by fetching fewer pages
|
|
59
|
+
const allEvents = await fetchActiveEvents(limit);
|
|
60
|
+
// Extract ALL markets from all events
|
|
61
|
+
const allMarkets = [];
|
|
62
|
+
for (const event of allEvents) {
|
|
63
|
+
const markets = event.markets || [];
|
|
64
|
+
for (const market of markets) {
|
|
65
|
+
const unifiedMarket = (0, utils_1.mapMarketToUnified)(event, market);
|
|
66
|
+
if (unifiedMarket) {
|
|
67
|
+
allMarkets.push(unifiedMarket);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Sort by 24h volume
|
|
72
|
+
if (params?.sort === 'volume') {
|
|
73
|
+
allMarkets.sort((a, b) => b.volume24h - a.volume24h);
|
|
74
|
+
}
|
|
75
|
+
else if (params?.sort === 'liquidity') {
|
|
76
|
+
allMarkets.sort((a, b) => b.liquidity - a.liquidity);
|
|
77
|
+
}
|
|
78
|
+
return allMarkets.slice(0, limit);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error("Error fetching Kalshi data:", error);
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchOHLCV = fetchOHLCV;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
async function fetchOHLCV(id, params) {
|
|
10
|
+
try {
|
|
11
|
+
// Kalshi API expects uppercase tickers
|
|
12
|
+
// Handle virtual "-NO" suffix by stripping it (fetching the underlying market history)
|
|
13
|
+
const cleanedId = id.replace(/-NO$/, '');
|
|
14
|
+
const normalizedId = cleanedId.toUpperCase();
|
|
15
|
+
const interval = (0, utils_1.mapIntervalToKalshi)(params.resolution);
|
|
16
|
+
// Heuristic for series_ticker
|
|
17
|
+
const parts = normalizedId.split('-');
|
|
18
|
+
if (parts.length < 2) {
|
|
19
|
+
throw new Error(`Invalid Kalshi Ticker format: "${id}". Expected format like "FED-25JAN29-B4.75".`);
|
|
20
|
+
}
|
|
21
|
+
const seriesTicker = parts.slice(0, -1).join('-');
|
|
22
|
+
const url = `https://api.elections.kalshi.com/trade-api/v2/series/${seriesTicker}/markets/${normalizedId}/candlesticks`;
|
|
23
|
+
const queryParams = { period_interval: interval };
|
|
24
|
+
const now = Math.floor(Date.now() / 1000);
|
|
25
|
+
let startTs = now - (24 * 60 * 60);
|
|
26
|
+
let endTs = now;
|
|
27
|
+
if (params.start) {
|
|
28
|
+
startTs = Math.floor(params.start.getTime() / 1000);
|
|
29
|
+
}
|
|
30
|
+
if (params.end) {
|
|
31
|
+
endTs = Math.floor(params.end.getTime() / 1000);
|
|
32
|
+
if (!params.start) {
|
|
33
|
+
startTs = endTs - (24 * 60 * 60);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
queryParams.start_ts = startTs;
|
|
37
|
+
queryParams.end_ts = endTs;
|
|
38
|
+
const response = await axios_1.default.get(url, { params: queryParams });
|
|
39
|
+
const candles = response.data.candlesticks || [];
|
|
40
|
+
const mappedCandles = candles.map((c) => {
|
|
41
|
+
// Priority:
|
|
42
|
+
// 1. Transaction price (close)
|
|
43
|
+
// 2. Mid price (average of yes_ask and yes_bid close)
|
|
44
|
+
// 3. Fallback to 0 if everything is missing
|
|
45
|
+
const p = c.price || {};
|
|
46
|
+
const ask = c.yes_ask || {};
|
|
47
|
+
const bid = c.yes_bid || {};
|
|
48
|
+
const getVal = (field) => {
|
|
49
|
+
if (p[field] !== null && p[field] !== undefined)
|
|
50
|
+
return p[field];
|
|
51
|
+
if (ask[field] !== null && bid[field] !== null && ask[field] !== undefined && bid[field] !== undefined) {
|
|
52
|
+
return (ask[field] + bid[field]) / 2;
|
|
53
|
+
}
|
|
54
|
+
return p.previous || 0;
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
timestamp: c.end_period_ts * 1000,
|
|
58
|
+
open: getVal('open') / 100,
|
|
59
|
+
high: getVal('high') / 100,
|
|
60
|
+
low: getVal('low') / 100,
|
|
61
|
+
close: getVal('close') / 100,
|
|
62
|
+
volume: c.volume || 0
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
if (params.limit && mappedCandles.length > params.limit) {
|
|
66
|
+
return mappedCandles.slice(-params.limit);
|
|
67
|
+
}
|
|
68
|
+
return mappedCandles;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (axios_1.default.isAxiosError(error) && error.response) {
|
|
72
|
+
const apiError = error.response.data?.error || error.response.data?.message || "Unknown API Error";
|
|
73
|
+
throw new Error(`Kalshi History API Error (${error.response.status}): ${apiError}. Used Ticker: ${id}`);
|
|
74
|
+
}
|
|
75
|
+
console.error(`Unexpected error fetching Kalshi history for ${id}:`, error);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|