pmxtjs 0.1.2 → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 qoery.com
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -22,8 +22,8 @@ export declare abstract class PredictionMarketExchange {
22
22
  */
23
23
  abstract searchMarkets(query: string, params?: MarketFilterParams): Promise<UnifiedMarket[]>;
24
24
  /**
25
- * Fetch historical price data for a specific market or outcome.
26
- * @param id - The market ID or specific outcome ID/Token ID depending on the exchange
25
+ * Fetch historical price data for a specific market outcome.
26
+ * @param id - The Outcome ID (MarketOutcome.id). This should be the ID of the specific tradeable asset.
27
27
  */
28
28
  getMarketHistory(id: string, params: HistoryFilterParams): Promise<PriceCandle[]>;
29
29
  /**
@@ -6,8 +6,8 @@ exports.PredictionMarketExchange = void 0;
6
6
  // ----------------------------------------------------------------------------
7
7
  class PredictionMarketExchange {
8
8
  /**
9
- * Fetch historical price data for a specific market or outcome.
10
- * @param id - The market ID or specific outcome ID/Token ID depending on the exchange
9
+ * Fetch historical price data for a specific market outcome.
10
+ * @param id - The Outcome ID (MarketOutcome.id). This should be the ID of the specific tradeable asset.
11
11
  */
12
12
  async getMarketHistory(id, params) {
13
13
  throw new Error("Method getMarketHistory not implemented.");
@@ -116,13 +116,13 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
116
116
  }
117
117
  const outcomes = [
118
118
  {
119
- id: 'yes',
119
+ id: market.ticker, // The actual market ticker (primary tradeable)
120
120
  label: candidateName || 'Yes',
121
121
  price: price,
122
122
  priceChange24h: priceChange
123
123
  },
124
124
  {
125
- id: 'no',
125
+ id: `${market.ticker}-NO`, // Virtual ID for the No outcome
126
126
  label: candidateName ? `Not ${candidateName}` : 'No',
127
127
  price: 1 - price,
128
128
  priceChange24h: -priceChange // Inverse change for No? simplified assumption
@@ -211,7 +211,9 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
211
211
  async getMarketHistory(id, params) {
212
212
  try {
213
213
  // Kalshi API expects uppercase tickers
214
- const normalizedId = id.toUpperCase();
214
+ // Handle virtual "-NO" suffix by stripping it (fetching the underlying market history)
215
+ const cleanedId = id.replace(/-NO$/, '');
216
+ const normalizedId = cleanedId.toUpperCase();
215
217
  const interval = this.mapIntervalToKalshi(params.resolution);
216
218
  // Heuristic for series_ticker
217
219
  const parts = normalizedId.split('-');
@@ -261,7 +263,8 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
261
263
  }
262
264
  async getOrderBook(id) {
263
265
  try {
264
- const url = `https://api.elections.kalshi.com/trade-api/v2/markets/${id}/orderbook`;
266
+ const ticker = id.replace(/-NO$/, '');
267
+ const url = `https://api.elections.kalshi.com/trade-api/v2/markets/${ticker}/orderbook`;
265
268
  const response = await axios_1.default.get(url);
266
269
  const data = response.data.orderbook;
267
270
  // Structure: { yes: [[price, qty], ...], no: [[price, qty], ...] }
@@ -285,10 +288,11 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
285
288
  }
286
289
  async getTradeHistory(id, params) {
287
290
  try {
291
+ const ticker = id.replace(/-NO$/, '');
288
292
  const url = `https://api.elections.kalshi.com/trade-api/v2/markets/trades`;
289
293
  const response = await axios_1.default.get(url, {
290
294
  params: {
291
- ticker: id,
295
+ ticker: ticker,
292
296
  limit: params.limit || 100
293
297
  }
294
298
  });
@@ -95,11 +95,12 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
95
95
  priceChange = Number(market.oneDayPriceChange || 0);
96
96
  }
97
97
  outcomes.push({
98
- id: String(index),
98
+ id: clobTokenIds[index] || String(index), // Use CLOB Token ID as the primary ID
99
99
  label: outcomeLabel,
100
100
  price: parseFloat(rawPrice) || 0,
101
101
  priceChange24h: priceChange,
102
102
  metadata: {
103
+ // clobTokenId is now the main ID, but keeping it in metadata for backward compat if needed
103
104
  clobTokenId: clobTokenIds[index]
104
105
  }
105
106
  });
@@ -218,7 +219,7 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
218
219
  priceChange = Number(market.oneDayPriceChange || 0);
219
220
  }
220
221
  outcomes.push({
221
- id: String(index),
222
+ id: clobTokenIds[index] || String(index),
222
223
  label: outcomeLabel,
223
224
  price: parseFloat(outcomePrices[index] || "0") || 0,
224
225
  priceChange24h: priceChange,
@@ -273,7 +274,7 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
273
274
  async getMarketHistory(id, params) {
274
275
  // ID Validation: Polymarket CLOB requires a Token ID (long numeric string) not a Market ID
275
276
  if (id.length < 10 && /^\d+$/.test(id)) {
276
- throw new Error(`Invalid ID for Polymarket history: "${id}". You provided a Market ID, but Polymarket's CLOB API requires a Token ID. Use outcome.metadata.clobTokenId instead.`);
277
+ 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'.`);
277
278
  }
278
279
  try {
279
280
  const fidelity = this.mapIntervalToFidelity(params.resolution);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "0.1.2",
3
+ "version": "0.2.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",
@@ -18,14 +18,15 @@
18
18
  },
19
19
  "keywords": [],
20
20
  "author": "",
21
- "license": "ISC",
21
+ "license": "MIT",
22
22
  "type": "commonjs",
23
23
  "dependencies": {
24
24
  "axios": "^1.7.9",
25
+ "jest": "^30.2.0",
25
26
  "tsx": "^4.21.0"
26
27
  },
27
28
  "devDependencies": {
28
- "typescript": "^5.9.3",
29
- "@types/node": "^25.0.3"
29
+ "@types/node": "^25.0.3",
30
+ "typescript": "^5.9.3"
30
31
  }
31
- }
32
+ }
package/readme.md CHANGED
@@ -1,7 +1,53 @@
1
- # pmxt
1
+ # pmxt [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=The%20ccxt%20for%20prediction%20markets.&url=https://github.com/qoery-com/pmxt&hashtags=predictionmarkets,trading)
2
2
 
3
3
  **The ccxt for prediction markets.** A unified API for accessing prediction market data across multiple exchanges.
4
4
 
5
+ <img width="3840" height="2160" alt="plot" src="https://github.com/user-attachments/assets/ed77d244-c95f-4fe0-a7a7-89af713c053f" />
6
+
7
+ <div align="center">
8
+ <table>
9
+ <tr>
10
+ <td rowspan="3">
11
+ <a href="https://www.producthunt.com/products/qoery-python-sdk?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-qoery-python-sdk" target="_blank" rel="noopener noreferrer"><img alt="Qoery Python SDK - 50% cheaper crypto data. Now in Python. | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1056631&amp;theme=light&amp;t=1767263265752"></a>
12
+ </td>
13
+ <td>
14
+ <img src="https://img.shields.io/github/watchers/qoery-com/pmxt?style=social" alt="GitHub watchers">
15
+ </td>
16
+ <td>
17
+ <a href="https://www.npmjs.com/package/pmxtjs"><img src="https://img.shields.io/npm/dt/pmxtjs" alt="Downloads"></a>
18
+ </td>
19
+ </tr>
20
+ <tr>
21
+ <td>
22
+ <img src="https://img.shields.io/github/forks/qoery-com/pmxt?style=social" alt="GitHub forks">
23
+ </td>
24
+ <td>
25
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
26
+ </td>
27
+ </tr>
28
+ <tr>
29
+ <td>
30
+ <a href="https://github.com/qoery-com/pmxt/stargazers"><img src="https://img.shields.io/github/stars/qoery-com/pmxt?refresh=1" alt="GitHub stars"></a>
31
+ </td>
32
+ <td>
33
+ <!-- Space for future badge -->
34
+ </td>
35
+ </tr>
36
+ </table>
37
+ </div>
38
+
39
+ <p align="center">
40
+ <img src="https://polymarket.com/favicon.ico" alt="Polymarket" width="40" height="40">
41
+ &nbsp;&nbsp;&nbsp;&nbsp;
42
+ <img src="https://kalshi.com/favicon.ico" alt="Kalshi" width="40" height="40">
43
+ &nbsp;&nbsp;&nbsp;&nbsp;
44
+ <img src="https://manifold.markets/logo.svg" alt="Manifold Markets" width="40" height="40">
45
+ &nbsp;&nbsp;&nbsp;&nbsp;
46
+ <img src="https://metaculus.com/favicon.ico" alt="Metaculus" width="40" height="40">
47
+ &nbsp;&nbsp;&nbsp;&nbsp;
48
+ <img src="https://predictit.org/favicon.ico" alt="PredictIt" width="40" height="40">
49
+ </p>
50
+
5
51
  ## Why pmxt?
6
52
 
7
53
  Different prediction market platforms have different APIs, data formats, and conventions. pmxt provides a single, consistent interface to work with all of them.
@@ -13,7 +59,7 @@ Search for markets across Polymarket and Kalshi using the same API:
13
59
  ```typescript
14
60
  import { PolymarketExchange, KalshiExchange } from 'pmxtjs';
15
61
 
16
- const query = process.argv[2] || 'Fed';
62
+ const query = process.argv[2] || 'Who will Trump nominate as Fed Chair?';
17
63
  console.log(`Searching for "${query}"...\n`);
18
64
 
19
65
  // Polymarket
@@ -60,3 +106,6 @@ Check out the [examples](pmxt/examples/) directory for more use cases:
60
106
  - Historical prices
61
107
  - Event price tracking
62
108
  - Recent trades
109
+
110
+
111
+ [![Stargazers repo roster for @qoery-com/pmxt](https://reporoster.com/stars/qoery-com/pmxt)](https://github.com/qoery-com/pmxt/stargazers)