pmxtjs 0.3.0 → 0.3.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/API_REFERENCE.md CHANGED
@@ -1,86 +1,254 @@
1
1
  # Prediction Market API Reference
2
2
 
3
- This project implements a **Unified Interface** for interacting with multiple prediction market exchanges (Kalshi, Polymarket) identically.
3
+ A unified interface for interacting with multiple prediction market exchanges (Kalshi, Polymarket) identically.
4
4
 
5
- ## Usage
5
+ ## Installation & Usage
6
6
 
7
- All components are available directly from the `pmxt` library.
7
+ ```bash
8
+ npm install pmxtjs
9
+ ```
8
10
 
9
11
  ```typescript
10
12
  import pmxt from 'pmxtjs';
11
13
 
12
- // ccxt-style instantiation
13
14
  const polymarket = new pmxt.polymarket();
14
15
  const kalshi = new pmxt.kalshi();
15
16
  ```
16
17
 
17
- ## 1. Unified Interface (`PredictionMarketExchange`)
18
+ ---
18
19
 
19
- All exchanges implement this core interface for discovery.
20
+ ## Core Methods
20
21
 
21
22
  ### `fetchMarkets(params?)`
22
- Retrieves active markets normalized into a standard format.
23
- - **Input**: `limit`, `sort` ('volume' | 'liquidity' | 'newest')
24
- - **Output**: `Promise<UnifiedMarket[]>`
23
+ Get active markets from an exchange.
24
+
25
+ ```typescript
26
+ const markets = await polymarket.fetchMarkets({
27
+ limit: 20,
28
+ offset: 0,
29
+ sort: 'volume' // 'volume' | 'liquidity' | 'newest'
30
+ });
31
+ ```
25
32
 
26
33
  ### `searchMarkets(query, params?)`
27
- Search markets by title/description across the exchange(s).
28
- - **Input**: `query` (string), `limit`
29
- - **Output**: `Promise<UnifiedMarket[]>`
34
+ Search markets by keyword. By default, searches only in titles.
35
+
36
+ ```typescript
37
+ const results = await kalshi.searchMarkets('Fed rates', {
38
+ limit: 10,
39
+ searchIn: 'title' // 'title' (default) | 'description' | 'both'
40
+ });
41
+ ```
42
+
43
+ ### `getMarketsBySlug(slug)`
44
+ Fetch markets by URL slug/ticker.
45
+
46
+ ```typescript
47
+ // Polymarket: use URL slug
48
+ const polyMarkets = await polymarket.getMarketsBySlug('who-will-trump-nominate-as-fed-chair');
49
+
50
+ // Kalshi: use event ticker (auto-uppercased)
51
+ const kalshiMarkets = await kalshi.getMarketsBySlug('FED-25JAN');
52
+ ```
30
53
 
31
54
  ---
32
55
 
33
- ## 2. Deep-Dive Interface (Exchange Only)
56
+ ## Deep-Dive Methods
57
+
58
+ ### `fetchOHLCV(outcomeId, params)`
59
+ Get historical price candles.
60
+
61
+ **CRITICAL**: Use `outcome.id`, not `market.id`.
62
+ - **Polymarket**: `outcome.id` is the CLOB Token ID
63
+ - **Kalshi**: `outcome.id` is the Market Ticker
64
+
65
+ ```typescript
66
+ const markets = await polymarket.searchMarkets('Trump');
67
+ const outcomeId = markets[0].outcomes[0].id; // Get the outcome ID
68
+
69
+ const candles = await polymarket.fetchOHLCV(outcomeId, {
70
+ resolution: '1h', // '1m' | '5m' | '15m' | '1h' | '6h' | '1d'
71
+ start: new Date('2024-01-01'),
72
+ end: new Date('2024-01-31'),
73
+ limit: 100
74
+ });
75
+ ```
76
+
77
+ ### `fetchOrderBook(outcomeId)`
78
+ Get current bids/asks.
34
79
 
35
- Methods available on specific exchange instances (`KalshiExchange`, `PolymarketExchange`) for detailed data.
80
+ ```typescript
81
+ const orderBook = await kalshi.fetchOrderBook('FED-25JAN');
82
+ console.log('Best bid:', orderBook.bids[0].price);
83
+ console.log('Best ask:', orderBook.asks[0].price);
84
+ ```
36
85
 
37
- ### `fetchOHLCV(id, params)`
38
- Fetches OHLCV candlesticks.
39
- - **Input**: `id`, `resolution` (1m, 1h, 1d), `start`, `end`.
40
- - **Output**: `Promise<PriceCandle[]>`
41
- - **Important**:
42
- - **Kalshi**: Uses the Market Ticker (e.g., `FED-25DEC`). Returns **native OHLCV** (Open/High/Low/Close data is distinct).
43
- - **Polymarket**: Uses the **CLOB Token ID** found in `outcome.metadata.clobTokenId`. Returns **synthetic candles** (Open=High=Low=Close) derived from raw price points.
86
+ ### `fetchTrades(outcomeId, params)`
87
+ Get trade history.
44
88
 
45
- ### `fetchOrderBook(id)`
46
- Fetches live Bids/Asks.
47
- - **Input**: `id` (Ticker for Kalshi, Token ID for Polymarket).
48
- - **Output**: `Promise<OrderBook>` (Normalized: "No" bids becomes "Yes" asks).
89
+ **Note**: Polymarket requires API key. Use `fetchOHLCV` for public historical data.
49
90
 
50
- ### `fetchTrades(id, params)`
51
- Fetches the "Tape" (raw transaction history).
52
- - **Input**: `id`, `limit`.
53
- - **Output**: `Promise<Trade[]>`
54
- - **Note**:
55
- - **Kalshi**: Publicly accessible for all tickers.
56
- - **Polymarket**: Requires L2 Authentication (API Key). Without a key, this method will throw an unauthorized error. Use `fetchOHLCV` for public historical data.
91
+ ```typescript
92
+ const trades = await kalshi.fetchTrades('FED-25JAN', {
93
+ resolution: '1h',
94
+ limit: 100
95
+ });
96
+ ```
57
97
 
58
98
  ---
59
99
 
60
- ## 3. Data Models
100
+ ## Data Models
61
101
 
62
- ### `UnifiedMarket` (The Standard Atom)
102
+ ### `UnifiedMarket`
63
103
  ```typescript
64
104
  {
65
- id: string; // Exchange-specific ID
66
- title: string; // "Who will win the election?"
67
- description: string; // Detailed context/rules of the market
68
- outcomes: [
69
- {
70
- id: string,
71
- label: "Trump",
72
- price: 0.52,
73
- priceChange24h: 0.05, // Optional: Change in price over last 24h
74
- metadata: { ... } // Exchange-specific keys (clobTokenId)
75
- }
76
- ];
105
+ id: string; // Market ID
106
+ title: string;
107
+ description: string;
108
+ outcomes: MarketOutcome[]; // All tradeable outcomes
109
+
77
110
  resolutionDate: Date;
78
- volume24h: number;
79
- liquidity: number;
111
+ volume24h: number; // USD
112
+ volume?: number; // Total volume (USD)
113
+ liquidity: number; // USD
114
+ openInterest?: number; // USD
115
+
80
116
  url: string;
81
- image?: string; // Optional: Thumbnail URL
82
- category?: string; // Optional: Primary category (e.g., "Politics")
83
- tags?: string[]; // Optional: Searchable tags
117
+ image?: string;
118
+ category?: string;
119
+ tags?: string[];
84
120
  }
85
121
  ```
86
122
 
123
+ ### `MarketOutcome`
124
+ ```typescript
125
+ {
126
+ id: string; // ⚠️ Use this for fetchOHLCV/fetchOrderBook/fetchTrades
127
+ // Polymarket: CLOB Token ID
128
+ // Kalshi: Market Ticker
129
+ label: string; // "Trump", "Yes", etc.
130
+ price: number; // 0.0 to 1.0 (probability)
131
+ priceChange24h?: number;
132
+ metadata?: {
133
+ clobTokenId?: string; // Polymarket only
134
+ }
135
+ }
136
+ ```
137
+
138
+ ### Other Types
139
+ ```typescript
140
+ interface PriceCandle {
141
+ timestamp: number; // Unix ms
142
+ open: number; // 0.0 to 1.0
143
+ high: number;
144
+ low: number;
145
+ close: number;
146
+ volume?: number;
147
+ }
148
+
149
+ interface OrderBook {
150
+ bids: OrderLevel[]; // Sorted high to low
151
+ asks: OrderLevel[]; // Sorted low to high
152
+ timestamp?: number;
153
+ }
154
+
155
+ interface OrderLevel {
156
+ price: number; // 0.0 to 1.0
157
+ size: number; // Contracts
158
+ }
159
+
160
+ interface Trade {
161
+ id: string;
162
+ timestamp: number; // Unix ms
163
+ price: number; // 0.0 to 1.0
164
+ amount: number;
165
+ side: 'buy' | 'sell' | 'unknown';
166
+ }
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Complete Workflow Example
172
+
173
+ ```typescript
174
+ // 1. Search for markets
175
+ const markets = await polymarket.searchMarkets('Fed Chair');
176
+ const market = markets[0];
177
+
178
+ // 2. Get outcome details
179
+ const outcome = market.outcomes[0];
180
+ console.log(`${outcome.label}: ${(outcome.price * 100).toFixed(1)}%`);
181
+
182
+ // 3. Fetch historical data (use outcome.id!)
183
+ const candles = await polymarket.fetchOHLCV(outcome.id, {
184
+ resolution: '1d',
185
+ limit: 30
186
+ });
187
+
188
+ // 4. Get current order book
189
+ const orderBook = await polymarket.fetchOrderBook(outcome.id);
190
+ const spread = orderBook.asks[0].price - orderBook.bids[0].price;
191
+ console.log(`Spread: ${(spread * 100).toFixed(2)}%`);
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Exchange Differences
197
+
198
+ | Feature | Polymarket | Kalshi |
199
+ |---------|-----------|--------|
200
+ | **Sorting** | Server-side | Client-side (slower for large sets) |
201
+ | **Market ID** | UUID | Event Ticker (e.g., "PRES-2024") |
202
+ | **Outcome ID** | CLOB Token ID | Market Ticker (e.g., "FED-25JAN") |
203
+ | **OHLCV Quality** | Synthetic (O=H=L=C) | Native (distinct values) |
204
+ | **Auth Required** | Only for `fetchTrades()` | No (all public) |
205
+ | **Slug Format** | lowercase-with-hyphens | UPPERCASE (auto-normalized) |
206
+
207
+ ---
208
+
209
+ ## Error Handling
210
+
211
+ ```typescript
212
+ try {
213
+ const markets = await kalshi.getMarketsBySlug('INVALID-TICKER');
214
+ } catch (error) {
215
+ // Kalshi: "Event not found: INVALID-TICKER"
216
+ // Polymarket: Returns empty array []
217
+ console.error(error.message);
218
+ }
219
+ ```
220
+
221
+ **Common Errors**:
222
+ - `404`: Market/event doesn't exist
223
+ - `401`: Missing API key (Polymarket `fetchTrades`)
224
+ - `429`: Rate limited
225
+ - `500`: Exchange API issue
226
+
227
+ ---
228
+
229
+ ## Type Exports
230
+
231
+ ```typescript
232
+ import pmxt, {
233
+ UnifiedMarket,
234
+ MarketOutcome,
235
+ PriceCandle,
236
+ OrderBook,
237
+ Trade,
238
+ CandleInterval,
239
+ MarketFilterParams,
240
+ HistoryFilterParams
241
+ } from 'pmxtjs';
242
+ ```
243
+
244
+ ---
245
+
246
+ ## Quick Reference
247
+
248
+ - **Prices**: Always 0.0-1.0 (multiply by 100 for %)
249
+ - **Timestamps**: Unix milliseconds
250
+ - **Volumes**: USD
251
+ - **IDs**: Use `outcome.id` for deep-dive methods, not `market.id`
252
+
253
+ For more examples, see [`examples/`](examples/).
254
+
@@ -3,6 +3,7 @@ export interface MarketFilterParams {
3
3
  limit?: number;
4
4
  offset?: number;
5
5
  sort?: 'volume' | 'liquidity' | 'newest';
6
+ searchIn?: 'title' | 'description' | 'both';
6
7
  }
7
8
  export interface HistoryFilterParams {
8
9
  resolution: CandleInterval;
@@ -18,7 +19,7 @@ export declare abstract class PredictionMarketExchange {
18
19
  abstract fetchMarkets(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
19
20
  /**
20
21
  * Search for markets matching a keyword query.
21
- * Searches across title and description fields.
22
+ * By default, searches only in market titles. Use params.searchIn to search descriptions or both.
22
23
  */
23
24
  abstract searchMarkets(query: string, params?: MarketFilterParams): Promise<UnifiedMarket[]>;
24
25
  /**
@@ -149,8 +149,16 @@ 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 searchIn = params?.searchIn || 'title'; // Default to title-only search
153
+ const filtered = markets.filter(market => {
154
+ const titleMatch = (market.title || '').toLowerCase().includes(lowerQuery);
155
+ const descMatch = (market.description || '').toLowerCase().includes(lowerQuery);
156
+ if (searchIn === 'title')
157
+ return titleMatch;
158
+ if (searchIn === 'description')
159
+ return descMatch;
160
+ return titleMatch || descMatch; // 'both'
161
+ });
154
162
  const limit = params?.limit || 20;
155
163
  return filtered.slice(0, limit);
156
164
  }
@@ -149,8 +149,8 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
149
149
  }
150
150
  async searchMarkets(query, params) {
151
151
  // Polymarket Gamma API doesn't support native search
152
- // Fetch a larger batch and filter client-side
153
- const searchLimit = 100; // Fetch more markets to search through
152
+ // Fetch all active markets and filter client-side
153
+ const searchLimit = 100000; // Fetch all markets for comprehensive search
154
154
  try {
155
155
  // Fetch markets with a higher limit
156
156
  const markets = await this.fetchMarkets({
@@ -159,8 +159,16 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
159
159
  });
160
160
  // Client-side text filtering
161
161
  const lowerQuery = query.toLowerCase();
162
- const filtered = markets.filter(market => (market.title || '').toLowerCase().includes(lowerQuery) ||
163
- (market.description || '').toLowerCase().includes(lowerQuery));
162
+ const searchIn = params?.searchIn || 'title'; // Default to title-only search
163
+ const filtered = markets.filter(market => {
164
+ const titleMatch = (market.title || '').toLowerCase().includes(lowerQuery);
165
+ const descMatch = (market.description || '').toLowerCase().includes(lowerQuery);
166
+ if (searchIn === 'title')
167
+ return titleMatch;
168
+ if (searchIn === 'description')
169
+ return descMatch;
170
+ return titleMatch || descMatch; // 'both'
171
+ });
164
172
  // Apply limit to filtered results
165
173
  const limit = params?.limit || 20;
166
174
  return filtered.slice(0, limit);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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",
package/readme.md CHANGED
@@ -54,33 +54,19 @@ Different prediction market platforms have different APIs, data formats, and con
54
54
 
55
55
  ## Quick Example
56
56
 
57
- Search for markets across Polymarket and Kalshi using the same API:
57
+ Get the current price for any market in seconds:
58
58
 
59
59
  ```typescript
60
60
  import pmxt from 'pmxtjs';
61
61
 
62
- const query = process.argv[2] || 'Who will Trump nominate as Fed Chair?';
63
- console.log(`Searching for "${query}"...\n`);
62
+ async function main() {
63
+ const poly = new pmxt.polymarket();
64
+ const [market] = await poly.searchMarkets('Trump');
64
65
 
65
- // Polymarket
66
- const polymarket = new pmxt.polymarket();
67
- const polyResults = await polymarket.searchMarkets(query, { sort: 'volume' });
66
+ console.log(`${market.title} - ${market.outcomes[0].label}: ${marketoutcomes[0].price * 100}%`);
67
+ }
68
68
 
69
- console.log(`--- Polymarket Found ${polyResults.length} ---`);
70
- polyResults.slice(0, 10).forEach(m => {
71
- const label = m.outcomes[0]?.label || 'Unknown';
72
- console.log(`[${m.id}] ${m.title} - ${label} (Vol24h: $${m.volume24h.toLocaleString()})`);
73
- });
74
-
75
- // Kalshi
76
- const kalshi = new pmxt.kalshi();
77
- const kalshiResults = await kalshi.searchMarkets(query);
78
-
79
- console.log(`\n--- Kalshi Found ${kalshiResults.length} ---`);
80
- kalshiResults.slice(0, 10).forEach(m => {
81
- const label = m.outcomes[0]?.label || 'Unknown';
82
- console.log(`[${m.id}] ${m.title} - ${label} (Vol24h: $${m.volume24h.toLocaleString()})`);
83
- });
69
+ main();
84
70
  ```
85
71
 
86
72
  ## Installation