pmxtjs 2.8.0 → 2.9.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/esm/pmxt/server-manager.js +8 -3
- package/dist/pmxt/server-manager.js +8 -3
- package/generated/package.json +1 -1
- package/package.json +2 -2
- package/pmxt/server-manager.ts +9 -3
- package/pmxt/client.js +0 -957
- package/pmxt/models.js +0 -60
- package/pmxt/server-manager.js +0 -204
|
@@ -104,14 +104,16 @@ export class ServerManager {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
// Locate pmxt-ensure-server
|
|
107
|
-
|
|
107
|
+
const isWindows = process.platform === 'win32';
|
|
108
|
+
const launcherName = isWindows ? 'pmxt-ensure-server.js' : 'pmxt-ensure-server';
|
|
109
|
+
let launcherPath = launcherName; // Default to PATH
|
|
108
110
|
try {
|
|
109
111
|
// Try to resolve from pmxt-core dependency
|
|
110
112
|
// For CommonJS build (which is primary), we can use require directly
|
|
111
113
|
// For ESM build, this will be transpiled appropriately
|
|
112
114
|
const corePackageJson = require.resolve('pmxt-core/package.json');
|
|
113
115
|
const coreDir = dirname(corePackageJson);
|
|
114
|
-
const binPath = join(coreDir, 'bin',
|
|
116
|
+
const binPath = join(coreDir, 'bin', launcherName);
|
|
115
117
|
if (existsSync(binPath)) {
|
|
116
118
|
launcherPath = binPath;
|
|
117
119
|
}
|
|
@@ -122,8 +124,11 @@ export class ServerManager {
|
|
|
122
124
|
}
|
|
123
125
|
// Try to start the server using pmxt-ensure-server
|
|
124
126
|
const { spawn } = await import("child_process");
|
|
127
|
+
// On Windows, .js scripts must be run via node explicitly
|
|
128
|
+
const spawnCmd = launcherPath.endsWith('.js') ? 'node' : launcherPath;
|
|
129
|
+
const spawnArgs = launcherPath.endsWith('.js') ? [launcherPath] : [];
|
|
125
130
|
try {
|
|
126
|
-
const proc = spawn(
|
|
131
|
+
const proc = spawn(spawnCmd, spawnArgs, {
|
|
127
132
|
detached: true,
|
|
128
133
|
stdio: "ignore",
|
|
129
134
|
});
|
|
@@ -140,14 +140,16 @@ class ServerManager {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
// Locate pmxt-ensure-server
|
|
143
|
-
|
|
143
|
+
const isWindows = process.platform === 'win32';
|
|
144
|
+
const launcherName = isWindows ? 'pmxt-ensure-server.js' : 'pmxt-ensure-server';
|
|
145
|
+
let launcherPath = launcherName; // Default to PATH
|
|
144
146
|
try {
|
|
145
147
|
// Try to resolve from pmxt-core dependency
|
|
146
148
|
// For CommonJS build (which is primary), we can use require directly
|
|
147
149
|
// For ESM build, this will be transpiled appropriately
|
|
148
150
|
const corePackageJson = require.resolve('pmxt-core/package.json');
|
|
149
151
|
const coreDir = (0, path_1.dirname)(corePackageJson);
|
|
150
|
-
const binPath = (0, path_1.join)(coreDir, 'bin',
|
|
152
|
+
const binPath = (0, path_1.join)(coreDir, 'bin', launcherName);
|
|
151
153
|
if ((0, fs_1.existsSync)(binPath)) {
|
|
152
154
|
launcherPath = binPath;
|
|
153
155
|
}
|
|
@@ -158,8 +160,11 @@ class ServerManager {
|
|
|
158
160
|
}
|
|
159
161
|
// Try to start the server using pmxt-ensure-server
|
|
160
162
|
const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process")));
|
|
163
|
+
// On Windows, .js scripts must be run via node explicitly
|
|
164
|
+
const spawnCmd = launcherPath.endsWith('.js') ? 'node' : launcherPath;
|
|
165
|
+
const spawnArgs = launcherPath.endsWith('.js') ? [launcherPath] : [];
|
|
161
166
|
try {
|
|
162
|
-
const proc = spawn(
|
|
167
|
+
const proc = spawn(spawnCmd, spawnArgs, {
|
|
163
168
|
detached: true,
|
|
164
169
|
stdio: "ignore",
|
|
165
170
|
});
|
package/generated/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxtjs",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "Unified prediction market data API - The ccxt for prediction markets",
|
|
5
5
|
"author": "PMXT Contributors",
|
|
6
6
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"unified"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"pmxt-core": "2.
|
|
45
|
+
"pmxt-core": "2.9.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/jest": "^30.0.0",
|
package/pmxt/server-manager.ts
CHANGED
|
@@ -130,7 +130,9 @@ export class ServerManager {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// Locate pmxt-ensure-server
|
|
133
|
-
|
|
133
|
+
const isWindows = process.platform === 'win32';
|
|
134
|
+
const launcherName = isWindows ? 'pmxt-ensure-server.js' : 'pmxt-ensure-server';
|
|
135
|
+
let launcherPath = launcherName; // Default to PATH
|
|
134
136
|
|
|
135
137
|
try {
|
|
136
138
|
// Try to resolve from pmxt-core dependency
|
|
@@ -138,7 +140,7 @@ export class ServerManager {
|
|
|
138
140
|
// For ESM build, this will be transpiled appropriately
|
|
139
141
|
const corePackageJson = require.resolve('pmxt-core/package.json');
|
|
140
142
|
const coreDir = dirname(corePackageJson);
|
|
141
|
-
const binPath = join(coreDir, 'bin',
|
|
143
|
+
const binPath = join(coreDir, 'bin', launcherName);
|
|
142
144
|
|
|
143
145
|
if (existsSync(binPath)) {
|
|
144
146
|
launcherPath = binPath;
|
|
@@ -151,8 +153,12 @@ export class ServerManager {
|
|
|
151
153
|
// Try to start the server using pmxt-ensure-server
|
|
152
154
|
const { spawn } = await import("child_process");
|
|
153
155
|
|
|
156
|
+
// On Windows, .js scripts must be run via node explicitly
|
|
157
|
+
const spawnCmd = launcherPath.endsWith('.js') ? 'node' : launcherPath;
|
|
158
|
+
const spawnArgs = launcherPath.endsWith('.js') ? [launcherPath] : [];
|
|
159
|
+
|
|
154
160
|
try {
|
|
155
|
-
const proc = spawn(
|
|
161
|
+
const proc = spawn(spawnCmd, spawnArgs, {
|
|
156
162
|
detached: true,
|
|
157
163
|
stdio: "ignore",
|
|
158
164
|
});
|
package/pmxt/client.js
DELETED
|
@@ -1,957 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Exchange client implementations.
|
|
4
|
-
*
|
|
5
|
-
* This module provides clean, TypeScript-friendly wrappers around the auto-generated
|
|
6
|
-
* OpenAPI client, matching the Python API exactly.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.Limitless = exports.Kalshi = exports.Polymarket = exports.Exchange = void 0;
|
|
10
|
-
const index_js_1 = require("../generated/src/index.js");
|
|
11
|
-
const models_js_1 = require("./models.js");
|
|
12
|
-
const server_manager_js_1 = require("./server-manager.js");
|
|
13
|
-
// Converter functions
|
|
14
|
-
function convertMarket(raw) {
|
|
15
|
-
const outcomes = (raw.outcomes || []).map((o) => ({
|
|
16
|
-
outcomeId: o.outcomeId,
|
|
17
|
-
label: o.label,
|
|
18
|
-
price: o.price,
|
|
19
|
-
priceChange24h: o.priceChange24h,
|
|
20
|
-
metadata: o.metadata,
|
|
21
|
-
}));
|
|
22
|
-
const convertOutcome = (o) => o ? ({
|
|
23
|
-
outcomeId: o.outcomeId,
|
|
24
|
-
label: o.label,
|
|
25
|
-
price: o.price,
|
|
26
|
-
priceChange24h: o.priceChange24h,
|
|
27
|
-
metadata: o.metadata,
|
|
28
|
-
}) : undefined;
|
|
29
|
-
return {
|
|
30
|
-
marketId: raw.marketId,
|
|
31
|
-
title: raw.title,
|
|
32
|
-
outcomes,
|
|
33
|
-
volume24h: raw.volume24h || 0,
|
|
34
|
-
liquidity: raw.liquidity || 0,
|
|
35
|
-
url: raw.url,
|
|
36
|
-
description: raw.description,
|
|
37
|
-
resolutionDate: raw.resolutionDate ? new Date(raw.resolutionDate) : undefined,
|
|
38
|
-
volume: raw.volume,
|
|
39
|
-
openInterest: raw.openInterest,
|
|
40
|
-
image: raw.image,
|
|
41
|
-
category: raw.category,
|
|
42
|
-
tags: raw.tags,
|
|
43
|
-
yes: convertOutcome(raw.yes),
|
|
44
|
-
no: convertOutcome(raw.no),
|
|
45
|
-
up: convertOutcome(raw.up),
|
|
46
|
-
down: convertOutcome(raw.down),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function convertCandle(raw) {
|
|
50
|
-
return {
|
|
51
|
-
timestamp: raw.timestamp,
|
|
52
|
-
open: raw.open,
|
|
53
|
-
high: raw.high,
|
|
54
|
-
low: raw.low,
|
|
55
|
-
close: raw.close,
|
|
56
|
-
volume: raw.volume,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
function convertOrderBook(raw) {
|
|
60
|
-
const bids = (raw.bids || []).map((b) => ({
|
|
61
|
-
price: b.price,
|
|
62
|
-
size: b.size,
|
|
63
|
-
}));
|
|
64
|
-
const asks = (raw.asks || []).map((a) => ({
|
|
65
|
-
price: a.price,
|
|
66
|
-
size: a.size,
|
|
67
|
-
}));
|
|
68
|
-
return {
|
|
69
|
-
bids,
|
|
70
|
-
asks,
|
|
71
|
-
timestamp: raw.timestamp,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function convertTrade(raw) {
|
|
75
|
-
return {
|
|
76
|
-
id: raw.id,
|
|
77
|
-
timestamp: raw.timestamp,
|
|
78
|
-
price: raw.price,
|
|
79
|
-
amount: raw.amount,
|
|
80
|
-
side: raw.side || "unknown",
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function convertOrder(raw) {
|
|
84
|
-
return {
|
|
85
|
-
id: raw.id,
|
|
86
|
-
marketId: raw.marketId,
|
|
87
|
-
outcomeId: raw.outcomeId,
|
|
88
|
-
side: raw.side,
|
|
89
|
-
type: raw.type,
|
|
90
|
-
amount: raw.amount,
|
|
91
|
-
status: raw.status,
|
|
92
|
-
filled: raw.filled,
|
|
93
|
-
remaining: raw.remaining,
|
|
94
|
-
timestamp: raw.timestamp,
|
|
95
|
-
price: raw.price,
|
|
96
|
-
fee: raw.fee,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
function convertPosition(raw) {
|
|
100
|
-
return {
|
|
101
|
-
marketId: raw.marketId,
|
|
102
|
-
outcomeId: raw.outcomeId,
|
|
103
|
-
outcomeLabel: raw.outcomeLabel,
|
|
104
|
-
size: raw.size,
|
|
105
|
-
entryPrice: raw.entryPrice,
|
|
106
|
-
currentPrice: raw.currentPrice,
|
|
107
|
-
unrealizedPnL: raw.unrealizedPnL,
|
|
108
|
-
realizedPnL: raw.realizedPnL,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function convertBalance(raw) {
|
|
112
|
-
return {
|
|
113
|
-
currency: raw.currency,
|
|
114
|
-
total: raw.total,
|
|
115
|
-
available: raw.available,
|
|
116
|
-
locked: raw.locked,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
function convertEvent(raw) {
|
|
120
|
-
const markets = models_js_1.MarketList.from((raw.markets || []).map(convertMarket));
|
|
121
|
-
return {
|
|
122
|
-
id: raw.id,
|
|
123
|
-
title: raw.title,
|
|
124
|
-
description: raw.description,
|
|
125
|
-
slug: raw.slug,
|
|
126
|
-
markets,
|
|
127
|
-
url: raw.url,
|
|
128
|
-
image: raw.image,
|
|
129
|
-
category: raw.category,
|
|
130
|
-
tags: raw.tags,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Base class for prediction market exchanges.
|
|
135
|
-
*
|
|
136
|
-
* This provides a unified interface for interacting with different
|
|
137
|
-
* prediction market platforms (Polymarket, Kalshi, etc.).
|
|
138
|
-
*/
|
|
139
|
-
class Exchange {
|
|
140
|
-
constructor(exchangeName, options = {}) {
|
|
141
|
-
this.exchangeName = exchangeName.toLowerCase();
|
|
142
|
-
this.apiKey = options.apiKey;
|
|
143
|
-
this.privateKey = options.privateKey;
|
|
144
|
-
this.proxyAddress = options.proxyAddress;
|
|
145
|
-
this.signatureType = options.signatureType;
|
|
146
|
-
let baseUrl = options.baseUrl || "http://localhost:3847";
|
|
147
|
-
const autoStartServer = options.autoStartServer !== false;
|
|
148
|
-
// Initialize server manager
|
|
149
|
-
this.serverManager = new server_manager_js_1.ServerManager({ baseUrl });
|
|
150
|
-
// Configure the API client with the initial base URL (will be updated if port changes)
|
|
151
|
-
this.config = new index_js_1.Configuration({ basePath: baseUrl });
|
|
152
|
-
this.api = new index_js_1.DefaultApi(this.config);
|
|
153
|
-
// Initialize the server connection asynchronously
|
|
154
|
-
this.initPromise = this.initializeServer(autoStartServer);
|
|
155
|
-
}
|
|
156
|
-
async initializeServer(autoStartServer) {
|
|
157
|
-
if (autoStartServer) {
|
|
158
|
-
try {
|
|
159
|
-
await this.serverManager.ensureServerRunning();
|
|
160
|
-
// Get the actual port the server is running on
|
|
161
|
-
// (may differ from default if default port was busy)
|
|
162
|
-
const actualPort = this.serverManager.getRunningPort();
|
|
163
|
-
const newBaseUrl = `http://localhost:${actualPort}`;
|
|
164
|
-
const accessToken = this.serverManager.getAccessToken();
|
|
165
|
-
const headers = {};
|
|
166
|
-
if (accessToken) {
|
|
167
|
-
headers['x-pmxt-access-token'] = accessToken;
|
|
168
|
-
}
|
|
169
|
-
// Update API client with actual base URL
|
|
170
|
-
this.config = new index_js_1.Configuration({
|
|
171
|
-
basePath: newBaseUrl,
|
|
172
|
-
headers
|
|
173
|
-
});
|
|
174
|
-
this.api = new index_js_1.DefaultApi(this.config);
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
throw new Error(`Failed to start PMXT server: ${error}\n\n` +
|
|
178
|
-
`Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
|
|
179
|
-
`Or start the server manually: pmxt-server`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
handleResponse(response) {
|
|
184
|
-
if (!response.success) {
|
|
185
|
-
const error = response.error || {};
|
|
186
|
-
throw new Error(error.message || "Unknown error");
|
|
187
|
-
}
|
|
188
|
-
return response.data;
|
|
189
|
-
}
|
|
190
|
-
getCredentials() {
|
|
191
|
-
if (!this.apiKey && !this.privateKey) {
|
|
192
|
-
return undefined;
|
|
193
|
-
}
|
|
194
|
-
return {
|
|
195
|
-
apiKey: this.apiKey,
|
|
196
|
-
privateKey: this.privateKey,
|
|
197
|
-
funderAddress: this.proxyAddress,
|
|
198
|
-
signatureType: this.signatureType,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
// Market Data Methods
|
|
202
|
-
/**
|
|
203
|
-
* Get active markets from the exchange.
|
|
204
|
-
*
|
|
205
|
-
* @param params - Optional filter parameters
|
|
206
|
-
* @returns List of unified markets
|
|
207
|
-
*
|
|
208
|
-
* @example
|
|
209
|
-
* ```typescript
|
|
210
|
-
* const markets = await exchange.fetchMarkets({ limit: 20, sort: "volume" });
|
|
211
|
-
* ```
|
|
212
|
-
*/
|
|
213
|
-
async fetchMarkets(params) {
|
|
214
|
-
await this.initPromise;
|
|
215
|
-
try {
|
|
216
|
-
const args = [];
|
|
217
|
-
if (params) {
|
|
218
|
-
args.push(params);
|
|
219
|
-
}
|
|
220
|
-
const requestBody = {
|
|
221
|
-
args,
|
|
222
|
-
credentials: this.getCredentials()
|
|
223
|
-
};
|
|
224
|
-
const response = await this.api.fetchMarkets({
|
|
225
|
-
exchange: this.exchangeName,
|
|
226
|
-
fetchMarketsRequest: requestBody,
|
|
227
|
-
});
|
|
228
|
-
const data = this.handleResponse(response);
|
|
229
|
-
return data.map(convertMarket);
|
|
230
|
-
}
|
|
231
|
-
catch (error) {
|
|
232
|
-
throw new Error(`Failed to fetch markets: ${error}`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Get historical price candles.
|
|
237
|
-
*
|
|
238
|
-
* @param outcomeId - Outcome ID (from market.outcomes[].outcomeId)
|
|
239
|
-
* @param params - History filter parameters
|
|
240
|
-
* @returns List of price candles
|
|
241
|
-
*
|
|
242
|
-
* @example
|
|
243
|
-
* ```typescript
|
|
244
|
-
* const markets = await exchange.fetchMarkets({ query: "Trump" });
|
|
245
|
-
* const outcomeId = markets[0].outcomes[0].outcomeId;
|
|
246
|
-
* const candles = await exchange.fetchOHLCV(outcomeId, {
|
|
247
|
-
* resolution: "1h",
|
|
248
|
-
* limit: 100
|
|
249
|
-
* });
|
|
250
|
-
* ```
|
|
251
|
-
*/
|
|
252
|
-
async fetchOHLCV(outcomeId, params) {
|
|
253
|
-
await this.initPromise;
|
|
254
|
-
try {
|
|
255
|
-
const paramsDict = { resolution: params.resolution };
|
|
256
|
-
if (params.start) {
|
|
257
|
-
paramsDict.start = params.start.toISOString();
|
|
258
|
-
}
|
|
259
|
-
if (params.end) {
|
|
260
|
-
paramsDict.end = params.end.toISOString();
|
|
261
|
-
}
|
|
262
|
-
if (params.limit) {
|
|
263
|
-
paramsDict.limit = params.limit;
|
|
264
|
-
}
|
|
265
|
-
const requestBody = {
|
|
266
|
-
args: [outcomeId, paramsDict],
|
|
267
|
-
credentials: this.getCredentials()
|
|
268
|
-
};
|
|
269
|
-
const response = await this.api.fetchOHLCV({
|
|
270
|
-
exchange: this.exchangeName,
|
|
271
|
-
fetchOHLCVRequest: requestBody,
|
|
272
|
-
});
|
|
273
|
-
const data = this.handleResponse(response);
|
|
274
|
-
return data.map(convertCandle);
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
throw new Error(`Failed to fetch OHLCV: ${error}`);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Get current order book for an outcome.
|
|
282
|
-
*
|
|
283
|
-
* @param outcomeId - Outcome ID
|
|
284
|
-
* @returns Current order book
|
|
285
|
-
*
|
|
286
|
-
* @example
|
|
287
|
-
* ```typescript
|
|
288
|
-
* const orderBook = await exchange.fetchOrderBook(outcomeId);
|
|
289
|
-
* console.log(`Best bid: ${orderBook.bids[0].price}`);
|
|
290
|
-
* console.log(`Best ask: ${orderBook.asks[0].price}`);
|
|
291
|
-
* ```
|
|
292
|
-
*/
|
|
293
|
-
async fetchOrderBook(outcomeId) {
|
|
294
|
-
await this.initPromise;
|
|
295
|
-
try {
|
|
296
|
-
const requestBody = {
|
|
297
|
-
args: [outcomeId],
|
|
298
|
-
credentials: this.getCredentials()
|
|
299
|
-
};
|
|
300
|
-
const response = await this.api.fetchOrderBook({
|
|
301
|
-
exchange: this.exchangeName,
|
|
302
|
-
fetchOrderBookRequest: requestBody,
|
|
303
|
-
});
|
|
304
|
-
const data = this.handleResponse(response);
|
|
305
|
-
return convertOrderBook(data);
|
|
306
|
-
}
|
|
307
|
-
catch (error) {
|
|
308
|
-
throw new Error(`Failed to fetch order book: ${error}`);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Get trade history for an outcome.
|
|
313
|
-
*
|
|
314
|
-
* Note: Polymarket requires API key.
|
|
315
|
-
*
|
|
316
|
-
* @param outcomeId - Outcome ID
|
|
317
|
-
* @param params - History filter parameters
|
|
318
|
-
* @returns List of trades
|
|
319
|
-
*/
|
|
320
|
-
async fetchTrades(outcomeId, params) {
|
|
321
|
-
await this.initPromise;
|
|
322
|
-
try {
|
|
323
|
-
const paramsDict = { resolution: params.resolution };
|
|
324
|
-
if (params.limit) {
|
|
325
|
-
paramsDict.limit = params.limit;
|
|
326
|
-
}
|
|
327
|
-
const requestBody = {
|
|
328
|
-
args: [outcomeId, paramsDict],
|
|
329
|
-
credentials: this.getCredentials()
|
|
330
|
-
};
|
|
331
|
-
const response = await this.api.fetchTrades({
|
|
332
|
-
exchange: this.exchangeName,
|
|
333
|
-
fetchTradesRequest: requestBody,
|
|
334
|
-
});
|
|
335
|
-
const data = this.handleResponse(response);
|
|
336
|
-
return data.map(convertTrade);
|
|
337
|
-
}
|
|
338
|
-
catch (error) {
|
|
339
|
-
throw new Error(`Failed to fetch trades: ${error}`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
// WebSocket Streaming Methods
|
|
343
|
-
/**
|
|
344
|
-
* Watch real-time order book updates via WebSocket.
|
|
345
|
-
*
|
|
346
|
-
* Returns a promise that resolves with the next order book update.
|
|
347
|
-
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
348
|
-
*
|
|
349
|
-
* @param outcomeId - Outcome ID to watch
|
|
350
|
-
* @param limit - Optional depth limit for order book
|
|
351
|
-
* @returns Next order book update
|
|
352
|
-
*
|
|
353
|
-
* @example
|
|
354
|
-
* ```typescript
|
|
355
|
-
* // Stream order book updates
|
|
356
|
-
* while (true) {
|
|
357
|
-
* const orderBook = await exchange.watchOrderBook(outcomeId);
|
|
358
|
-
* console.log(`Best bid: ${orderBook.bids[0].price}`);
|
|
359
|
-
* console.log(`Best ask: ${orderBook.asks[0].price}`);
|
|
360
|
-
* }
|
|
361
|
-
* ```
|
|
362
|
-
*/
|
|
363
|
-
async watchOrderBook(outcomeId, limit) {
|
|
364
|
-
await this.initPromise;
|
|
365
|
-
try {
|
|
366
|
-
const args = [outcomeId];
|
|
367
|
-
if (limit !== undefined) {
|
|
368
|
-
args.push(limit);
|
|
369
|
-
}
|
|
370
|
-
const requestBody = {
|
|
371
|
-
args,
|
|
372
|
-
credentials: this.getCredentials()
|
|
373
|
-
};
|
|
374
|
-
const response = await this.api.watchOrderBook({
|
|
375
|
-
exchange: this.exchangeName,
|
|
376
|
-
watchOrderBookRequest: requestBody,
|
|
377
|
-
});
|
|
378
|
-
const data = this.handleResponse(response);
|
|
379
|
-
return convertOrderBook(data);
|
|
380
|
-
}
|
|
381
|
-
catch (error) {
|
|
382
|
-
throw new Error(`Failed to watch order book: ${error}`);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Watch real-time trade updates via WebSocket.
|
|
387
|
-
*
|
|
388
|
-
* Returns a promise that resolves with the next trade(s).
|
|
389
|
-
* Call repeatedly in a loop to stream updates (CCXT Pro pattern).
|
|
390
|
-
*
|
|
391
|
-
* @param outcomeId - Outcome ID to watch
|
|
392
|
-
* @param since - Optional timestamp to filter trades from
|
|
393
|
-
* @param limit - Optional limit for number of trades
|
|
394
|
-
* @returns Next trade update(s)
|
|
395
|
-
*
|
|
396
|
-
* @example
|
|
397
|
-
* ```typescript
|
|
398
|
-
* // Stream trade updates
|
|
399
|
-
* while (true) {
|
|
400
|
-
* const trades = await exchange.watchTrades(outcomeId);
|
|
401
|
-
* for (const trade of trades) {
|
|
402
|
-
* console.log(`Trade: ${trade.price} @ ${trade.amount}`);
|
|
403
|
-
* }
|
|
404
|
-
* }
|
|
405
|
-
* ```
|
|
406
|
-
*/
|
|
407
|
-
async watchTrades(outcomeId, since, limit) {
|
|
408
|
-
await this.initPromise;
|
|
409
|
-
try {
|
|
410
|
-
const args = [outcomeId];
|
|
411
|
-
if (since !== undefined) {
|
|
412
|
-
args.push(since);
|
|
413
|
-
}
|
|
414
|
-
if (limit !== undefined) {
|
|
415
|
-
args.push(limit);
|
|
416
|
-
}
|
|
417
|
-
const requestBody = {
|
|
418
|
-
args,
|
|
419
|
-
credentials: this.getCredentials()
|
|
420
|
-
};
|
|
421
|
-
const response = await this.api.watchTrades({
|
|
422
|
-
exchange: this.exchangeName,
|
|
423
|
-
watchTradesRequest: requestBody,
|
|
424
|
-
});
|
|
425
|
-
const data = this.handleResponse(response);
|
|
426
|
-
return data.map(convertTrade);
|
|
427
|
-
}
|
|
428
|
-
catch (error) {
|
|
429
|
-
throw new Error(`Failed to watch trades: ${error}`);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
// Trading Methods (require authentication)
|
|
433
|
-
/**
|
|
434
|
-
* Create a new order.
|
|
435
|
-
*
|
|
436
|
-
* @param params - Order parameters
|
|
437
|
-
* @returns Created order
|
|
438
|
-
*
|
|
439
|
-
* @example
|
|
440
|
-
* ```typescript
|
|
441
|
-
* const order = await exchange.createOrder({
|
|
442
|
-
* marketId: "663583",
|
|
443
|
-
* outcomeId: "10991849...",
|
|
444
|
-
* side: "buy",
|
|
445
|
-
* type: "limit",
|
|
446
|
-
* amount: 10,
|
|
447
|
-
* price: 0.55
|
|
448
|
-
* });
|
|
449
|
-
* ```
|
|
450
|
-
*/
|
|
451
|
-
async createOrder(params) {
|
|
452
|
-
await this.initPromise;
|
|
453
|
-
try {
|
|
454
|
-
const paramsDict = {
|
|
455
|
-
marketId: params.marketId,
|
|
456
|
-
outcomeId: params.outcomeId,
|
|
457
|
-
side: params.side,
|
|
458
|
-
type: params.type,
|
|
459
|
-
amount: params.amount,
|
|
460
|
-
};
|
|
461
|
-
if (params.price !== undefined) {
|
|
462
|
-
paramsDict.price = params.price;
|
|
463
|
-
}
|
|
464
|
-
if (params.fee !== undefined) {
|
|
465
|
-
paramsDict.fee = params.fee;
|
|
466
|
-
}
|
|
467
|
-
const requestBody = {
|
|
468
|
-
args: [paramsDict],
|
|
469
|
-
credentials: this.getCredentials()
|
|
470
|
-
};
|
|
471
|
-
const response = await this.api.createOrder({
|
|
472
|
-
exchange: this.exchangeName,
|
|
473
|
-
createOrderRequest: requestBody,
|
|
474
|
-
});
|
|
475
|
-
const data = this.handleResponse(response);
|
|
476
|
-
return convertOrder(data);
|
|
477
|
-
}
|
|
478
|
-
catch (error) {
|
|
479
|
-
throw new Error(`Failed to create order: ${error}`);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Cancel an open order.
|
|
484
|
-
*
|
|
485
|
-
* @param orderId - Order ID to cancel
|
|
486
|
-
* @returns Cancelled order
|
|
487
|
-
*/
|
|
488
|
-
async cancelOrder(orderId) {
|
|
489
|
-
await this.initPromise;
|
|
490
|
-
try {
|
|
491
|
-
const requestBody = {
|
|
492
|
-
args: [orderId],
|
|
493
|
-
credentials: this.getCredentials()
|
|
494
|
-
};
|
|
495
|
-
const response = await this.api.cancelOrder({
|
|
496
|
-
exchange: this.exchangeName,
|
|
497
|
-
cancelOrderRequest: requestBody,
|
|
498
|
-
});
|
|
499
|
-
const data = this.handleResponse(response);
|
|
500
|
-
return convertOrder(data);
|
|
501
|
-
}
|
|
502
|
-
catch (error) {
|
|
503
|
-
throw new Error(`Failed to cancel order: ${error}`);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
/**
|
|
507
|
-
* Get details of a specific order.
|
|
508
|
-
*
|
|
509
|
-
* @param orderId - Order ID
|
|
510
|
-
* @returns Order details
|
|
511
|
-
*/
|
|
512
|
-
async fetchOrder(orderId) {
|
|
513
|
-
await this.initPromise;
|
|
514
|
-
try {
|
|
515
|
-
const requestBody = {
|
|
516
|
-
args: [orderId],
|
|
517
|
-
credentials: this.getCredentials()
|
|
518
|
-
};
|
|
519
|
-
const response = await this.api.fetchOrder({
|
|
520
|
-
exchange: this.exchangeName,
|
|
521
|
-
cancelOrderRequest: requestBody,
|
|
522
|
-
});
|
|
523
|
-
const data = this.handleResponse(response);
|
|
524
|
-
return convertOrder(data);
|
|
525
|
-
}
|
|
526
|
-
catch (error) {
|
|
527
|
-
throw new Error(`Failed to fetch order: ${error}`);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Get all open orders, optionally filtered by market.
|
|
532
|
-
*
|
|
533
|
-
* @param marketId - Optional market ID to filter by
|
|
534
|
-
* @returns List of open orders
|
|
535
|
-
*/
|
|
536
|
-
async fetchOpenOrders(marketId) {
|
|
537
|
-
await this.initPromise;
|
|
538
|
-
try {
|
|
539
|
-
const args = [];
|
|
540
|
-
if (marketId) {
|
|
541
|
-
args.push(marketId);
|
|
542
|
-
}
|
|
543
|
-
const requestBody = {
|
|
544
|
-
args,
|
|
545
|
-
credentials: this.getCredentials()
|
|
546
|
-
};
|
|
547
|
-
const response = await this.api.fetchOpenOrders({
|
|
548
|
-
exchange: this.exchangeName,
|
|
549
|
-
fetchOpenOrdersRequest: requestBody,
|
|
550
|
-
});
|
|
551
|
-
const data = this.handleResponse(response);
|
|
552
|
-
return data.map(convertOrder);
|
|
553
|
-
}
|
|
554
|
-
catch (error) {
|
|
555
|
-
throw new Error(`Failed to fetch open orders: ${error}`);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
// Account Methods
|
|
559
|
-
/**
|
|
560
|
-
* Get current positions across all markets.
|
|
561
|
-
*
|
|
562
|
-
* @returns List of positions
|
|
563
|
-
*/
|
|
564
|
-
async fetchPositions() {
|
|
565
|
-
await this.initPromise;
|
|
566
|
-
try {
|
|
567
|
-
const requestBody = {
|
|
568
|
-
args: [],
|
|
569
|
-
credentials: this.getCredentials()
|
|
570
|
-
};
|
|
571
|
-
const response = await this.api.fetchPositions({
|
|
572
|
-
exchange: this.exchangeName,
|
|
573
|
-
fetchPositionsRequest: requestBody,
|
|
574
|
-
});
|
|
575
|
-
const data = this.handleResponse(response);
|
|
576
|
-
return data.map(convertPosition);
|
|
577
|
-
}
|
|
578
|
-
catch (error) {
|
|
579
|
-
throw new Error(`Failed to fetch positions: ${error}`);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Get account balance.
|
|
584
|
-
*
|
|
585
|
-
* @returns List of balances (by currency)
|
|
586
|
-
*/
|
|
587
|
-
async fetchBalance() {
|
|
588
|
-
await this.initPromise;
|
|
589
|
-
try {
|
|
590
|
-
const requestBody = {
|
|
591
|
-
args: [],
|
|
592
|
-
credentials: this.getCredentials()
|
|
593
|
-
};
|
|
594
|
-
const response = await this.api.fetchBalance({
|
|
595
|
-
exchange: this.exchangeName,
|
|
596
|
-
fetchPositionsRequest: requestBody,
|
|
597
|
-
});
|
|
598
|
-
const data = this.handleResponse(response);
|
|
599
|
-
return data.map(convertBalance);
|
|
600
|
-
}
|
|
601
|
-
catch (error) {
|
|
602
|
-
throw new Error(`Failed to fetch balance: ${error}`);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Calculate the average execution price for a given amount by walking the order book.
|
|
607
|
-
* Uses the sidecar server for calculation to ensure consistency.
|
|
608
|
-
*
|
|
609
|
-
* @param orderBook - The current order book
|
|
610
|
-
* @param side - 'buy' or 'sell'
|
|
611
|
-
* @param amount - The amount to execute
|
|
612
|
-
* @returns The volume-weighted average price, or 0 if insufficient liquidity
|
|
613
|
-
*/
|
|
614
|
-
async getExecutionPrice(orderBook, side, amount) {
|
|
615
|
-
const result = await this.getExecutionPriceDetailed(orderBook, side, amount);
|
|
616
|
-
return result.fullyFilled ? result.price : 0;
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* Calculate detailed execution price information.
|
|
620
|
-
* Uses the sidecar server for calculation to ensure consistency.
|
|
621
|
-
*
|
|
622
|
-
* @param orderBook - The current order book
|
|
623
|
-
* @param side - 'buy' or 'sell'
|
|
624
|
-
* @param amount - The amount to execute
|
|
625
|
-
* @returns Detailed execution result
|
|
626
|
-
*/
|
|
627
|
-
async getExecutionPriceDetailed(orderBook, side, amount) {
|
|
628
|
-
await this.initPromise;
|
|
629
|
-
try {
|
|
630
|
-
const body = {
|
|
631
|
-
args: [orderBook, side, amount]
|
|
632
|
-
};
|
|
633
|
-
const credentials = this.getCredentials();
|
|
634
|
-
if (credentials) {
|
|
635
|
-
body.credentials = credentials;
|
|
636
|
-
}
|
|
637
|
-
const url = `${this.config.basePath}/api/${this.exchangeName}/getExecutionPriceDetailed`;
|
|
638
|
-
const response = await fetch(url, {
|
|
639
|
-
method: 'POST',
|
|
640
|
-
headers: {
|
|
641
|
-
'Content-Type': 'application/json',
|
|
642
|
-
...this.config.headers
|
|
643
|
-
},
|
|
644
|
-
body: JSON.stringify(body)
|
|
645
|
-
});
|
|
646
|
-
if (!response.ok) {
|
|
647
|
-
const error = await response.json().catch(() => ({}));
|
|
648
|
-
throw new Error(error.error?.message || response.statusText);
|
|
649
|
-
}
|
|
650
|
-
const json = await response.json();
|
|
651
|
-
return this.handleResponse(json);
|
|
652
|
-
}
|
|
653
|
-
catch (error) {
|
|
654
|
-
throw new Error(`Failed to get execution price: ${error}`);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
// ----------------------------------------------------------------------------
|
|
658
|
-
// Filtering Methods
|
|
659
|
-
// ----------------------------------------------------------------------------
|
|
660
|
-
/**
|
|
661
|
-
* Filter markets based on criteria or custom function.
|
|
662
|
-
*
|
|
663
|
-
* @param markets - Array of markets to filter
|
|
664
|
-
* @param criteria - Filter criteria object, string (simple text search), or predicate function
|
|
665
|
-
* @returns Filtered array of markets
|
|
666
|
-
*
|
|
667
|
-
* @example Simple text search
|
|
668
|
-
* api.filterMarkets(markets, 'Trump')
|
|
669
|
-
*
|
|
670
|
-
* @example Advanced filtering
|
|
671
|
-
* api.filterMarkets(markets, {
|
|
672
|
-
* text: 'Trump',
|
|
673
|
-
* searchIn: ['title', 'tags'],
|
|
674
|
-
* volume24h: { min: 10000 },
|
|
675
|
-
* category: 'Politics',
|
|
676
|
-
* price: { outcome: 'yes', max: 0.5 }
|
|
677
|
-
* })
|
|
678
|
-
*
|
|
679
|
-
* @example Custom predicate
|
|
680
|
-
* api.filterMarkets(markets, m => m.liquidity > 5000 && m.yes?.price < 0.3)
|
|
681
|
-
*/
|
|
682
|
-
filterMarkets(markets, criteria) {
|
|
683
|
-
// Handle predicate function
|
|
684
|
-
if (typeof criteria === 'function') {
|
|
685
|
-
return markets.filter(criteria);
|
|
686
|
-
}
|
|
687
|
-
// Handle simple string search
|
|
688
|
-
if (typeof criteria === 'string') {
|
|
689
|
-
const lowerQuery = criteria.toLowerCase();
|
|
690
|
-
return markets.filter(m => m.title.toLowerCase().includes(lowerQuery));
|
|
691
|
-
}
|
|
692
|
-
// Handle criteria object
|
|
693
|
-
return markets.filter(market => {
|
|
694
|
-
// Text search
|
|
695
|
-
if (criteria.text) {
|
|
696
|
-
const lowerQuery = criteria.text.toLowerCase();
|
|
697
|
-
const searchIn = criteria.searchIn || ['title'];
|
|
698
|
-
let textMatch = false;
|
|
699
|
-
for (const field of searchIn) {
|
|
700
|
-
if (field === 'title' && market.title?.toLowerCase().includes(lowerQuery)) {
|
|
701
|
-
textMatch = true;
|
|
702
|
-
break;
|
|
703
|
-
}
|
|
704
|
-
if (field === 'description' && market.description?.toLowerCase().includes(lowerQuery)) {
|
|
705
|
-
textMatch = true;
|
|
706
|
-
break;
|
|
707
|
-
}
|
|
708
|
-
if (field === 'category' && market.category?.toLowerCase().includes(lowerQuery)) {
|
|
709
|
-
textMatch = true;
|
|
710
|
-
break;
|
|
711
|
-
}
|
|
712
|
-
if (field === 'tags' && market.tags?.some(tag => tag.toLowerCase().includes(lowerQuery))) {
|
|
713
|
-
textMatch = true;
|
|
714
|
-
break;
|
|
715
|
-
}
|
|
716
|
-
if (field === 'outcomes' && market.outcomes?.some(o => o.label.toLowerCase().includes(lowerQuery))) {
|
|
717
|
-
textMatch = true;
|
|
718
|
-
break;
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
if (!textMatch)
|
|
722
|
-
return false;
|
|
723
|
-
}
|
|
724
|
-
// Category filter
|
|
725
|
-
if (criteria.category && market.category !== criteria.category) {
|
|
726
|
-
return false;
|
|
727
|
-
}
|
|
728
|
-
// Tags filter (match ANY of the provided tags)
|
|
729
|
-
if (criteria.tags && criteria.tags.length > 0) {
|
|
730
|
-
const hasMatchingTag = criteria.tags.some(tag => market.tags?.some(marketTag => marketTag.toLowerCase() === tag.toLowerCase()));
|
|
731
|
-
if (!hasMatchingTag)
|
|
732
|
-
return false;
|
|
733
|
-
}
|
|
734
|
-
// Volume24h filter
|
|
735
|
-
if (criteria.volume24h) {
|
|
736
|
-
if (criteria.volume24h.min !== undefined && market.volume24h < criteria.volume24h.min) {
|
|
737
|
-
return false;
|
|
738
|
-
}
|
|
739
|
-
if (criteria.volume24h.max !== undefined && market.volume24h > criteria.volume24h.max) {
|
|
740
|
-
return false;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
// Volume filter
|
|
744
|
-
if (criteria.volume) {
|
|
745
|
-
if (criteria.volume.min !== undefined && (market.volume || 0) < criteria.volume.min) {
|
|
746
|
-
return false;
|
|
747
|
-
}
|
|
748
|
-
if (criteria.volume.max !== undefined && (market.volume || 0) > criteria.volume.max) {
|
|
749
|
-
return false;
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
// Liquidity filter
|
|
753
|
-
if (criteria.liquidity) {
|
|
754
|
-
if (criteria.liquidity.min !== undefined && market.liquidity < criteria.liquidity.min) {
|
|
755
|
-
return false;
|
|
756
|
-
}
|
|
757
|
-
if (criteria.liquidity.max !== undefined && market.liquidity > criteria.liquidity.max) {
|
|
758
|
-
return false;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
// OpenInterest filter
|
|
762
|
-
if (criteria.openInterest) {
|
|
763
|
-
if (criteria.openInterest.min !== undefined && (market.openInterest || 0) < criteria.openInterest.min) {
|
|
764
|
-
return false;
|
|
765
|
-
}
|
|
766
|
-
if (criteria.openInterest.max !== undefined && (market.openInterest || 0) > criteria.openInterest.max) {
|
|
767
|
-
return false;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
// ResolutionDate filter
|
|
771
|
-
if (criteria.resolutionDate && market.resolutionDate) {
|
|
772
|
-
const resDate = market.resolutionDate;
|
|
773
|
-
if (criteria.resolutionDate.before && resDate >= criteria.resolutionDate.before) {
|
|
774
|
-
return false;
|
|
775
|
-
}
|
|
776
|
-
if (criteria.resolutionDate.after && resDate <= criteria.resolutionDate.after) {
|
|
777
|
-
return false;
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
// Price filter (for binary markets)
|
|
781
|
-
if (criteria.price) {
|
|
782
|
-
const outcome = market[criteria.price.outcome];
|
|
783
|
-
if (!outcome)
|
|
784
|
-
return false;
|
|
785
|
-
if (criteria.price.min !== undefined && outcome.price < criteria.price.min) {
|
|
786
|
-
return false;
|
|
787
|
-
}
|
|
788
|
-
if (criteria.price.max !== undefined && outcome.price > criteria.price.max) {
|
|
789
|
-
return false;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
// Price change filter
|
|
793
|
-
if (criteria.priceChange24h) {
|
|
794
|
-
const outcome = market[criteria.priceChange24h.outcome];
|
|
795
|
-
if (!outcome || outcome.priceChange24h === undefined)
|
|
796
|
-
return false;
|
|
797
|
-
if (criteria.priceChange24h.min !== undefined && outcome.priceChange24h < criteria.priceChange24h.min) {
|
|
798
|
-
return false;
|
|
799
|
-
}
|
|
800
|
-
if (criteria.priceChange24h.max !== undefined && outcome.priceChange24h > criteria.priceChange24h.max) {
|
|
801
|
-
return false;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
return true;
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* Filter events based on criteria or custom function.
|
|
809
|
-
*
|
|
810
|
-
* @param events - Array of events to filter
|
|
811
|
-
* @param criteria - Filter criteria object, string (simple text search), or predicate function
|
|
812
|
-
* @returns Filtered array of events
|
|
813
|
-
*
|
|
814
|
-
* @example Simple text search
|
|
815
|
-
* api.filterEvents(events, 'Trump')
|
|
816
|
-
*
|
|
817
|
-
* @example Advanced filtering
|
|
818
|
-
* api.filterEvents(events, {
|
|
819
|
-
* text: 'Election',
|
|
820
|
-
* searchIn: ['title', 'tags'],
|
|
821
|
-
* category: 'Politics',
|
|
822
|
-
* marketCount: { min: 5 }
|
|
823
|
-
* })
|
|
824
|
-
*
|
|
825
|
-
* @example Custom predicate
|
|
826
|
-
* api.filterEvents(events, e => e.markets.length > 10)
|
|
827
|
-
*/
|
|
828
|
-
filterEvents(events, criteria) {
|
|
829
|
-
// Handle predicate function
|
|
830
|
-
if (typeof criteria === 'function') {
|
|
831
|
-
return events.filter(criteria);
|
|
832
|
-
}
|
|
833
|
-
// Handle simple string search
|
|
834
|
-
if (typeof criteria === 'string') {
|
|
835
|
-
const lowerQuery = criteria.toLowerCase();
|
|
836
|
-
return events.filter(e => e.title.toLowerCase().includes(lowerQuery));
|
|
837
|
-
}
|
|
838
|
-
// Handle criteria object
|
|
839
|
-
return events.filter(event => {
|
|
840
|
-
// Text search
|
|
841
|
-
if (criteria.text) {
|
|
842
|
-
const lowerQuery = criteria.text.toLowerCase();
|
|
843
|
-
const searchIn = criteria.searchIn || ['title'];
|
|
844
|
-
let textMatch = false;
|
|
845
|
-
for (const field of searchIn) {
|
|
846
|
-
if (field === 'title' && event.title?.toLowerCase().includes(lowerQuery)) {
|
|
847
|
-
textMatch = true;
|
|
848
|
-
break;
|
|
849
|
-
}
|
|
850
|
-
if (field === 'description' && event.description?.toLowerCase().includes(lowerQuery)) {
|
|
851
|
-
textMatch = true;
|
|
852
|
-
break;
|
|
853
|
-
}
|
|
854
|
-
if (field === 'category' && event.category?.toLowerCase().includes(lowerQuery)) {
|
|
855
|
-
textMatch = true;
|
|
856
|
-
break;
|
|
857
|
-
}
|
|
858
|
-
if (field === 'tags' && event.tags?.some(tag => tag.toLowerCase().includes(lowerQuery))) {
|
|
859
|
-
textMatch = true;
|
|
860
|
-
break;
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
if (!textMatch)
|
|
864
|
-
return false;
|
|
865
|
-
}
|
|
866
|
-
// Category filter
|
|
867
|
-
if (criteria.category && event.category !== criteria.category) {
|
|
868
|
-
return false;
|
|
869
|
-
}
|
|
870
|
-
// Tags filter (match ANY of the provided tags)
|
|
871
|
-
if (criteria.tags && criteria.tags.length > 0) {
|
|
872
|
-
const hasMatchingTag = criteria.tags.some(tag => event.tags?.some(eventTag => eventTag.toLowerCase() === tag.toLowerCase()));
|
|
873
|
-
if (!hasMatchingTag)
|
|
874
|
-
return false;
|
|
875
|
-
}
|
|
876
|
-
// Market count filter
|
|
877
|
-
if (criteria.marketCount) {
|
|
878
|
-
const count = event.markets.length;
|
|
879
|
-
if (criteria.marketCount.min !== undefined && count < criteria.marketCount.min) {
|
|
880
|
-
return false;
|
|
881
|
-
}
|
|
882
|
-
if (criteria.marketCount.max !== undefined && count > criteria.marketCount.max) {
|
|
883
|
-
return false;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
// Total volume filter
|
|
887
|
-
if (criteria.totalVolume) {
|
|
888
|
-
const totalVolume = event.markets.reduce((sum, m) => sum + m.volume24h, 0);
|
|
889
|
-
if (criteria.totalVolume.min !== undefined && totalVolume < criteria.totalVolume.min) {
|
|
890
|
-
return false;
|
|
891
|
-
}
|
|
892
|
-
if (criteria.totalVolume.max !== undefined && totalVolume > criteria.totalVolume.max) {
|
|
893
|
-
return false;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
return true;
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
exports.Exchange = Exchange;
|
|
901
|
-
class Polymarket extends Exchange {
|
|
902
|
-
constructor(options = {}) {
|
|
903
|
-
// Default to gnosis-safe signature type
|
|
904
|
-
const polyOptions = {
|
|
905
|
-
signatureType: 'gnosis-safe',
|
|
906
|
-
...options
|
|
907
|
-
};
|
|
908
|
-
super("polymarket", polyOptions);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
exports.Polymarket = Polymarket;
|
|
912
|
-
/**
|
|
913
|
-
* Kalshi exchange client.
|
|
914
|
-
*
|
|
915
|
-
* @example
|
|
916
|
-
* ```typescript
|
|
917
|
-
* // Public data (no auth)
|
|
918
|
-
* const kalshi = new Kalshi();
|
|
919
|
-
* const markets = await kalshi.fetchMarkets({ query: "Fed rates" });
|
|
920
|
-
*
|
|
921
|
-
* // Trading (requires auth)
|
|
922
|
-
* const kalshi = new Kalshi({
|
|
923
|
-
* apiKey: process.env.KALSHI_API_KEY,
|
|
924
|
-
* privateKey: process.env.KALSHI_PRIVATE_KEY
|
|
925
|
-
* });
|
|
926
|
-
* const balance = await kalshi.fetchBalance();
|
|
927
|
-
* ```
|
|
928
|
-
*/
|
|
929
|
-
class Kalshi extends Exchange {
|
|
930
|
-
constructor(options = {}) {
|
|
931
|
-
super("kalshi", options);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
exports.Kalshi = Kalshi;
|
|
935
|
-
/**
|
|
936
|
-
* Limitless exchange client.
|
|
937
|
-
*
|
|
938
|
-
* @example
|
|
939
|
-
* ```typescript
|
|
940
|
-
* // Public data (no auth)
|
|
941
|
-
* const limitless = new Limitless();
|
|
942
|
-
* const markets = await limitless.fetchMarkets({ query: "Trump" });
|
|
943
|
-
*
|
|
944
|
-
* // Trading (requires auth)
|
|
945
|
-
* const limitless = new Limitless({
|
|
946
|
-
* apiKey: process.env.LIMITLESS_API_KEY,
|
|
947
|
-
* privateKey: process.env.LIMITLESS_PRIVATE_KEY
|
|
948
|
-
* });
|
|
949
|
-
* const balance = await limitless.fetchBalance();
|
|
950
|
-
* ```
|
|
951
|
-
*/
|
|
952
|
-
class Limitless extends Exchange {
|
|
953
|
-
constructor(options = {}) {
|
|
954
|
-
super("limitless", options);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
exports.Limitless = Limitless;
|
package/pmxt/models.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Data models for PMXT TypeScript SDK.
|
|
4
|
-
*
|
|
5
|
-
* These are clean TypeScript interfaces that provide a user-friendly API.
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.MarketList = void 0;
|
|
9
|
-
/**
|
|
10
|
-
* A list of UnifiedMarket objects with a convenience match() method.
|
|
11
|
-
* Extends Array so all standard array operations work unchanged.
|
|
12
|
-
*/
|
|
13
|
-
class MarketList extends Array {
|
|
14
|
-
/**
|
|
15
|
-
* Find a single market by case-insensitive substring match.
|
|
16
|
-
*
|
|
17
|
-
* @param query - Substring to search for
|
|
18
|
-
* @param searchIn - Fields to search in (default: ['title'])
|
|
19
|
-
* @returns The matching UnifiedMarket
|
|
20
|
-
* @throws Error if zero or multiple markets match
|
|
21
|
-
*/
|
|
22
|
-
match(query, searchIn) {
|
|
23
|
-
const fields = searchIn || ['title'];
|
|
24
|
-
const lowerQuery = query.toLowerCase();
|
|
25
|
-
const matches = [];
|
|
26
|
-
for (const m of this) {
|
|
27
|
-
for (const field of fields) {
|
|
28
|
-
if (field === 'title' && m.title?.toLowerCase().includes(lowerQuery)) {
|
|
29
|
-
matches.push(m);
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
if (field === 'description' && m.description?.toLowerCase().includes(lowerQuery)) {
|
|
33
|
-
matches.push(m);
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
if (field === 'category' && m.category?.toLowerCase().includes(lowerQuery)) {
|
|
37
|
-
matches.push(m);
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
if (field === 'tags' && m.tags?.some(t => t.toLowerCase().includes(lowerQuery))) {
|
|
41
|
-
matches.push(m);
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
if (field === 'outcomes' && m.outcomes?.some(o => o.label.toLowerCase().includes(lowerQuery))) {
|
|
45
|
-
matches.push(m);
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (matches.length === 0) {
|
|
51
|
-
throw new Error(`No markets matching '${query}'`);
|
|
52
|
-
}
|
|
53
|
-
if (matches.length > 1) {
|
|
54
|
-
const titles = matches.map(m => m.title);
|
|
55
|
-
throw new Error(`Multiple markets matching '${query}': ${JSON.stringify(titles)}`);
|
|
56
|
-
}
|
|
57
|
-
return matches[0];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
exports.MarketList = MarketList;
|
package/pmxt/server-manager.js
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Server manager for PMXT TypeScript SDK.
|
|
4
|
-
*
|
|
5
|
-
* Handles automatic server startup and health checks.
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.ServerManager = void 0;
|
|
9
|
-
const index_js_1 = require("../generated/src/index.js");
|
|
10
|
-
const fs_1 = require("fs");
|
|
11
|
-
const os_1 = require("os");
|
|
12
|
-
const path_1 = require("path");
|
|
13
|
-
class ServerManager {
|
|
14
|
-
constructor(options = {}) {
|
|
15
|
-
this.baseUrl = options.baseUrl || `http://localhost:${ServerManager.DEFAULT_PORT}`;
|
|
16
|
-
this.maxRetries = options.maxRetries || 30;
|
|
17
|
-
this.retryDelayMs = options.retryDelayMs || 1000;
|
|
18
|
-
this.lockPath = (0, path_1.join)((0, os_1.homedir)(), '.pmxt', 'server.lock');
|
|
19
|
-
const config = new index_js_1.Configuration({ basePath: this.baseUrl });
|
|
20
|
-
this.api = new index_js_1.DefaultApi(config);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Read server information from lock file.
|
|
24
|
-
*/
|
|
25
|
-
getServerInfo() {
|
|
26
|
-
try {
|
|
27
|
-
if (!(0, fs_1.existsSync)(this.lockPath)) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
const content = (0, fs_1.readFileSync)(this.lockPath, 'utf-8');
|
|
31
|
-
return JSON.parse(content);
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Get the actual port the server is running on.
|
|
39
|
-
*
|
|
40
|
-
* This reads the lock file to determine the actual port,
|
|
41
|
-
* which may differ from the default if the default port was busy.
|
|
42
|
-
*/
|
|
43
|
-
getRunningPort() {
|
|
44
|
-
const info = this.getServerInfo();
|
|
45
|
-
return info?.port || ServerManager.DEFAULT_PORT;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Get the access token from the lock file.
|
|
49
|
-
*/
|
|
50
|
-
getAccessToken() {
|
|
51
|
-
const info = this.getServerInfo();
|
|
52
|
-
return info?.accessToken;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Check if the server is running.
|
|
56
|
-
*/
|
|
57
|
-
async isServerRunning() {
|
|
58
|
-
// Read lock file to get current port
|
|
59
|
-
const port = this.getRunningPort();
|
|
60
|
-
try {
|
|
61
|
-
// Use native fetch to check health on the actual running port
|
|
62
|
-
// This avoids issues where this.api is configured with the wrong port
|
|
63
|
-
const response = await fetch(`http://localhost:${port}/health`);
|
|
64
|
-
if (response.ok) {
|
|
65
|
-
const data = await response.json();
|
|
66
|
-
return data.status === "ok";
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Wait for the server to be ready.
|
|
76
|
-
*/
|
|
77
|
-
async waitForServer() {
|
|
78
|
-
for (let i = 0; i < this.maxRetries; i++) {
|
|
79
|
-
if (await this.isServerRunning()) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
await new Promise((resolve) => setTimeout(resolve, this.retryDelayMs));
|
|
83
|
-
}
|
|
84
|
-
throw new Error(`Server did not start within ${(this.maxRetries * this.retryDelayMs) / 1000}s`);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Ensure the server is running, starting it if necessary.
|
|
88
|
-
*/
|
|
89
|
-
async ensureServerRunning() {
|
|
90
|
-
// Check for force restart
|
|
91
|
-
if (process.env.PMXT_ALWAYS_RESTART === '1') {
|
|
92
|
-
await this.killOldServer();
|
|
93
|
-
}
|
|
94
|
-
// Check if already running and version matches
|
|
95
|
-
if (await this.isServerRunning()) {
|
|
96
|
-
if (await this.isVersionMismatch()) {
|
|
97
|
-
await this.killOldServer();
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// Locate pmxt-ensure-server
|
|
104
|
-
let launcherPath = 'pmxt-ensure-server'; // Default to PATH
|
|
105
|
-
try {
|
|
106
|
-
// Try to resolve from pmxt-core dependency
|
|
107
|
-
// For CommonJS build (which is primary), we can use require directly
|
|
108
|
-
// For ESM build, this will be transpiled appropriately
|
|
109
|
-
const corePackageJson = require.resolve('pmxt-core/package.json');
|
|
110
|
-
const coreDir = (0, path_1.dirname)(corePackageJson);
|
|
111
|
-
const binPath = (0, path_1.join)(coreDir, 'bin', 'pmxt-ensure-server');
|
|
112
|
-
if ((0, fs_1.existsSync)(binPath)) {
|
|
113
|
-
launcherPath = binPath;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
// If resolution fails, fall back to PATH
|
|
118
|
-
// This could happen in dev environments where pmxt-core is globally installed
|
|
119
|
-
}
|
|
120
|
-
// Try to start the server using pmxt-ensure-server
|
|
121
|
-
const { spawn } = await Promise.resolve().then(() => require("child_process"));
|
|
122
|
-
try {
|
|
123
|
-
const proc = spawn(launcherPath, [], {
|
|
124
|
-
detached: true,
|
|
125
|
-
stdio: "ignore",
|
|
126
|
-
});
|
|
127
|
-
proc.unref();
|
|
128
|
-
// Wait for server to be ready
|
|
129
|
-
await this.waitForServer();
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
throw new Error(`Failed to start PMXT server: ${error}\n\n` +
|
|
133
|
-
`Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
|
|
134
|
-
`Or start the server manually: pmxt-server`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
async isVersionMismatch() {
|
|
138
|
-
const info = this.getServerInfo();
|
|
139
|
-
if (!info || !info.version) {
|
|
140
|
-
return true; // Old server without version
|
|
141
|
-
}
|
|
142
|
-
try {
|
|
143
|
-
// 1. Try to find package.json relative to the installed location (Production)
|
|
144
|
-
let corePackageJsonPath;
|
|
145
|
-
try {
|
|
146
|
-
corePackageJsonPath = require.resolve('pmxt-core/package.json');
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
// 2. Try dev path (Monorepo)
|
|
150
|
-
const devPath = (0, path_1.join)((0, path_1.dirname)(__dirname), '../../core/package.json');
|
|
151
|
-
if ((0, fs_1.existsSync)(devPath)) {
|
|
152
|
-
corePackageJsonPath = devPath;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (corePackageJsonPath && (0, fs_1.existsSync)(corePackageJsonPath)) {
|
|
156
|
-
const content = (0, fs_1.readFileSync)(corePackageJsonPath, 'utf-8');
|
|
157
|
-
const pkg = JSON.parse(content);
|
|
158
|
-
// Check if running version starts with package version
|
|
159
|
-
// (Server version might have extra hash in dev mode)
|
|
160
|
-
if (pkg.version && !info.version.startsWith(pkg.version)) {
|
|
161
|
-
return true;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
// Ignore errors
|
|
167
|
-
}
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Stop the currently running server.
|
|
172
|
-
*/
|
|
173
|
-
async stop() {
|
|
174
|
-
await this.killOldServer();
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Restart the server.
|
|
178
|
-
*/
|
|
179
|
-
async restart() {
|
|
180
|
-
await this.stop();
|
|
181
|
-
await this.ensureServerRunning();
|
|
182
|
-
}
|
|
183
|
-
async killOldServer() {
|
|
184
|
-
const info = this.getServerInfo();
|
|
185
|
-
if (info && info.pid) {
|
|
186
|
-
try {
|
|
187
|
-
process.kill(info.pid, 'SIGTERM');
|
|
188
|
-
// Brief wait
|
|
189
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
190
|
-
}
|
|
191
|
-
catch {
|
|
192
|
-
// Ignore
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
// Remove lock file (best effort)
|
|
196
|
-
try {
|
|
197
|
-
const { unlinkSync } = await Promise.resolve().then(() => require('fs'));
|
|
198
|
-
unlinkSync(this.lockPath);
|
|
199
|
-
}
|
|
200
|
-
catch { }
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
exports.ServerManager = ServerManager;
|
|
204
|
-
ServerManager.DEFAULT_PORT = 3847;
|