pmxt-core 2.51.3 → 2.52.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 +7 -0
- package/dist/BaseExchange.js +10 -0
- package/dist/exchanges/gemini-titan/errors.d.ts +5 -0
- package/dist/exchanges/gemini-titan/errors.js +37 -2
- package/dist/exchanges/gemini-titan/fetcher.d.ts +33 -0
- package/dist/exchanges/gemini-titan/fetcher.js +77 -0
- package/dist/exchanges/gemini-titan/index.d.ts +4 -0
- package/dist/exchanges/gemini-titan/index.js +22 -1
- package/dist/exchanges/hyperliquid/index.js +14 -1
- package/dist/exchanges/hyperliquid/normalizer.js +35 -2
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/kalshi/fetcher.d.ts +37 -2
- package/dist/exchanges/kalshi/fetcher.js +10 -0
- package/dist/exchanges/kalshi/index.d.ts +7 -0
- package/dist/exchanges/kalshi/index.js +9 -0
- package/dist/exchanges/kalshi/normalizer.js +34 -10
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/index.d.ts +1 -0
- package/dist/exchanges/limitless/index.js +5 -0
- package/dist/exchanges/limitless/websocket.js +6 -2
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +1 -1
- 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_us/websocket.js +17 -1
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/rain/index.d.ts +2 -0
- package/dist/exchanges/rain/index.js +9 -0
- package/dist/exchanges/smarkets/normalizer.js +8 -6
- package/dist/exchanges/suibets/api.d.ts +0 -1
- package/dist/exchanges/suibets/api.js +0 -1
- package/dist/exchanges/suibets/fetcher.d.ts +6 -0
- package/dist/exchanges/suibets/normalizer.js +2 -1
- package/dist/server/method-verbs.json +10 -0
- package/dist/server/openapi.yaml +35 -2
- package/dist/types.d.ts +4 -0
- package/package.json +4 -4
package/dist/BaseExchange.d.ts
CHANGED
|
@@ -526,6 +526,13 @@ export declare abstract class PredictionMarketExchange {
|
|
|
526
526
|
* @throws EventNotFound if no event matches the parameters
|
|
527
527
|
*/
|
|
528
528
|
fetchEvent(params?: EventFetchParams): Promise<UnifiedEvent>;
|
|
529
|
+
/**
|
|
530
|
+
* Fetch venue-native metadata for a specific event when the exchange
|
|
531
|
+
* exposes a dedicated metadata endpoint.
|
|
532
|
+
*
|
|
533
|
+
* Kalshi implements this for `GET /events/{event_ticker}/metadata`.
|
|
534
|
+
*/
|
|
535
|
+
fetchEventMetadata(eventTicker: string): Promise<Record<string, unknown>>;
|
|
529
536
|
/**
|
|
530
537
|
* Fetch historical OHLCV (candlestick) price data for a specific market outcome.
|
|
531
538
|
*
|
package/dist/BaseExchange.js
CHANGED
|
@@ -487,6 +487,16 @@ class PredictionMarketExchange {
|
|
|
487
487
|
}
|
|
488
488
|
return events[0];
|
|
489
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Fetch venue-native metadata for a specific event when the exchange
|
|
492
|
+
* exposes a dedicated metadata endpoint.
|
|
493
|
+
*
|
|
494
|
+
* Kalshi implements this for `GET /events/{event_ticker}/metadata`.
|
|
495
|
+
*/
|
|
496
|
+
async fetchEventMetadata(eventTicker) {
|
|
497
|
+
void eventTicker;
|
|
498
|
+
throw new Error("Method fetchEventMetadata not implemented.");
|
|
499
|
+
}
|
|
490
500
|
/**
|
|
491
501
|
* Fetch historical OHLCV (candlestick) price data for a specific market outcome.
|
|
492
502
|
*
|
|
@@ -10,10 +10,15 @@ import { BadRequest } from '../../errors';
|
|
|
10
10
|
* - InvalidSignature -> AuthenticationError
|
|
11
11
|
* - InsufficientFunds -> InsufficientFunds
|
|
12
12
|
* - InvalidQuantity, InvalidPrice, MarketNotOpen -> InvalidOrder
|
|
13
|
+
* - TermsNotAccepted -> AuthenticationError (with auto-accept flow)
|
|
13
14
|
*/
|
|
14
15
|
export declare class GeminiErrorMapper extends ErrorMapper {
|
|
15
16
|
constructor();
|
|
16
17
|
protected extractErrorMessage(error: unknown): string;
|
|
18
|
+
/**
|
|
19
|
+
* Check if an error is related to terms acceptance
|
|
20
|
+
*/
|
|
21
|
+
private isTermsError;
|
|
17
22
|
protected mapBadRequestError(message: string, data: unknown): BadRequest;
|
|
18
23
|
mapError(error: unknown): ReturnType<ErrorMapper['mapError']>;
|
|
19
24
|
}
|
|
@@ -7,6 +7,14 @@ exports.geminiErrorMapper = exports.GeminiErrorMapper = void 0;
|
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const error_mapper_1 = require("../../utils/error-mapper");
|
|
9
9
|
const errors_1 = require("../../errors");
|
|
10
|
+
// Terms-related error patterns
|
|
11
|
+
const TERMS_ERROR_PATTERNS = [
|
|
12
|
+
'terms_not_accepted',
|
|
13
|
+
'terms required',
|
|
14
|
+
'prediction markets terms',
|
|
15
|
+
'accept terms',
|
|
16
|
+
'terms must be accepted',
|
|
17
|
+
];
|
|
10
18
|
/**
|
|
11
19
|
* Maps Gemini Titan API errors to PMXT unified error classes.
|
|
12
20
|
*
|
|
@@ -17,6 +25,7 @@ const errors_1 = require("../../errors");
|
|
|
17
25
|
* - InvalidSignature -> AuthenticationError
|
|
18
26
|
* - InsufficientFunds -> InsufficientFunds
|
|
19
27
|
* - InvalidQuantity, InvalidPrice, MarketNotOpen -> InvalidOrder
|
|
28
|
+
* - TermsNotAccepted -> AuthenticationError (with auto-accept flow)
|
|
20
29
|
*/
|
|
21
30
|
class GeminiErrorMapper extends error_mapper_1.ErrorMapper {
|
|
22
31
|
constructor() {
|
|
@@ -37,12 +46,25 @@ class GeminiErrorMapper extends error_mapper_1.ErrorMapper {
|
|
|
37
46
|
}
|
|
38
47
|
return super.extractErrorMessage(error);
|
|
39
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if an error is related to terms acceptance
|
|
51
|
+
*/
|
|
52
|
+
isTermsError(message) {
|
|
53
|
+
const lowerMessage = message.toLowerCase();
|
|
54
|
+
return TERMS_ERROR_PATTERNS.some(pattern => lowerMessage.includes(pattern));
|
|
55
|
+
}
|
|
40
56
|
mapBadRequestError(message, data) {
|
|
41
57
|
const reason = typeof data === 'object' && data !== null && 'reason' in data
|
|
42
58
|
? String(data.reason)
|
|
43
59
|
: '';
|
|
44
60
|
const lowerReason = reason.toLowerCase();
|
|
45
61
|
const lowerMessage = message.toLowerCase();
|
|
62
|
+
// ✅ Check for terms-related errors first
|
|
63
|
+
if (this.isTermsError(lowerMessage) || this.isTermsError(lowerReason)) {
|
|
64
|
+
return new errors_1.AuthenticationError(`Gemini Prediction Markets terms must be accepted before placing orders. ` +
|
|
65
|
+
`The adapter will automatically accept terms on your behalf. ` +
|
|
66
|
+
`Original error: ${message}`, this.exchangeName);
|
|
67
|
+
}
|
|
46
68
|
if (lowerReason.includes('insufficientfunds') || lowerMessage.includes('insufficient')) {
|
|
47
69
|
return new errors_1.InsufficientFunds(message, this.exchangeName);
|
|
48
70
|
}
|
|
@@ -56,8 +78,7 @@ class GeminiErrorMapper extends error_mapper_1.ErrorMapper {
|
|
|
56
78
|
return new errors_1.InvalidOrder(message, this.exchangeName);
|
|
57
79
|
}
|
|
58
80
|
if (lowerReason.includes('invalidsignature') ||
|
|
59
|
-
lowerReason.includes('invalidapikey')
|
|
60
|
-
lowerMessage.includes('terms_not_accepted')) {
|
|
81
|
+
lowerReason.includes('invalidapikey')) {
|
|
61
82
|
return new errors_1.AuthenticationError(message, this.exchangeName);
|
|
62
83
|
}
|
|
63
84
|
return super.mapBadRequestError(message, data);
|
|
@@ -68,6 +89,20 @@ class GeminiErrorMapper extends error_mapper_1.ErrorMapper {
|
|
|
68
89
|
const retryAfterSeconds = retryAfter ? parseInt(retryAfter, 10) : undefined;
|
|
69
90
|
return new errors_1.RateLimitExceeded(this.extractErrorMessage(error), retryAfterSeconds, this.exchangeName);
|
|
70
91
|
}
|
|
92
|
+
// ✅ Check for terms errors in non-4xx responses
|
|
93
|
+
if (axios_1.default.isAxiosError(error) && error.response?.data) {
|
|
94
|
+
const data = error.response.data;
|
|
95
|
+
const message = typeof data === 'object' && data !== null && 'message' in data
|
|
96
|
+
? String(data.message)
|
|
97
|
+
: typeof data === 'string'
|
|
98
|
+
? data
|
|
99
|
+
: '';
|
|
100
|
+
if (this.isTermsError(message)) {
|
|
101
|
+
return new errors_1.AuthenticationError(`Gemini Prediction Markets terms must be accepted before placing orders. ` +
|
|
102
|
+
`The adapter will automatically accept terms on your behalf. ` +
|
|
103
|
+
`Original error: ${message}`, this.exchangeName);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
71
106
|
return super.mapError(error);
|
|
72
107
|
}
|
|
73
108
|
}
|
|
@@ -6,13 +6,42 @@ export declare class GeminiFetcher implements IExchangeFetcher<GeminiRawEvent, G
|
|
|
6
6
|
private readonly ctx;
|
|
7
7
|
private readonly baseUrl;
|
|
8
8
|
private readonly auth;
|
|
9
|
+
private readonly httpClient;
|
|
9
10
|
private symbolToEventTicker;
|
|
11
|
+
private termsAccepted;
|
|
10
12
|
constructor(ctx: FetcherContext, baseUrl: string, auth?: GeminiAuth);
|
|
11
13
|
fetchRawMarkets(params?: MarketFilterParams): Promise<GeminiRawEvent[]>;
|
|
12
14
|
fetchRawEvents(params: EventFetchParams): Promise<GeminiRawEvent[]>;
|
|
13
15
|
fetchRawSingleEvent(eventTicker: string): Promise<GeminiRawEvent>;
|
|
14
16
|
fetchRawOrderBook(instrumentSymbol: string): Promise<GeminiRawOrderBook | undefined>;
|
|
15
17
|
getEventTickerForSymbol(instrumentSymbol: string): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Get current terms version and content
|
|
20
|
+
*/
|
|
21
|
+
getTerms(): Promise<{
|
|
22
|
+
version: string;
|
|
23
|
+
content: string;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Check if API key has accepted the latest terms
|
|
27
|
+
*/
|
|
28
|
+
getTermsStatus(): Promise<{
|
|
29
|
+
hasAcceptedLatest: boolean;
|
|
30
|
+
acceptedVersion?: string;
|
|
31
|
+
latestVersion?: string;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Accept the latest terms version
|
|
35
|
+
*/
|
|
36
|
+
acceptTerms(): Promise<{
|
|
37
|
+
accepted: boolean;
|
|
38
|
+
version: string;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Ensure terms are accepted before placing orders.
|
|
42
|
+
* This is called automatically before order submission.
|
|
43
|
+
*/
|
|
44
|
+
ensureTermsAccepted(): Promise<void>;
|
|
16
45
|
submitRawOrder(payload: Record<string, unknown>): Promise<GeminiRawOrder>;
|
|
17
46
|
cancelRawOrder(orderId: number): Promise<GeminiRawOrder>;
|
|
18
47
|
fetchRawActiveOrders(symbol?: string): Promise<GeminiRawOrder[]>;
|
|
@@ -20,5 +49,9 @@ export declare class GeminiFetcher implements IExchangeFetcher<GeminiRawEvent, G
|
|
|
20
49
|
private fetchPaginatedOrders;
|
|
21
50
|
fetchRawPositions(): Promise<GeminiRawPosition[]>;
|
|
22
51
|
private get;
|
|
52
|
+
/**
|
|
53
|
+
* Authenticated GET request
|
|
54
|
+
*/
|
|
55
|
+
private getAuthenticated;
|
|
23
56
|
private postAuthenticated;
|
|
24
57
|
}
|
|
@@ -9,12 +9,16 @@ class GeminiFetcher {
|
|
|
9
9
|
ctx;
|
|
10
10
|
baseUrl;
|
|
11
11
|
auth;
|
|
12
|
+
httpClient; // Add httpClient
|
|
12
13
|
// Index mapping instrumentSymbol -> eventTicker, built during fetchRawEvents
|
|
13
14
|
symbolToEventTicker = new Map();
|
|
15
|
+
// Track terms acceptance status to avoid repeated checks
|
|
16
|
+
termsAccepted = false;
|
|
14
17
|
constructor(ctx, baseUrl, auth) {
|
|
15
18
|
this.ctx = ctx;
|
|
16
19
|
this.baseUrl = baseUrl;
|
|
17
20
|
this.auth = auth;
|
|
21
|
+
this.httpClient = ctx.http; // Initialize httpClient from ctx
|
|
18
22
|
}
|
|
19
23
|
// -- Public data -----------------------------------------------------------
|
|
20
24
|
async fetchRawMarkets(params) {
|
|
@@ -95,8 +99,60 @@ class GeminiFetcher {
|
|
|
95
99
|
getEventTickerForSymbol(instrumentSymbol) {
|
|
96
100
|
return this.symbolToEventTicker.get(instrumentSymbol);
|
|
97
101
|
}
|
|
102
|
+
// -- Terms Acceptance Flow -----------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Get current terms version and content
|
|
105
|
+
*/
|
|
106
|
+
async getTerms() {
|
|
107
|
+
return this.getAuthenticated('/v1/prediction-markets/terms');
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check if API key has accepted the latest terms
|
|
111
|
+
*/
|
|
112
|
+
async getTermsStatus() {
|
|
113
|
+
return this.getAuthenticated('/v1/prediction-markets/terms/status');
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Accept the latest terms version
|
|
117
|
+
*/
|
|
118
|
+
async acceptTerms() {
|
|
119
|
+
const result = await this.postAuthenticated('/v1/prediction-markets/terms/accept', {});
|
|
120
|
+
this.termsAccepted = true;
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Ensure terms are accepted before placing orders.
|
|
125
|
+
* This is called automatically before order submission.
|
|
126
|
+
*/
|
|
127
|
+
async ensureTermsAccepted() {
|
|
128
|
+
// Skip if already accepted in this session
|
|
129
|
+
if (this.termsAccepted) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const status = await this.getTermsStatus();
|
|
134
|
+
if (!status.hasAcceptedLatest) {
|
|
135
|
+
// Terms not accepted - accept them
|
|
136
|
+
await this.acceptTerms();
|
|
137
|
+
// Log acceptance (using logger instead of console if available)
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
this.termsAccepted = true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
// If terms check fails with a specific error, re-throw
|
|
145
|
+
if (error.message?.includes('TERMS') || error.message?.includes('terms')) {
|
|
146
|
+
throw errors_1.geminiErrorMapper.mapError(error);
|
|
147
|
+
}
|
|
148
|
+
// Otherwise log warning but don't block order submission
|
|
149
|
+
// The order will fail with a clear error if terms are required
|
|
150
|
+
}
|
|
151
|
+
}
|
|
98
152
|
// -- Authenticated endpoints -----------------------------------------------
|
|
99
153
|
async submitRawOrder(payload) {
|
|
154
|
+
// ✅ Ensure terms are accepted before placing order
|
|
155
|
+
await this.ensureTermsAccepted();
|
|
100
156
|
return this.postAuthenticated('/v1/prediction-markets/order', payload);
|
|
101
157
|
}
|
|
102
158
|
async cancelRawOrder(orderId) {
|
|
@@ -153,6 +209,27 @@ class GeminiFetcher {
|
|
|
153
209
|
throw errors_1.geminiErrorMapper.mapError(error);
|
|
154
210
|
}
|
|
155
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Authenticated GET request
|
|
214
|
+
*/
|
|
215
|
+
async getAuthenticated(path) {
|
|
216
|
+
if (!this.auth) {
|
|
217
|
+
throw new Error('Authentication required. Provide apiKey and apiSecret.');
|
|
218
|
+
}
|
|
219
|
+
const url = `${this.baseUrl}${path}`;
|
|
220
|
+
const payload = {
|
|
221
|
+
request: path,
|
|
222
|
+
nonce: this.auth.nonce(),
|
|
223
|
+
};
|
|
224
|
+
const headers = this.auth.buildHeaders(payload);
|
|
225
|
+
try {
|
|
226
|
+
const response = await this.httpClient.get(url, { headers });
|
|
227
|
+
return response.data;
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
throw errors_1.geminiErrorMapper.mapError(error);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
156
233
|
async postAuthenticated(path, extraFields) {
|
|
157
234
|
if (!this.auth) {
|
|
158
235
|
throw new Error('Authentication required. Provide apiKey and apiSecret.');
|
|
@@ -33,3 +33,7 @@ export declare class GeminiTitanExchange extends PredictionMarketExchange {
|
|
|
33
33
|
watchTrades(outcomeId: string): Promise<Trade[]>;
|
|
34
34
|
close(): Promise<void>;
|
|
35
35
|
}
|
|
36
|
+
export { GeminiFetcher } from './fetcher';
|
|
37
|
+
export { GeminiAuth } from './auth';
|
|
38
|
+
export { geminiErrorMapper } from './errors';
|
|
39
|
+
export * from './types';
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GeminiTitanExchange = void 0;
|
|
17
|
+
exports.geminiErrorMapper = exports.GeminiAuth = exports.GeminiFetcher = exports.GeminiTitanExchange = void 0;
|
|
4
18
|
const BaseExchange_1 = require("../../BaseExchange");
|
|
5
19
|
const errors_1 = require("../../errors");
|
|
6
20
|
const config_1 = require("./config");
|
|
@@ -258,3 +272,10 @@ class GeminiTitanExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
258
272
|
}
|
|
259
273
|
}
|
|
260
274
|
exports.GeminiTitanExchange = GeminiTitanExchange;
|
|
275
|
+
var fetcher_2 = require("./fetcher");
|
|
276
|
+
Object.defineProperty(exports, "GeminiFetcher", { enumerable: true, get: function () { return fetcher_2.GeminiFetcher; } });
|
|
277
|
+
var auth_2 = require("./auth");
|
|
278
|
+
Object.defineProperty(exports, "GeminiAuth", { enumerable: true, get: function () { return auth_2.GeminiAuth; } });
|
|
279
|
+
var errors_3 = require("./errors");
|
|
280
|
+
Object.defineProperty(exports, "geminiErrorMapper", { enumerable: true, get: function () { return errors_3.geminiErrorMapper; } });
|
|
281
|
+
__exportStar(require("./types"), exports);
|
|
@@ -185,12 +185,25 @@ class HyperliquidExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
185
185
|
? { limit: { tif: 'Ioc' } }
|
|
186
186
|
: { limit: { tif: 'Gtc' } },
|
|
187
187
|
};
|
|
188
|
-
// Key order matters for msgpack hash: type, orders, grouping
|
|
188
|
+
// Key order matters for msgpack hash: type, orders, grouping, builder
|
|
189
189
|
const action = {
|
|
190
190
|
type: 'order',
|
|
191
191
|
orders: [orderWire],
|
|
192
192
|
grouping: 'na',
|
|
193
193
|
};
|
|
194
|
+
if (params.builder !== undefined || params.builderFee !== undefined) {
|
|
195
|
+
if (!params.builder) {
|
|
196
|
+
throw new Error('Hyperliquid builderFee requires builder address');
|
|
197
|
+
}
|
|
198
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(params.builder)) {
|
|
199
|
+
throw new Error(`Invalid Hyperliquid builder address: ${params.builder}`);
|
|
200
|
+
}
|
|
201
|
+
const builderFee = params.builderFee ?? 0;
|
|
202
|
+
if (!Number.isInteger(builderFee) || builderFee < 0) {
|
|
203
|
+
throw new Error('Hyperliquid builderFee must be a non-negative integer in tenths of a basis point');
|
|
204
|
+
}
|
|
205
|
+
action.builder = { b: params.builder.toLowerCase(), f: builderFee };
|
|
206
|
+
}
|
|
194
207
|
return {
|
|
195
208
|
exchange: this.name,
|
|
196
209
|
params,
|
|
@@ -73,6 +73,38 @@ function parseExpiryDate(expiry) {
|
|
|
73
73
|
const [, year, month, day, hour, minute] = match;
|
|
74
74
|
return new Date(`${year}-${month}-${day}T${hour}:${minute}:00Z`);
|
|
75
75
|
}
|
|
76
|
+
const MONTH_INDEX = {
|
|
77
|
+
January: 0, February: 1, March: 2, April: 3, May: 4, June: 5,
|
|
78
|
+
July: 6, August: 7, September: 8, October: 9, November: 10, December: 11,
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Fall back to scanning the question's prose description for a resolution
|
|
82
|
+
* deadline. Hyperliquid puts no structured endTime on the Outcome/Question
|
|
83
|
+
* objects, but every non-recurring question's description contains at least
|
|
84
|
+
* one "<Month> <DD>[,] <YYYY>" date (e.g. "by October 14, 2026 at 23:59 UTC").
|
|
85
|
+
* Returns the LATEST such date — questions often mention an event date and a
|
|
86
|
+
* later resolution deadline, and we want the deadline. Defaults to 23:59 UTC
|
|
87
|
+
* when no time is given.
|
|
88
|
+
*/
|
|
89
|
+
function parseDeadlineFromText(text) {
|
|
90
|
+
if (!text)
|
|
91
|
+
return undefined;
|
|
92
|
+
const re = /(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2}),?\s+(\d{4})(?:\s+at\s+(\d{1,2}):(\d{2})\s*UTC)?/g;
|
|
93
|
+
let latest;
|
|
94
|
+
let m;
|
|
95
|
+
while ((m = re.exec(text)) !== null) {
|
|
96
|
+
const [, monthName, day, year, hour, minute] = m;
|
|
97
|
+
const month = MONTH_INDEX[monthName];
|
|
98
|
+
if (month === undefined)
|
|
99
|
+
continue;
|
|
100
|
+
const d = new Date(Date.UTC(parseInt(year, 10), month, parseInt(day, 10), hour !== undefined ? parseInt(hour, 10) : 23, minute !== undefined ? parseInt(minute, 10) : 59, 0));
|
|
101
|
+
if (Number.isNaN(d.getTime()))
|
|
102
|
+
continue;
|
|
103
|
+
if (!latest || d > latest)
|
|
104
|
+
latest = d;
|
|
105
|
+
}
|
|
106
|
+
return latest;
|
|
107
|
+
}
|
|
76
108
|
/**
|
|
77
109
|
* Build a human-readable title from the parsed description.
|
|
78
110
|
*/
|
|
@@ -154,10 +186,11 @@ class HyperliquidNormalizer {
|
|
|
154
186
|
price: noPrice,
|
|
155
187
|
});
|
|
156
188
|
}
|
|
157
|
-
// Resolution date: prefer outcome-level expiry,
|
|
189
|
+
// Resolution date: prefer outcome-level expiry, then question-level expiry tag,
|
|
190
|
+
// then a deadline phrase scraped from the question's prose description.
|
|
158
191
|
const expiryDate = parsed.expiryDate
|
|
159
192
|
?? questionParsed?.expiryDate
|
|
160
|
-
??
|
|
193
|
+
?? parseDeadlineFromText(raw.question?.description ?? "");
|
|
161
194
|
// Build a descriptive title from the parsed description
|
|
162
195
|
const title = buildTitle(outcome.name, parsed, questionParsed);
|
|
163
196
|
// Derive underlying from outcome or question
|
|
@@ -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-
|
|
3
|
+
* Generated at: 2026-07-18T00:54:13.384Z
|
|
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-
|
|
6
|
+
* Generated at: 2026-07-18T00:54:13.384Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -70,25 +70,48 @@ export interface KalshiRawEventPage {
|
|
|
70
70
|
}
|
|
71
71
|
export interface KalshiRawCandlestick {
|
|
72
72
|
end_period_ts: number;
|
|
73
|
+
/** @deprecated Old API field — new API uses `volume_fp` (string) */
|
|
73
74
|
volume?: number;
|
|
75
|
+
/** New API field: cumulative volume as a fixed-point string */
|
|
76
|
+
volume_fp?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Kalshi candlestick prices come nested under `price`. The new API returns
|
|
79
|
+
* dollar-denominated string fields (e.g. `close_dollars: "0.9700"`); older
|
|
80
|
+
* responses used integer cent fields (e.g. `close: 97`). Both shapes are
|
|
81
|
+
* supported by the normalizer.
|
|
82
|
+
*/
|
|
74
83
|
price?: {
|
|
75
84
|
open?: number;
|
|
76
85
|
high?: number;
|
|
77
86
|
low?: number;
|
|
78
87
|
close?: number;
|
|
79
88
|
previous?: number;
|
|
89
|
+
open_dollars?: string;
|
|
90
|
+
high_dollars?: string;
|
|
91
|
+
low_dollars?: string;
|
|
92
|
+
close_dollars?: string;
|
|
93
|
+
mean_dollars?: string;
|
|
94
|
+
previous_dollars?: string;
|
|
80
95
|
};
|
|
81
96
|
yes_ask?: {
|
|
82
97
|
open?: number;
|
|
83
98
|
high?: number;
|
|
84
99
|
low?: number;
|
|
85
100
|
close?: number;
|
|
101
|
+
open_dollars?: string;
|
|
102
|
+
high_dollars?: string;
|
|
103
|
+
low_dollars?: string;
|
|
104
|
+
close_dollars?: string;
|
|
86
105
|
};
|
|
87
106
|
yes_bid?: {
|
|
88
107
|
open?: number;
|
|
89
108
|
high?: number;
|
|
90
109
|
low?: number;
|
|
91
110
|
close?: number;
|
|
111
|
+
open_dollars?: string;
|
|
112
|
+
high_dollars?: string;
|
|
113
|
+
low_dollars?: string;
|
|
114
|
+
close_dollars?: string;
|
|
92
115
|
};
|
|
93
116
|
[key: string]: unknown;
|
|
94
117
|
}
|
|
@@ -114,7 +137,12 @@ export interface KalshiRawTrade {
|
|
|
114
137
|
count?: number;
|
|
115
138
|
/** New API field: count as a string e.g. "424.00" */
|
|
116
139
|
count_fp?: string;
|
|
117
|
-
|
|
140
|
+
/** @deprecated removal deadline May 14, 2026 */
|
|
141
|
+
taker_side?: string;
|
|
142
|
+
/** New v2 directional fields */
|
|
143
|
+
taker_outcome_side?: string;
|
|
144
|
+
taker_book_side?: string;
|
|
145
|
+
is_block_trade?: boolean;
|
|
118
146
|
[key: string]: unknown;
|
|
119
147
|
}
|
|
120
148
|
export interface KalshiRawFill {
|
|
@@ -126,8 +154,14 @@ export interface KalshiRawFill {
|
|
|
126
154
|
/** @deprecated Old API field */
|
|
127
155
|
count?: number;
|
|
128
156
|
count_fp?: string;
|
|
129
|
-
side: string;
|
|
130
157
|
order_id: string;
|
|
158
|
+
/** @deprecated removal deadline May 14, 2026 */
|
|
159
|
+
side?: string;
|
|
160
|
+
/** @deprecated removal deadline May 14, 2026 */
|
|
161
|
+
action?: string;
|
|
162
|
+
/** New v2 directional fields */
|
|
163
|
+
outcome_side?: string;
|
|
164
|
+
book_side?: string;
|
|
131
165
|
[key: string]: unknown;
|
|
132
166
|
}
|
|
133
167
|
export interface KalshiRawOrder {
|
|
@@ -174,6 +208,7 @@ export declare class KalshiFetcher implements IExchangeFetcher<KalshiRawEvent, K
|
|
|
174
208
|
fetchRawOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
|
|
175
209
|
fetchRawHistoricalOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
|
|
176
210
|
fetchRawSeriesList(): Promise<KalshiRawSeries[]>;
|
|
211
|
+
fetchRawEventMetadata(eventTicker: string): Promise<Record<string, unknown>>;
|
|
177
212
|
fetchRawSeriesMap(): Promise<Map<string, KalshiSeriesInfo>>;
|
|
178
213
|
fetchRawEventByTicker(eventTicker: string): Promise<KalshiRawEvent[]>;
|
|
179
214
|
private fetchRawEventsDefault;
|
|
@@ -238,6 +238,16 @@ class KalshiFetcher {
|
|
|
238
238
|
throw errors_1.kalshiErrorMapper.mapError(e);
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
+
async fetchRawEventMetadata(eventTicker) {
|
|
242
|
+
try {
|
|
243
|
+
return this.ctx.callApi('GetEventMetadata', {
|
|
244
|
+
event_ticker: eventTicker.toUpperCase(),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
throw errors_1.kalshiErrorMapper.mapError(e);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
241
251
|
async fetchRawSeriesMap() {
|
|
242
252
|
try {
|
|
243
253
|
const data = await this.ctx.callApi('GetSeriesList');
|
|
@@ -24,6 +24,13 @@ export declare class KalshiExchange extends PredictionMarketExchange {
|
|
|
24
24
|
protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
|
|
25
25
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
26
26
|
protected fetchSeriesImpl(params: SeriesFetchParams): Promise<UnifiedSeries[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Fetch Kalshi's venue-native per-event metadata payload.
|
|
29
|
+
*
|
|
30
|
+
* This surfaces Kalshi's `GET /events/{event_ticker}/metadata` endpoint,
|
|
31
|
+
* which is distinct from the normalized `fetchEvent` response.
|
|
32
|
+
*/
|
|
33
|
+
fetchEventMetadata(eventTicker: string): Promise<Record<string, unknown>>;
|
|
27
34
|
fetchEventsPage(params?: EventFetchParams): Promise<{
|
|
28
35
|
events: UnifiedEvent[];
|
|
29
36
|
cursor: string | null;
|
|
@@ -151,6 +151,15 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
151
151
|
}
|
|
152
152
|
return filtered;
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Fetch Kalshi's venue-native per-event metadata payload.
|
|
156
|
+
*
|
|
157
|
+
* This surfaces Kalshi's `GET /events/{event_ticker}/metadata` endpoint,
|
|
158
|
+
* which is distinct from the normalized `fetchEvent` response.
|
|
159
|
+
*/
|
|
160
|
+
async fetchEventMetadata(eventTicker) {
|
|
161
|
+
return this.fetcher.fetchRawEventMetadata(eventTicker);
|
|
162
|
+
}
|
|
154
163
|
async fetchEventsPage(params = {}) {
|
|
155
164
|
const page = await this.fetcher.fetchRawEventPage(params);
|
|
156
165
|
const query = (params?.query || '').toLowerCase();
|
|
@@ -180,24 +180,47 @@ class KalshiNormalizer {
|
|
|
180
180
|
const p = c.price || {};
|
|
181
181
|
const ask = c.yes_ask || {};
|
|
182
182
|
const bid = c.yes_bid || {};
|
|
183
|
+
/**
|
|
184
|
+
* Kalshi's new candlestick API returns prices as dollar-denominated
|
|
185
|
+
* strings under `*_dollars` fields (e.g. `close_dollars: "0.9700"`).
|
|
186
|
+
* The legacy API returned integer cents under bare fields
|
|
187
|
+
* (e.g. `close: 97`). Prefer dollars when present; fall back to
|
|
188
|
+
* cents (converted via {@link fromKalshiCents}).
|
|
189
|
+
*/
|
|
183
190
|
const getVal = (field) => {
|
|
191
|
+
const dollarsKey = `${field}_dollars`;
|
|
192
|
+
const pd = p[dollarsKey];
|
|
193
|
+
const ad = ask[dollarsKey];
|
|
194
|
+
const bd = bid[dollarsKey];
|
|
195
|
+
if (pd != null)
|
|
196
|
+
return Number(pd);
|
|
197
|
+
if (ad != null && bd != null) {
|
|
198
|
+
return (Number(ad) + Number(bd)) / 2;
|
|
199
|
+
}
|
|
184
200
|
const pf = p[field];
|
|
185
201
|
const af = ask[field];
|
|
186
202
|
const bf = bid[field];
|
|
187
203
|
if (pf != null)
|
|
188
|
-
return pf;
|
|
204
|
+
return (0, price_1.fromKalshiCents)(pf);
|
|
189
205
|
if (af != null && bf != null) {
|
|
190
|
-
return (af + bf) / 2;
|
|
206
|
+
return ((0, price_1.fromKalshiCents)(af) + (0, price_1.fromKalshiCents)(bf)) / 2;
|
|
191
207
|
}
|
|
192
|
-
|
|
208
|
+
if (p.previous_dollars != null)
|
|
209
|
+
return Number(p.previous_dollars);
|
|
210
|
+
if (p.previous != null)
|
|
211
|
+
return (0, price_1.fromKalshiCents)(p.previous);
|
|
212
|
+
return 0;
|
|
193
213
|
};
|
|
214
|
+
const volume = c.volume_fp != null
|
|
215
|
+
? parseFloat(c.volume_fp) || 0
|
|
216
|
+
: (c.volume || 0);
|
|
194
217
|
return {
|
|
195
218
|
timestamp: c.end_period_ts * 1000,
|
|
196
|
-
open:
|
|
197
|
-
high:
|
|
198
|
-
low:
|
|
199
|
-
close:
|
|
200
|
-
volume
|
|
219
|
+
open: getVal('open'),
|
|
220
|
+
high: getVal('high'),
|
|
221
|
+
low: getVal('low'),
|
|
222
|
+
close: getVal('close'),
|
|
223
|
+
volume,
|
|
201
224
|
};
|
|
202
225
|
});
|
|
203
226
|
if (params.limit && candles.length > params.limit) {
|
|
@@ -251,7 +274,8 @@ class KalshiNormalizer {
|
|
|
251
274
|
timestamp: new Date(raw.created_time).getTime(),
|
|
252
275
|
price,
|
|
253
276
|
amount,
|
|
254
|
-
|
|
277
|
+
// Support legacy taker_side, fallback to v2 taker_outcome_side
|
|
278
|
+
side: (raw.taker_side || raw.taker_outcome_side) === 'yes' ? 'buy' : 'sell',
|
|
255
279
|
};
|
|
256
280
|
}
|
|
257
281
|
normalizeUserTrade(raw, _index) {
|
|
@@ -268,7 +292,7 @@ class KalshiNormalizer {
|
|
|
268
292
|
timestamp: new Date(raw.created_time).getTime(),
|
|
269
293
|
price,
|
|
270
294
|
amount,
|
|
271
|
-
side: raw.side === 'yes' ? 'buy' : 'sell',
|
|
295
|
+
side: (raw.side || raw.outcome_side) === 'yes' ? 'buy' : 'sell',
|
|
272
296
|
orderId: raw.order_id,
|
|
273
297
|
};
|
|
274
298
|
}
|
|
@@ -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-
|
|
3
|
+
* Generated at: 2026-07-18T00:54:13.429Z
|
|
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-
|
|
6
|
+
* Generated at: 2026-07-18T00:54:13.429Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.limitlessApiSpec = {
|
|
@@ -42,6 +42,7 @@ export declare class LimitlessExchange extends PredictionMarketExchange {
|
|
|
42
42
|
fetchPositions(address?: string): Promise<Position[]>;
|
|
43
43
|
fetchBalance(address?: string): Promise<Balance[]>;
|
|
44
44
|
watchOrderBook(outcomeId: string, limit?: number, _params?: Record<string, any>): Promise<OrderBook>;
|
|
45
|
+
unwatchOrderBook(outcomeId: string): Promise<void>;
|
|
45
46
|
watchTrades(outcomeId: string, address?: string, since?: number, limit?: number): Promise<Trade[]>;
|
|
46
47
|
/**
|
|
47
48
|
* Watch AMM price updates for a market address (Limitless only).
|
|
@@ -423,6 +423,11 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
423
423
|
const ws = this.ensureWs();
|
|
424
424
|
return ws.watchOrderBook(slug);
|
|
425
425
|
}
|
|
426
|
+
async unwatchOrderBook(outcomeId) {
|
|
427
|
+
const slug = await this.resolveSlug(outcomeId);
|
|
428
|
+
const ws = this.ensureWs();
|
|
429
|
+
await ws.unsubscribe(slug);
|
|
430
|
+
}
|
|
426
431
|
async watchTrades(outcomeId, address, since, limit) {
|
|
427
432
|
const slug = await this.resolveSlug(outcomeId);
|
|
428
433
|
const ws = this.ensureWs();
|
|
@@ -155,8 +155,12 @@ class LimitlessWebSocket {
|
|
|
155
155
|
return await Promise.race([wsUpdatePromise, timeoutPromise]);
|
|
156
156
|
}
|
|
157
157
|
catch (err) {
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
logger_1.logger.error('LimitlessWS: watchOrderBook failed', {
|
|
159
|
+
market: marketSlug,
|
|
160
|
+
error: err instanceof Error ? err.message : String(err)
|
|
161
|
+
});
|
|
162
|
+
// Do not return fake liquidity. Force the upstream loop to handle the break.
|
|
163
|
+
throw err;
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
/**
|
|
@@ -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-
|
|
3
|
+
* Generated at: 2026-07-18T00:54:13.441Z
|
|
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-
|
|
6
|
+
* Generated at: 2026-07-18T00:54:13.441Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.myriadApiSpec = {
|
|
@@ -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-
|
|
3
|
+
* Generated at: 2026-07-18T00:54:13.446Z
|
|
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-
|
|
6
|
+
* Generated at: 2026-07-18T00:54:13.446Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.opinionApiSpec = {
|
|
@@ -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-
|
|
3
|
+
* Generated at: 2026-07-18T00:54:13.392Z
|
|
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-
|
|
6
|
+
* Generated at: 2026-07-18T00:54:13.392Z
|
|
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-
|
|
3
|
+
* Generated at: 2026-07-18T00:54:13.410Z
|
|
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-
|
|
6
|
+
* Generated at: 2026-07-18T00:54:13.410Z
|
|
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-
|
|
3
|
+
* Generated at: 2026-07-18T00:54:13.407Z
|
|
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-
|
|
6
|
+
* Generated at: 2026-07-18T00:54:13.407Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketGammaSpec = {
|
|
@@ -116,7 +116,23 @@ class PolymarketUSWebSocket {
|
|
|
116
116
|
socket.on('trade', (msg) => this.handleTrade(msg));
|
|
117
117
|
socket.on('error', (err) => this.handleError(err));
|
|
118
118
|
socket.on('close', () => this.handleClose());
|
|
119
|
-
|
|
119
|
+
// Wrap connection in a timeout with proper garbage collection
|
|
120
|
+
const CONNECTION_TIMEOUT_MS = 15000; // 15s is standard for trading API handshakes
|
|
121
|
+
let timeoutHandle;
|
|
122
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
123
|
+
timeoutHandle = setTimeout(() => {
|
|
124
|
+
reject(new Error(`PolymarketUS WebSocket connection timed out after ${CONNECTION_TIMEOUT_MS}ms`));
|
|
125
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
await Promise.race([
|
|
129
|
+
socket.connect(),
|
|
130
|
+
timeoutPromise
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
clearTimeout(timeoutHandle); // Always clear the timer so the event loop can breathe
|
|
135
|
+
}
|
|
120
136
|
this.socket = socket;
|
|
121
137
|
})();
|
|
122
138
|
try {
|
|
@@ -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-
|
|
3
|
+
* Generated at: 2026-07-18T00:54:13.436Z
|
|
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-
|
|
6
|
+
* Generated at: 2026-07-18T00:54:13.436Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.probableApiSpec = {
|
|
@@ -5,6 +5,7 @@ export declare class RainExchange extends PredictionMarketExchange {
|
|
|
5
5
|
protected readonly capabilityOverrides: {
|
|
6
6
|
fetchSeries: false;
|
|
7
7
|
fetchOrderBook: "emulated";
|
|
8
|
+
fetchOrderBooks: "emulated";
|
|
8
9
|
watchOrderBook: "emulated";
|
|
9
10
|
watchTrades: "emulated";
|
|
10
11
|
fetchOpenOrders: "emulated";
|
|
@@ -21,6 +22,7 @@ export declare class RainExchange extends PredictionMarketExchange {
|
|
|
21
22
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
22
23
|
fetchOHLCV(outcomeId: string, params: OHLCVParams): Promise<PriceCandle[]>;
|
|
23
24
|
fetchOrderBook(outcomeId: string, _limit?: number, _params?: Record<string, any>): Promise<OrderBook>;
|
|
25
|
+
fetchOrderBooks(outcomeIds: string[]): Promise<Record<string, OrderBook>>;
|
|
24
26
|
fetchTrades(outcomeId: string, params: TradesParams | HistoryFilterParams): Promise<Trade[]>;
|
|
25
27
|
fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
|
|
26
28
|
fetchPositions(address?: string): Promise<Position[]>;
|
|
@@ -37,6 +37,8 @@ class RainExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
37
37
|
capabilityOverrides = {
|
|
38
38
|
fetchSeries: false,
|
|
39
39
|
fetchOrderBook: 'emulated',
|
|
40
|
+
// Emulated batch: loops the single-outcome fetchOrderBook (no native batch endpoint).
|
|
41
|
+
fetchOrderBooks: 'emulated',
|
|
40
42
|
watchOrderBook: 'emulated',
|
|
41
43
|
watchTrades: 'emulated',
|
|
42
44
|
// Trading is on-chain via Rain SDK + viem. open/closed orders + fetchOrder
|
|
@@ -115,6 +117,13 @@ class RainExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
115
117
|
return { bids: [], asks: [], timestamp: Date.now() };
|
|
116
118
|
return this.normalizer.normalizeOrderBook(raw, outcomeId);
|
|
117
119
|
}
|
|
120
|
+
async fetchOrderBooks(outcomeIds) {
|
|
121
|
+
const response = {};
|
|
122
|
+
for (const outcomeId of outcomeIds) {
|
|
123
|
+
response[outcomeId] = await this.fetchOrderBook(outcomeId);
|
|
124
|
+
}
|
|
125
|
+
return response;
|
|
126
|
+
}
|
|
118
127
|
async fetchTrades(outcomeId, params) {
|
|
119
128
|
const parts = outcomeId.split(':');
|
|
120
129
|
if (parts.length < 2) {
|
|
@@ -48,6 +48,13 @@ function buildContractOutcome(contract, marketId) {
|
|
|
48
48
|
price: 0,
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
function parseSmarketsResolutionDate(event) {
|
|
52
|
+
// Smarkets list and detail endpoints currently return end_date as null for
|
|
53
|
+
// many events. start_datetime is an event start, not a market resolution,
|
|
54
|
+
// so leave the unified resolution date unset instead of using a misleading
|
|
55
|
+
// fallback that can corrupt cross-venue ordering.
|
|
56
|
+
return event.end_date ? new Date(event.end_date) : undefined;
|
|
57
|
+
}
|
|
51
58
|
// ----------------------------------------------------------------------------
|
|
52
59
|
// Normalizer
|
|
53
60
|
// ----------------------------------------------------------------------------
|
|
@@ -87,12 +94,7 @@ class SmarketsNormalizer {
|
|
|
87
94
|
tags.push(cat);
|
|
88
95
|
}
|
|
89
96
|
}
|
|
90
|
-
|
|
91
|
-
const resolutionDate = event.end_date
|
|
92
|
-
? new Date(event.end_date)
|
|
93
|
-
: event.start_datetime
|
|
94
|
-
? new Date(event.start_datetime)
|
|
95
|
-
: new Date();
|
|
97
|
+
const resolutionDate = parseSmarketsResolutionDate(event);
|
|
96
98
|
const um = {
|
|
97
99
|
marketId: market.id,
|
|
98
100
|
eventId: event.id,
|
|
@@ -11,5 +11,4 @@
|
|
|
11
11
|
* GET /api/p2p/offers - List open P2P offers (status, matchId, sport, limit, offset)
|
|
12
12
|
* GET /api/p2p/offers/:id - Get a single P2P offer by ID
|
|
13
13
|
* GET /api/p2p/my?wallet=... - Get user activity (created offers, matched bets, parlays)
|
|
14
|
-
* GET /api/events/upcoming - List upcoming sports events (sport, limit)
|
|
15
14
|
*/
|
|
@@ -12,6 +12,5 @@
|
|
|
12
12
|
* GET /api/p2p/offers - List open P2P offers (status, matchId, sport, limit, offset)
|
|
13
13
|
* GET /api/p2p/offers/:id - Get a single P2P offer by ID
|
|
14
14
|
* GET /api/p2p/my?wallet=... - Get user activity (created offers, matched bets, parlays)
|
|
15
|
-
* GET /api/events/upcoming - List upcoming sports events (sport, limit)
|
|
16
15
|
*/
|
|
17
16
|
// No runtime exports — this file serves as API documentation only.
|
|
@@ -21,6 +21,12 @@ export interface SuibetsRawOffer {
|
|
|
21
21
|
isOnchain?: boolean;
|
|
22
22
|
onchainOfferId?: string;
|
|
23
23
|
leagueName?: string;
|
|
24
|
+
onchainState?: {
|
|
25
|
+
makerRemaining?: string;
|
|
26
|
+
totalLiquidity?: string;
|
|
27
|
+
filledAmount?: string;
|
|
28
|
+
status?: string;
|
|
29
|
+
};
|
|
24
30
|
}
|
|
25
31
|
export interface SuibetsRawEvent {
|
|
26
32
|
id: string;
|
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SuibetsNormalizer = void 0;
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
5
|
function liquidity(offer) {
|
|
6
|
-
|
|
6
|
+
// Prioritize live onchainState.makerRemaining data before falling back to legacy fields
|
|
7
|
+
const remaining = offer.onchainState?.makerRemaining ?? offer.remainingStake ?? offer.creatorStake;
|
|
7
8
|
return (0, utils_1.mistToSui)(remaining);
|
|
8
9
|
}
|
|
9
10
|
class SuibetsNormalizer {
|
package/dist/server/openapi.yaml
CHANGED
|
@@ -2,8 +2,8 @@ openapi: 3.0.0
|
|
|
2
2
|
info:
|
|
3
3
|
title: PMXT Sidecar API
|
|
4
4
|
description: >-
|
|
5
|
-
A unified local sidecar API for prediction markets
|
|
6
|
-
|
|
5
|
+
A unified local sidecar API for supported prediction markets. This API acts as a JSON-RPC-style gateway over PMXT
|
|
6
|
+
exchange implementations, with venue and write support varying by method and capability.
|
|
7
7
|
version: 0.4.4
|
|
8
8
|
servers:
|
|
9
9
|
- url: 'http://localhost:3847'
|
|
@@ -680,6 +680,33 @@ paths:
|
|
|
680
680
|
description: >-
|
|
681
681
|
Fetch a single event by lookup parameters. Convenience wrapper around fetchEvents() that returns a single result
|
|
682
682
|
or throws EventNotFound.
|
|
683
|
+
'/api/{exchange}/fetchEventMetadata':
|
|
684
|
+
get:
|
|
685
|
+
summary: Fetch Event Metadata
|
|
686
|
+
operationId: fetchEventMetadata
|
|
687
|
+
parameters:
|
|
688
|
+
- $ref: '#/components/parameters/ExchangeParam'
|
|
689
|
+
- in: query
|
|
690
|
+
name: eventTicker
|
|
691
|
+
required: true
|
|
692
|
+
schema:
|
|
693
|
+
type: string
|
|
694
|
+
responses:
|
|
695
|
+
'200':
|
|
696
|
+
description: Fetch Event Metadata response
|
|
697
|
+
content:
|
|
698
|
+
application/json:
|
|
699
|
+
schema:
|
|
700
|
+
allOf:
|
|
701
|
+
- $ref: '#/components/schemas/BaseResponse'
|
|
702
|
+
- type: object
|
|
703
|
+
properties:
|
|
704
|
+
data:
|
|
705
|
+
type: object
|
|
706
|
+
additionalProperties: {}
|
|
707
|
+
description: >-
|
|
708
|
+
Fetch venue-native metadata for a specific event when the exchange exposes a dedicated metadata endpoint. Kalshi
|
|
709
|
+
implements this for `GET /events/{event_ticker}/metadata`.
|
|
683
710
|
'/api/{exchange}/fetchOHLCV':
|
|
684
711
|
get:
|
|
685
712
|
summary: Fetch OHLCV
|
|
@@ -3654,6 +3681,12 @@ components:
|
|
|
3654
3681
|
fee:
|
|
3655
3682
|
type: number
|
|
3656
3683
|
description: 'Optional fee rate (e.g., 1000 for 0.1%)'
|
|
3684
|
+
builder:
|
|
3685
|
+
type: string
|
|
3686
|
+
description: Hyperliquid builder address to attach to the order action.
|
|
3687
|
+
builderFee:
|
|
3688
|
+
type: number
|
|
3689
|
+
description: Hyperliquid builder fee in tenths of a basis point (e.g. 10 = 1 bp).
|
|
3657
3690
|
tickSize:
|
|
3658
3691
|
type: number
|
|
3659
3692
|
description: Optional override for Limitless/Polymarket
|
package/dist/types.d.ts
CHANGED
|
@@ -290,6 +290,10 @@ export interface CreateOrderParams {
|
|
|
290
290
|
denom?: 'usdc' | 'shares';
|
|
291
291
|
slippage_pct?: number;
|
|
292
292
|
fee?: number;
|
|
293
|
+
/** Hyperliquid builder address to attach to the order action. */
|
|
294
|
+
builder?: string;
|
|
295
|
+
/** Hyperliquid builder fee in tenths of a basis point (e.g. 10 = 1 bp). */
|
|
296
|
+
builderFee?: number;
|
|
293
297
|
tickSize?: number;
|
|
294
298
|
negRisk?: boolean;
|
|
295
299
|
onBehalfOf?: number;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxt-core",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.52.0",
|
|
4
|
+
"description": "Local sidecar API for supported prediction markets.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"repository": {
|
|
@@ -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.
|
|
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.
|
|
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.52.0,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.52.0,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",
|