pmxt-core 2.1.1 → 2.1.3
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 +6 -2
- package/dist/BaseExchange.js +4 -0
- package/dist/exchanges/kalshi/fetchEvents.js +49 -12
- package/dist/exchanges/kalshi/fetchMarkets.js +1 -1
- package/dist/exchanges/limitless/errors.js +4 -4
- package/dist/exchanges/limitless/fetchEvents.js +17 -5
- package/dist/exchanges/limitless/fetchMarkets.js +41 -4
- package/dist/exchanges/polymarket/fetchEvents.js +35 -10
- package/dist/exchanges/polymarket/fetchMarkets.js +2 -2
- package/dist/exchanges/polymarket/index.d.ts +5 -4
- package/dist/exchanges/polymarket/index.js +24 -54
- package/dist/types.d.ts +1 -0
- package/dist/utils/error-mapper.js +8 -0
- package/package.json +3 -3
package/dist/BaseExchange.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface MarketFilterParams {
|
|
|
4
4
|
limit?: number;
|
|
5
5
|
offset?: number;
|
|
6
6
|
sort?: 'volume' | 'liquidity' | 'newest';
|
|
7
|
-
status?: 'active' | 'closed' | 'all';
|
|
7
|
+
status?: 'active' | 'inactive' | 'closed' | 'all';
|
|
8
8
|
searchIn?: 'title' | 'description' | 'both';
|
|
9
9
|
query?: string;
|
|
10
10
|
slug?: string;
|
|
@@ -17,7 +17,7 @@ export interface EventFetchParams {
|
|
|
17
17
|
query?: string;
|
|
18
18
|
limit?: number;
|
|
19
19
|
offset?: number;
|
|
20
|
-
status?: 'active' | 'closed' | 'all';
|
|
20
|
+
status?: 'active' | 'inactive' | 'closed' | 'all';
|
|
21
21
|
searchIn?: 'title' | 'description' | 'both';
|
|
22
22
|
}
|
|
23
23
|
export interface HistoryFilterParams {
|
|
@@ -114,6 +114,8 @@ export declare abstract class PredictionMarketExchange {
|
|
|
114
114
|
* @param params.searchIn - Where to search ('title' | 'description' | 'both')
|
|
115
115
|
* @returns Array of unified markets
|
|
116
116
|
*
|
|
117
|
+
* @note Some exchanges (like Limitless) may only support status 'active' for search results.
|
|
118
|
+
*
|
|
117
119
|
* @example-ts Fetch markets
|
|
118
120
|
* const markets = await exchange.fetchMarkets({ query: 'Trump', limit: 10000 });
|
|
119
121
|
* console.log(markets[0].title);
|
|
@@ -140,6 +142,8 @@ export declare abstract class PredictionMarketExchange {
|
|
|
140
142
|
* @param params.searchIn - Where to search ('title' | 'description' | 'both')
|
|
141
143
|
* @returns Array of unified events
|
|
142
144
|
*
|
|
145
|
+
* @note Some exchanges (like Limitless) may only support status 'active' for search results.
|
|
146
|
+
*
|
|
143
147
|
* @example-ts Search events
|
|
144
148
|
* const events = await exchange.fetchEvents({ query: 'Fed Chair' });
|
|
145
149
|
* const fedEvent = events[0];
|
package/dist/BaseExchange.js
CHANGED
|
@@ -22,6 +22,8 @@ class PredictionMarketExchange {
|
|
|
22
22
|
* @param params.searchIn - Where to search ('title' | 'description' | 'both')
|
|
23
23
|
* @returns Array of unified markets
|
|
24
24
|
*
|
|
25
|
+
* @note Some exchanges (like Limitless) may only support status 'active' for search results.
|
|
26
|
+
*
|
|
25
27
|
* @example-ts Fetch markets
|
|
26
28
|
* const markets = await exchange.fetchMarkets({ query: 'Trump', limit: 10000 });
|
|
27
29
|
* console.log(markets[0].title);
|
|
@@ -50,6 +52,8 @@ class PredictionMarketExchange {
|
|
|
50
52
|
* @param params.searchIn - Where to search ('title' | 'description' | 'both')
|
|
51
53
|
* @returns Array of unified events
|
|
52
54
|
*
|
|
55
|
+
* @note Some exchanges (like Limitless) may only support status 'active' for search results.
|
|
56
|
+
*
|
|
53
57
|
* @example-ts Search events
|
|
54
58
|
* const events = await exchange.fetchEvents({ query: 'Fed Chair' });
|
|
55
59
|
* const fedEvent = events[0];
|
|
@@ -10,19 +10,57 @@ const errors_1 = require("./errors");
|
|
|
10
10
|
async function fetchEvents(params) {
|
|
11
11
|
try {
|
|
12
12
|
const status = params?.status || 'active';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const limit = params?.limit || 10000;
|
|
14
|
+
const query = (params?.query || '').toLowerCase();
|
|
15
|
+
const fetchAllWithStatus = async (apiStatus) => {
|
|
16
|
+
let allEvents = [];
|
|
17
|
+
let cursor = null;
|
|
18
|
+
let page = 0;
|
|
19
|
+
const MAX_PAGES = 1000; // Safety cap against infinite loops
|
|
20
|
+
const BATCH_SIZE = 200; // Max limit per Kalshi API docs
|
|
21
|
+
do {
|
|
22
|
+
const queryParams = {
|
|
23
|
+
limit: BATCH_SIZE,
|
|
24
|
+
with_nested_markets: true,
|
|
25
|
+
status: apiStatus
|
|
26
|
+
};
|
|
27
|
+
if (cursor)
|
|
28
|
+
queryParams.cursor = cursor;
|
|
29
|
+
const response = await axios_1.default.get(utils_1.KALSHI_API_URL, { params: queryParams });
|
|
30
|
+
const events = response.data.events || [];
|
|
31
|
+
if (events.length === 0)
|
|
32
|
+
break;
|
|
33
|
+
allEvents = allEvents.concat(events);
|
|
34
|
+
cursor = response.data.cursor;
|
|
35
|
+
page++;
|
|
36
|
+
// If we have no search query and have fetched enough events, we can stop early
|
|
37
|
+
if (!query && allEvents.length >= limit * 1.5) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
} while (cursor && page < MAX_PAGES);
|
|
41
|
+
return allEvents;
|
|
20
42
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
43
|
+
let events = [];
|
|
44
|
+
if (status === 'all') {
|
|
45
|
+
const [openEvents, closedEvents, settledEvents] = await Promise.all([
|
|
46
|
+
fetchAllWithStatus('open'),
|
|
47
|
+
fetchAllWithStatus('closed'),
|
|
48
|
+
fetchAllWithStatus('settled')
|
|
49
|
+
]);
|
|
50
|
+
events = [...openEvents, ...closedEvents, ...settledEvents];
|
|
51
|
+
}
|
|
52
|
+
else if (status === 'closed' || status === 'inactive') {
|
|
53
|
+
const [closedEvents, settledEvents] = await Promise.all([
|
|
54
|
+
fetchAllWithStatus('closed'),
|
|
55
|
+
fetchAllWithStatus('settled')
|
|
56
|
+
]);
|
|
57
|
+
events = [...closedEvents, ...settledEvents];
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
events = await fetchAllWithStatus('open');
|
|
61
|
+
}
|
|
24
62
|
const filtered = events.filter((event) => {
|
|
25
|
-
return (event.title || '').toLowerCase().includes(
|
|
63
|
+
return (event.title || '').toLowerCase().includes(query);
|
|
26
64
|
});
|
|
27
65
|
const unifiedEvents = filtered.map((event) => {
|
|
28
66
|
const markets = [];
|
|
@@ -47,7 +85,6 @@ async function fetchEvents(params) {
|
|
|
47
85
|
};
|
|
48
86
|
return unifiedEvent;
|
|
49
87
|
});
|
|
50
|
-
const limit = params?.limit || 10000;
|
|
51
88
|
return unifiedEvents.slice(0, limit);
|
|
52
89
|
}
|
|
53
90
|
catch (error) {
|
|
@@ -162,7 +162,7 @@ async function fetchMarketsDefault(params) {
|
|
|
162
162
|
// Map 'active' -> 'open', 'closed' -> 'closed'
|
|
163
163
|
// Kalshi statuses: 'open', 'closed', 'settled'
|
|
164
164
|
let apiStatus = 'open';
|
|
165
|
-
if (status === 'closed')
|
|
165
|
+
if (status === 'closed' || status === 'inactive')
|
|
166
166
|
apiStatus = 'closed';
|
|
167
167
|
else if (status === 'all')
|
|
168
168
|
apiStatus = 'open'; // Fallback for all? Or loop? For now default to open.
|
|
@@ -25,14 +25,14 @@ class LimitlessErrorMapper extends error_mapper_1.ErrorMapper {
|
|
|
25
25
|
const data = error.response.data;
|
|
26
26
|
// Limitless uses errorMsg field (CLOB client)
|
|
27
27
|
if (data.errorMsg) {
|
|
28
|
-
return data.errorMsg;
|
|
28
|
+
return typeof data.errorMsg === 'string' ? data.errorMsg : JSON.stringify(data.errorMsg);
|
|
29
29
|
}
|
|
30
30
|
// Also check standard error paths
|
|
31
31
|
if (data.error?.message) {
|
|
32
|
-
return data.error.message;
|
|
32
|
+
return String(data.error.message);
|
|
33
33
|
}
|
|
34
34
|
if (data.message) {
|
|
35
|
-
return data.message;
|
|
35
|
+
return String(data.message);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
return super.extractErrorMessage(error);
|
|
@@ -41,7 +41,7 @@ class LimitlessErrorMapper extends error_mapper_1.ErrorMapper {
|
|
|
41
41
|
* Override to detect Limitless-specific error patterns
|
|
42
42
|
*/
|
|
43
43
|
mapBadRequestError(message, data) {
|
|
44
|
-
const lowerMessage = message.toLowerCase();
|
|
44
|
+
const lowerMessage = (message || '').toString().toLowerCase();
|
|
45
45
|
// Limitless-specific authentication errors (400 status)
|
|
46
46
|
if (lowerMessage.includes('api key') ||
|
|
47
47
|
lowerMessage.includes('proxy') ||
|
|
@@ -8,17 +8,29 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
8
8
|
const utils_1 = require("./utils");
|
|
9
9
|
const errors_1 = require("./errors");
|
|
10
10
|
async function fetchEvents(params) {
|
|
11
|
-
if (params?.status === 'closed') {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
11
|
try {
|
|
12
|
+
// NOTE: The Limitless /markets/search endpoint currently only returns active/funded markets.
|
|
13
|
+
// It does not include expired or resolved markets in search results.
|
|
14
|
+
// Consequently, status 'inactive' will likely return 0 results and 'all' will only show active markets.
|
|
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 || 10000
|
|
18
|
+
limit: params?.limit || 10000,
|
|
19
|
+
similarityThreshold: 0.5
|
|
19
20
|
}
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
+
let markets = response.data?.markets || [];
|
|
23
|
+
// Filter by status based on expired/resolved state
|
|
24
|
+
// Active: not expired and not resolved
|
|
25
|
+
// Inactive: expired OR resolved (has winningOutcomeIndex)
|
|
26
|
+
const status = params?.status || 'active';
|
|
27
|
+
if (status === 'active') {
|
|
28
|
+
markets = markets.filter((m) => !m.expired && m.winningOutcomeIndex === null);
|
|
29
|
+
}
|
|
30
|
+
else if (status === 'inactive' || status === 'closed') {
|
|
31
|
+
markets = markets.filter((m) => m.expired === true || m.winningOutcomeIndex !== null);
|
|
32
|
+
}
|
|
33
|
+
// If status === 'all', don't filter
|
|
22
34
|
return markets.map((market) => {
|
|
23
35
|
let marketsList = [];
|
|
24
36
|
if (market.markets && Array.isArray(market.markets)) {
|
|
@@ -1,25 +1,60 @@
|
|
|
1
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.fetchMarkets = fetchMarkets;
|
|
7
|
-
const sdk_1 = require("@limitless-exchange/sdk");
|
|
8
40
|
const axios_1 = __importDefault(require("axios"));
|
|
9
41
|
const utils_1 = require("./utils");
|
|
10
42
|
const errors_1 = require("./errors");
|
|
11
43
|
async function fetchMarkets(params, apiKey) {
|
|
12
44
|
// Limitless API currently only supports fetching active markets for lists
|
|
13
|
-
|
|
45
|
+
// Early return to avoid SDK initialization in tests
|
|
46
|
+
if (params?.status === 'inactive' || params?.status === 'closed') {
|
|
14
47
|
return [];
|
|
15
48
|
}
|
|
49
|
+
// Lazy import SDK to avoid initialization when not needed
|
|
50
|
+
const { HttpClient, MarketFetcher } = await Promise.resolve().then(() => __importStar(require('@limitless-exchange/sdk')));
|
|
16
51
|
try {
|
|
17
52
|
// Create HTTP client (no auth needed for market data)
|
|
18
|
-
const httpClient = new
|
|
53
|
+
const httpClient = new HttpClient({
|
|
19
54
|
baseURL: utils_1.LIMITLESS_API_URL,
|
|
20
55
|
apiKey: apiKey, // Optional - not required for public market data
|
|
21
56
|
});
|
|
22
|
-
const marketFetcher = new
|
|
57
|
+
const marketFetcher = new MarketFetcher(httpClient);
|
|
23
58
|
// Handle slug-based lookup
|
|
24
59
|
if (params?.slug) {
|
|
25
60
|
return await fetchMarketsBySlug(marketFetcher, params.slug);
|
|
@@ -44,6 +79,8 @@ async function fetchMarketsBySlug(marketFetcher, slug) {
|
|
|
44
79
|
}
|
|
45
80
|
async function searchMarkets(marketFetcher, query, params) {
|
|
46
81
|
// SDK doesn't have a search method yet, use axios directly
|
|
82
|
+
// NOTE: The Limitless /markets/search endpoint currently only returns active/funded markets.
|
|
83
|
+
// It does not include expired or resolved markets in search results.
|
|
47
84
|
const response = await axios_1.default.get(`${utils_1.LIMITLESS_API_URL}/markets/search`, {
|
|
48
85
|
params: {
|
|
49
86
|
query: query,
|
|
@@ -15,20 +15,45 @@ async function fetchEvents(params) {
|
|
|
15
15
|
const status = params.status || 'active';
|
|
16
16
|
const queryParams = {
|
|
17
17
|
q: params.query,
|
|
18
|
-
limit_per_type: 50,
|
|
19
|
-
events_status: status === 'all' ? undefined : status,
|
|
18
|
+
limit_per_type: 50,
|
|
20
19
|
sort: 'volume',
|
|
21
20
|
ascending: false
|
|
22
21
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const fetchWithStatus = async (eventStatus) => {
|
|
23
|
+
const currentParams = { ...queryParams, events_status: eventStatus };
|
|
24
|
+
return (0, utils_1.paginateSearchParallel)(utils_1.GAMMA_SEARCH_URL, currentParams, limit * 10);
|
|
25
|
+
};
|
|
26
|
+
// Client-side filtering logic
|
|
27
|
+
// The API returns active events when querying for 'closed' status sometimes.
|
|
28
|
+
// We must strictly filter based on the event's `active` and `closed` properties.
|
|
29
|
+
const filterActive = (e) => e.active === true;
|
|
30
|
+
const filterClosed = (e) => e.closed === true;
|
|
31
|
+
let events = [];
|
|
32
|
+
if (status === 'all') {
|
|
33
|
+
const [activeEvents, closedEvents] = await Promise.all([
|
|
34
|
+
fetchWithStatus('active'),
|
|
35
|
+
fetchWithStatus('closed')
|
|
36
|
+
]);
|
|
37
|
+
// Merge and de-duplicate by ID
|
|
38
|
+
const seenIds = new Set();
|
|
39
|
+
events = [...activeEvents, ...closedEvents].filter(event => {
|
|
40
|
+
const id = event.id || event.slug;
|
|
41
|
+
if (seenIds.has(id))
|
|
42
|
+
return false;
|
|
43
|
+
seenIds.add(id);
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else if (status === 'active') {
|
|
48
|
+
const rawEvents = await fetchWithStatus('active');
|
|
49
|
+
events = rawEvents.filter(filterActive);
|
|
26
50
|
}
|
|
27
|
-
else if (status === 'closed') {
|
|
28
|
-
|
|
51
|
+
else if (status === 'inactive' || status === 'closed') {
|
|
52
|
+
// Polymarket sometimes returns active events when querying for closed
|
|
53
|
+
// So we fetch 'closed' but strictly filter
|
|
54
|
+
const rawEvents = await fetchWithStatus('closed');
|
|
55
|
+
events = rawEvents.filter(filterClosed);
|
|
29
56
|
}
|
|
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
57
|
// Client-side filtering to ensure title matches (API does fuzzy search)
|
|
33
58
|
const lowerQuery = params.query.toLowerCase();
|
|
34
59
|
const searchIn = params.searchIn || 'title';
|
|
@@ -65,7 +90,7 @@ async function fetchEvents(params) {
|
|
|
65
90
|
};
|
|
66
91
|
return unifiedEvent;
|
|
67
92
|
});
|
|
68
|
-
return unifiedEvents;
|
|
93
|
+
return unifiedEvents.slice(0, limit);
|
|
69
94
|
}
|
|
70
95
|
catch (error) {
|
|
71
96
|
throw errors_1.polymarketErrorMapper.mapError(error);
|
|
@@ -51,7 +51,7 @@ async function searchMarkets(query, params) {
|
|
|
51
51
|
const queryParams = {
|
|
52
52
|
q: query,
|
|
53
53
|
limit_per_type: 50, // Fetch 50 events per page
|
|
54
|
-
events_status: params?.status === 'all' ? undefined : (params?.status || 'active'),
|
|
54
|
+
events_status: params?.status === 'all' ? undefined : (params?.status === 'inactive' || params?.status === 'closed' ? 'closed' : 'active'),
|
|
55
55
|
sort: 'volume',
|
|
56
56
|
ascending: false
|
|
57
57
|
};
|
|
@@ -98,7 +98,7 @@ async function fetchMarketsDefault(params) {
|
|
|
98
98
|
queryParams.active = 'true';
|
|
99
99
|
queryParams.closed = 'false';
|
|
100
100
|
}
|
|
101
|
-
else if (status === 'closed') {
|
|
101
|
+
else if (status === 'closed' || status === 'inactive') {
|
|
102
102
|
queryParams.active = 'false';
|
|
103
103
|
queryParams.closed = 'true';
|
|
104
104
|
}
|
|
@@ -20,12 +20,13 @@ export declare class PolymarketExchange extends PredictionMarketExchange {
|
|
|
20
20
|
* Ensure authentication is initialized before trading operations.
|
|
21
21
|
*/
|
|
22
22
|
private ensureAuth;
|
|
23
|
-
createOrder(params: CreateOrderParams): Promise<Order>;
|
|
24
23
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* Pre-warm the SDK's internal caches for a token by fetching tick size,
|
|
25
|
+
* fee rate, and neg-risk in parallel. Call this when you start watching
|
|
26
|
+
* a market so that subsequent createOrder calls hit only POST /order.
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
preWarmMarket(outcomeId: string): Promise<void>;
|
|
29
|
+
createOrder(params: CreateOrderParams): Promise<Order>;
|
|
29
30
|
cancelOrder(orderId: string): Promise<Order>;
|
|
30
31
|
fetchOrder(orderId: string): Promise<Order>;
|
|
31
32
|
fetchOpenOrders(marketId?: string): Promise<Order[]>;
|
|
@@ -73,6 +73,20 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
73
73
|
}
|
|
74
74
|
return this.auth;
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Pre-warm the SDK's internal caches for a token by fetching tick size,
|
|
78
|
+
* fee rate, and neg-risk in parallel. Call this when you start watching
|
|
79
|
+
* a market so that subsequent createOrder calls hit only POST /order.
|
|
80
|
+
*/
|
|
81
|
+
async preWarmMarket(outcomeId) {
|
|
82
|
+
const auth = this.ensureAuth();
|
|
83
|
+
const client = await auth.getClobClient();
|
|
84
|
+
await Promise.all([
|
|
85
|
+
client.getTickSize(outcomeId),
|
|
86
|
+
client.getFeeRateBps(outcomeId),
|
|
87
|
+
client.getNegRisk(outcomeId),
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
76
90
|
async createOrder(params) {
|
|
77
91
|
try {
|
|
78
92
|
const auth = this.ensureAuth();
|
|
@@ -85,22 +99,8 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
85
99
|
}
|
|
86
100
|
// For market orders, use max slippage: 0.99 for BUY (willing to pay up to 99%), 0.01 for SELL (willing to accept down to 1%)
|
|
87
101
|
const price = params.price || (side === clob_client_1.Side.BUY ? 0.99 : 0.01);
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
if (params.tickSize) {
|
|
91
|
-
tickSize = params.tickSize.toString();
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
// Fetch the order book to infer tick size from price levels
|
|
95
|
-
try {
|
|
96
|
-
const orderBook = await this.fetchOrderBook(params.outcomeId);
|
|
97
|
-
tickSize = this.inferTickSize(orderBook);
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
// Fallback to 0.01 if order book fetch fails (standard for Polymarket)
|
|
101
|
-
tickSize = "0.01";
|
|
102
|
-
}
|
|
103
|
-
}
|
|
102
|
+
// Use provided tickSize, or let the SDK resolve it from its own cache / API
|
|
103
|
+
const tickSize = params.tickSize ? params.tickSize.toString() : undefined;
|
|
104
104
|
const orderArgs = {
|
|
105
105
|
tokenID: params.outcomeId,
|
|
106
106
|
price: price,
|
|
@@ -110,10 +110,14 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
110
110
|
if (params.fee !== undefined && params.fee !== null) {
|
|
111
111
|
orderArgs.feeRateBps = params.fee;
|
|
112
112
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
tickSize
|
|
116
|
-
}
|
|
113
|
+
const options = {};
|
|
114
|
+
if (tickSize) {
|
|
115
|
+
options.tickSize = tickSize;
|
|
116
|
+
}
|
|
117
|
+
if (params.negRisk !== undefined) {
|
|
118
|
+
options.negRisk = params.negRisk;
|
|
119
|
+
}
|
|
120
|
+
const response = await client.createAndPostOrder(orderArgs, options);
|
|
117
121
|
if (!response || !response.success) {
|
|
118
122
|
throw new Error(`${response?.errorMsg || 'Order placement failed'} (Response: ${JSON.stringify(response)})`);
|
|
119
123
|
}
|
|
@@ -136,40 +140,6 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
136
140
|
throw errors_1.polymarketErrorMapper.mapError(error);
|
|
137
141
|
}
|
|
138
142
|
}
|
|
139
|
-
/**
|
|
140
|
-
* Infer the tick size from order book price levels.
|
|
141
|
-
* Analyzes the decimal precision of existing orders to determine the market's tick size.
|
|
142
|
-
*/
|
|
143
|
-
inferTickSize(orderBook) {
|
|
144
|
-
const allPrices = [
|
|
145
|
-
...orderBook.bids.map(b => b.price),
|
|
146
|
-
...orderBook.asks.map(a => a.price)
|
|
147
|
-
];
|
|
148
|
-
if (allPrices.length === 0) {
|
|
149
|
-
return "0.01"; // Default fallback for Polymarket
|
|
150
|
-
}
|
|
151
|
-
// Find the smallest non-zero decimal increment
|
|
152
|
-
let minIncrement = 1;
|
|
153
|
-
for (const price of allPrices) {
|
|
154
|
-
const priceStr = price.toString();
|
|
155
|
-
const decimalPart = priceStr.split('.')[1];
|
|
156
|
-
if (decimalPart) {
|
|
157
|
-
const decimals = decimalPart.length;
|
|
158
|
-
const increment = Math.pow(10, -decimals);
|
|
159
|
-
if (increment < minIncrement) {
|
|
160
|
-
minIncrement = increment;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// Map to valid tick sizes: 0.1, 0.01, 0.001, 0.0001
|
|
165
|
-
if (minIncrement >= 0.1)
|
|
166
|
-
return "0.1";
|
|
167
|
-
if (minIncrement >= 0.01)
|
|
168
|
-
return "0.01";
|
|
169
|
-
if (minIncrement >= 0.001)
|
|
170
|
-
return "0.001";
|
|
171
|
-
return "0.0001";
|
|
172
|
-
}
|
|
173
143
|
async cancelOrder(orderId) {
|
|
174
144
|
try {
|
|
175
145
|
const auth = this.ensureAuth();
|
package/dist/types.d.ts
CHANGED
|
@@ -231,6 +231,14 @@ class ErrorMapper {
|
|
|
231
231
|
return error;
|
|
232
232
|
}
|
|
233
233
|
// Unknown error format
|
|
234
|
+
if (typeof error === 'object' && error !== null) {
|
|
235
|
+
try {
|
|
236
|
+
return JSON.stringify(error, Object.getOwnPropertyNames(error));
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
return String(error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
234
242
|
return String(error);
|
|
235
243
|
}
|
|
236
244
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxt-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
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.3,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.3,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"
|