pmxtjs 0.2.1 → 0.3.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 +8 -10
- package/dist/BaseExchange.d.ts +3 -3
- package/dist/BaseExchange.js +6 -6
- package/dist/exchanges/Kalshi.d.ts +3 -3
- package/dist/exchanges/Kalshi.js +30 -13
- package/dist/exchanges/Polymarket.d.ts +4 -4
- package/dist/exchanges/Polymarket.js +10 -7
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/package.json +1 -1
- package/readme.md +3 -3
package/API_REFERENCE.md
CHANGED
|
@@ -7,13 +7,11 @@ This project implements a **Unified Interface** for interacting with multiple pr
|
|
|
7
7
|
All components are available directly from the `pmxt` library.
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
-
import
|
|
11
|
-
PolymarketExchange,
|
|
12
|
-
KalshiExchange
|
|
13
|
-
} from './pmxt';
|
|
10
|
+
import pmxt from 'pmxtjs';
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
const
|
|
12
|
+
// ccxt-style instantiation
|
|
13
|
+
const polymarket = new pmxt.polymarket();
|
|
14
|
+
const kalshi = new pmxt.kalshi();
|
|
17
15
|
```
|
|
18
16
|
|
|
19
17
|
## 1. Unified Interface (`PredictionMarketExchange`)
|
|
@@ -36,7 +34,7 @@ Search markets by title/description across the exchange(s).
|
|
|
36
34
|
|
|
37
35
|
Methods available on specific exchange instances (`KalshiExchange`, `PolymarketExchange`) for detailed data.
|
|
38
36
|
|
|
39
|
-
### `
|
|
37
|
+
### `fetchOHLCV(id, params)`
|
|
40
38
|
Fetches OHLCV candlesticks.
|
|
41
39
|
- **Input**: `id`, `resolution` (1m, 1h, 1d), `start`, `end`.
|
|
42
40
|
- **Output**: `Promise<PriceCandle[]>`
|
|
@@ -44,18 +42,18 @@ Fetches OHLCV candlesticks.
|
|
|
44
42
|
- **Kalshi**: Uses the Market Ticker (e.g., `FED-25DEC`). Returns **native OHLCV** (Open/High/Low/Close data is distinct).
|
|
45
43
|
- **Polymarket**: Uses the **CLOB Token ID** found in `outcome.metadata.clobTokenId`. Returns **synthetic candles** (Open=High=Low=Close) derived from raw price points.
|
|
46
44
|
|
|
47
|
-
### `
|
|
45
|
+
### `fetchOrderBook(id)`
|
|
48
46
|
Fetches live Bids/Asks.
|
|
49
47
|
- **Input**: `id` (Ticker for Kalshi, Token ID for Polymarket).
|
|
50
48
|
- **Output**: `Promise<OrderBook>` (Normalized: "No" bids becomes "Yes" asks).
|
|
51
49
|
|
|
52
|
-
### `
|
|
50
|
+
### `fetchTrades(id, params)`
|
|
53
51
|
Fetches the "Tape" (raw transaction history).
|
|
54
52
|
- **Input**: `id`, `limit`.
|
|
55
53
|
- **Output**: `Promise<Trade[]>`
|
|
56
54
|
- **Note**:
|
|
57
55
|
- **Kalshi**: Publicly accessible for all tickers.
|
|
58
|
-
- **Polymarket**: Requires L2 Authentication (API Key). Without a key, this method will throw an unauthorized error. Use `
|
|
56
|
+
- **Polymarket**: Requires L2 Authentication (API Key). Without a key, this method will throw an unauthorized error. Use `fetchOHLCV` for public historical data.
|
|
59
57
|
|
|
60
58
|
---
|
|
61
59
|
|
package/dist/BaseExchange.d.ts
CHANGED
|
@@ -25,15 +25,15 @@ export declare abstract class PredictionMarketExchange {
|
|
|
25
25
|
* Fetch historical price data for a specific market outcome.
|
|
26
26
|
* @param id - The Outcome ID (MarketOutcome.id). This should be the ID of the specific tradeable asset.
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
fetchOHLCV(id: string, params: HistoryFilterParams): Promise<PriceCandle[]>;
|
|
29
29
|
/**
|
|
30
30
|
* Fetch the current order book (bids/asks) for a specific outcome.
|
|
31
31
|
* Essential for calculating localized spread and depth.
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
fetchOrderBook(id: string): Promise<OrderBook>;
|
|
34
34
|
/**
|
|
35
35
|
* Fetch raw trade history.
|
|
36
36
|
* Useful for generating synthetic OHLCV candles if the exchange doesn't provide them natively.
|
|
37
37
|
*/
|
|
38
|
-
|
|
38
|
+
fetchTrades(id: string, params: HistoryFilterParams): Promise<Trade[]>;
|
|
39
39
|
}
|
package/dist/BaseExchange.js
CHANGED
|
@@ -9,22 +9,22 @@ class PredictionMarketExchange {
|
|
|
9
9
|
* Fetch historical price data for a specific market outcome.
|
|
10
10
|
* @param id - The Outcome ID (MarketOutcome.id). This should be the ID of the specific tradeable asset.
|
|
11
11
|
*/
|
|
12
|
-
async
|
|
13
|
-
throw new Error("Method
|
|
12
|
+
async fetchOHLCV(id, params) {
|
|
13
|
+
throw new Error("Method fetchOHLCV not implemented.");
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Fetch the current order book (bids/asks) for a specific outcome.
|
|
17
17
|
* Essential for calculating localized spread and depth.
|
|
18
18
|
*/
|
|
19
|
-
async
|
|
20
|
-
throw new Error("Method
|
|
19
|
+
async fetchOrderBook(id) {
|
|
20
|
+
throw new Error("Method fetchOrderBook not implemented.");
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* Fetch raw trade history.
|
|
24
24
|
* Useful for generating synthetic OHLCV candles if the exchange doesn't provide them natively.
|
|
25
25
|
*/
|
|
26
|
-
async
|
|
27
|
-
throw new Error("Method
|
|
26
|
+
async fetchTrades(id, params) {
|
|
27
|
+
throw new Error("Method fetchTrades not implemented.");
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
exports.PredictionMarketExchange = PredictionMarketExchange;
|
|
@@ -14,7 +14,7 @@ export declare class KalshiExchange extends PredictionMarketExchange {
|
|
|
14
14
|
*/
|
|
15
15
|
getMarketsBySlug(eventTicker: string): Promise<UnifiedMarket[]>;
|
|
16
16
|
private mapIntervalToKalshi;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
fetchOHLCV(id: string, params: HistoryFilterParams): Promise<PriceCandle[]>;
|
|
18
|
+
fetchOrderBook(id: string): Promise<OrderBook>;
|
|
19
|
+
fetchTrades(id: string, params: HistoryFilterParams): Promise<Trade[]>;
|
|
20
20
|
}
|
package/dist/exchanges/Kalshi.js
CHANGED
|
@@ -149,8 +149,8 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
149
149
|
try {
|
|
150
150
|
const markets = await this.fetchMarkets({ ...params, limit: fetchLimit });
|
|
151
151
|
const lowerQuery = query.toLowerCase();
|
|
152
|
-
const filtered = markets.filter(market => market.title.toLowerCase().includes(lowerQuery) ||
|
|
153
|
-
market.description.toLowerCase().includes(lowerQuery));
|
|
152
|
+
const filtered = markets.filter(market => (market.title || '').toLowerCase().includes(lowerQuery) ||
|
|
153
|
+
(market.description || '').toLowerCase().includes(lowerQuery));
|
|
154
154
|
const limit = params?.limit || 20;
|
|
155
155
|
return filtered.slice(0, limit);
|
|
156
156
|
}
|
|
@@ -208,7 +208,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
208
208
|
};
|
|
209
209
|
return mapping[interval];
|
|
210
210
|
}
|
|
211
|
-
async
|
|
211
|
+
async fetchOHLCV(id, params) {
|
|
212
212
|
try {
|
|
213
213
|
// Kalshi API expects uppercase tickers
|
|
214
214
|
// Handle virtual "-NO" suffix by stripping it (fetching the underlying market history)
|
|
@@ -239,14 +239,31 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
239
239
|
queryParams.end_ts = endTs;
|
|
240
240
|
const response = await axios_1.default.get(url, { params: queryParams });
|
|
241
241
|
const candles = response.data.candlesticks || [];
|
|
242
|
-
const mappedCandles = candles.map((c) =>
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
242
|
+
const mappedCandles = candles.map((c) => {
|
|
243
|
+
// Priority:
|
|
244
|
+
// 1. Transaction price (close)
|
|
245
|
+
// 2. Mid price (average of yes_ask and yes_bid close)
|
|
246
|
+
// 3. Fallback to 0 if everything is missing
|
|
247
|
+
const p = c.price || {};
|
|
248
|
+
const ask = c.yes_ask || {};
|
|
249
|
+
const bid = c.yes_bid || {};
|
|
250
|
+
const getVal = (field) => {
|
|
251
|
+
if (p[field] !== null && p[field] !== undefined)
|
|
252
|
+
return p[field];
|
|
253
|
+
if (ask[field] !== null && bid[field] !== null && ask[field] !== undefined && bid[field] !== undefined) {
|
|
254
|
+
return (ask[field] + bid[field]) / 2;
|
|
255
|
+
}
|
|
256
|
+
return p.previous || 0;
|
|
257
|
+
};
|
|
258
|
+
return {
|
|
259
|
+
timestamp: c.end_period_ts * 1000,
|
|
260
|
+
open: getVal('open') / 100,
|
|
261
|
+
high: getVal('high') / 100,
|
|
262
|
+
low: getVal('low') / 100,
|
|
263
|
+
close: getVal('close') / 100,
|
|
264
|
+
volume: c.volume || 0
|
|
265
|
+
};
|
|
266
|
+
});
|
|
250
267
|
if (params.limit && mappedCandles.length > params.limit) {
|
|
251
268
|
return mappedCandles.slice(-params.limit);
|
|
252
269
|
}
|
|
@@ -261,7 +278,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
261
278
|
throw error;
|
|
262
279
|
}
|
|
263
280
|
}
|
|
264
|
-
async
|
|
281
|
+
async fetchOrderBook(id) {
|
|
265
282
|
try {
|
|
266
283
|
const ticker = id.replace(/-NO$/, '');
|
|
267
284
|
const url = `https://api.elections.kalshi.com/trade-api/v2/markets/${ticker}/orderbook`;
|
|
@@ -286,7 +303,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
286
303
|
return { bids: [], asks: [] };
|
|
287
304
|
}
|
|
288
305
|
}
|
|
289
|
-
async
|
|
306
|
+
async fetchTrades(id, params) {
|
|
290
307
|
try {
|
|
291
308
|
const ticker = id.replace(/-NO$/, '');
|
|
292
309
|
const url = `https://api.elections.kalshi.com/trade-api/v2/markets/trades`;
|
|
@@ -20,19 +20,19 @@ export declare class PolymarketExchange extends PredictionMarketExchange {
|
|
|
20
20
|
* Fetch historical price data (OHLCV candles) for a specific token.
|
|
21
21
|
* @param id - The CLOB token ID (e.g., outcome token ID)
|
|
22
22
|
*/
|
|
23
|
-
|
|
23
|
+
fetchOHLCV(id: string, params: HistoryFilterParams): Promise<PriceCandle[]>;
|
|
24
24
|
/**
|
|
25
25
|
* Fetch the current order book for a specific token.
|
|
26
26
|
* @param id - The CLOB token ID
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
fetchOrderBook(id: string): Promise<OrderBook>;
|
|
29
29
|
/**
|
|
30
30
|
* Fetch raw trade history for a specific token.
|
|
31
31
|
* @param id - The CLOB token ID
|
|
32
32
|
*
|
|
33
33
|
* NOTE: Polymarket's /trades endpoint currently requires L2 Authentication (API Key).
|
|
34
34
|
* This method will return an empty array if an API key is not provided in headers.
|
|
35
|
-
* Use
|
|
35
|
+
* Use fetchOHLCV for public historical price data instead.
|
|
36
36
|
*/
|
|
37
|
-
|
|
37
|
+
fetchTrades(id: string, params: HistoryFilterParams): Promise<Trade[]>;
|
|
38
38
|
}
|
|
@@ -64,6 +64,7 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
64
64
|
outcomePrices = typeof market.outcomePrices === 'string' ? JSON.parse(market.outcomePrices) : (market.outcomePrices || []);
|
|
65
65
|
}
|
|
66
66
|
catch (e) {
|
|
67
|
+
console.warn(`Error parsing outcomes for market ${market.id}:`, e);
|
|
67
68
|
}
|
|
68
69
|
// Extract CLOB token IDs for granular operations
|
|
69
70
|
let clobTokenIds = [];
|
|
@@ -158,8 +159,8 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
158
159
|
});
|
|
159
160
|
// Client-side text filtering
|
|
160
161
|
const lowerQuery = query.toLowerCase();
|
|
161
|
-
const filtered = markets.filter(market => market.title.toLowerCase().includes(lowerQuery) ||
|
|
162
|
-
market.description.toLowerCase().includes(lowerQuery));
|
|
162
|
+
const filtered = markets.filter(market => (market.title || '').toLowerCase().includes(lowerQuery) ||
|
|
163
|
+
(market.description || '').toLowerCase().includes(lowerQuery));
|
|
163
164
|
// Apply limit to filtered results
|
|
164
165
|
const limit = params?.limit || 20;
|
|
165
166
|
return filtered.slice(0, limit);
|
|
@@ -201,7 +202,9 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
201
202
|
outcomePrices = typeof market.outcomePrices === 'string' ? JSON.parse(market.outcomePrices) : (market.outcomePrices || []);
|
|
202
203
|
clobTokenIds = typeof market.clobTokenIds === 'string' ? JSON.parse(market.clobTokenIds) : (market.clobTokenIds || []);
|
|
203
204
|
}
|
|
204
|
-
catch (e) {
|
|
205
|
+
catch (e) {
|
|
206
|
+
console.warn(`Error parsing outcomes for market ${market.id}:`, e);
|
|
207
|
+
}
|
|
205
208
|
let candidateName = market.groupItemTitle;
|
|
206
209
|
if (!candidateName && market.question)
|
|
207
210
|
candidateName = market.question;
|
|
@@ -271,7 +274,7 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
271
274
|
* Fetch historical price data (OHLCV candles) for a specific token.
|
|
272
275
|
* @param id - The CLOB token ID (e.g., outcome token ID)
|
|
273
276
|
*/
|
|
274
|
-
async
|
|
277
|
+
async fetchOHLCV(id, params) {
|
|
275
278
|
// ID Validation: Polymarket CLOB requires a Token ID (long numeric string) not a Market ID
|
|
276
279
|
if (id.length < 10 && /^\d+$/.test(id)) {
|
|
277
280
|
throw new Error(`Invalid ID for Polymarket history: "${id}". You provided a Market ID, but Polymarket's CLOB API requires a Token ID. Ensure you are using 'outcome.id'.`);
|
|
@@ -336,7 +339,7 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
336
339
|
* Fetch the current order book for a specific token.
|
|
337
340
|
* @param id - The CLOB token ID
|
|
338
341
|
*/
|
|
339
|
-
async
|
|
342
|
+
async fetchOrderBook(id) {
|
|
340
343
|
try {
|
|
341
344
|
const response = await axios_1.default.get(`${this.clobUrl}/book`, {
|
|
342
345
|
params: { token_id: id }
|
|
@@ -368,9 +371,9 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
368
371
|
*
|
|
369
372
|
* NOTE: Polymarket's /trades endpoint currently requires L2 Authentication (API Key).
|
|
370
373
|
* This method will return an empty array if an API key is not provided in headers.
|
|
371
|
-
* Use
|
|
374
|
+
* Use fetchOHLCV for public historical price data instead.
|
|
372
375
|
*/
|
|
373
|
-
async
|
|
376
|
+
async fetchTrades(id, params) {
|
|
374
377
|
// ID Validation
|
|
375
378
|
if (id.length < 10 && /^\d+$/.test(id)) {
|
|
376
379
|
throw new Error(`Invalid ID for Polymarket trades: "${id}". You provided a Market ID, but Polymarket's CLOB API requires a Token ID.`);
|
package/dist/index.d.ts
CHANGED
|
@@ -2,3 +2,12 @@ export * from './BaseExchange';
|
|
|
2
2
|
export * from './types';
|
|
3
3
|
export * from './exchanges/Polymarket';
|
|
4
4
|
export * from './exchanges/Kalshi';
|
|
5
|
+
import { PolymarketExchange } from './exchanges/Polymarket';
|
|
6
|
+
import { KalshiExchange } from './exchanges/Kalshi';
|
|
7
|
+
declare const pmxt: {
|
|
8
|
+
polymarket: typeof PolymarketExchange;
|
|
9
|
+
kalshi: typeof KalshiExchange;
|
|
10
|
+
Polymarket: typeof PolymarketExchange;
|
|
11
|
+
Kalshi: typeof KalshiExchange;
|
|
12
|
+
};
|
|
13
|
+
export default pmxt;
|
package/dist/index.js
CHANGED
|
@@ -18,3 +18,12 @@ __exportStar(require("./BaseExchange"), exports);
|
|
|
18
18
|
__exportStar(require("./types"), exports);
|
|
19
19
|
__exportStar(require("./exchanges/Polymarket"), exports);
|
|
20
20
|
__exportStar(require("./exchanges/Kalshi"), exports);
|
|
21
|
+
const Polymarket_1 = require("./exchanges/Polymarket");
|
|
22
|
+
const Kalshi_1 = require("./exchanges/Kalshi");
|
|
23
|
+
const pmxt = {
|
|
24
|
+
polymarket: Polymarket_1.PolymarketExchange,
|
|
25
|
+
kalshi: Kalshi_1.KalshiExchange,
|
|
26
|
+
Polymarket: Polymarket_1.PolymarketExchange,
|
|
27
|
+
Kalshi: Kalshi_1.KalshiExchange
|
|
28
|
+
};
|
|
29
|
+
exports.default = pmxt;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -57,13 +57,13 @@ Different prediction market platforms have different APIs, data formats, and con
|
|
|
57
57
|
Search for markets across Polymarket and Kalshi using the same API:
|
|
58
58
|
|
|
59
59
|
```typescript
|
|
60
|
-
import
|
|
60
|
+
import pmxt from 'pmxtjs';
|
|
61
61
|
|
|
62
62
|
const query = process.argv[2] || 'Who will Trump nominate as Fed Chair?';
|
|
63
63
|
console.log(`Searching for "${query}"...\n`);
|
|
64
64
|
|
|
65
65
|
// Polymarket
|
|
66
|
-
const polymarket = new
|
|
66
|
+
const polymarket = new pmxt.polymarket();
|
|
67
67
|
const polyResults = await polymarket.searchMarkets(query, { sort: 'volume' });
|
|
68
68
|
|
|
69
69
|
console.log(`--- Polymarket Found ${polyResults.length} ---`);
|
|
@@ -73,7 +73,7 @@ polyResults.slice(0, 10).forEach(m => {
|
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
// Kalshi
|
|
76
|
-
const kalshi = new
|
|
76
|
+
const kalshi = new pmxt.kalshi();
|
|
77
77
|
const kalshiResults = await kalshi.searchMarkets(query);
|
|
78
78
|
|
|
79
79
|
console.log(`\n--- Kalshi Found ${kalshiResults.length} ---`);
|