pmxt-core 2.1.0 → 2.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.
- package/dist/BaseExchange.d.ts +2 -2
- package/dist/BaseExchange.js +2 -2
- package/dist/exchanges/kalshi/fetchEvents.js +1 -1
- package/dist/exchanges/kalshi/fetchMarkets.js +3 -3
- package/dist/exchanges/limitless/fetchEvents.js +1 -1
- package/dist/exchanges/limitless/fetchMarkets.js +3 -3
- package/dist/exchanges/polymarket/fetchEvents.js +27 -23
- package/dist/exchanges/polymarket/fetchMarkets.js +39 -21
- package/dist/exchanges/polymarket/utils.d.ts +6 -0
- package/dist/exchanges/polymarket/utils.js +42 -1
- package/package.json +3 -3
package/dist/BaseExchange.d.ts
CHANGED
|
@@ -115,14 +115,14 @@ export declare abstract class PredictionMarketExchange {
|
|
|
115
115
|
* @returns Array of unified markets
|
|
116
116
|
*
|
|
117
117
|
* @example-ts Fetch markets
|
|
118
|
-
* const markets = await exchange.fetchMarkets({ query: 'Trump', limit:
|
|
118
|
+
* const markets = await exchange.fetchMarkets({ query: 'Trump', limit: 10000 });
|
|
119
119
|
* console.log(markets[0].title);
|
|
120
120
|
*
|
|
121
121
|
* @example-ts Get market by slug
|
|
122
122
|
* const markets = await exchange.fetchMarkets({ slug: 'will-trump-win' });
|
|
123
123
|
*
|
|
124
124
|
* @example-python Fetch markets
|
|
125
|
-
* markets = exchange.fetch_markets(query='Trump', limit=
|
|
125
|
+
* markets = exchange.fetch_markets(query='Trump', limit=10000)
|
|
126
126
|
* print(markets[0].title)
|
|
127
127
|
*
|
|
128
128
|
* @example-python Get market by slug
|
package/dist/BaseExchange.js
CHANGED
|
@@ -23,14 +23,14 @@ class PredictionMarketExchange {
|
|
|
23
23
|
* @returns Array of unified markets
|
|
24
24
|
*
|
|
25
25
|
* @example-ts Fetch markets
|
|
26
|
-
* const markets = await exchange.fetchMarkets({ query: 'Trump', limit:
|
|
26
|
+
* const markets = await exchange.fetchMarkets({ query: 'Trump', limit: 10000 });
|
|
27
27
|
* console.log(markets[0].title);
|
|
28
28
|
*
|
|
29
29
|
* @example-ts Get market by slug
|
|
30
30
|
* const markets = await exchange.fetchMarkets({ slug: 'will-trump-win' });
|
|
31
31
|
*
|
|
32
32
|
* @example-python Fetch markets
|
|
33
|
-
* markets = exchange.fetch_markets(query='Trump', limit=
|
|
33
|
+
* markets = exchange.fetch_markets(query='Trump', limit=10000)
|
|
34
34
|
* print(markets[0].title)
|
|
35
35
|
*
|
|
36
36
|
* @example-python Get market by slug
|
|
@@ -138,7 +138,7 @@ async function fetchMarketsBySlug(eventTicker) {
|
|
|
138
138
|
}
|
|
139
139
|
async function searchMarkets(query, params) {
|
|
140
140
|
// We must fetch ALL markets to search them locally since we don't have server-side search
|
|
141
|
-
const searchLimit =
|
|
141
|
+
const searchLimit = 10000;
|
|
142
142
|
const markets = await fetchMarketsDefault({ ...params, limit: searchLimit });
|
|
143
143
|
const lowerQuery = query.toLowerCase();
|
|
144
144
|
const searchIn = params?.searchIn || 'title'; // Default to title-only search
|
|
@@ -151,11 +151,11 @@ async function searchMarkets(query, params) {
|
|
|
151
151
|
return descMatch;
|
|
152
152
|
return titleMatch || descMatch; // 'both'
|
|
153
153
|
});
|
|
154
|
-
const limit = params?.limit ||
|
|
154
|
+
const limit = params?.limit || 10000;
|
|
155
155
|
return filtered.slice(0, limit);
|
|
156
156
|
}
|
|
157
157
|
async function fetchMarketsDefault(params) {
|
|
158
|
-
const limit = params?.limit ||
|
|
158
|
+
const limit = params?.limit || 10000;
|
|
159
159
|
const offset = params?.offset || 0;
|
|
160
160
|
const now = Date.now();
|
|
161
161
|
const status = params?.status || 'active'; // Default to 'active'
|
|
@@ -15,7 +15,7 @@ async function fetchEvents(params) {
|
|
|
15
15
|
const response = await axios_1.default.get(`${utils_1.LIMITLESS_API_URL}/markets/search`, {
|
|
16
16
|
params: {
|
|
17
17
|
query: params.query,
|
|
18
|
-
limit: params?.limit ||
|
|
18
|
+
limit: params?.limit || 10000
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
21
|
const markets = response.data?.markets || [];
|
|
@@ -47,7 +47,7 @@ async function searchMarkets(marketFetcher, query, params) {
|
|
|
47
47
|
const response = await axios_1.default.get(`${utils_1.LIMITLESS_API_URL}/markets/search`, {
|
|
48
48
|
params: {
|
|
49
49
|
query: query,
|
|
50
|
-
limit: params?.limit ||
|
|
50
|
+
limit: params?.limit || 10000,
|
|
51
51
|
page: params?.page || 1,
|
|
52
52
|
similarityThreshold: params?.similarityThreshold || 0.5
|
|
53
53
|
}
|
|
@@ -71,10 +71,10 @@ async function searchMarkets(marketFetcher, query, params) {
|
|
|
71
71
|
}
|
|
72
72
|
return allMarkets
|
|
73
73
|
.filter((m) => m !== null && m.outcomes.length > 0)
|
|
74
|
-
.slice(0, params?.limit ||
|
|
74
|
+
.slice(0, params?.limit || 10000);
|
|
75
75
|
}
|
|
76
76
|
async function fetchMarketsDefault(marketFetcher, params) {
|
|
77
|
-
const limit = params?.limit ||
|
|
77
|
+
const limit = params?.limit || 10000;
|
|
78
78
|
const offset = params?.offset || 0;
|
|
79
79
|
// Map sort parameter to SDK's sortBy
|
|
80
80
|
let sortBy = 'lp_rewards';
|
|
@@ -4,41 +4,47 @@ exports.fetchEvents = fetchEvents;
|
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
6
|
async function fetchEvents(params) {
|
|
7
|
-
const searchLimit = 100000; // Fetch all events for comprehensive search
|
|
8
7
|
try {
|
|
9
|
-
|
|
8
|
+
if (!params.query) {
|
|
9
|
+
// If no query is provided, we can't use the search endpoint effectively.
|
|
10
|
+
// However, the BaseExchange interface enforces query presence for fetchEvents.
|
|
11
|
+
// Just in case, we return empty or throw.
|
|
12
|
+
throw new Error("Query is required for Polymarket event search");
|
|
13
|
+
}
|
|
14
|
+
const limit = params.limit || 10000;
|
|
15
|
+
const status = params.status || 'active';
|
|
10
16
|
const queryParams = {
|
|
11
|
-
|
|
17
|
+
q: params.query,
|
|
18
|
+
limit_per_type: 50, // Fetch 50 per page for better efficiency
|
|
19
|
+
events_status: status === 'all' ? undefined : status,
|
|
20
|
+
sort: 'volume',
|
|
21
|
+
ascending: false
|
|
12
22
|
};
|
|
23
|
+
// If specific status requested
|
|
13
24
|
if (status === 'active') {
|
|
14
|
-
queryParams.
|
|
15
|
-
queryParams.closed = 'false';
|
|
25
|
+
queryParams.events_status = 'active';
|
|
16
26
|
}
|
|
17
27
|
else if (status === 'closed') {
|
|
18
|
-
queryParams.
|
|
19
|
-
queryParams.closed = 'true';
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
// 'all' - no filter, maybe handled by default or API behavior
|
|
28
|
+
queryParams.events_status = 'closed';
|
|
23
29
|
}
|
|
24
|
-
//
|
|
25
|
-
const events = await (0, utils_1.
|
|
26
|
-
// Client-side
|
|
27
|
-
const lowerQuery =
|
|
28
|
-
const searchIn = params
|
|
29
|
-
const
|
|
30
|
+
// Use parallel pagination to fetch all matching events
|
|
31
|
+
const events = await (0, utils_1.paginateSearchParallel)(utils_1.GAMMA_SEARCH_URL, queryParams, limit * 10);
|
|
32
|
+
// Client-side filtering to ensure title matches (API does fuzzy search)
|
|
33
|
+
const lowerQuery = params.query.toLowerCase();
|
|
34
|
+
const searchIn = params.searchIn || 'title';
|
|
35
|
+
const filteredEvents = events.filter((event) => {
|
|
30
36
|
const titleMatch = (event.title || '').toLowerCase().includes(lowerQuery);
|
|
31
37
|
const descMatch = (event.description || '').toLowerCase().includes(lowerQuery);
|
|
32
38
|
if (searchIn === 'title')
|
|
33
39
|
return titleMatch;
|
|
34
40
|
if (searchIn === 'description')
|
|
35
41
|
return descMatch;
|
|
36
|
-
return titleMatch || descMatch;
|
|
42
|
+
return titleMatch || descMatch; // 'both'
|
|
37
43
|
});
|
|
38
|
-
// Map to UnifiedEvent
|
|
39
|
-
const unifiedEvents =
|
|
44
|
+
// Map events to UnifiedEvent
|
|
45
|
+
const unifiedEvents = filteredEvents.map((event) => {
|
|
40
46
|
const markets = [];
|
|
41
|
-
if (event.markets) {
|
|
47
|
+
if (event.markets && Array.isArray(event.markets)) {
|
|
42
48
|
for (const market of event.markets) {
|
|
43
49
|
const unifiedMarket = (0, utils_1.mapMarketToUnified)(event, market, { useQuestionAsCandidateFallback: true });
|
|
44
50
|
if (unifiedMarket) {
|
|
@@ -59,9 +65,7 @@ async function fetchEvents(params) {
|
|
|
59
65
|
};
|
|
60
66
|
return unifiedEvent;
|
|
61
67
|
});
|
|
62
|
-
|
|
63
|
-
const limit = params?.limit || 20;
|
|
64
|
-
return unifiedEvents.slice(0, limit);
|
|
68
|
+
return unifiedEvents;
|
|
65
69
|
}
|
|
66
70
|
catch (error) {
|
|
67
71
|
throw errors_1.polymarketErrorMapper.mapError(error);
|
|
@@ -45,30 +45,48 @@ async function fetchMarketsBySlug(slug) {
|
|
|
45
45
|
return unifiedMarkets;
|
|
46
46
|
}
|
|
47
47
|
async function searchMarkets(query, params) {
|
|
48
|
-
const
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
const limit = params?.limit || 10000;
|
|
49
|
+
// Use parallel pagination to fetch all matching events
|
|
50
|
+
// Each event can contain multiple markets, so we need a larger pool
|
|
51
|
+
const queryParams = {
|
|
52
|
+
q: query,
|
|
53
|
+
limit_per_type: 50, // Fetch 50 events per page
|
|
54
|
+
events_status: params?.status === 'all' ? undefined : (params?.status || 'active'),
|
|
55
|
+
sort: 'volume',
|
|
56
|
+
ascending: false
|
|
57
|
+
};
|
|
58
|
+
// Fetch events with parallel pagination
|
|
59
|
+
const events = await (0, utils_1.paginateSearchParallel)(utils_1.GAMMA_SEARCH_URL, queryParams, limit * 5);
|
|
60
|
+
const unifiedMarkets = [];
|
|
55
61
|
const lowerQuery = query.toLowerCase();
|
|
56
|
-
const searchIn = params?.searchIn || 'title';
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
const searchIn = params?.searchIn || 'title';
|
|
63
|
+
// Flatten events into markets
|
|
64
|
+
for (const event of events) {
|
|
65
|
+
if (!event.markets)
|
|
66
|
+
continue;
|
|
67
|
+
for (const market of event.markets) {
|
|
68
|
+
const unifiedMarket = (0, utils_1.mapMarketToUnified)(event, market, { useQuestionAsCandidateFallback: true });
|
|
69
|
+
if (!unifiedMarket)
|
|
70
|
+
continue;
|
|
71
|
+
// Apply client-side filtering on market title
|
|
72
|
+
const titleMatch = (unifiedMarket.title || '').toLowerCase().includes(lowerQuery);
|
|
73
|
+
const descMatch = (unifiedMarket.description || '').toLowerCase().includes(lowerQuery);
|
|
74
|
+
let matches = false;
|
|
75
|
+
if (searchIn === 'title')
|
|
76
|
+
matches = titleMatch;
|
|
77
|
+
else if (searchIn === 'description')
|
|
78
|
+
matches = descMatch;
|
|
79
|
+
else
|
|
80
|
+
matches = titleMatch || descMatch;
|
|
81
|
+
if (matches) {
|
|
82
|
+
unifiedMarkets.push(unifiedMarket);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return unifiedMarkets.slice(0, limit);
|
|
69
87
|
}
|
|
70
88
|
async function fetchMarketsDefault(params) {
|
|
71
|
-
const limit = params?.limit ||
|
|
89
|
+
const limit = params?.limit || 10000; // Higher default for better coverage
|
|
72
90
|
const offset = params?.offset || 0;
|
|
73
91
|
// Map generic sort params to Polymarket Gamma API params
|
|
74
92
|
let queryParams = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UnifiedMarket, CandleInterval } from '../../types';
|
|
2
2
|
export declare const GAMMA_API_URL = "https://gamma-api.polymarket.com/events";
|
|
3
|
+
export declare const GAMMA_SEARCH_URL = "https://gamma-api.polymarket.com/public-search";
|
|
3
4
|
export declare const CLOB_API_URL = "https://clob.polymarket.com";
|
|
4
5
|
export declare const DATA_API_URL = "https://data-api.polymarket.com";
|
|
5
6
|
export declare function mapMarketToUnified(event: any, market: any, options?: {
|
|
@@ -11,3 +12,8 @@ export declare function mapIntervalToFidelity(interval: CandleInterval): number;
|
|
|
11
12
|
* Polymarket Gamma API has a hard limit of 500 results per request.
|
|
12
13
|
*/
|
|
13
14
|
export declare function paginateParallel(url: string, params: any, maxResults?: number): Promise<any[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Fetch all results from Gamma public-search API using parallel pagination.
|
|
17
|
+
* Uses 'page' parameter instead of 'offset'.
|
|
18
|
+
*/
|
|
19
|
+
export declare function paginateSearchParallel(url: string, params: any, maxResults?: number): Promise<any[]>;
|
|
@@ -33,12 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.DATA_API_URL = exports.CLOB_API_URL = exports.GAMMA_API_URL = void 0;
|
|
36
|
+
exports.DATA_API_URL = exports.CLOB_API_URL = exports.GAMMA_SEARCH_URL = exports.GAMMA_API_URL = void 0;
|
|
37
37
|
exports.mapMarketToUnified = mapMarketToUnified;
|
|
38
38
|
exports.mapIntervalToFidelity = mapIntervalToFidelity;
|
|
39
39
|
exports.paginateParallel = paginateParallel;
|
|
40
|
+
exports.paginateSearchParallel = paginateSearchParallel;
|
|
40
41
|
const market_utils_1 = require("../../utils/market-utils");
|
|
41
42
|
exports.GAMMA_API_URL = 'https://gamma-api.polymarket.com/events';
|
|
43
|
+
exports.GAMMA_SEARCH_URL = 'https://gamma-api.polymarket.com/public-search';
|
|
42
44
|
exports.CLOB_API_URL = 'https://clob.polymarket.com';
|
|
43
45
|
exports.DATA_API_URL = 'https://data-api.polymarket.com';
|
|
44
46
|
function mapMarketToUnified(event, market, options = {}) {
|
|
@@ -171,3 +173,42 @@ async function paginateParallel(url, params, maxResults = 10000) {
|
|
|
171
173
|
}));
|
|
172
174
|
return [firstPage, ...remainingPages].flat();
|
|
173
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Fetch all results from Gamma public-search API using parallel pagination.
|
|
178
|
+
* Uses 'page' parameter instead of 'offset'.
|
|
179
|
+
*/
|
|
180
|
+
async function paginateSearchParallel(url, params, maxResults = 10000) {
|
|
181
|
+
const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default;
|
|
182
|
+
// 1. Fetch the first page to check pagination info
|
|
183
|
+
const firstPageResponse = await axios.get(url, {
|
|
184
|
+
params: { ...params, page: 1 }
|
|
185
|
+
});
|
|
186
|
+
const data = firstPageResponse.data;
|
|
187
|
+
const firstPageEvents = data.events || [];
|
|
188
|
+
const pagination = data.pagination;
|
|
189
|
+
// If no more pages, return what we have
|
|
190
|
+
if (!pagination?.hasMore || firstPageEvents.length === 0) {
|
|
191
|
+
return firstPageEvents;
|
|
192
|
+
}
|
|
193
|
+
// 2. Calculate how many pages to fetch based on totalResults and limit_per_type
|
|
194
|
+
const limitPerType = params.limit_per_type || 20;
|
|
195
|
+
const totalResults = Math.min(pagination.totalResults || 0, maxResults);
|
|
196
|
+
const totalPages = Math.ceil(totalResults / limitPerType);
|
|
197
|
+
// Fetch remaining pages in parallel
|
|
198
|
+
const pageNumbers = [];
|
|
199
|
+
for (let i = 2; i <= totalPages; i++) {
|
|
200
|
+
pageNumbers.push(i);
|
|
201
|
+
}
|
|
202
|
+
const remainingPages = await Promise.all(pageNumbers.map(async (pageNum) => {
|
|
203
|
+
try {
|
|
204
|
+
const res = await axios.get(url, {
|
|
205
|
+
params: { ...params, page: pageNum }
|
|
206
|
+
});
|
|
207
|
+
return res.data?.events || [];
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
return []; // Swallow individual page errors to be robust
|
|
211
|
+
}
|
|
212
|
+
}));
|
|
213
|
+
return [firstPageEvents, ...remainingPages].flat();
|
|
214
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxt-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.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",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"test": "jest -c jest.config.js",
|
|
30
30
|
"server": "tsx watch src/server/index.ts",
|
|
31
31
|
"server:prod": "node dist/server/index.js",
|
|
32
|
-
"generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.1.
|
|
33
|
-
"generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.1.
|
|
32
|
+
"generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.1.1,library=urllib3",
|
|
33
|
+
"generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.1.1,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
|
|
34
34
|
"extract:jsdoc": "node ../scripts/extract-jsdoc.js",
|
|
35
35
|
"generate:docs": "npm run extract:jsdoc && node ../scripts/generate-api-docs.js",
|
|
36
36
|
"generate:sdk:all": "npm run generate:sdk:python && npm run generate:sdk:typescript && npm run generate:docs"
|