pmxt-core 2.43.17 → 2.43.19
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/exchanges/baozi/utils.js +4 -4
- package/dist/exchanges/gemini-titan/normalizer.js +1 -1
- package/dist/exchanges/gemini-titan/types.d.ts +2 -1
- package/dist/exchanges/hyperliquid/fetcher.d.ts +4 -1
- package/dist/exchanges/hyperliquid/fetcher.js +2 -2
- package/dist/exchanges/hyperliquid/normalizer.js +4 -3
- package/dist/exchanges/hyperliquid/utils.d.ts +8 -0
- package/dist/exchanges/hyperliquid/utils.js +11 -0
- package/dist/exchanges/kalshi/api.d.ts +9 -1
- package/dist/exchanges/kalshi/api.js +11 -1
- package/dist/exchanges/kalshi/fetcher.d.ts +0 -1
- package/dist/exchanges/kalshi/normalizer.js +4 -4
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/client.js +2 -2
- package/dist/exchanges/limitless/config.d.ts +1 -0
- package/dist/exchanges/limitless/config.js +4 -0
- package/dist/exchanges/limitless/index.js +35 -4
- package/dist/exchanges/metaculus/errors.js +5 -0
- package/dist/exchanges/metaculus/fetchEvents.js +1 -1
- package/dist/exchanges/metaculus/fetchMarkets.js +1 -1
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/fetcher.d.ts +1 -1
- package/dist/exchanges/myriad/normalizer.js +1 -1
- package/dist/exchanges/myriad/utils.js +1 -1
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +2 -2
- package/dist/exchanges/opinion/config.d.ts +3 -3
- package/dist/exchanges/opinion/config.js +3 -3
- package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
- package/dist/exchanges/polymarket/api-clob.js +1 -1
- package/dist/exchanges/polymarket/api-data.d.ts +1 -1
- package/dist/exchanges/polymarket/api-data.js +1 -1
- package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
- package/dist/exchanges/polymarket/api-gamma.js +1 -1
- package/dist/exchanges/polymarket/utils.js +1 -1
- package/dist/exchanges/polymarket_us/normalizer.js +7 -4
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/websocket.js +12 -2
- package/dist/router/client.js +1 -1
- package/dist/server/feed-routes.js +28 -14
- package/dist/server/ws-handler.js +5 -4
- package/dist/subscriber/external/goldsky.js +17 -5
- package/package.json +3 -3
|
@@ -324,8 +324,8 @@ function mapBooleanToUnified(market, pubkey) {
|
|
|
324
324
|
volume: totalPoolSol,
|
|
325
325
|
liquidity: totalPoolSol,
|
|
326
326
|
url: `https://baozi.bet/market/${pubkey}`,
|
|
327
|
-
category:
|
|
328
|
-
tags: [layerName(market.layer)
|
|
327
|
+
category: undefined,
|
|
328
|
+
tags: [`tier:${layerName(market.layer)}`, 'solana', 'pari-mutuel'],
|
|
329
329
|
};
|
|
330
330
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
331
331
|
return um;
|
|
@@ -359,8 +359,8 @@ function mapRaceToUnified(market, pubkey) {
|
|
|
359
359
|
volume: totalPoolSol,
|
|
360
360
|
liquidity: totalPoolSol,
|
|
361
361
|
url: `https://baozi.bet/market/${pubkey}`,
|
|
362
|
-
category:
|
|
363
|
-
tags: [layerName(market.layer)
|
|
362
|
+
category: undefined,
|
|
363
|
+
tags: [`tier:${layerName(market.layer)}`, 'solana', 'pari-mutuel', 'race'],
|
|
364
364
|
};
|
|
365
365
|
// For 2-outcome races, add binary convenience getters
|
|
366
366
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
@@ -90,7 +90,7 @@ class GeminiNormalizer {
|
|
|
90
90
|
description: raw.description ?? '',
|
|
91
91
|
slug: raw.slug ?? raw.ticker.toLowerCase(),
|
|
92
92
|
markets: [],
|
|
93
|
-
volume24h: raw.
|
|
93
|
+
volume24h: raw.volume24h ? parseFloat(raw.volume24h) : 0,
|
|
94
94
|
url: buildExchangeUrl(raw.ticker),
|
|
95
95
|
category: raw.category,
|
|
96
96
|
tags: raw.tags ?? [],
|
|
@@ -43,7 +43,7 @@ export interface GeminiRawEvent {
|
|
|
43
43
|
imageUrl?: string;
|
|
44
44
|
type: string;
|
|
45
45
|
category?: string;
|
|
46
|
-
series?: string;
|
|
46
|
+
series?: Record<string, any> | null;
|
|
47
47
|
ticker: string;
|
|
48
48
|
status: string;
|
|
49
49
|
resolvedAt?: string | null;
|
|
@@ -52,6 +52,7 @@ export interface GeminiRawEvent {
|
|
|
52
52
|
expiryDate?: string;
|
|
53
53
|
contracts: GeminiRawContract[];
|
|
54
54
|
volume?: string;
|
|
55
|
+
volume24h?: string;
|
|
55
56
|
liquidity?: string;
|
|
56
57
|
tags?: string[];
|
|
57
58
|
subcategory?: Record<string, unknown>;
|
|
@@ -9,6 +9,7 @@ export interface HyperliquidRawOutcome {
|
|
|
9
9
|
name: string;
|
|
10
10
|
description: string;
|
|
11
11
|
sideSpecs: HyperliquidRawSideSpec[];
|
|
12
|
+
quoteToken: string;
|
|
12
13
|
}
|
|
13
14
|
export interface HyperliquidRawQuestion {
|
|
14
15
|
question: number;
|
|
@@ -52,6 +53,7 @@ export interface HyperliquidRawTrade {
|
|
|
52
53
|
hash: string;
|
|
53
54
|
time: number;
|
|
54
55
|
tid: number;
|
|
56
|
+
users: string[];
|
|
55
57
|
}
|
|
56
58
|
export interface HyperliquidRawMid {
|
|
57
59
|
[coin: string]: string;
|
|
@@ -71,6 +73,7 @@ export interface HyperliquidRawFill {
|
|
|
71
73
|
fee: string;
|
|
72
74
|
tid: number;
|
|
73
75
|
feeToken: string;
|
|
76
|
+
builderFee?: string;
|
|
74
77
|
}
|
|
75
78
|
export interface HyperliquidRawOpenOrder {
|
|
76
79
|
coin: string;
|
|
@@ -79,7 +82,7 @@ export interface HyperliquidRawOpenOrder {
|
|
|
79
82
|
side: string;
|
|
80
83
|
sz: string;
|
|
81
84
|
timestamp: number;
|
|
82
|
-
origSz
|
|
85
|
+
origSz?: string;
|
|
83
86
|
cloid?: string;
|
|
84
87
|
}
|
|
85
88
|
export interface HyperliquidRawPosition {
|
|
@@ -130,8 +130,8 @@ class HyperliquidFetcher {
|
|
|
130
130
|
return this.postInfo({ type: 'allMids' });
|
|
131
131
|
}
|
|
132
132
|
getMidForOutcome(mids, outcomeId) {
|
|
133
|
-
const
|
|
134
|
-
return mids[
|
|
133
|
+
const midKey = (0, utils_1.toMidKey)(outcomeId);
|
|
134
|
+
return mids[midKey];
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
exports.HyperliquidFetcher = HyperliquidFetcher;
|
|
@@ -209,8 +209,8 @@ class HyperliquidNormalizer {
|
|
|
209
209
|
const outcome = outcomeMeta.outcomes.find(o => o.outcome === outcomeId);
|
|
210
210
|
if (!outcome)
|
|
211
211
|
continue;
|
|
212
|
-
const
|
|
213
|
-
const midPrice = mids[
|
|
212
|
+
const midKey = (0, utils_1.toMidKey)(outcomeId);
|
|
213
|
+
const midPrice = mids[midKey];
|
|
214
214
|
const market = this.normalizeMarket({
|
|
215
215
|
outcome,
|
|
216
216
|
question: raw,
|
|
@@ -272,8 +272,9 @@ class HyperliquidNormalizer {
|
|
|
272
272
|
};
|
|
273
273
|
}
|
|
274
274
|
normalizeOpenOrder(raw) {
|
|
275
|
-
const origSz = parseFloat(raw.origSz);
|
|
276
275
|
const currentSz = parseFloat(raw.sz);
|
|
276
|
+
// origSz is only available from frontendOpenOrders; fall back to sz
|
|
277
|
+
const origSz = raw.origSz !== undefined ? parseFloat(raw.origSz) : currentSz;
|
|
277
278
|
return {
|
|
278
279
|
id: String(raw.oid),
|
|
279
280
|
marketId: this.coinToMarketId(raw.coin),
|
|
@@ -25,6 +25,14 @@ export declare function fromCoinEncoding(encoding: number): {
|
|
|
25
25
|
outcomeId: number;
|
|
26
26
|
side: 'yes' | 'no';
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Convert an outcome ID to the allMids lookup key.
|
|
30
|
+
*
|
|
31
|
+
* The allMids endpoint keys prediction-market outcomes as "@{outcomeId}"
|
|
32
|
+
* (e.g. "@8"), which is distinct from the "#encoding" coin notation used
|
|
33
|
+
* for orders and positions.
|
|
34
|
+
*/
|
|
35
|
+
export declare function toMidKey(outcomeId: number): string;
|
|
28
36
|
/**
|
|
29
37
|
* Build a unique market ID string from an outcome.
|
|
30
38
|
* We use "hl-outcome-{outcomeId}" as the market ID.
|
|
@@ -4,6 +4,7 @@ exports.encodeAssetId = encodeAssetId;
|
|
|
4
4
|
exports.decodeAssetId = decodeAssetId;
|
|
5
5
|
exports.toCoinNotation = toCoinNotation;
|
|
6
6
|
exports.fromCoinEncoding = fromCoinEncoding;
|
|
7
|
+
exports.toMidKey = toMidKey;
|
|
7
8
|
exports.toMarketId = toMarketId;
|
|
8
9
|
exports.fromMarketId = fromMarketId;
|
|
9
10
|
exports.toOutcomeId = toOutcomeId;
|
|
@@ -50,6 +51,16 @@ function fromCoinEncoding(encoding) {
|
|
|
50
51
|
side: sideValue === config_1.SIDE_YES ? 'yes' : 'no',
|
|
51
52
|
};
|
|
52
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Convert an outcome ID to the allMids lookup key.
|
|
56
|
+
*
|
|
57
|
+
* The allMids endpoint keys prediction-market outcomes as "@{outcomeId}"
|
|
58
|
+
* (e.g. "@8"), which is distinct from the "#encoding" coin notation used
|
|
59
|
+
* for orders and positions.
|
|
60
|
+
*/
|
|
61
|
+
function toMidKey(outcomeId) {
|
|
62
|
+
return `@${outcomeId}`;
|
|
63
|
+
}
|
|
53
64
|
/**
|
|
54
65
|
* Build a unique market ID string from an outcome.
|
|
55
66
|
* We use "hl-outcome-{outcomeId}" as the market ID.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-24T16:33:23.368Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const kalshiApiSpec: {
|
|
@@ -425,6 +425,14 @@ export declare const kalshiApiSpec: {
|
|
|
425
425
|
kalshiAccessSignature: never[];
|
|
426
426
|
kalshiAccessTimestamp: never[];
|
|
427
427
|
}[];
|
|
428
|
+
parameters: {
|
|
429
|
+
name: string;
|
|
430
|
+
in: string;
|
|
431
|
+
required: boolean;
|
|
432
|
+
schema: {
|
|
433
|
+
type: string;
|
|
434
|
+
};
|
|
435
|
+
}[];
|
|
428
436
|
};
|
|
429
437
|
};
|
|
430
438
|
"/portfolio/subaccounts": {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.kalshiApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-24T16:33:23.368Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -647,6 +647,16 @@ exports.kalshiApiSpec = {
|
|
|
647
647
|
"kalshiAccessSignature": [],
|
|
648
648
|
"kalshiAccessTimestamp": []
|
|
649
649
|
}
|
|
650
|
+
],
|
|
651
|
+
"parameters": [
|
|
652
|
+
{
|
|
653
|
+
"name": "subaccount",
|
|
654
|
+
"in": "query",
|
|
655
|
+
"required": false,
|
|
656
|
+
"schema": {
|
|
657
|
+
"type": "integer"
|
|
658
|
+
}
|
|
659
|
+
}
|
|
650
660
|
]
|
|
651
661
|
}
|
|
652
662
|
},
|
|
@@ -86,7 +86,7 @@ class KalshiNormalizer {
|
|
|
86
86
|
resolutionDate: new Date(market.expiration_time),
|
|
87
87
|
volume24h: parseFloat(market.volume_24h_fp ?? '') || Number(market.volume_24h || market.volume || 0),
|
|
88
88
|
volume: parseFloat(market.volume_fp ?? '') || Number(market.volume || 0),
|
|
89
|
-
liquidity:
|
|
89
|
+
liquidity: parseFloat(String(market.liquidity_dollars || market.liquidity || '0')) || 0,
|
|
90
90
|
openInterest: parseFloat(market.open_interest_fp ?? '') || Number(market.open_interest || 0),
|
|
91
91
|
url: `https://kalshi.com/events/${event.event_ticker}`,
|
|
92
92
|
category: event.category,
|
|
@@ -103,7 +103,7 @@ class KalshiNormalizer {
|
|
|
103
103
|
return {
|
|
104
104
|
id: raw.event_ticker,
|
|
105
105
|
title: raw.title,
|
|
106
|
-
description:
|
|
106
|
+
description: this.deriveEventDescription(raw.markets || []),
|
|
107
107
|
slug: raw.event_ticker,
|
|
108
108
|
markets,
|
|
109
109
|
volume24h: markets.reduce((sum, m) => sum + m.volume24h, 0),
|
|
@@ -111,7 +111,7 @@ class KalshiNormalizer {
|
|
|
111
111
|
? markets.reduce((sum, m) => sum + (m.volume ?? 0), 0)
|
|
112
112
|
: undefined,
|
|
113
113
|
url: `https://kalshi.com/events/${raw.event_ticker}`,
|
|
114
|
-
image: raw.image_url,
|
|
114
|
+
image: raw.image_url ?? undefined,
|
|
115
115
|
category: raw.category,
|
|
116
116
|
tags: raw.tags || [],
|
|
117
117
|
};
|
|
@@ -337,7 +337,7 @@ function eventVolume(event) {
|
|
|
337
337
|
return (event.markets || []).reduce((sum, m) => sum + (parseFloat(m.volume_fp ?? '') || Number(m.volume || 0)), 0);
|
|
338
338
|
}
|
|
339
339
|
function eventLiquidity(event) {
|
|
340
|
-
return (event.markets || []).reduce((sum, m) => sum + (parseFloat(m.open_interest_fp ?? '') ||
|
|
340
|
+
return (event.markets || []).reduce((sum, m) => sum + (parseFloat(m.open_interest_fp ?? '') || parseFloat(m.liquidity_dollars || m.open_interest || m.liquidity || '0') || 0), 0);
|
|
341
341
|
}
|
|
342
342
|
function eventNewest(event) {
|
|
343
343
|
const times = (event.markets || [])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-24T16:33:23.408Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const limitlessApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.limitlessApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-24T16:33:23.408Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.limitlessApiSpec = {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.LimitlessClient = void 0;
|
|
4
4
|
const sdk_1 = require("@limitless-exchange/sdk");
|
|
5
5
|
const ethers_1 = require("ethers");
|
|
6
|
+
const config_1 = require("./config");
|
|
6
7
|
const DEFAULT_LIMITLESS_API_URL = process.env.LIMITLESS_BASE_URL || 'https://api.limitless.exchange';
|
|
7
8
|
/**
|
|
8
9
|
* Wrapper client for Limitless Exchange using the official SDK.
|
|
@@ -298,8 +299,7 @@ class LimitlessClient {
|
|
|
298
299
|
// USDC on Base
|
|
299
300
|
const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
300
301
|
const ABI = ["function balanceOf(address) view returns (uint256)", "function decimals() view returns (uint8)"];
|
|
301
|
-
|
|
302
|
-
const provider = new ethers_1.providers.StaticJsonRpcProvider('https://mainnet.base.org', {
|
|
302
|
+
const provider = new ethers_1.providers.StaticJsonRpcProvider(config_1.LIMITLESS_RPC_URL, {
|
|
303
303
|
chainId: 8453,
|
|
304
304
|
name: 'base',
|
|
305
305
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LIMITLESS_RPC_URL: string;
|
|
@@ -12,6 +12,7 @@ const openapi_1 = require("../../utils/openapi");
|
|
|
12
12
|
const api_1 = require("./api");
|
|
13
13
|
const auth_1 = require("./auth");
|
|
14
14
|
const client_1 = require("./client");
|
|
15
|
+
const config_1 = require("./config");
|
|
15
16
|
const errors_2 = require("./errors");
|
|
16
17
|
const fetcher_1 = require("./fetcher");
|
|
17
18
|
const normalizer_1 = require("./normalizer");
|
|
@@ -152,7 +153,13 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
152
153
|
}
|
|
153
154
|
async fetchOHLCV(outcomeId, params) {
|
|
154
155
|
const slug = await this.resolveSlug(outcomeId);
|
|
156
|
+
if (!this.fetcher.fetchRawOHLCV) {
|
|
157
|
+
throw new Error('fetchRawOHLCV is not implemented for this exchange');
|
|
158
|
+
}
|
|
155
159
|
const rawPrices = await this.fetcher.fetchRawOHLCV(slug, params);
|
|
160
|
+
if (!this.normalizer.normalizeOHLCV) {
|
|
161
|
+
throw new Error('normalizeOHLCV is not implemented for this exchange');
|
|
162
|
+
}
|
|
156
163
|
return this.normalizer.normalizeOHLCV(rawPrices, params);
|
|
157
164
|
}
|
|
158
165
|
async fetchOrderBook(outcomeId, limit, params) {
|
|
@@ -160,7 +167,13 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
160
167
|
outcomeId = resolved.outcomeId;
|
|
161
168
|
params = resolved.params;
|
|
162
169
|
const slug = await this.resolveSlug(outcomeId);
|
|
170
|
+
if (!this.fetcher.fetchRawOrderBook) {
|
|
171
|
+
throw new Error('fetchRawOrderBook is not implemented for this exchange');
|
|
172
|
+
}
|
|
163
173
|
const rawOrderBook = await this.fetcher.fetchRawOrderBook(slug);
|
|
174
|
+
if (!this.normalizer.normalizeOrderBook) {
|
|
175
|
+
throw new Error('normalizeOrderBook is not implemented for this exchange');
|
|
176
|
+
}
|
|
164
177
|
const orderBook = this.normalizer.normalizeOrderBook(rawOrderBook, outcomeId);
|
|
165
178
|
// The Limitless API always returns the Yes-side order book regardless
|
|
166
179
|
// of which token is queried. If the caller asked for the No token,
|
|
@@ -198,13 +211,27 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
198
211
|
'It will be removed in v3.0.0. Please remove it from your code.');
|
|
199
212
|
}
|
|
200
213
|
const slug = await this.resolveSlug(outcomeId);
|
|
214
|
+
if (!this.fetcher.fetchRawTrades) {
|
|
215
|
+
throw new Error('fetchRawTrades is not implemented for this exchange');
|
|
216
|
+
}
|
|
201
217
|
const rawTrades = await this.fetcher.fetchRawTrades(slug, params);
|
|
202
|
-
|
|
218
|
+
if (!this.normalizer.normalizeTrade) {
|
|
219
|
+
throw new Error('normalizeTrade is not implemented for this exchange');
|
|
220
|
+
}
|
|
221
|
+
const normalizeTrade = this.normalizer.normalizeTrade;
|
|
222
|
+
return rawTrades.map((raw, i) => normalizeTrade(raw, i));
|
|
203
223
|
}
|
|
204
224
|
async fetchMyTrades(params) {
|
|
205
225
|
const auth = this.ensureAuth();
|
|
226
|
+
if (!this.fetcher.fetchRawMyTrades) {
|
|
227
|
+
throw new Error('fetchRawMyTrades is not implemented for this exchange');
|
|
228
|
+
}
|
|
206
229
|
const rawTrades = await this.fetcher.fetchRawMyTrades(params || {}, auth.getApiKey());
|
|
207
|
-
|
|
230
|
+
if (!this.normalizer.normalizeUserTrade) {
|
|
231
|
+
throw new Error('normalizeUserTrade is not implemented for this exchange');
|
|
232
|
+
}
|
|
233
|
+
const normalizeUserTrade = this.normalizer.normalizeUserTrade;
|
|
234
|
+
return rawTrades.map((raw, i) => normalizeUserTrade(raw, i));
|
|
208
235
|
}
|
|
209
236
|
// ------------------------------------------------------------------------
|
|
210
237
|
// Trading (kept in SDK class -- uses LimitlessClient)
|
|
@@ -364,7 +391,11 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
364
391
|
// Public endpoint -- no auth needed when an address is explicitly supplied.
|
|
365
392
|
const account = address ?? this.ensureAuth().getAddress();
|
|
366
393
|
const rawItems = await this.fetcher.fetchRawPositions(account);
|
|
367
|
-
|
|
394
|
+
if (!this.normalizer.normalizePosition) {
|
|
395
|
+
throw new Error('normalizePosition is not implemented for this exchange');
|
|
396
|
+
}
|
|
397
|
+
const normalizePosition = this.normalizer.normalizePosition;
|
|
398
|
+
return rawItems.map((raw) => normalizePosition(raw));
|
|
368
399
|
}
|
|
369
400
|
async fetchBalance(address) {
|
|
370
401
|
try {
|
|
@@ -464,7 +495,7 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
464
495
|
//
|
|
465
496
|
// Static network avoids ethers v5 auto-detect (eth_chainId), which can throw
|
|
466
497
|
// noNetwork / NETWORK_ERROR on flaky public RPCs (#92).
|
|
467
|
-
const provider = new ethers_1.providers.StaticJsonRpcProvider(
|
|
498
|
+
const provider = new ethers_1.providers.StaticJsonRpcProvider(config_1.LIMITLESS_RPC_URL, {
|
|
468
499
|
chainId: 8453,
|
|
469
500
|
name: 'base',
|
|
470
501
|
});
|
|
@@ -49,6 +49,11 @@ class MetaculusErrorMapper extends error_mapper_1.ErrorMapper {
|
|
|
49
49
|
if (lower.includes('authenticated') || lower.includes('api token') || lower.includes('log in')) {
|
|
50
50
|
return new errors_1.AuthenticationError('Metaculus API token required. Pass { apiToken: "..." } when constructing MetaculusExchange.', this.exchangeName);
|
|
51
51
|
}
|
|
52
|
+
// Feature-gated 403: API forecasting not enabled for the account
|
|
53
|
+
if (lower.includes('api_forecasting_not_enabled')) {
|
|
54
|
+
return new errors_1.PermissionDenied('Metaculus API forecasting is not enabled for your account. '
|
|
55
|
+
+ 'Visit your Metaculus account settings to enable it, or contact Metaculus support.', this.exchangeName);
|
|
56
|
+
}
|
|
52
57
|
return new errors_1.PermissionDenied('You do not have permission for this operation. '
|
|
53
58
|
+ 'Check your Metaculus account permissions and API token scope.', this.exchangeName);
|
|
54
59
|
}
|
|
@@ -31,7 +31,7 @@ async function fetchPostPages(callApi, apiParams, targetCount) {
|
|
|
31
31
|
const results = data.results ?? [];
|
|
32
32
|
if (results.length === 0)
|
|
33
33
|
break;
|
|
34
|
-
all
|
|
34
|
+
all.push(...results);
|
|
35
35
|
offset += results.length;
|
|
36
36
|
page++;
|
|
37
37
|
if (targetCount && all.length >= targetCount)
|
|
@@ -44,7 +44,7 @@ async function fetchPostPages(callApi, apiParams, targetCount) {
|
|
|
44
44
|
const results = data.results ?? [];
|
|
45
45
|
if (results.length === 0)
|
|
46
46
|
break;
|
|
47
|
-
all
|
|
47
|
+
all.push(...results);
|
|
48
48
|
offset += results.length;
|
|
49
49
|
page++;
|
|
50
50
|
// Early-exit when we have enough results (with buffer for filtering)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-24T16:33:23.421Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const myriadApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.myriadApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-24T16:33:23.421Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.myriadApiSpec = {
|
|
@@ -33,7 +33,7 @@ class MyriadNormalizer {
|
|
|
33
33
|
const status = typeof raw.state === 'string' ? (0, utils_1.mapMarketState)(raw.state) : undefined;
|
|
34
34
|
const um = {
|
|
35
35
|
marketId: `${raw.networkId}:${raw.id}`,
|
|
36
|
-
eventId: raw.
|
|
36
|
+
eventId: raw.eventId ? String(raw.eventId) : undefined,
|
|
37
37
|
title: raw.title || '',
|
|
38
38
|
description: raw.description || '',
|
|
39
39
|
outcomes,
|
|
@@ -65,7 +65,7 @@ function mapMarketToUnified(market) {
|
|
|
65
65
|
}));
|
|
66
66
|
const um = {
|
|
67
67
|
marketId: `${market.networkId}:${market.id}`,
|
|
68
|
-
eventId: market.
|
|
68
|
+
eventId: market.eventId ? String(market.eventId) : undefined,
|
|
69
69
|
title: market.title || '',
|
|
70
70
|
description: market.description || '',
|
|
71
71
|
outcomes,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-24T16:33:23.424Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const opinionApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.opinionApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-24T16:33:23.424Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.opinionApiSpec = {
|
|
@@ -14,7 +14,7 @@ exports.opinionApiSpec = {
|
|
|
14
14
|
},
|
|
15
15
|
"servers": [
|
|
16
16
|
{
|
|
17
|
-
"url": "https://
|
|
17
|
+
"url": "https://proxy.opinion.trade:8443/openapi"
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
20
|
"security": [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export declare const DEFAULT_OPINION_API_URL
|
|
2
|
-
export declare const OPINION_WS_URL
|
|
1
|
+
export declare const DEFAULT_OPINION_API_URL: string;
|
|
2
|
+
export declare const OPINION_WS_URL: string;
|
|
3
3
|
export declare const OPINION_MAX_PAGE_SIZE = 20;
|
|
4
4
|
export declare const OPINION_CHAIN_ID = 56;
|
|
5
|
-
export declare const OPINION_DEFAULT_RPC_URL
|
|
5
|
+
export declare const OPINION_DEFAULT_RPC_URL: string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OPINION_DEFAULT_RPC_URL = exports.OPINION_CHAIN_ID = exports.OPINION_MAX_PAGE_SIZE = exports.OPINION_WS_URL = exports.DEFAULT_OPINION_API_URL = void 0;
|
|
4
|
-
exports.DEFAULT_OPINION_API_URL = "https://
|
|
5
|
-
exports.OPINION_WS_URL = "wss://ws.opinion.trade";
|
|
4
|
+
exports.DEFAULT_OPINION_API_URL = process.env.OPINION_API_URL || "https://proxy.opinion.trade:8443/openapi";
|
|
5
|
+
exports.OPINION_WS_URL = process.env.OPINION_WS_URL || "wss://ws.opinion.trade";
|
|
6
6
|
exports.OPINION_MAX_PAGE_SIZE = 20;
|
|
7
7
|
// CLOB SDK chain constants
|
|
8
8
|
exports.OPINION_CHAIN_ID = 56;
|
|
9
|
-
exports.OPINION_DEFAULT_RPC_URL = 'https://bsc-dataseed.binance.org';
|
|
9
|
+
exports.OPINION_DEFAULT_RPC_URL = process.env.OPINION_RPC_URL || 'https://bsc-dataseed.binance.org';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-24T16:33:23.375Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const polymarketClobSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.polymarketClobSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-24T16:33:23.375Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketClobSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-24T16:33:23.391Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const polymarketDataSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.polymarketDataSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-24T16:33:23.391Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketDataSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-24T16:33:23.388Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const polymarketGammaSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.polymarketGammaSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-24T16:33:23.388Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketGammaSpec = {
|
|
@@ -97,7 +97,7 @@ function mapMarketToUnified(event, market, options = {}) {
|
|
|
97
97
|
description: market.description || event.description,
|
|
98
98
|
slug: typeof market.slug === 'string' && market.slug.length > 0 ? market.slug : undefined,
|
|
99
99
|
outcomes: outcomes,
|
|
100
|
-
resolutionDate: market.endDate ? new Date(market.endDate) : (market.
|
|
100
|
+
resolutionDate: market.endDate ? new Date(market.endDate) : (market.endDateIso ? new Date(market.endDateIso) : new Date()),
|
|
101
101
|
volume24h: Number(market.volume24hr || market.volume_24h || 0),
|
|
102
102
|
volume: Number(market.volume || 0),
|
|
103
103
|
liquidity: Number(market.liquidity || market.rewards?.liquidity || 0),
|
|
@@ -333,16 +333,19 @@ class PolymarketUSNormalizer {
|
|
|
333
333
|
// running average price; we derive it from total cost / size.
|
|
334
334
|
const totalCost = (0, price_1.fromAmount)(pos.cost);
|
|
335
335
|
const entryPrice = size > 0 ? Math.abs(totalCost) / size : 0;
|
|
336
|
+
// cashValue is the current mark-to-market value of the position.
|
|
337
|
+
// unrealizedPnL = cashValue - abs(cost).
|
|
338
|
+
const cashValue = (0, price_1.fromAmount)(pos.cashValue);
|
|
339
|
+
const unrealizedPnL = cashValue - Math.abs(totalCost);
|
|
340
|
+
const currentPrice = size > 0 ? cashValue / size : 0;
|
|
336
341
|
results.push({
|
|
337
342
|
marketId: slug,
|
|
338
343
|
outcomeId: isLong ? `${slug}:long` : `${slug}:short`,
|
|
339
344
|
outcomeLabel: isLong ? 'long' : 'short',
|
|
340
345
|
size,
|
|
341
346
|
entryPrice,
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
currentPrice: 0,
|
|
345
|
-
unrealizedPnL: 0,
|
|
347
|
+
currentPrice,
|
|
348
|
+
unrealizedPnL,
|
|
346
349
|
realizedPnL: (0, price_1.fromAmount)(pos.realized),
|
|
347
350
|
});
|
|
348
351
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-24T16:33:23.414Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const probableApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.probableApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-24T16:33:23.414Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.probableApiSpec = {
|
|
@@ -43,10 +43,20 @@ class ProbableWebSocket {
|
|
|
43
43
|
this.subscriptions.set(tokenId, sub);
|
|
44
44
|
}
|
|
45
45
|
// Return a promise that resolves on the next orderbook update
|
|
46
|
+
let resolver;
|
|
46
47
|
const dataPromise = new Promise((resolve, reject) => {
|
|
47
|
-
|
|
48
|
+
resolver = { resolve, reject };
|
|
49
|
+
this.getOrderBookQueue(tokenId).push(resolver);
|
|
50
|
+
});
|
|
51
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${tokenId}')`).finally(() => {
|
|
52
|
+
const queue = this.orderBookResolvers.get(tokenId);
|
|
53
|
+
if (queue) {
|
|
54
|
+
const idx = queue.indexOf(resolver);
|
|
55
|
+
if (idx !== -1) {
|
|
56
|
+
queue.splice(idx, 1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
48
59
|
});
|
|
49
|
-
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${tokenId}')`);
|
|
50
60
|
}
|
|
51
61
|
handleOrderBookUpdate(tokenId, data) {
|
|
52
62
|
const bids = (data.bids || []).map((b) => ({
|
package/dist/router/client.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.PmxtApiClient = void 0;
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const errors_1 = require("../errors");
|
|
9
|
-
const DEFAULT_BASE_URL = 'https://api.pmxt.dev';
|
|
9
|
+
const DEFAULT_BASE_URL = process.env.PMXT_API_URL || 'https://api.pmxt.dev';
|
|
10
10
|
class PmxtApiClient {
|
|
11
11
|
http;
|
|
12
12
|
constructor(apiKey, baseUrl) {
|
|
@@ -36,8 +36,8 @@ function createFeedRouter() {
|
|
|
36
36
|
router.get('/:feed/fetchTicker', async (req, res, next) => {
|
|
37
37
|
try {
|
|
38
38
|
const symbol = req.query.symbol;
|
|
39
|
-
if (
|
|
40
|
-
res.status(400).json({ success: false, error: 'Missing required parameter: symbol' });
|
|
39
|
+
if (typeof symbol !== 'string') {
|
|
40
|
+
res.status(400).json({ success: false, error: 'Missing required query parameter: symbol' });
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
const data = await req._feed.fetchTicker(symbol);
|
|
@@ -51,6 +51,10 @@ function createFeedRouter() {
|
|
|
51
51
|
router.get('/:feed/fetchTickers', async (req, res, next) => {
|
|
52
52
|
try {
|
|
53
53
|
const symbolsRaw = req.query.symbols;
|
|
54
|
+
if (symbolsRaw !== undefined && typeof symbolsRaw !== 'string') {
|
|
55
|
+
res.status(400).json({ success: false, error: 'Invalid query parameter: symbols must be a string' });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
54
58
|
const symbols = symbolsRaw ? symbolsRaw.split(',').map((s) => s.trim()) : undefined;
|
|
55
59
|
const data = await req._feed.fetchTickers(symbols);
|
|
56
60
|
res.json({ success: true, data });
|
|
@@ -63,11 +67,16 @@ function createFeedRouter() {
|
|
|
63
67
|
router.get('/:feed/fetchOHLCV', async (req, res, next) => {
|
|
64
68
|
try {
|
|
65
69
|
const symbol = req.query.symbol;
|
|
66
|
-
if (
|
|
67
|
-
res.status(400).json({ success: false, error: 'Missing required parameter: symbol' });
|
|
70
|
+
if (typeof symbol !== 'string') {
|
|
71
|
+
res.status(400).json({ success: false, error: 'Missing required query parameter: symbol' });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const timeframe = req.query.timeframe;
|
|
75
|
+
if (timeframe !== undefined && typeof timeframe !== 'string') {
|
|
76
|
+
res.status(400).json({ success: false, error: 'Invalid query parameter: timeframe must be a string' });
|
|
68
77
|
return;
|
|
69
78
|
}
|
|
70
|
-
const data = await req._feed.fetchOHLCV(symbol,
|
|
79
|
+
const data = await req._feed.fetchOHLCV(symbol, timeframe || '1h', req.query.since ? Number(req.query.since) : undefined, req.query.limit ? Number(req.query.limit) : undefined);
|
|
71
80
|
res.json({ success: true, data });
|
|
72
81
|
}
|
|
73
82
|
catch (error) {
|
|
@@ -83,8 +92,8 @@ function createFeedRouter() {
|
|
|
83
92
|
return;
|
|
84
93
|
}
|
|
85
94
|
const symbol = req.query.symbol;
|
|
86
|
-
if (
|
|
87
|
-
res.status(400).json({ success: false, error: 'Missing required parameter: symbol' });
|
|
95
|
+
if (typeof symbol !== 'string') {
|
|
96
|
+
res.status(400).json({ success: false, error: 'Missing required query parameter: symbol' });
|
|
88
97
|
return;
|
|
89
98
|
}
|
|
90
99
|
const data = await feed.fetchOrderBook(symbol, req.query.limit ? Number(req.query.limit) : undefined);
|
|
@@ -103,8 +112,8 @@ function createFeedRouter() {
|
|
|
103
112
|
return;
|
|
104
113
|
}
|
|
105
114
|
const feedName = req.query.feed;
|
|
106
|
-
if (
|
|
107
|
-
res.status(400).json({ success: false, error: 'Missing required parameter: feed' });
|
|
115
|
+
if (typeof feedName !== 'string') {
|
|
116
|
+
res.status(400).json({ success: false, error: 'Missing required query parameter: feed' });
|
|
108
117
|
return;
|
|
109
118
|
}
|
|
110
119
|
const data = await feed.fetchOracleRound({ feed: feedName });
|
|
@@ -123,8 +132,8 @@ function createFeedRouter() {
|
|
|
123
132
|
return;
|
|
124
133
|
}
|
|
125
134
|
const feedName = req.query.feed;
|
|
126
|
-
if (
|
|
127
|
-
res.status(400).json({ success: false, error: 'Missing required parameter: feed' });
|
|
135
|
+
if (typeof feedName !== 'string') {
|
|
136
|
+
res.status(400).json({ success: false, error: 'Missing required query parameter: feed' });
|
|
128
137
|
return;
|
|
129
138
|
}
|
|
130
139
|
const data = await feed.fetchOracleHistory({
|
|
@@ -146,15 +155,20 @@ function createFeedRouter() {
|
|
|
146
155
|
return;
|
|
147
156
|
}
|
|
148
157
|
const symbol = req.query.symbol;
|
|
149
|
-
if (
|
|
150
|
-
res.status(400).json({ success: false, error: 'Missing required parameter: symbol' });
|
|
158
|
+
if (typeof symbol !== 'string') {
|
|
159
|
+
res.status(400).json({ success: false, error: 'Missing required query parameter: symbol' });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const order = req.query.order;
|
|
163
|
+
if (order !== undefined && typeof order !== 'string') {
|
|
164
|
+
res.status(400).json({ success: false, error: 'Invalid query parameter: order must be a string' });
|
|
151
165
|
return;
|
|
152
166
|
}
|
|
153
167
|
const data = await feed.fetchHistoricalPrices(symbol, {
|
|
154
168
|
fromTimestamp: req.query.fromTimestamp ? Number(req.query.fromTimestamp) : undefined,
|
|
155
169
|
untilTimestamp: req.query.untilTimestamp ? Number(req.query.untilTimestamp) : undefined,
|
|
156
170
|
maxSize: req.query.maxSize ? Number(req.query.maxSize) : undefined,
|
|
157
|
-
order:
|
|
171
|
+
order: order,
|
|
158
172
|
});
|
|
159
173
|
res.json({ success: true, data });
|
|
160
174
|
}
|
|
@@ -158,8 +158,9 @@ function createWebSocketHandler(options = {}) {
|
|
|
158
158
|
const id = parsed.id;
|
|
159
159
|
const action = parsed.action;
|
|
160
160
|
const exchange = parsed.exchange;
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
const method = parsed.method;
|
|
162
|
+
if (!id || !action || !exchange || !method) {
|
|
163
|
+
sendError(ws, id, "Missing required fields: id, action, exchange, method");
|
|
163
164
|
return;
|
|
164
165
|
}
|
|
165
166
|
const exchangeName = exchange.toLowerCase();
|
|
@@ -168,7 +169,7 @@ function createWebSocketHandler(options = {}) {
|
|
|
168
169
|
id,
|
|
169
170
|
action: "subscribe",
|
|
170
171
|
exchange: exchangeName,
|
|
171
|
-
method
|
|
172
|
+
method,
|
|
172
173
|
args: parsed.args || [],
|
|
173
174
|
credentials: parsed.credentials,
|
|
174
175
|
};
|
|
@@ -179,7 +180,7 @@ function createWebSocketHandler(options = {}) {
|
|
|
179
180
|
id,
|
|
180
181
|
action: "unsubscribe",
|
|
181
182
|
exchange: exchangeName,
|
|
182
|
-
method
|
|
183
|
+
method,
|
|
183
184
|
args: parsed.args || [],
|
|
184
185
|
};
|
|
185
186
|
handleUnsubscribe(ws, state, msg, exchangeName);
|
|
@@ -6,7 +6,10 @@ const logger_1 = require("../../utils/logger");
|
|
|
6
6
|
// Polymarket endpoints
|
|
7
7
|
// ----------------------------------------------------------------------------
|
|
8
8
|
// Reference: https://docs.polymarket.com/market-data/subgraph
|
|
9
|
-
const POLYMARKET_TRADES_ENDPOINT =
|
|
9
|
+
const POLYMARKET_TRADES_ENDPOINT = process.env.POLYMARKET_GOLDSKY_URL ||
|
|
10
|
+
'https://api.goldsky.com/api/public/project_cl6mb8i9h0003e201j6li0diw/subgraphs/orderbook-subgraph/prod/gn';
|
|
11
|
+
const GOLDSKY_MAKER_TRADES_LIMIT = 5;
|
|
12
|
+
const GOLDSKY_TAKER_TRADES_LIMIT = 20;
|
|
10
13
|
// NOTE: orderBy must use `id` (primary key) on pnl-subgraph and positions-subgraph.
|
|
11
14
|
// Sorting by any unindexed column (e.g. amount, balance) causes a statement timeout.
|
|
12
15
|
// ----------------------------------------------------------------------------
|
|
@@ -27,7 +30,7 @@ const BUILD_POLYMARKET_TRADES_AS_MAKER_QUERY = (address, url) => ({
|
|
|
27
30
|
query GetPolymarketTradesMaker($address: Bytes!) {
|
|
28
31
|
orderFilledEvents(
|
|
29
32
|
where: { maker: $address }
|
|
30
|
-
first:
|
|
33
|
+
first: ${GOLDSKY_MAKER_TRADES_LIMIT}
|
|
31
34
|
orderBy: timestamp
|
|
32
35
|
orderDirection: desc
|
|
33
36
|
) {${TRADES_FIELDS}
|
|
@@ -42,7 +45,7 @@ const BUILD_POLYMARKET_TRADES_AS_TAKER_QUERY = (address, url) => ({
|
|
|
42
45
|
query GetPolymarketTradesTaker($address: Bytes!) {
|
|
43
46
|
orderFilledEvents(
|
|
44
47
|
where: { taker: $address }
|
|
45
|
-
first:
|
|
48
|
+
first: ${GOLDSKY_TAKER_TRADES_LIMIT}
|
|
46
49
|
orderBy: timestamp
|
|
47
50
|
orderDirection: desc
|
|
48
51
|
) {${TRADES_FIELDS}
|
|
@@ -277,12 +280,17 @@ class GoldSkySubscriber {
|
|
|
277
280
|
if (this.config.apiKey) {
|
|
278
281
|
headers['Authorization'] = `Bearer ${this.config.apiKey}`;
|
|
279
282
|
}
|
|
283
|
+
const timeoutSignal = AbortSignal.timeout(30_000);
|
|
284
|
+
const combinedController = new AbortController();
|
|
285
|
+
const onAbort = () => combinedController.abort();
|
|
286
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
287
|
+
timeoutSignal.addEventListener('abort', onAbort, { once: true });
|
|
280
288
|
try {
|
|
281
289
|
const res = await fetch(q.url, {
|
|
282
290
|
method: 'POST',
|
|
283
291
|
headers,
|
|
284
292
|
body: JSON.stringify({ query: q.query, variables: q.variables ?? {} }),
|
|
285
|
-
signal,
|
|
293
|
+
signal: combinedController.signal,
|
|
286
294
|
});
|
|
287
295
|
if (!res.ok) {
|
|
288
296
|
logger_1.logger.warn(`GoldSkySubscriber: HTTP ${res.status} from ${q.url}`);
|
|
@@ -296,11 +304,15 @@ class GoldSkySubscriber {
|
|
|
296
304
|
return json?.data ?? null;
|
|
297
305
|
}
|
|
298
306
|
catch (err) {
|
|
299
|
-
if (err?.name !== 'AbortError') {
|
|
307
|
+
if (err?.name !== 'AbortError' && err?.name !== 'TimeoutError') {
|
|
300
308
|
logger_1.logger.warn(`GoldSkySubscriber: Fetch failed for ${q.url}`, { error: String(err) });
|
|
301
309
|
}
|
|
302
310
|
return null;
|
|
303
311
|
}
|
|
312
|
+
finally {
|
|
313
|
+
signal.removeEventListener('abort', onAbort);
|
|
314
|
+
timeoutSignal.removeEventListener('abort', onAbort);
|
|
315
|
+
}
|
|
304
316
|
}
|
|
305
317
|
}
|
|
306
318
|
exports.GoldSkySubscriber = GoldSkySubscriber;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxt-core",
|
|
3
|
-
"version": "2.43.
|
|
3
|
+
"version": "2.43.19",
|
|
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.43.
|
|
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.43.
|
|
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.43.19,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.43.19,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
|
|
34
34
|
"fetch:openapi": "node scripts/fetch-openapi-specs.js",
|
|
35
35
|
"extract:jsdoc": "node ../scripts/extract-jsdoc.js",
|
|
36
36
|
"generate:docs": "npm run extract:jsdoc && node ../scripts/generate-api-docs.js",
|