pmxt-core 2.37.14 → 2.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaseExchange.d.ts +14 -14
- package/dist/BaseExchange.js +19 -19
- package/dist/exchanges/baozi/index.d.ts +2 -2
- package/dist/exchanges/baozi/index.js +5 -5
- package/dist/exchanges/hyperliquid/auth.d.ts +30 -0
- package/dist/exchanges/hyperliquid/auth.js +145 -0
- package/dist/exchanges/hyperliquid/config.d.ts +17 -0
- package/dist/exchanges/hyperliquid/config.js +28 -0
- package/dist/exchanges/hyperliquid/errors.d.ts +18 -0
- package/dist/exchanges/hyperliquid/errors.js +61 -0
- package/dist/exchanges/hyperliquid/fetcher.d.ts +140 -0
- package/dist/exchanges/hyperliquid/fetcher.js +137 -0
- package/dist/exchanges/hyperliquid/index.d.ts +31 -0
- package/dist/exchanges/hyperliquid/index.js +219 -0
- package/dist/exchanges/hyperliquid/normalizer.d.ts +18 -0
- package/dist/exchanges/hyperliquid/normalizer.js +339 -0
- package/dist/exchanges/hyperliquid/utils.d.ts +41 -0
- package/dist/exchanges/hyperliquid/utils.js +76 -0
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/kalshi/index.d.ts +6 -6
- package/dist/exchanges/kalshi/index.js +14 -14
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/index.d.ts +5 -5
- package/dist/exchanges/limitless/index.js +15 -12
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/index.d.ts +5 -5
- package/dist/exchanges/myriad/index.js +14 -14
- package/dist/exchanges/myriad/websocket.d.ts +2 -2
- package/dist/exchanges/myriad/websocket.js +12 -12
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +1 -1
- package/dist/exchanges/opinion/index.d.ts +4 -4
- package/dist/exchanges/opinion/index.js +9 -9
- 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/index.d.ts +6 -6
- package/dist/exchanges/polymarket/index.js +19 -19
- package/dist/exchanges/polymarket/websocket.d.ts +3 -3
- package/dist/exchanges/polymarket/websocket.js +21 -21
- package/dist/exchanges/polymarket_us/index.d.ts +3 -3
- package/dist/exchanges/polymarket_us/index.js +7 -7
- package/dist/exchanges/polymarket_us/websocket.d.ts +2 -2
- package/dist/exchanges/polymarket_us/websocket.js +6 -6
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/index.d.ts +4 -4
- package/dist/exchanges/probable/index.js +9 -9
- package/dist/exchanges/smarkets/index.d.ts +2 -2
- package/dist/exchanges/smarkets/index.js +5 -5
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -1
- package/dist/router/Router.d.ts +1 -1
- package/dist/router/Router.js +3 -3
- package/dist/server/exchange-factory.js +6 -0
- package/dist/server/method-verbs.json +7 -7
- package/dist/server/openapi.yaml +18 -3
- package/package.json +4 -3
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HyperliquidNormalizer = void 0;
|
|
4
|
+
const market_utils_1 = require("../../utils/market-utils");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
const config_1 = require("./config");
|
|
7
|
+
function parseDescription(description) {
|
|
8
|
+
// Hyperliquid outcome descriptions use key:value pairs separated by |
|
|
9
|
+
// e.g. "class:priceBinary|underlying:BTC|expiry:20260509-0600|targetPrice:79583|period:1d"
|
|
10
|
+
// or simple values like "other", "index:0"
|
|
11
|
+
const result = { raw: description };
|
|
12
|
+
const parts = description.split('|');
|
|
13
|
+
for (const part of parts) {
|
|
14
|
+
const colonIdx = part.indexOf(':');
|
|
15
|
+
if (colonIdx === -1)
|
|
16
|
+
continue;
|
|
17
|
+
const key = part.slice(0, colonIdx);
|
|
18
|
+
const value = part.slice(colonIdx + 1);
|
|
19
|
+
switch (key) {
|
|
20
|
+
case 'class':
|
|
21
|
+
result.class = value;
|
|
22
|
+
break;
|
|
23
|
+
case 'underlying':
|
|
24
|
+
result.underlying = value;
|
|
25
|
+
break;
|
|
26
|
+
case 'expiry': {
|
|
27
|
+
result.expiry = value;
|
|
28
|
+
result.expiryDate = parseExpiryDate(value);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case 'targetPrice':
|
|
32
|
+
result.targetPrice = value;
|
|
33
|
+
break;
|
|
34
|
+
case 'priceThresholds':
|
|
35
|
+
result.priceThresholds = value.split(',');
|
|
36
|
+
break;
|
|
37
|
+
case 'period':
|
|
38
|
+
result.period = value;
|
|
39
|
+
break;
|
|
40
|
+
case 'index':
|
|
41
|
+
result.index = value;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parse Hyperliquid's expiry format "YYYYMMDD-HHmm" into a Date.
|
|
49
|
+
* Example: "20260509-0600" -> 2026-05-09T06:00:00Z
|
|
50
|
+
*/
|
|
51
|
+
function parseExpiryDate(expiry) {
|
|
52
|
+
const match = expiry.match(/^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})$/);
|
|
53
|
+
if (!match)
|
|
54
|
+
return undefined;
|
|
55
|
+
const [, year, month, day, hour, minute] = match;
|
|
56
|
+
return new Date(`${year}-${month}-${day}T${hour}:${minute}:00Z`);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build a human-readable title from the parsed description.
|
|
60
|
+
*/
|
|
61
|
+
function buildTitle(rawName, parsed, questionParsed) {
|
|
62
|
+
// priceBinary: "BTC > $79,583 @ May 9, 2026"
|
|
63
|
+
if (parsed.class === 'priceBinary' && parsed.underlying && parsed.targetPrice) {
|
|
64
|
+
const price = Number(parsed.targetPrice).toLocaleString('en-US');
|
|
65
|
+
const dateStr = parsed.expiryDate
|
|
66
|
+
? parsed.expiryDate.toISOString().replace('T', ' ').replace(':00.000Z', ' UTC')
|
|
67
|
+
: '';
|
|
68
|
+
return `${parsed.underlying} > $${price}${dateStr ? ` @ ${dateStr}` : ''}`;
|
|
69
|
+
}
|
|
70
|
+
// Named outcome in a priceBucket question: use thresholds from question
|
|
71
|
+
if (parsed.index !== undefined && questionParsed?.priceThresholds && questionParsed.underlying) {
|
|
72
|
+
const idx = parseInt(parsed.index, 10);
|
|
73
|
+
const thresholds = questionParsed.priceThresholds;
|
|
74
|
+
const underlying = questionParsed.underlying;
|
|
75
|
+
if (idx === 0 && thresholds.length > 0) {
|
|
76
|
+
return `${underlying} < $${Number(thresholds[0]).toLocaleString('en-US')}`;
|
|
77
|
+
}
|
|
78
|
+
if (idx === thresholds.length) {
|
|
79
|
+
return `${underlying} > $${Number(thresholds[thresholds.length - 1]).toLocaleString('en-US')}`;
|
|
80
|
+
}
|
|
81
|
+
if (idx > 0 && idx <= thresholds.length) {
|
|
82
|
+
const lo = Number(thresholds[idx - 1]).toLocaleString('en-US');
|
|
83
|
+
const hi = Number(thresholds[idx]).toLocaleString('en-US');
|
|
84
|
+
return `${underlying} $${lo} - $${hi}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return rawName;
|
|
88
|
+
}
|
|
89
|
+
function buildMarketUrl(outcomeId) {
|
|
90
|
+
return `https://app.hyperliquid.xyz/trade/${encodeURIComponent(`#${outcomeId * 10}`)}`;
|
|
91
|
+
}
|
|
92
|
+
function buildEventUrl(questionId) {
|
|
93
|
+
return `https://app.hyperliquid.xyz/trade/${encodeURIComponent(`#${questionId}`)}`;
|
|
94
|
+
}
|
|
95
|
+
function isOutcomeCoin(coin) {
|
|
96
|
+
return coin.startsWith('#');
|
|
97
|
+
}
|
|
98
|
+
// ----------------------------------------------------------------------------
|
|
99
|
+
// Normalizer
|
|
100
|
+
// ----------------------------------------------------------------------------
|
|
101
|
+
class HyperliquidNormalizer {
|
|
102
|
+
normalizeMarket(raw) {
|
|
103
|
+
if (!raw || !raw.outcome)
|
|
104
|
+
return null;
|
|
105
|
+
const outcome = raw.outcome;
|
|
106
|
+
const outcomeId = outcome.outcome;
|
|
107
|
+
const parsed = parseDescription(outcome.description);
|
|
108
|
+
// Also parse the question description for context (e.g. priceThresholds)
|
|
109
|
+
const questionParsed = raw.question
|
|
110
|
+
? parseDescription(raw.question.description)
|
|
111
|
+
: undefined;
|
|
112
|
+
const midPrice = raw.midPrice ? parseFloat(raw.midPrice) : 0.5;
|
|
113
|
+
const yesPrice = Math.max(0, Math.min(1, midPrice));
|
|
114
|
+
const noPrice = Math.max(0, Math.min(1, 1 - midPrice));
|
|
115
|
+
const outcomes = [];
|
|
116
|
+
for (const side of outcome.sideSpecs) {
|
|
117
|
+
const sideKey = side.name.toLowerCase() === 'yes' ? 'yes' : 'no';
|
|
118
|
+
outcomes.push({
|
|
119
|
+
outcomeId: (0, utils_1.toOutcomeId)(outcomeId, sideKey),
|
|
120
|
+
marketId: (0, utils_1.toMarketId)(outcomeId),
|
|
121
|
+
label: side.name,
|
|
122
|
+
price: sideKey === 'yes' ? yesPrice : noPrice,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// If no sideSpecs provided, default to Yes/No
|
|
126
|
+
if (outcomes.length === 0) {
|
|
127
|
+
outcomes.push({
|
|
128
|
+
outcomeId: (0, utils_1.toOutcomeId)(outcomeId, 'yes'),
|
|
129
|
+
marketId: (0, utils_1.toMarketId)(outcomeId),
|
|
130
|
+
label: 'Yes',
|
|
131
|
+
price: yesPrice,
|
|
132
|
+
}, {
|
|
133
|
+
outcomeId: (0, utils_1.toOutcomeId)(outcomeId, 'no'),
|
|
134
|
+
marketId: (0, utils_1.toMarketId)(outcomeId),
|
|
135
|
+
label: 'No',
|
|
136
|
+
price: noPrice,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// Resolution date: prefer outcome-level expiry, fall back to question-level
|
|
140
|
+
const expiryDate = parsed.expiryDate
|
|
141
|
+
?? questionParsed?.expiryDate
|
|
142
|
+
?? undefined;
|
|
143
|
+
// Build a descriptive title from the parsed description
|
|
144
|
+
const title = buildTitle(outcome.name, parsed, questionParsed);
|
|
145
|
+
// Derive underlying from outcome or question
|
|
146
|
+
const underlying = parsed.underlying ?? questionParsed?.underlying;
|
|
147
|
+
const tags = [];
|
|
148
|
+
if (underlying) {
|
|
149
|
+
tags.push(underlying);
|
|
150
|
+
}
|
|
151
|
+
tags.push('Outcome Markets');
|
|
152
|
+
const category = underlying ? 'Crypto' : undefined;
|
|
153
|
+
const um = {
|
|
154
|
+
marketId: (0, utils_1.toMarketId)(outcomeId),
|
|
155
|
+
eventId: raw.question ? String(raw.question.question) : undefined,
|
|
156
|
+
title,
|
|
157
|
+
description: outcome.description,
|
|
158
|
+
slug: `hl-${outcomeId}`,
|
|
159
|
+
outcomes,
|
|
160
|
+
resolutionDate: expiryDate ?? new Date(0),
|
|
161
|
+
volume24h: 0,
|
|
162
|
+
liquidity: 0,
|
|
163
|
+
url: buildMarketUrl(outcomeId),
|
|
164
|
+
category,
|
|
165
|
+
tags,
|
|
166
|
+
tickSize: 0.001,
|
|
167
|
+
status: 'active',
|
|
168
|
+
};
|
|
169
|
+
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
170
|
+
return um;
|
|
171
|
+
}
|
|
172
|
+
normalizeEvent(raw) {
|
|
173
|
+
if (!raw)
|
|
174
|
+
return null;
|
|
175
|
+
const parsed = parseDescription(raw.description);
|
|
176
|
+
const underlying = parsed.underlying;
|
|
177
|
+
// Build a more descriptive event title
|
|
178
|
+
let title = raw.name;
|
|
179
|
+
if (parsed.class === 'priceBucket' && underlying && parsed.priceThresholds) {
|
|
180
|
+
const thresholds = parsed.priceThresholds.map(t => `$${Number(t).toLocaleString('en-US')}`).join(', ');
|
|
181
|
+
const dateStr = parsed.expiryDate
|
|
182
|
+
? parsed.expiryDate.toISOString().replace('T', ' ').replace(':00.000Z', ' UTC')
|
|
183
|
+
: '';
|
|
184
|
+
title = `${underlying} Price Bucket [${thresholds}]${dateStr ? ` @ ${dateStr}` : ''}`;
|
|
185
|
+
}
|
|
186
|
+
const tags = ['Outcome Markets'];
|
|
187
|
+
if (underlying)
|
|
188
|
+
tags.push(underlying);
|
|
189
|
+
return {
|
|
190
|
+
id: String(raw.question),
|
|
191
|
+
title,
|
|
192
|
+
description: raw.description,
|
|
193
|
+
slug: `hl-question-${raw.question}`,
|
|
194
|
+
markets: [],
|
|
195
|
+
volume24h: 0,
|
|
196
|
+
url: buildEventUrl(raw.question),
|
|
197
|
+
category: underlying ? 'Crypto' : undefined,
|
|
198
|
+
tags,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
normalizeEventWithMarkets(raw, outcomeMeta, mids) {
|
|
202
|
+
const event = this.normalizeEvent(raw);
|
|
203
|
+
if (!event)
|
|
204
|
+
return null;
|
|
205
|
+
const markets = [];
|
|
206
|
+
for (const outcomeId of raw.namedOutcomes) {
|
|
207
|
+
if (raw.settledNamedOutcomes.includes(outcomeId))
|
|
208
|
+
continue;
|
|
209
|
+
const outcome = outcomeMeta.outcomes.find(o => o.outcome === outcomeId);
|
|
210
|
+
if (!outcome)
|
|
211
|
+
continue;
|
|
212
|
+
const yesCoin = (0, utils_1.toCoinNotation)(outcomeId, 'yes');
|
|
213
|
+
const midPrice = mids[yesCoin];
|
|
214
|
+
const market = this.normalizeMarket({
|
|
215
|
+
outcome,
|
|
216
|
+
question: raw,
|
|
217
|
+
midPrice,
|
|
218
|
+
});
|
|
219
|
+
if (market) {
|
|
220
|
+
markets.push(market);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
...event,
|
|
225
|
+
markets,
|
|
226
|
+
volume24h: markets.reduce((sum, m) => sum + m.volume24h, 0),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
normalizeOrderBook(raw, _id) {
|
|
230
|
+
const [rawBids, rawAsks] = raw.levels;
|
|
231
|
+
const bids = rawBids.map(level => ({
|
|
232
|
+
price: parseFloat(level.px),
|
|
233
|
+
size: parseFloat(level.sz),
|
|
234
|
+
}));
|
|
235
|
+
const asks = rawAsks.map(level => ({
|
|
236
|
+
price: parseFloat(level.px),
|
|
237
|
+
size: parseFloat(level.sz),
|
|
238
|
+
}));
|
|
239
|
+
return {
|
|
240
|
+
bids: [...bids].sort((a, b) => b.price - a.price),
|
|
241
|
+
asks: [...asks].sort((a, b) => a.price - b.price),
|
|
242
|
+
timestamp: raw.time,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
normalizeOHLCV(raw, _params) {
|
|
246
|
+
return raw.map(candle => ({
|
|
247
|
+
timestamp: candle.t,
|
|
248
|
+
open: parseFloat(candle.o),
|
|
249
|
+
high: parseFloat(candle.h),
|
|
250
|
+
low: parseFloat(candle.l),
|
|
251
|
+
close: parseFloat(candle.c),
|
|
252
|
+
volume: parseFloat(candle.v),
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
normalizeTrade(raw, _index) {
|
|
256
|
+
return {
|
|
257
|
+
id: String(raw.tid),
|
|
258
|
+
timestamp: raw.time,
|
|
259
|
+
price: parseFloat(raw.px),
|
|
260
|
+
amount: parseFloat(raw.sz),
|
|
261
|
+
side: raw.side === 'B' ? 'buy' : raw.side === 'A' ? 'sell' : 'unknown',
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
normalizeUserTrade(raw, _index) {
|
|
265
|
+
return {
|
|
266
|
+
id: String(raw.tid),
|
|
267
|
+
timestamp: raw.time,
|
|
268
|
+
price: parseFloat(raw.px),
|
|
269
|
+
amount: parseFloat(raw.sz),
|
|
270
|
+
side: raw.side === 'B' ? 'buy' : raw.side === 'A' ? 'sell' : 'unknown',
|
|
271
|
+
orderId: String(raw.oid),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
normalizeOpenOrder(raw) {
|
|
275
|
+
const origSz = parseFloat(raw.origSz);
|
|
276
|
+
const currentSz = parseFloat(raw.sz);
|
|
277
|
+
return {
|
|
278
|
+
id: String(raw.oid),
|
|
279
|
+
marketId: this.coinToMarketId(raw.coin),
|
|
280
|
+
outcomeId: this.coinToOutcomeId(raw.coin),
|
|
281
|
+
side: raw.side === 'B' ? 'buy' : 'sell',
|
|
282
|
+
type: 'limit',
|
|
283
|
+
price: parseFloat(raw.limitPx),
|
|
284
|
+
amount: origSz,
|
|
285
|
+
status: currentSz < origSz ? 'open' : 'pending',
|
|
286
|
+
filled: origSz - currentSz,
|
|
287
|
+
remaining: currentSz,
|
|
288
|
+
timestamp: raw.timestamp,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
normalizePosition(raw) {
|
|
292
|
+
if (!isOutcomeCoin(raw.coin)) {
|
|
293
|
+
return {
|
|
294
|
+
marketId: raw.coin,
|
|
295
|
+
outcomeId: raw.coin,
|
|
296
|
+
outcomeLabel: raw.coin,
|
|
297
|
+
size: parseFloat(raw.szi),
|
|
298
|
+
entryPrice: raw.entryPx ? parseFloat(raw.entryPx) : 0,
|
|
299
|
+
currentPrice: 0,
|
|
300
|
+
unrealizedPnL: parseFloat(raw.unrealizedPnl),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
marketId: this.coinToMarketId(raw.coin),
|
|
305
|
+
outcomeId: this.coinToOutcomeId(raw.coin),
|
|
306
|
+
outcomeLabel: raw.coin,
|
|
307
|
+
size: parseFloat(raw.szi),
|
|
308
|
+
entryPrice: raw.entryPx ? parseFloat(raw.entryPx) : 0,
|
|
309
|
+
currentPrice: 0,
|
|
310
|
+
unrealizedPnL: parseFloat(raw.unrealizedPnl),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
normalizeBalance(raw) {
|
|
314
|
+
const summary = raw.crossMarginSummary;
|
|
315
|
+
const total = parseFloat(summary.accountValue);
|
|
316
|
+
const locked = parseFloat(summary.totalMarginUsed);
|
|
317
|
+
return [{
|
|
318
|
+
currency: 'USDH',
|
|
319
|
+
total,
|
|
320
|
+
available: total - locked,
|
|
321
|
+
locked,
|
|
322
|
+
}];
|
|
323
|
+
}
|
|
324
|
+
// -- Private helpers -------------------------------------------------------
|
|
325
|
+
coinToMarketId(coin) {
|
|
326
|
+
if (!coin.startsWith('#'))
|
|
327
|
+
return coin;
|
|
328
|
+
const encoding = parseInt(coin.slice(1), 10);
|
|
329
|
+
const { outcomeId } = (0, utils_1.decodeAssetId)(config_1.OUTCOME_ASSET_BASE + encoding);
|
|
330
|
+
return (0, utils_1.toMarketId)(outcomeId);
|
|
331
|
+
}
|
|
332
|
+
coinToOutcomeId(coin) {
|
|
333
|
+
if (!coin.startsWith('#'))
|
|
334
|
+
return coin;
|
|
335
|
+
const encoding = parseInt(coin.slice(1), 10);
|
|
336
|
+
return String(config_1.OUTCOME_ASSET_BASE + encoding);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
exports.HyperliquidNormalizer = HyperliquidNormalizer;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encode an outcome ID and side into a Hyperliquid asset ID.
|
|
3
|
+
*
|
|
4
|
+
* Formula: 100_000_000 + (10 * outcomeId) + side
|
|
5
|
+
* where side = 0 (Yes) or 1 (No).
|
|
6
|
+
*/
|
|
7
|
+
export declare function encodeAssetId(outcomeId: number, side: 'yes' | 'no'): number;
|
|
8
|
+
/**
|
|
9
|
+
* Decode a Hyperliquid asset ID back into outcome ID and side.
|
|
10
|
+
*/
|
|
11
|
+
export declare function decodeAssetId(assetId: number): {
|
|
12
|
+
outcomeId: number;
|
|
13
|
+
side: 'yes' | 'no';
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Convert an outcome ID and side to the Hyperliquid coin notation (#encoding).
|
|
17
|
+
*
|
|
18
|
+
* Example: outcome 1, Yes -> "#10"
|
|
19
|
+
*/
|
|
20
|
+
export declare function toCoinNotation(outcomeId: number, side: 'yes' | 'no'): string;
|
|
21
|
+
/**
|
|
22
|
+
* Convert an asset encoding (the number after #) to outcome ID and side.
|
|
23
|
+
*/
|
|
24
|
+
export declare function fromCoinEncoding(encoding: number): {
|
|
25
|
+
outcomeId: number;
|
|
26
|
+
side: 'yes' | 'no';
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Build a unique market ID string from an outcome.
|
|
30
|
+
* We use "hl-outcome-{outcomeId}" as the market ID.
|
|
31
|
+
*/
|
|
32
|
+
export declare function toMarketId(outcomeId: number): string;
|
|
33
|
+
/**
|
|
34
|
+
* Extract the numeric outcome ID from our market ID format.
|
|
35
|
+
*/
|
|
36
|
+
export declare function fromMarketId(marketId: string): number;
|
|
37
|
+
/**
|
|
38
|
+
* Build an outcome ID string for the unified type.
|
|
39
|
+
* Format: "{assetId}" which is the full numeric asset ID.
|
|
40
|
+
*/
|
|
41
|
+
export declare function toOutcomeId(outcomeId: number, side: 'yes' | 'no'): string;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encodeAssetId = encodeAssetId;
|
|
4
|
+
exports.decodeAssetId = decodeAssetId;
|
|
5
|
+
exports.toCoinNotation = toCoinNotation;
|
|
6
|
+
exports.fromCoinEncoding = fromCoinEncoding;
|
|
7
|
+
exports.toMarketId = toMarketId;
|
|
8
|
+
exports.fromMarketId = fromMarketId;
|
|
9
|
+
exports.toOutcomeId = toOutcomeId;
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
/**
|
|
12
|
+
* Encode an outcome ID and side into a Hyperliquid asset ID.
|
|
13
|
+
*
|
|
14
|
+
* Formula: 100_000_000 + (10 * outcomeId) + side
|
|
15
|
+
* where side = 0 (Yes) or 1 (No).
|
|
16
|
+
*/
|
|
17
|
+
function encodeAssetId(outcomeId, side) {
|
|
18
|
+
const sideValue = side === 'yes' ? config_1.SIDE_YES : config_1.SIDE_NO;
|
|
19
|
+
return config_1.OUTCOME_ASSET_BASE + (config_1.OUTCOME_MULTIPLIER * outcomeId) + sideValue;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Decode a Hyperliquid asset ID back into outcome ID and side.
|
|
23
|
+
*/
|
|
24
|
+
function decodeAssetId(assetId) {
|
|
25
|
+
const offset = assetId - config_1.OUTCOME_ASSET_BASE;
|
|
26
|
+
const sideValue = offset % config_1.OUTCOME_MULTIPLIER;
|
|
27
|
+
const outcomeId = (offset - sideValue) / config_1.OUTCOME_MULTIPLIER;
|
|
28
|
+
return {
|
|
29
|
+
outcomeId,
|
|
30
|
+
side: sideValue === config_1.SIDE_YES ? 'yes' : 'no',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Convert an outcome ID and side to the Hyperliquid coin notation (#encoding).
|
|
35
|
+
*
|
|
36
|
+
* Example: outcome 1, Yes -> "#10"
|
|
37
|
+
*/
|
|
38
|
+
function toCoinNotation(outcomeId, side) {
|
|
39
|
+
const encoding = config_1.OUTCOME_MULTIPLIER * outcomeId + (side === 'yes' ? config_1.SIDE_YES : config_1.SIDE_NO);
|
|
40
|
+
return `#${encoding}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Convert an asset encoding (the number after #) to outcome ID and side.
|
|
44
|
+
*/
|
|
45
|
+
function fromCoinEncoding(encoding) {
|
|
46
|
+
const sideValue = encoding % config_1.OUTCOME_MULTIPLIER;
|
|
47
|
+
const outcomeId = (encoding - sideValue) / config_1.OUTCOME_MULTIPLIER;
|
|
48
|
+
return {
|
|
49
|
+
outcomeId,
|
|
50
|
+
side: sideValue === config_1.SIDE_YES ? 'yes' : 'no',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build a unique market ID string from an outcome.
|
|
55
|
+
* We use "hl-outcome-{outcomeId}" as the market ID.
|
|
56
|
+
*/
|
|
57
|
+
function toMarketId(outcomeId) {
|
|
58
|
+
return `hl-outcome-${outcomeId}`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extract the numeric outcome ID from our market ID format.
|
|
62
|
+
*/
|
|
63
|
+
function fromMarketId(marketId) {
|
|
64
|
+
const match = marketId.match(/^hl-outcome-(\d+)$/);
|
|
65
|
+
if (!match) {
|
|
66
|
+
throw new Error(`Invalid Hyperliquid market ID: ${marketId}`);
|
|
67
|
+
}
|
|
68
|
+
return parseInt(match[1], 10);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Build an outcome ID string for the unified type.
|
|
72
|
+
* Format: "{assetId}" which is the full numeric asset ID.
|
|
73
|
+
*/
|
|
74
|
+
function toOutcomeId(outcomeId, side) {
|
|
75
|
+
return String(encodeAssetId(outcomeId, side));
|
|
76
|
+
}
|
|
@@ -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-08T20:27:07.004Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const kalshiApiSpec: {
|
|
@@ -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-08T20:27:07.004Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -23,9 +23,9 @@ export declare class KalshiExchange extends PredictionMarketExchange {
|
|
|
23
23
|
private ensureAuth;
|
|
24
24
|
protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
|
|
25
25
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
26
|
-
fetchOHLCV(
|
|
27
|
-
fetchOrderBook(
|
|
28
|
-
fetchTrades(
|
|
26
|
+
fetchOHLCV(outcomeId: string, params: OHLCVParams): Promise<PriceCandle[]>;
|
|
27
|
+
fetchOrderBook(outcomeId: string): Promise<OrderBook>;
|
|
28
|
+
fetchTrades(outcomeId: string, params: TradesParams | HistoryFilterParams): Promise<Trade[]>;
|
|
29
29
|
fetchBalance(): Promise<Balance[]>;
|
|
30
30
|
fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
|
|
31
31
|
fetchPositions(): Promise<Position[]>;
|
|
@@ -38,8 +38,8 @@ export declare class KalshiExchange extends PredictionMarketExchange {
|
|
|
38
38
|
fetchOrder(orderId: string): Promise<Order>;
|
|
39
39
|
fetchOpenOrders(marketId?: string): Promise<Order[]>;
|
|
40
40
|
private ws?;
|
|
41
|
-
watchOrderBook(
|
|
42
|
-
watchOrderBooks(
|
|
43
|
-
watchTrades(
|
|
41
|
+
watchOrderBook(outcomeId: string, limit?: number): Promise<OrderBook>;
|
|
42
|
+
watchOrderBooks(outcomeIds: string[], limit?: number): Promise<Record<string, OrderBook>>;
|
|
43
|
+
watchTrades(outcomeId: string, address?: string, since?: number, limit?: number): Promise<Trade[]>;
|
|
44
44
|
close(): Promise<void>;
|
|
45
45
|
}
|
|
@@ -117,21 +117,21 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
117
117
|
.filter((e) => e !== null)
|
|
118
118
|
.slice(0, limit);
|
|
119
119
|
}
|
|
120
|
-
async fetchOHLCV(
|
|
121
|
-
const rawCandles = await this.fetcher.fetchRawOHLCV(
|
|
120
|
+
async fetchOHLCV(outcomeId, params) {
|
|
121
|
+
const rawCandles = await this.fetcher.fetchRawOHLCV(outcomeId, params);
|
|
122
122
|
return this.normalizer.normalizeOHLCV(rawCandles, params);
|
|
123
123
|
}
|
|
124
|
-
async fetchOrderBook(
|
|
125
|
-
(0, validation_1.validateIdFormat)(
|
|
126
|
-
const raw = await this.fetcher.fetchRawOrderBook(
|
|
127
|
-
return this.normalizer.normalizeOrderBook(raw,
|
|
124
|
+
async fetchOrderBook(outcomeId) {
|
|
125
|
+
(0, validation_1.validateIdFormat)(outcomeId, "OrderBook");
|
|
126
|
+
const raw = await this.fetcher.fetchRawOrderBook(outcomeId);
|
|
127
|
+
return this.normalizer.normalizeOrderBook(raw, outcomeId);
|
|
128
128
|
}
|
|
129
|
-
async fetchTrades(
|
|
129
|
+
async fetchTrades(outcomeId, params) {
|
|
130
130
|
if ("resolution" in params && params.resolution !== undefined) {
|
|
131
131
|
console.warn('[pmxt] Warning: The "resolution" parameter is deprecated for fetchTrades() and will be ignored. ' +
|
|
132
132
|
"It will be removed in v3.0.0. Please remove it from your code.");
|
|
133
133
|
}
|
|
134
|
-
const rawTrades = await this.fetcher.fetchRawTrades(
|
|
134
|
+
const rawTrades = await this.fetcher.fetchRawTrades(outcomeId, params);
|
|
135
135
|
return rawTrades.map((raw, i) => this.normalizer.normalizeTrade(raw, i));
|
|
136
136
|
}
|
|
137
137
|
// ----------------------------------------------------------------------------
|
|
@@ -251,7 +251,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
251
251
|
// WebSocket
|
|
252
252
|
// ----------------------------------------------------------------------------
|
|
253
253
|
ws;
|
|
254
|
-
async watchOrderBook(
|
|
254
|
+
async watchOrderBook(outcomeId, limit) {
|
|
255
255
|
const auth = this.ensureAuth();
|
|
256
256
|
if (!this.ws) {
|
|
257
257
|
const wsConfigWithUrl = {
|
|
@@ -260,10 +260,10 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
260
260
|
};
|
|
261
261
|
this.ws = new websocket_1.KalshiWebSocket(auth, wsConfigWithUrl);
|
|
262
262
|
}
|
|
263
|
-
const marketTicker =
|
|
263
|
+
const marketTicker = outcomeId.replace(/-NO$/, "");
|
|
264
264
|
return this.ws.watchOrderBook(marketTicker);
|
|
265
265
|
}
|
|
266
|
-
async watchOrderBooks(
|
|
266
|
+
async watchOrderBooks(outcomeIds, limit) {
|
|
267
267
|
const auth = this.ensureAuth();
|
|
268
268
|
if (!this.ws) {
|
|
269
269
|
const wsConfigWithUrl = {
|
|
@@ -272,10 +272,10 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
272
272
|
};
|
|
273
273
|
this.ws = new websocket_1.KalshiWebSocket(auth, wsConfigWithUrl);
|
|
274
274
|
}
|
|
275
|
-
const marketTickers =
|
|
275
|
+
const marketTickers = outcomeIds.map((oid) => oid.replace(/-NO$/, ""));
|
|
276
276
|
return this.ws.watchOrderBooks(marketTickers);
|
|
277
277
|
}
|
|
278
|
-
async watchTrades(
|
|
278
|
+
async watchTrades(outcomeId, address, since, limit) {
|
|
279
279
|
const auth = this.ensureAuth();
|
|
280
280
|
if (!this.ws) {
|
|
281
281
|
const wsConfigWithUrl = {
|
|
@@ -284,7 +284,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
284
284
|
};
|
|
285
285
|
this.ws = new websocket_1.KalshiWebSocket(auth, wsConfigWithUrl);
|
|
286
286
|
}
|
|
287
|
-
const marketTicker =
|
|
287
|
+
const marketTicker = outcomeId.replace(/-NO$/, "");
|
|
288
288
|
return this.ws.watchTrades(marketTicker);
|
|
289
289
|
}
|
|
290
290
|
async close() {
|
|
@@ -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-08T20:27:07.072Z
|
|
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-08T20:27:07.072Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.limitlessApiSpec = {
|
|
@@ -27,10 +27,10 @@ export declare class LimitlessExchange extends PredictionMarketExchange {
|
|
|
27
27
|
private getHeaders;
|
|
28
28
|
protected fetchMarketsImpl(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
|
|
29
29
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
30
|
-
fetchOHLCV(
|
|
31
|
-
fetchOrderBook(
|
|
30
|
+
fetchOHLCV(outcomeId: string, params: OHLCVParams): Promise<PriceCandle[]>;
|
|
31
|
+
fetchOrderBook(outcomeId: string, side?: 'yes' | 'no'): Promise<OrderBook>;
|
|
32
32
|
private isNoOutcome;
|
|
33
|
-
fetchTrades(
|
|
33
|
+
fetchTrades(outcomeId: string, params: TradesParams | HistoryFilterParams): Promise<Trade[]>;
|
|
34
34
|
fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
|
|
35
35
|
createOrder(params: CreateOrderParams): Promise<Order>;
|
|
36
36
|
cancelOrder(orderId: string): Promise<Order>;
|
|
@@ -40,8 +40,8 @@ export declare class LimitlessExchange extends PredictionMarketExchange {
|
|
|
40
40
|
fetchAllOrders(params?: OrderHistoryParams): Promise<Order[]>;
|
|
41
41
|
fetchPositions(address?: string): Promise<Position[]>;
|
|
42
42
|
fetchBalance(address?: string): Promise<Balance[]>;
|
|
43
|
-
watchOrderBook(
|
|
44
|
-
watchTrades(
|
|
43
|
+
watchOrderBook(outcomeId: string, limit?: number): Promise<OrderBook>;
|
|
44
|
+
watchTrades(outcomeId: string, address?: string, since?: number, limit?: number): Promise<Trade[]>;
|
|
45
45
|
/**
|
|
46
46
|
* Watch AMM price updates for a market address (Limitless only).
|
|
47
47
|
* Requires WebSocket connection.
|