pmxtjs 0.1.0 → 0.1.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.
Files changed (58) hide show
  1. package/dist/BaseExchange.js +30 -0
  2. package/{src/exchanges/Kalshi.ts → dist/exchanges/Kalshi.js} +75 -106
  3. package/{src/exchanges/Polymarket.ts → dist/exchanges/Polymarket.js} +107 -144
  4. package/dist/index.js +20 -0
  5. package/dist/types.js +5 -0
  6. package/package.json +4 -1
  7. package/API_REFERENCE.md +0 -88
  8. package/coverage/clover.xml +0 -334
  9. package/coverage/coverage-final.json +0 -4
  10. package/coverage/lcov-report/base.css +0 -224
  11. package/coverage/lcov-report/block-navigation.js +0 -87
  12. package/coverage/lcov-report/favicon.png +0 -0
  13. package/coverage/lcov-report/index.html +0 -131
  14. package/coverage/lcov-report/pmxt/BaseExchange.ts.html +0 -256
  15. package/coverage/lcov-report/pmxt/exchanges/Kalshi.ts.html +0 -1132
  16. package/coverage/lcov-report/pmxt/exchanges/Polymarket.ts.html +0 -1456
  17. package/coverage/lcov-report/pmxt/exchanges/index.html +0 -131
  18. package/coverage/lcov-report/pmxt/index.html +0 -116
  19. package/coverage/lcov-report/prettify.css +0 -1
  20. package/coverage/lcov-report/prettify.js +0 -2
  21. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  22. package/coverage/lcov-report/sorter.js +0 -210
  23. package/coverage/lcov-report/src/BaseExchange.ts.html +0 -256
  24. package/coverage/lcov-report/src/exchanges/Kalshi.ts.html +0 -1132
  25. package/coverage/lcov-report/src/exchanges/Polymarket.ts.html +0 -1456
  26. package/coverage/lcov-report/src/exchanges/index.html +0 -131
  27. package/coverage/lcov-report/src/index.html +0 -116
  28. package/coverage/lcov.info +0 -766
  29. package/examples/get_event_prices.ts +0 -37
  30. package/examples/historical_prices.ts +0 -117
  31. package/examples/orderbook.ts +0 -102
  32. package/examples/recent_trades.ts +0 -29
  33. package/examples/search_events.ts +0 -68
  34. package/examples/search_market.ts +0 -29
  35. package/jest.config.js +0 -11
  36. package/pmxt-0.1.0.tgz +0 -0
  37. package/src/BaseExchange.ts +0 -57
  38. package/src/index.ts +0 -5
  39. package/src/types.ts +0 -61
  40. package/test/exchanges/kalshi/ApiErrors.test.ts +0 -132
  41. package/test/exchanges/kalshi/EmptyResponse.test.ts +0 -44
  42. package/test/exchanges/kalshi/FetchAndNormalizeMarkets.test.ts +0 -56
  43. package/test/exchanges/kalshi/LiveApi.integration.test.ts +0 -40
  44. package/test/exchanges/kalshi/MarketHistory.test.ts +0 -185
  45. package/test/exchanges/kalshi/OrderBook.test.ts +0 -149
  46. package/test/exchanges/kalshi/SearchMarkets.test.ts +0 -174
  47. package/test/exchanges/kalshi/VolumeFallback.test.ts +0 -44
  48. package/test/exchanges/polymarket/DataValidation.test.ts +0 -271
  49. package/test/exchanges/polymarket/ErrorHandling.test.ts +0 -34
  50. package/test/exchanges/polymarket/FetchAndNormalizeMarkets.test.ts +0 -68
  51. package/test/exchanges/polymarket/GetMarketsBySlug.test.ts +0 -268
  52. package/test/exchanges/polymarket/LiveApi.integration.test.ts +0 -44
  53. package/test/exchanges/polymarket/MarketHistory.test.ts +0 -207
  54. package/test/exchanges/polymarket/OrderBook.test.ts +0 -167
  55. package/test/exchanges/polymarket/RequestParameters.test.ts +0 -39
  56. package/test/exchanges/polymarket/SearchMarkets.test.ts +0 -176
  57. package/test/exchanges/polymarket/TradeHistory.test.ts +0 -248
  58. package/tsconfig.json +0 -12
@@ -1,103 +1,101 @@
1
- import axios from 'axios';
2
- import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams } from '../BaseExchange';
3
- import { UnifiedMarket, MarketOutcome, PriceCandle, CandleInterval, OrderBook, Trade } from '../types';
4
-
5
- export class PolymarketExchange extends PredictionMarketExchange {
6
- get name(): string {
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.PolymarketExchange = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const BaseExchange_1 = require("../BaseExchange");
9
+ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
10
+ constructor() {
11
+ super(...arguments);
12
+ // Utilizing the Gamma API for rich metadata and list view data
13
+ this.baseUrl = 'https://gamma-api.polymarket.com/events';
14
+ // CLOB API for orderbook, trades, and timeseries
15
+ this.clobUrl = 'https://clob.polymarket.com';
16
+ }
17
+ get name() {
7
18
  return 'Polymarket';
8
19
  }
9
-
10
- // Utilizing the Gamma API for rich metadata and list view data
11
- private readonly baseUrl = 'https://gamma-api.polymarket.com/events';
12
- // CLOB API for orderbook, trades, and timeseries
13
- private readonly clobUrl = 'https://clob.polymarket.com';
14
-
15
- async fetchMarkets(params?: MarketFilterParams): Promise<UnifiedMarket[]> {
16
- const limit = params?.limit || 200; // Higher default for better coverage
20
+ async fetchMarkets(params) {
21
+ const limit = params?.limit || 200; // Higher default for better coverage
17
22
  const offset = params?.offset || 0;
18
-
19
23
  // Map generic sort params to Polymarket Gamma API params
20
- let queryParams: any = {
24
+ let queryParams = {
21
25
  active: 'true',
22
26
  closed: 'false',
23
27
  limit: limit,
24
28
  offset: offset,
25
29
  };
26
-
27
30
  // Gamma API uses 'order' and 'ascending' for sorting
28
31
  if (params?.sort === 'volume') {
29
32
  queryParams.order = 'volume';
30
33
  queryParams.ascending = 'false';
31
- } else if (params?.sort === 'newest') {
34
+ }
35
+ else if (params?.sort === 'newest') {
32
36
  queryParams.order = 'startDate';
33
37
  queryParams.ascending = 'false';
34
- } else if (params?.sort === 'liquidity') {
38
+ }
39
+ else if (params?.sort === 'liquidity') {
35
40
  // queryParams.order = 'liquidity';
36
- } else {
41
+ }
42
+ else {
37
43
  // Default: do not send order param to avoid 422
38
44
  }
39
-
40
45
  try {
41
46
  // Fetch active events from Gamma
42
- const response = await axios.get(this.baseUrl, {
47
+ const response = await axios_1.default.get(this.baseUrl, {
43
48
  params: queryParams
44
49
  });
45
-
46
50
  const events = response.data;
47
- const unifiedMarkets: UnifiedMarket[] = [];
48
-
51
+ const unifiedMarkets = [];
49
52
  for (const event of events) {
50
53
  // Each event is a container (e.g. "US Election").
51
54
  // It contains specific "markets" (e.g. "Winner", "Pop Vote").
52
- if (!event.markets) continue;
53
-
55
+ if (!event.markets)
56
+ continue;
54
57
  for (const market of event.markets) {
55
- const outcomes: MarketOutcome[] = [];
56
-
58
+ const outcomes = [];
57
59
  // Polymarket Gamma often returns 'outcomes' and 'outcomePrices' as stringified JSON keys.
58
- let outcomeLabels: string[] = [];
59
- let outcomePrices: string[] = [];
60
-
60
+ let outcomeLabels = [];
61
+ let outcomePrices = [];
61
62
  try {
62
63
  outcomeLabels = typeof market.outcomes === 'string' ? JSON.parse(market.outcomes) : (market.outcomes || []);
63
64
  outcomePrices = typeof market.outcomePrices === 'string' ? JSON.parse(market.outcomePrices) : (market.outcomePrices || []);
64
- } catch (e) {
65
+ }
66
+ catch (e) {
65
67
  console.warn(`Failed to parse outcomes for market ${market.id}`, e);
66
68
  }
67
-
68
69
  // Extract CLOB token IDs for granular operations
69
- let clobTokenIds: string[] = [];
70
+ let clobTokenIds = [];
70
71
  try {
71
72
  clobTokenIds = typeof market.clobTokenIds === 'string' ? JSON.parse(market.clobTokenIds) : (market.clobTokenIds || []);
72
- } catch (e) {
73
+ }
74
+ catch (e) {
73
75
  console.warn(`Failed to parse clobTokenIds for market ${market.id}`, e);
74
76
  }
75
-
76
77
  // Extract candidate/option name from market question for better outcome labels
77
- let candidateName: string | null = null;
78
+ let candidateName = null;
78
79
  if (market.question && market.groupItemTitle) {
79
80
  candidateName = market.groupItemTitle;
80
81
  }
81
-
82
82
  if (outcomeLabels.length > 0) {
83
- outcomeLabels.forEach((label: string, index: number) => {
83
+ outcomeLabels.forEach((label, index) => {
84
84
  const rawPrice = outcomePrices[index] || "0";
85
-
86
85
  // For Yes/No markets with specific candidates, use the candidate name
87
86
  let outcomeLabel = label;
88
87
  if (candidateName && label.toLowerCase() === 'yes') {
89
88
  outcomeLabel = candidateName;
90
- } else if (candidateName && label.toLowerCase() === 'no') {
89
+ }
90
+ else if (candidateName && label.toLowerCase() === 'no') {
91
91
  outcomeLabel = `Not ${candidateName}`;
92
92
  }
93
-
94
93
  // 24h Price Change
95
94
  // Polymarket API provides 'oneDayPriceChange' on the market object
96
95
  let priceChange = 0;
97
96
  if (index === 0 || label.toLowerCase() === 'yes' || (candidateName && label === candidateName)) {
98
97
  priceChange = Number(market.oneDayPriceChange || 0);
99
98
  }
100
-
101
99
  outcomes.push({
102
100
  id: String(index),
103
101
  label: outcomeLabel,
@@ -109,7 +107,6 @@ export class PolymarketExchange extends PredictionMarketExchange {
109
107
  });
110
108
  });
111
109
  }
112
-
113
110
  unifiedMarkets.push({
114
111
  id: market.id,
115
112
  title: market.question ? `${event.title} - ${market.question}` : event.title,
@@ -123,115 +120,107 @@ export class PolymarketExchange extends PredictionMarketExchange {
123
120
  url: `https://polymarket.com/event/${event.slug}`,
124
121
  image: event.image || market.image || `https://polymarket.com/api/og?slug=${event.slug}`,
125
122
  category: event.category || event.tags?.[0]?.label,
126
- tags: event.tags?.map((t: any) => t.label) || []
123
+ tags: event.tags?.map((t) => t.label) || []
127
124
  });
128
125
  }
129
126
  }
130
-
131
127
  // Client-side Sort capability to ensure contract fulfillment
132
128
  // Often API filters are "good effort" or apply to the 'event' but not the 'market'
133
129
  if (params?.sort === 'volume') {
134
130
  unifiedMarkets.sort((a, b) => b.volume24h - a.volume24h);
135
- } else if (params?.sort === 'newest') {
131
+ }
132
+ else if (params?.sort === 'newest') {
136
133
  // unifiedMarkets.sort((a, b) => b.resolutionDate.getTime() - a.resolutionDate.getTime()); // Not quite 'newest'
137
- } else if (params?.sort === 'liquidity') {
134
+ }
135
+ else if (params?.sort === 'liquidity') {
138
136
  unifiedMarkets.sort((a, b) => b.liquidity - a.liquidity);
139
- } else {
137
+ }
138
+ else {
140
139
  // Default volume sort
141
140
  unifiedMarkets.sort((a, b) => b.volume24h - a.volume24h);
142
141
  }
143
-
144
142
  // Respect limit strictly after flattening
145
143
  return unifiedMarkets.slice(0, limit);
146
-
147
- } catch (error) {
144
+ }
145
+ catch (error) {
148
146
  console.error("Error fetching Polymarket data:", error);
149
147
  return [];
150
148
  }
151
149
  }
152
-
153
- async searchMarkets(query: string, params?: MarketFilterParams): Promise<UnifiedMarket[]> {
150
+ async searchMarkets(query, params) {
154
151
  // Polymarket Gamma API doesn't support native search
155
152
  // Fetch a larger batch and filter client-side
156
153
  const searchLimit = 100; // Fetch more markets to search through
157
-
158
154
  try {
159
155
  // Fetch markets with a higher limit
160
156
  const markets = await this.fetchMarkets({
161
157
  ...params,
162
158
  limit: searchLimit
163
159
  });
164
-
165
160
  // Client-side text filtering
166
161
  const lowerQuery = query.toLowerCase();
167
- const filtered = markets.filter(market =>
168
- market.title.toLowerCase().includes(lowerQuery) ||
169
- market.description.toLowerCase().includes(lowerQuery)
170
- );
171
-
162
+ const filtered = markets.filter(market => market.title.toLowerCase().includes(lowerQuery) ||
163
+ market.description.toLowerCase().includes(lowerQuery));
172
164
  // Apply limit to filtered results
173
165
  const limit = params?.limit || 20;
174
166
  return filtered.slice(0, limit);
175
-
176
- } catch (error) {
167
+ }
168
+ catch (error) {
177
169
  console.error("Error searching Polymarket data:", error);
178
170
  return [];
179
171
  }
180
172
  }
181
-
182
173
  /**
183
174
  * Fetch specific markets by their URL slug.
184
175
  * Useful for looking up a specific event from a URL.
185
176
  * @param slug - The event slug (e.g. "will-fed-cut-rates-in-march")
186
177
  */
187
- async getMarketsBySlug(slug: string): Promise<UnifiedMarket[]> {
178
+ async getMarketsBySlug(slug) {
188
179
  try {
189
- const response = await axios.get(this.baseUrl, {
180
+ const response = await axios_1.default.get(this.baseUrl, {
190
181
  params: { slug: slug }
191
182
  });
192
-
193
183
  const events = response.data;
194
- if (!events || events.length === 0) return [];
195
-
184
+ if (!events || events.length === 0)
185
+ return [];
196
186
  // We can reuse the logic from fetchMarkets if we extract it,
197
187
  // but for now I'll duplicate the extraction logic to keep it self-contained
198
188
  // and avoid safe refactoring risks.
199
189
  // Actually, fetchMarkets is built to work with the Gamma response structure.
200
190
  // So we can manually map the response using the same logic.
201
-
202
- const unifiedMarkets: UnifiedMarket[] = [];
203
-
191
+ const unifiedMarkets = [];
204
192
  for (const event of events) {
205
- if (!event.markets) continue;
206
-
193
+ if (!event.markets)
194
+ continue;
207
195
  for (const market of event.markets) {
208
- const outcomes: MarketOutcome[] = [];
209
- let outcomeLabels: string[] = [];
210
- let outcomePrices: string[] = [];
211
- let clobTokenIds: string[] = [];
212
-
196
+ const outcomes = [];
197
+ let outcomeLabels = [];
198
+ let outcomePrices = [];
199
+ let clobTokenIds = [];
213
200
  try {
214
201
  outcomeLabels = typeof market.outcomes === 'string' ? JSON.parse(market.outcomes) : (market.outcomes || []);
215
202
  outcomePrices = typeof market.outcomePrices === 'string' ? JSON.parse(market.outcomePrices) : (market.outcomePrices || []);
216
203
  clobTokenIds = typeof market.clobTokenIds === 'string' ? JSON.parse(market.clobTokenIds) : (market.clobTokenIds || []);
217
- } catch (e) { console.warn(`Parse error for market ${market.id}`, e); }
218
-
204
+ }
205
+ catch (e) {
206
+ console.warn(`Parse error for market ${market.id}`, e);
207
+ }
219
208
  let candidateName = market.groupItemTitle;
220
- if (!candidateName && market.question) candidateName = market.question;
221
-
209
+ if (!candidateName && market.question)
210
+ candidateName = market.question;
222
211
  if (outcomeLabels.length > 0) {
223
- outcomeLabels.forEach((label: string, index: number) => {
212
+ outcomeLabels.forEach((label, index) => {
224
213
  let outcomeLabel = label;
225
214
  // Clean up Yes/No labels if candidate name is available
226
- if (candidateName && label.toLowerCase() === 'yes') outcomeLabel = candidateName;
227
- else if (candidateName && label.toLowerCase() === 'no') outcomeLabel = `Not ${candidateName}`;
228
-
215
+ if (candidateName && label.toLowerCase() === 'yes')
216
+ outcomeLabel = candidateName;
217
+ else if (candidateName && label.toLowerCase() === 'no')
218
+ outcomeLabel = `Not ${candidateName}`;
229
219
  // 24h Price Change Logic
230
220
  let priceChange = 0;
231
221
  if (index === 0 || label.toLowerCase() === 'yes' || (candidateName && label === candidateName)) {
232
222
  priceChange = Number(market.oneDayPriceChange || 0);
233
223
  }
234
-
235
224
  outcomes.push({
236
225
  id: String(index),
237
226
  label: outcomeLabel,
@@ -243,7 +232,6 @@ export class PolymarketExchange extends PredictionMarketExchange {
243
232
  });
244
233
  });
245
234
  }
246
-
247
235
  unifiedMarkets.push({
248
236
  id: market.id,
249
237
  title: event.title,
@@ -257,23 +245,22 @@ export class PolymarketExchange extends PredictionMarketExchange {
257
245
  url: `https://polymarket.com/event/${event.slug}`,
258
246
  image: event.image || market.image,
259
247
  category: event.category || event.tags?.[0]?.label,
260
- tags: event.tags?.map((t: any) => t.label) || []
248
+ tags: event.tags?.map((t) => t.label) || []
261
249
  });
262
250
  }
263
251
  }
264
252
  return unifiedMarkets;
265
-
266
- } catch (error) {
253
+ }
254
+ catch (error) {
267
255
  console.error(`Error fetching Polymarket slug ${slug}:`, error);
268
256
  return [];
269
257
  }
270
258
  }
271
-
272
259
  /**
273
260
  * Map our generic CandleInterval to Polymarket's fidelity (in minutes)
274
261
  */
275
- private mapIntervalToFidelity(interval: CandleInterval): number {
276
- const mapping: Record<CandleInterval, number> = {
262
+ mapIntervalToFidelity(interval) {
263
+ const mapping = {
277
264
  '1m': 1,
278
265
  '5m': 5,
279
266
  '15m': 15,
@@ -283,26 +270,22 @@ export class PolymarketExchange extends PredictionMarketExchange {
283
270
  };
284
271
  return mapping[interval];
285
272
  }
286
-
287
273
  /**
288
274
  * Fetch historical price data (OHLCV candles) for a specific token.
289
275
  * @param id - The CLOB token ID (e.g., outcome token ID)
290
276
  */
291
- async getMarketHistory(id: string, params: HistoryFilterParams): Promise<PriceCandle[]> {
277
+ async getMarketHistory(id, params) {
292
278
  // ID Validation: Polymarket CLOB requires a Token ID (long numeric string) not a Market ID
293
279
  if (id.length < 10 && /^\d+$/.test(id)) {
294
280
  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.`);
295
281
  }
296
-
297
282
  try {
298
283
  const fidelity = this.mapIntervalToFidelity(params.resolution);
299
284
  const nowTs = Math.floor(Date.now() / 1000);
300
-
301
285
  // 1. Smart Lookback Calculation
302
286
  // If start/end not provided, calculate window based on limit * resolution
303
287
  let startTs = params.start ? Math.floor(params.start.getTime() / 1000) : 0;
304
288
  let endTs = params.end ? Math.floor(params.end.getTime() / 1000) : nowTs;
305
-
306
289
  if (!params.start) {
307
290
  // Default limit is usually 20 in the example, but safety margin is good.
308
291
  // If limit is not set, we default to 100 candles.
@@ -311,29 +294,23 @@ export class PolymarketExchange extends PredictionMarketExchange {
311
294
  const durationSeconds = count * fidelity * 60;
312
295
  startTs = endTs - durationSeconds;
313
296
  }
314
-
315
- const queryParams: any = {
297
+ const queryParams = {
316
298
  market: id,
317
299
  fidelity: fidelity,
318
300
  startTs: startTs,
319
301
  endTs: endTs
320
302
  };
321
-
322
- const response = await axios.get(`${this.clobUrl}/prices-history`, {
303
+ const response = await axios_1.default.get(`${this.clobUrl}/prices-history`, {
323
304
  params: queryParams
324
305
  });
325
-
326
306
  const history = response.data.history || [];
327
-
328
307
  // 2. Align Timestamps (Snap to Grid)
329
308
  // Polymarket returns random tick timestamps (e.g. 1:00:21).
330
309
  // We want to normalize this to the start of the bucket (1:00:00).
331
310
  const resolutionMs = fidelity * 60 * 1000;
332
-
333
- const candles: PriceCandle[] = history.map((item: any) => {
311
+ const candles = history.map((item) => {
334
312
  const rawMs = item.t * 1000;
335
313
  const snappedMs = Math.floor(rawMs / resolutionMs) * resolutionMs;
336
-
337
314
  return {
338
315
  timestamp: snappedMs, // Aligned timestamp
339
316
  open: item.p,
@@ -343,16 +320,14 @@ export class PolymarketExchange extends PredictionMarketExchange {
343
320
  volume: undefined
344
321
  };
345
322
  });
346
-
347
323
  // Apply limit if specified
348
324
  if (params.limit && candles.length > params.limit) {
349
325
  return candles.slice(-params.limit);
350
326
  }
351
-
352
327
  return candles;
353
-
354
- } catch (error: any) {
355
- if (axios.isAxiosError(error) && error.response) {
328
+ }
329
+ catch (error) {
330
+ if (axios_1.default.isAxiosError(error) && error.response) {
356
331
  const apiError = error.response.data?.error || error.response.data?.message || "Unknown API Error";
357
332
  throw new Error(`Polymarket History API Error (${error.response.status}): ${apiError}. Used ID: ${id}`);
358
333
  }
@@ -360,61 +335,53 @@ export class PolymarketExchange extends PredictionMarketExchange {
360
335
  throw error;
361
336
  }
362
337
  }
363
-
364
338
  /**
365
339
  * Fetch the current order book for a specific token.
366
340
  * @param id - The CLOB token ID
367
341
  */
368
- async getOrderBook(id: string): Promise<OrderBook> {
342
+ async getOrderBook(id) {
369
343
  try {
370
- const response = await axios.get(`${this.clobUrl}/book`, {
344
+ const response = await axios_1.default.get(`${this.clobUrl}/book`, {
371
345
  params: { token_id: id }
372
346
  });
373
-
374
347
  const data = response.data;
375
-
376
348
  // Response format: { bids: [{price: "0.52", size: "100"}], asks: [...] }
377
- const bids = (data.bids || []).map((level: any) => ({
349
+ const bids = (data.bids || []).map((level) => ({
378
350
  price: parseFloat(level.price),
379
351
  size: parseFloat(level.size)
380
- })).sort((a: { price: number, size: number }, b: { price: number, size: number }) => b.price - a.price); // Sort Bids Descending (Best/Highest first)
381
-
382
- const asks = (data.asks || []).map((level: any) => ({
352
+ })).sort((a, b) => b.price - a.price); // Sort Bids Descending (Best/Highest first)
353
+ const asks = (data.asks || []).map((level) => ({
383
354
  price: parseFloat(level.price),
384
355
  size: parseFloat(level.size)
385
- })).sort((a: { price: number, size: number }, b: { price: number, size: number }) => a.price - b.price); // Sort Asks Ascending (Best/Lowest first)
386
-
356
+ })).sort((a, b) => a.price - b.price); // Sort Asks Ascending (Best/Lowest first)
387
357
  return {
388
358
  bids,
389
359
  asks,
390
360
  timestamp: data.timestamp ? new Date(data.timestamp).getTime() : Date.now()
391
361
  };
392
-
393
- } catch (error) {
362
+ }
363
+ catch (error) {
394
364
  console.error(`Error fetching Polymarket orderbook for ${id}:`, error);
395
365
  return { bids: [], asks: [] };
396
366
  }
397
367
  }
398
-
399
368
  /**
400
369
  * Fetch raw trade history for a specific token.
401
370
  * @param id - The CLOB token ID
402
- *
371
+ *
403
372
  * NOTE: Polymarket's /trades endpoint currently requires L2 Authentication (API Key).
404
373
  * This method will return an empty array if an API key is not provided in headers.
405
374
  * Use getMarketHistory for public historical price data instead.
406
375
  */
407
- async getTradeHistory(id: string, params: HistoryFilterParams): Promise<Trade[]> {
376
+ async getTradeHistory(id, params) {
408
377
  // ID Validation
409
378
  if (id.length < 10 && /^\d+$/.test(id)) {
410
379
  throw new Error(`Invalid ID for Polymarket trades: "${id}". You provided a Market ID, but Polymarket's CLOB API requires a Token ID.`);
411
380
  }
412
-
413
381
  try {
414
- const queryParams: any = {
382
+ const queryParams = {
415
383
  market: id
416
384
  };
417
-
418
385
  // Add time filters if provided
419
386
  if (params.start) {
420
387
  queryParams.after = Math.floor(params.start.getTime() / 1000);
@@ -422,31 +389,26 @@ export class PolymarketExchange extends PredictionMarketExchange {
422
389
  if (params.end) {
423
390
  queryParams.before = Math.floor(params.end.getTime() / 1000);
424
391
  }
425
-
426
- const response = await axios.get(`${this.clobUrl}/trades`, {
392
+ const response = await axios_1.default.get(`${this.clobUrl}/trades`, {
427
393
  params: queryParams
428
394
  });
429
-
430
395
  // Response is an array of trade objects
431
396
  const trades = response.data || [];
432
-
433
- const mappedTrades: Trade[] = trades.map((trade: any) => ({
397
+ const mappedTrades = trades.map((trade) => ({
434
398
  id: trade.id || `${trade.timestamp}-${trade.price}`,
435
399
  timestamp: trade.timestamp * 1000, // Convert to milliseconds
436
400
  price: parseFloat(trade.price),
437
401
  amount: parseFloat(trade.size || trade.amount || 0),
438
402
  side: trade.side === 'BUY' ? 'buy' : trade.side === 'SELL' ? 'sell' : 'unknown'
439
403
  }));
440
-
441
404
  // Apply limit if specified
442
405
  if (params.limit && mappedTrades.length > params.limit) {
443
406
  return mappedTrades.slice(-params.limit); // Return most recent N trades
444
407
  }
445
-
446
408
  return mappedTrades;
447
-
448
- } catch (error: any) {
449
- if (axios.isAxiosError(error) && error.response) {
409
+ }
410
+ catch (error) {
411
+ if (axios_1.default.isAxiosError(error) && error.response) {
450
412
  const apiError = error.response.data?.error || error.response.data?.message || "Unknown API Error";
451
413
  throw new Error(`Polymarket Trades API Error (${error.response.status}): ${apiError}. Used ID: ${id}`);
452
414
  }
@@ -455,3 +417,4 @@ export class PolymarketExchange extends PredictionMarketExchange {
455
417
  }
456
418
  }
457
419
  }
420
+ exports.PolymarketExchange = PolymarketExchange;
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./BaseExchange"), exports);
18
+ __exportStar(require("./types"), exports);
19
+ __exportStar(require("./exchanges/Polymarket"), exports);
20
+ __exportStar(require("./exchanges/Kalshi"), exports);
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ // ----------------------------------------------------------------------------
3
+ // Core Data Models
4
+ // ----------------------------------------------------------------------------
5
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "0.1.0",
3
+ "version": "0.1.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",
7
+ "files": [
8
+ "dist"
9
+ ],
7
10
  "directories": {
8
11
  "example": "examples",
9
12
  "test": "test"
package/API_REFERENCE.md DELETED
@@ -1,88 +0,0 @@
1
- # Prediction Market API Reference
2
-
3
- This project implements a **Unified Interface** for interacting with multiple prediction market exchanges (Kalshi, Polymarket) identically.
4
-
5
- ## Usage
6
-
7
- All components are available directly from the `pmxt` library.
8
-
9
- ```typescript
10
- import {
11
- PolymarketExchange,
12
- KalshiExchange
13
- } from './pmxt';
14
-
15
- const polymarket = new PolymarketExchange();
16
- const kalshi = new KalshiExchange();
17
- ```
18
-
19
- ## 1. Unified Interface (`PredictionMarketExchange`)
20
-
21
- All exchanges implement this core interface for discovery.
22
-
23
- ### `fetchMarkets(params?)`
24
- Retrieves active markets normalized into a standard format.
25
- - **Input**: `limit`, `sort` ('volume' | 'liquidity' | 'newest')
26
- - **Output**: `Promise<UnifiedMarket[]>`
27
-
28
- ### `searchMarkets(query, params?)`
29
- Search markets by title/description across the exchange(s).
30
- - **Input**: `query` (string), `limit`
31
- - **Output**: `Promise<UnifiedMarket[]>`
32
-
33
- ---
34
-
35
- ## 2. Deep-Dive Interface (Exchange Only)
36
-
37
- Methods available on specific exchange instances (`KalshiExchange`, `PolymarketExchange`) for detailed data.
38
-
39
- ### `getMarketHistory(id, params)`
40
- Fetches OHLCV candlesticks.
41
- - **Input**: `id`, `resolution` (1m, 1h, 1d), `start`, `end`.
42
- - **Output**: `Promise<PriceCandle[]>`
43
- - **Important**:
44
- - **Kalshi**: Uses the Market Ticker (e.g., `FED-25DEC`). Returns **native OHLCV** (Open/High/Low/Close data is distinct).
45
- - **Polymarket**: Uses the **CLOB Token ID** found in `outcome.metadata.clobTokenId`. Returns **synthetic candles** (Open=High=Low=Close) derived from raw price points.
46
-
47
- ### `getOrderBook(id)`
48
- Fetches live Bids/Asks.
49
- - **Input**: `id` (Ticker for Kalshi, Token ID for Polymarket).
50
- - **Output**: `Promise<OrderBook>` (Normalized: "No" bids becomes "Yes" asks).
51
-
52
- ### `getTradeHistory(id, params)`
53
- Fetches the "Tape" (raw transaction history).
54
- - **Input**: `id`, `limit`.
55
- - **Output**: `Promise<Trade[]>`
56
- - **Note**:
57
- - **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.
59
-
60
- ---
61
-
62
- ## 3. Data Models
63
-
64
- ### `UnifiedMarket` (The Standard Atom)
65
- ```typescript
66
- {
67
- id: string; // Exchange-specific ID
68
- title: string; // "Who will win the election?"
69
- description: string; // Detailed context/rules of the market
70
- outcomes: [
71
- {
72
- id: string,
73
- label: "Trump",
74
- price: 0.52,
75
- priceChange24h: 0.05, // Optional: Change in price over last 24h
76
- metadata: { ... } // Exchange-specific keys (clobTokenId)
77
- }
78
- ];
79
- resolutionDate: Date;
80
- volume24h: number;
81
- liquidity: number;
82
- url: string;
83
- image?: string; // Optional: Thumbnail URL
84
- category?: string; // Optional: Primary category (e.g., "Politics")
85
- tags?: string[]; // Optional: Searchable tags
86
- }
87
- ```
88
-