pmxtjs 0.2.0 → 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 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
- const polymarket = new PolymarketExchange();
16
- const kalshi = new KalshiExchange();
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
- ### `getMarketHistory(id, params)`
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
- ### `getOrderBook(id)`
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
- ### `getTradeHistory(id, params)`
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 `getMarketHistory` for public historical data.
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
 
@@ -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
- getMarketHistory(id: string, params: HistoryFilterParams): Promise<PriceCandle[]>;
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
- getOrderBook(id: string): Promise<OrderBook>;
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
- getTradeHistory(id: string, params: HistoryFilterParams): Promise<Trade[]>;
38
+ fetchTrades(id: string, params: HistoryFilterParams): Promise<Trade[]>;
39
39
  }
@@ -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 getMarketHistory(id, params) {
13
- throw new Error("Method getMarketHistory not implemented.");
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 getOrderBook(id) {
20
- throw new Error("Method getOrderBook not implemented.");
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 getTradeHistory(id, params) {
27
- throw new Error("Method getTradeHistory not implemented.");
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
- getMarketHistory(id: string, params: HistoryFilterParams): Promise<PriceCandle[]>;
18
- getOrderBook(id: string): Promise<OrderBook>;
19
- getTradeHistory(id: string, params: HistoryFilterParams): Promise<Trade[]>;
17
+ fetchOHLCV(id: string, params: HistoryFilterParams): Promise<PriceCandle[]>;
18
+ fetchOrderBook(id: string): Promise<OrderBook>;
19
+ fetchTrades(id: string, params: HistoryFilterParams): Promise<Trade[]>;
20
20
  }
@@ -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 getMarketHistory(id, params) {
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
- timestamp: c.end_period_ts * 1000,
244
- open: (c.price.open || 0) / 100,
245
- high: (c.price.high || 0) / 100,
246
- low: (c.price.low || 0) / 100,
247
- close: (c.price.close || 0) / 100,
248
- volume: c.volume
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 getOrderBook(id) {
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 getTradeHistory(id, params) {
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
- getMarketHistory(id: string, params: HistoryFilterParams): Promise<PriceCandle[]>;
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
- getOrderBook(id: string): Promise<OrderBook>;
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 getMarketHistory for public historical price data instead.
35
+ * Use fetchOHLCV for public historical price data instead.
36
36
  */
37
- getTradeHistory(id: string, params: HistoryFilterParams): Promise<Trade[]>;
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) { /* ignore */ }
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 getMarketHistory(id, params) {
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 getOrderBook(id) {
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 getMarketHistory for public historical price data instead.
374
+ * Use fetchOHLCV for public historical price data instead.
372
375
  */
373
- async getTradeHistory(id, params) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "pmxt is a unified prediction market data API. The ccxt for prediction markets.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,7 +26,9 @@
26
26
  "tsx": "^4.21.0"
27
27
  },
28
28
  "devDependencies": {
29
+ "@types/jest": "^30.0.0",
29
30
  "@types/node": "^25.0.3",
31
+ "ts-jest": "^29.4.6",
30
32
  "typescript": "^5.9.3"
31
33
  }
32
- }
34
+ }
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 { PolymarketExchange, KalshiExchange } from 'pmxtjs';
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 PolymarketExchange();
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 KalshiExchange();
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} ---`);