pmxt-core 2.39.0 → 2.40.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/exchanges/baozi/fetcher.js +28 -8
- package/dist/exchanges/baozi/index.js +6 -4
- package/dist/exchanges/gemini-titan/auth.d.ts +34 -0
- package/dist/exchanges/gemini-titan/auth.js +80 -0
- package/dist/exchanges/gemini-titan/config.d.ts +15 -0
- package/dist/exchanges/gemini-titan/config.js +24 -0
- package/dist/exchanges/gemini-titan/errors.d.ts +20 -0
- package/dist/exchanges/gemini-titan/errors.js +75 -0
- package/dist/exchanges/gemini-titan/fetcher.d.ts +26 -0
- package/dist/exchanges/gemini-titan/fetcher.js +148 -0
- package/dist/exchanges/gemini-titan/index.d.ts +31 -0
- package/dist/exchanges/gemini-titan/index.js +188 -0
- package/dist/exchanges/gemini-titan/normalizer.d.ts +13 -0
- package/dist/exchanges/gemini-titan/normalizer.js +229 -0
- package/dist/exchanges/gemini-titan/types.d.ts +220 -0
- package/dist/exchanges/gemini-titan/types.js +6 -0
- package/dist/exchanges/gemini-titan/utils.d.ts +30 -0
- package/dist/exchanges/gemini-titan/utils.js +57 -0
- package/dist/exchanges/gemini-titan/websocket.d.ts +46 -0
- package/dist/exchanges/gemini-titan/websocket.js +295 -0
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/kalshi/fetcher.js +6 -2
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/index.js +3 -6
- package/dist/exchanges/metaculus/fetchEvents.js +7 -2
- package/dist/exchanges/mock/index.d.ts +55 -0
- package/dist/exchanges/mock/index.js +603 -0
- package/dist/exchanges/mock/seededRng.d.ts +10 -0
- package/dist/exchanges/mock/seededRng.js +48 -0
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/websocket.d.ts +4 -0
- package/dist/exchanges/myriad/websocket.js +51 -6
- 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/auth.js +5 -2
- package/dist/exchanges/polymarket/index.d.ts +2 -2
- package/dist/exchanges/polymarket/index.js +2 -1
- package/dist/exchanges/polymarket/websocket.d.ts +51 -0
- package/dist/exchanges/polymarket/websocket.js +125 -0
- package/dist/exchanges/polymarket_us/normalizer.js +5 -1
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/index.js +9 -6
- package/dist/exchanges/smarkets/fetcher.js +6 -2
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -1
- package/dist/router/Router.js +55 -21
- package/dist/server/exchange-factory.js +9 -0
- package/dist/server/openapi.yaml +22 -0
- package/dist/server/ws-handler.js +13 -3
- package/package.json +3 -3
- package/dist/exchanges/baozi/price.test.d.ts +0 -1
- package/dist/exchanges/baozi/price.test.js +0 -33
- package/dist/exchanges/kalshi/kalshi.test.d.ts +0 -1
- package/dist/exchanges/kalshi/kalshi.test.js +0 -641
- package/dist/exchanges/kalshi/price.test.d.ts +0 -1
- package/dist/exchanges/kalshi/price.test.js +0 -24
- package/dist/exchanges/myriad/price.test.d.ts +0 -1
- package/dist/exchanges/myriad/price.test.js +0 -17
- package/dist/exchanges/polymarket_us/errors.test.d.ts +0 -1
- package/dist/exchanges/polymarket_us/errors.test.js +0 -54
- package/dist/exchanges/polymarket_us/index.test.d.ts +0 -8
- package/dist/exchanges/polymarket_us/index.test.js +0 -237
- package/dist/exchanges/polymarket_us/normalizer.test.d.ts +0 -1
- package/dist/exchanges/polymarket_us/normalizer.test.js +0 -224
- package/dist/exchanges/polymarket_us/price.test.d.ts +0 -1
- package/dist/exchanges/polymarket_us/price.test.js +0 -131
- package/dist/exchanges/polymarket_us/websocket.test.d.ts +0 -8
- package/dist/exchanges/polymarket_us/websocket.test.js +0 -162
- package/dist/exchanges/smarkets/price.test.d.ts +0 -1
- package/dist/exchanges/smarkets/price.test.js +0 -50
- package/dist/router/Router.test.d.ts +0 -1
- package/dist/router/Router.test.js +0 -328
- package/dist/router/client.test.d.ts +0 -1
- package/dist/router/client.test.js +0 -177
|
@@ -5,6 +5,7 @@ exports.MyriadWebSocket = void 0;
|
|
|
5
5
|
// We implement a poll-based fallback that resolves promises
|
|
6
6
|
// on each polling interval, matching the CCXT Pro async pattern.
|
|
7
7
|
const DEFAULT_POLL_INTERVAL = 5000; // 5 seconds
|
|
8
|
+
const MAX_CONSECUTIVE_FAILURES = 5;
|
|
8
9
|
class MyriadWebSocket {
|
|
9
10
|
callApi;
|
|
10
11
|
fetchOrderBook;
|
|
@@ -12,8 +13,12 @@ class MyriadWebSocket {
|
|
|
12
13
|
orderBookTimers = new Map();
|
|
13
14
|
tradeTimers = new Map();
|
|
14
15
|
orderBookResolvers = new Map();
|
|
16
|
+
orderBookRejecters = new Map();
|
|
15
17
|
tradeResolvers = new Map();
|
|
18
|
+
tradeRejecters = new Map();
|
|
16
19
|
lastTradeTimestamp = new Map();
|
|
20
|
+
orderBookFailureCount = new Map();
|
|
21
|
+
tradeFailureCount = new Map();
|
|
17
22
|
closed = false;
|
|
18
23
|
constructor(callApi, fetchOrderBook, pollInterval) {
|
|
19
24
|
this.callApi = callApi;
|
|
@@ -23,11 +28,13 @@ class MyriadWebSocket {
|
|
|
23
28
|
async watchOrderBook(outcomeId) {
|
|
24
29
|
if (this.closed)
|
|
25
30
|
throw new Error('WebSocket connection is closed');
|
|
26
|
-
return new Promise((resolve) => {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
27
32
|
if (!this.orderBookResolvers.has(outcomeId)) {
|
|
28
33
|
this.orderBookResolvers.set(outcomeId, []);
|
|
34
|
+
this.orderBookRejecters.set(outcomeId, []);
|
|
29
35
|
}
|
|
30
36
|
this.orderBookResolvers.get(outcomeId).push(resolve);
|
|
37
|
+
this.orderBookRejecters.get(outcomeId).push(reject);
|
|
31
38
|
if (!this.orderBookTimers.has(outcomeId)) {
|
|
32
39
|
this.startOrderBookPolling(outcomeId);
|
|
33
40
|
}
|
|
@@ -36,11 +43,13 @@ class MyriadWebSocket {
|
|
|
36
43
|
async watchTrades(outcomeId) {
|
|
37
44
|
if (this.closed)
|
|
38
45
|
throw new Error('WebSocket connection is closed');
|
|
39
|
-
return new Promise((resolve) => {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
40
47
|
if (!this.tradeResolvers.has(outcomeId)) {
|
|
41
48
|
this.tradeResolvers.set(outcomeId, []);
|
|
49
|
+
this.tradeRejecters.set(outcomeId, []);
|
|
42
50
|
}
|
|
43
51
|
this.tradeResolvers.get(outcomeId).push(resolve);
|
|
52
|
+
this.tradeRejecters.get(outcomeId).push(reject);
|
|
44
53
|
if (!this.tradeTimers.has(outcomeId)) {
|
|
45
54
|
this.startTradePolling(outcomeId);
|
|
46
55
|
}
|
|
@@ -57,20 +66,39 @@ class MyriadWebSocket {
|
|
|
57
66
|
this.orderBookTimers.clear();
|
|
58
67
|
this.tradeTimers.clear();
|
|
59
68
|
this.orderBookResolvers.clear();
|
|
69
|
+
this.orderBookRejecters.clear();
|
|
60
70
|
this.tradeResolvers.clear();
|
|
71
|
+
this.tradeRejecters.clear();
|
|
61
72
|
}
|
|
62
73
|
startOrderBookPolling(id) {
|
|
63
74
|
const poll = async () => {
|
|
64
75
|
try {
|
|
65
76
|
const book = await this.fetchOrderBook(id);
|
|
77
|
+
this.orderBookFailureCount.set(id, 0);
|
|
66
78
|
const resolvers = this.orderBookResolvers.get(id) || [];
|
|
67
79
|
this.orderBookResolvers.set(id, []);
|
|
80
|
+
this.orderBookRejecters.set(id, []);
|
|
68
81
|
for (const resolve of resolvers) {
|
|
69
82
|
resolve(book);
|
|
70
83
|
}
|
|
71
84
|
}
|
|
72
|
-
catch {
|
|
73
|
-
|
|
85
|
+
catch (error) {
|
|
86
|
+
const failures = (this.orderBookFailureCount.get(id) || 0) + 1;
|
|
87
|
+
this.orderBookFailureCount.set(id, failures);
|
|
88
|
+
console.warn(`[Myriad] watchOrderBook poll failed for outcomeId=${id} (consecutive failures: ${failures}):`, error);
|
|
89
|
+
if (failures >= MAX_CONSECUTIVE_FAILURES) {
|
|
90
|
+
const timer = this.orderBookTimers.get(id);
|
|
91
|
+
if (timer)
|
|
92
|
+
clearInterval(timer);
|
|
93
|
+
this.orderBookTimers.delete(id);
|
|
94
|
+
this.orderBookFailureCount.delete(id);
|
|
95
|
+
const rejecters = this.orderBookRejecters.get(id) || [];
|
|
96
|
+
this.orderBookResolvers.set(id, []);
|
|
97
|
+
this.orderBookRejecters.set(id, []);
|
|
98
|
+
for (const reject of rejecters) {
|
|
99
|
+
reject(error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
74
102
|
}
|
|
75
103
|
};
|
|
76
104
|
// Immediate first poll
|
|
@@ -110,14 +138,31 @@ class MyriadWebSocket {
|
|
|
110
138
|
const maxTs = Math.max(...trades.map(t => t.timestamp));
|
|
111
139
|
this.lastTradeTimestamp.set(id, maxTs + 1);
|
|
112
140
|
}
|
|
141
|
+
this.tradeFailureCount.set(id, 0);
|
|
113
142
|
const resolvers = this.tradeResolvers.get(id) || [];
|
|
114
143
|
this.tradeResolvers.set(id, []);
|
|
144
|
+
this.tradeRejecters.set(id, []);
|
|
115
145
|
for (const resolve of resolvers) {
|
|
116
146
|
resolve(trades);
|
|
117
147
|
}
|
|
118
148
|
}
|
|
119
|
-
catch {
|
|
120
|
-
|
|
149
|
+
catch (error) {
|
|
150
|
+
const failures = (this.tradeFailureCount.get(id) || 0) + 1;
|
|
151
|
+
this.tradeFailureCount.set(id, failures);
|
|
152
|
+
console.warn(`[Myriad] watchTrades poll failed for outcomeId=${id} (consecutive failures: ${failures}):`, error);
|
|
153
|
+
if (failures >= MAX_CONSECUTIVE_FAILURES) {
|
|
154
|
+
const timer = this.tradeTimers.get(id);
|
|
155
|
+
if (timer)
|
|
156
|
+
clearInterval(timer);
|
|
157
|
+
this.tradeTimers.delete(id);
|
|
158
|
+
this.tradeFailureCount.delete(id);
|
|
159
|
+
const rejecters = this.tradeRejecters.get(id) || [];
|
|
160
|
+
this.tradeResolvers.set(id, []);
|
|
161
|
+
this.tradeRejecters.set(id, []);
|
|
162
|
+
for (const reject of rejecters) {
|
|
163
|
+
reject(error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
121
166
|
}
|
|
122
167
|
};
|
|
123
168
|
poll();
|
|
@@ -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-08T22:46:15.519Z
|
|
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-08T22:46:15.519Z
|
|
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-05-
|
|
3
|
+
* Generated at: 2026-05-08T22:46:15.474Z
|
|
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-08T22:46:15.474Z
|
|
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-08T22:46:15.487Z
|
|
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-08T22:46:15.487Z
|
|
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-08T22:46:15.485Z
|
|
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-08T22:46:15.485Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketGammaSpec = {
|
|
@@ -214,8 +214,11 @@ class PolymarketAuth {
|
|
|
214
214
|
signatureType = discovered.signatureType;
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
-
catch {
|
|
218
|
-
// Discovery failure — fall through to default below.
|
|
217
|
+
catch (err) {
|
|
218
|
+
// Discovery failure — fall through to default (Gnosis Safe) below.
|
|
219
|
+
// A network/HTTP error here does not block trading; we just lose
|
|
220
|
+
// the ability to auto-detect signatureType.
|
|
221
|
+
console.warn('[polymarket] signature-type discovery failed; defaulting to Gnosis Safe', { error: err instanceof Error ? err.message : String(err) });
|
|
219
222
|
}
|
|
220
223
|
}
|
|
221
224
|
if (signatureType === undefined) {
|
|
@@ -3,8 +3,8 @@ import { SubscribedAddressSnapshot, SubscriptionOption } from '../../subscriber/
|
|
|
3
3
|
import { buildPolymarketTradesActivity, POLYMARKET_DEFAULT_SUBSCRIPTION } from '../../subscriber/external/goldsky';
|
|
4
4
|
import { WatcherConfig } from '../../subscriber/watcher';
|
|
5
5
|
import { Balance, BuiltOrder, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from '../../types';
|
|
6
|
-
import { PolymarketWebSocketConfig } from './websocket';
|
|
7
|
-
export type { PolymarketWebSocketConfig, WatcherConfig };
|
|
6
|
+
import { PolymarketWebSocketConfig, UserChannelCallback, UserChannelEvent, PolymarketUserChannelCreds } from './websocket';
|
|
7
|
+
export type { PolymarketWebSocketConfig, WatcherConfig, UserChannelCallback, UserChannelEvent, PolymarketUserChannelCreds };
|
|
8
8
|
export { POLYMARKET_DEFAULT_SUBSCRIPTION, buildPolymarketTradesActivity };
|
|
9
9
|
export interface PolymarketExchangeOptions {
|
|
10
10
|
credentials?: ExchangeCredentials;
|
|
@@ -358,7 +358,8 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
358
358
|
total = onChain;
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
|
-
catch {
|
|
361
|
+
catch (err) {
|
|
362
|
+
console.warn('[polymarket] on-chain balance lookup failed; using CLOB balance only', { error: err instanceof Error ? err.message : String(err) });
|
|
362
363
|
}
|
|
363
364
|
}
|
|
364
365
|
// 2. Fetch open orders to calculate locked funds.
|
|
@@ -8,6 +8,34 @@
|
|
|
8
8
|
import { SubscribedAddressSnapshot, SubscriptionOption } from '../../subscriber/base';
|
|
9
9
|
import { WatcherConfig } from '../../subscriber/watcher';
|
|
10
10
|
import { OrderBook, Trade } from '../../types';
|
|
11
|
+
export interface PolymarketUserChannelCreds {
|
|
12
|
+
apiKey: string;
|
|
13
|
+
secret: string;
|
|
14
|
+
passphrase: string;
|
|
15
|
+
}
|
|
16
|
+
export interface UserTradeEvent {
|
|
17
|
+
asset_id: string;
|
|
18
|
+
event_type: 'trade';
|
|
19
|
+
price: string;
|
|
20
|
+
size: string;
|
|
21
|
+
side: string;
|
|
22
|
+
status: string;
|
|
23
|
+
maker_orders?: any[];
|
|
24
|
+
timestamp?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface UserOrderEvent {
|
|
27
|
+
asset_id: string;
|
|
28
|
+
event_type: 'order';
|
|
29
|
+
order_id: string;
|
|
30
|
+
price: string;
|
|
31
|
+
original_size: string;
|
|
32
|
+
matched_size?: string;
|
|
33
|
+
side: string;
|
|
34
|
+
type: string;
|
|
35
|
+
timestamp?: string;
|
|
36
|
+
}
|
|
37
|
+
export type UserChannelEvent = UserTradeEvent | UserOrderEvent;
|
|
38
|
+
export type UserChannelCallback = (event: UserChannelEvent) => void;
|
|
11
39
|
export interface PolymarketWebSocketConfig {
|
|
12
40
|
/** Reconnection check interval in milliseconds (default: 5000) */
|
|
13
41
|
reconnectIntervalMs?: number;
|
|
@@ -17,6 +45,8 @@ export interface PolymarketWebSocketConfig {
|
|
|
17
45
|
watcherConfig?: WatcherConfig;
|
|
18
46
|
/** Timeout in ms for watch methods to receive data (default: 30000). 0 = no timeout. */
|
|
19
47
|
watchTimeoutMs?: number;
|
|
48
|
+
/** API credentials for the authenticated user channel (fills/orders). */
|
|
49
|
+
userChannelCreds?: PolymarketUserChannelCreds;
|
|
20
50
|
}
|
|
21
51
|
/**
|
|
22
52
|
* Wrapper around @nevuamarkets/poly-websockets that provides CCXT Pro-style
|
|
@@ -36,6 +66,27 @@ export declare class PolymarketWebSocket {
|
|
|
36
66
|
watchTrades(outcomeId: string, address?: string): Promise<Trade[]>;
|
|
37
67
|
watchAddress(address: string, types: SubscriptionOption[]): Promise<SubscribedAddressSnapshot>;
|
|
38
68
|
unwatchAddress(address: string): Promise<void>;
|
|
69
|
+
private userWs;
|
|
70
|
+
private userCallbacks;
|
|
71
|
+
private userPingInterval;
|
|
72
|
+
private userReconnectTimer;
|
|
73
|
+
private userConditionIds;
|
|
74
|
+
/**
|
|
75
|
+
* Subscribe to authenticated fill/order events via Polymarket's user channel.
|
|
76
|
+
* Requires `userChannelCreds` in the config.
|
|
77
|
+
*
|
|
78
|
+
* @param conditionIds - Market condition IDs to monitor
|
|
79
|
+
* @param callback - Called for each trade or order event
|
|
80
|
+
*/
|
|
81
|
+
watchUserFills(conditionIds: string[], callback: UserChannelCallback): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Stop watching user fills and close the user channel WebSocket.
|
|
84
|
+
*/
|
|
85
|
+
unwatchUserFills(): Promise<void>;
|
|
86
|
+
private connectUserChannel;
|
|
87
|
+
private sendUserSubscription;
|
|
88
|
+
private scheduleUserReconnect;
|
|
89
|
+
private closeUserChannel;
|
|
39
90
|
close(): Promise<void>;
|
|
40
91
|
private ensureInitialized;
|
|
41
92
|
private handleBookSnapshot;
|
|
@@ -124,10 +124,135 @@ class PolymarketWebSocket {
|
|
|
124
124
|
async unwatchAddress(address) {
|
|
125
125
|
return this.watcher.unwatch(address);
|
|
126
126
|
}
|
|
127
|
+
// -----------------------------------------------------------------
|
|
128
|
+
// Authenticated User Channel (fills + order updates)
|
|
129
|
+
// -----------------------------------------------------------------
|
|
130
|
+
userWs = null;
|
|
131
|
+
userCallbacks = [];
|
|
132
|
+
userPingInterval = null;
|
|
133
|
+
userReconnectTimer = null;
|
|
134
|
+
userConditionIds = [];
|
|
135
|
+
/**
|
|
136
|
+
* Subscribe to authenticated fill/order events via Polymarket's user channel.
|
|
137
|
+
* Requires `userChannelCreds` in the config.
|
|
138
|
+
*
|
|
139
|
+
* @param conditionIds - Market condition IDs to monitor
|
|
140
|
+
* @param callback - Called for each trade or order event
|
|
141
|
+
*/
|
|
142
|
+
async watchUserFills(conditionIds, callback) {
|
|
143
|
+
const creds = this.config.userChannelCreds;
|
|
144
|
+
if (!creds) {
|
|
145
|
+
throw new Error('User channel requires API credentials. Pass userChannelCreds in PolymarketWebSocketConfig.');
|
|
146
|
+
}
|
|
147
|
+
this.userCallbacks.push(callback);
|
|
148
|
+
this.userConditionIds = [...new Set([...this.userConditionIds, ...conditionIds])];
|
|
149
|
+
if (this.userWs) {
|
|
150
|
+
// Already connected — just re-subscribe with updated condition IDs.
|
|
151
|
+
this.sendUserSubscription(creds);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
await this.connectUserChannel(creds);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Stop watching user fills and close the user channel WebSocket.
|
|
158
|
+
*/
|
|
159
|
+
async unwatchUserFills() {
|
|
160
|
+
this.userCallbacks = [];
|
|
161
|
+
this.userConditionIds = [];
|
|
162
|
+
this.closeUserChannel();
|
|
163
|
+
}
|
|
164
|
+
async connectUserChannel(creds) {
|
|
165
|
+
const WebSocket = (await Promise.resolve().then(() => __importStar(require('ws')))).default;
|
|
166
|
+
const url = 'wss://ws-subscriptions-clob.polymarket.com/ws/user';
|
|
167
|
+
this.userWs = new WebSocket(url);
|
|
168
|
+
this.userWs.on('open', () => {
|
|
169
|
+
console.log('[polymarket-ws] user channel connected');
|
|
170
|
+
this.sendUserSubscription(creds);
|
|
171
|
+
// Ping every 10 seconds to keep the connection alive.
|
|
172
|
+
if (this.userPingInterval)
|
|
173
|
+
clearInterval(this.userPingInterval);
|
|
174
|
+
this.userPingInterval = setInterval(() => {
|
|
175
|
+
if (this.userWs?.readyState === WebSocket.OPEN) {
|
|
176
|
+
this.userWs.ping();
|
|
177
|
+
}
|
|
178
|
+
}, 10_000);
|
|
179
|
+
});
|
|
180
|
+
this.userWs.on('message', (raw) => {
|
|
181
|
+
try {
|
|
182
|
+
const events = JSON.parse(raw.toString());
|
|
183
|
+
const arr = Array.isArray(events) ? events : [events];
|
|
184
|
+
for (const event of arr) {
|
|
185
|
+
for (const cb of this.userCallbacks) {
|
|
186
|
+
try {
|
|
187
|
+
cb(event);
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
console.error('[polymarket-ws] user callback error:', e);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// Non-JSON control message (pong, etc.) — ignore.
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
this.userWs.on('close', () => {
|
|
200
|
+
console.warn('[polymarket-ws] user channel disconnected, reconnecting in 5s');
|
|
201
|
+
this.scheduleUserReconnect(creds);
|
|
202
|
+
});
|
|
203
|
+
this.userWs.on('error', (err) => {
|
|
204
|
+
console.error('[polymarket-ws] user channel error:', err.message);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
sendUserSubscription(creds) {
|
|
208
|
+
if (!this.userWs || this.userWs.readyState !== 1)
|
|
209
|
+
return;
|
|
210
|
+
const msg = JSON.stringify({
|
|
211
|
+
auth: {
|
|
212
|
+
apiKey: creds.apiKey,
|
|
213
|
+
secret: creds.secret,
|
|
214
|
+
passphrase: creds.passphrase,
|
|
215
|
+
},
|
|
216
|
+
markets: this.userConditionIds,
|
|
217
|
+
type: 'user',
|
|
218
|
+
});
|
|
219
|
+
this.userWs.send(msg);
|
|
220
|
+
}
|
|
221
|
+
scheduleUserReconnect(creds) {
|
|
222
|
+
if (this.userReconnectTimer)
|
|
223
|
+
return;
|
|
224
|
+
this.userReconnectTimer = setTimeout(async () => {
|
|
225
|
+
this.userReconnectTimer = null;
|
|
226
|
+
if (this.userCallbacks.length > 0) {
|
|
227
|
+
try {
|
|
228
|
+
await this.connectUserChannel(creds);
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
console.error('[polymarket-ws] reconnect failed:', e.message);
|
|
232
|
+
this.scheduleUserReconnect(creds);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}, 5000);
|
|
236
|
+
}
|
|
237
|
+
closeUserChannel() {
|
|
238
|
+
if (this.userPingInterval) {
|
|
239
|
+
clearInterval(this.userPingInterval);
|
|
240
|
+
this.userPingInterval = null;
|
|
241
|
+
}
|
|
242
|
+
if (this.userReconnectTimer) {
|
|
243
|
+
clearTimeout(this.userReconnectTimer);
|
|
244
|
+
this.userReconnectTimer = null;
|
|
245
|
+
}
|
|
246
|
+
if (this.userWs) {
|
|
247
|
+
this.userWs.close();
|
|
248
|
+
this.userWs = null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
127
251
|
async close() {
|
|
128
252
|
if (this.manager) {
|
|
129
253
|
await this.manager.clearState();
|
|
130
254
|
}
|
|
255
|
+
this.closeUserChannel();
|
|
131
256
|
this.watcher.close();
|
|
132
257
|
}
|
|
133
258
|
async ensureInitialized() {
|
|
@@ -136,7 +136,7 @@ function intentToSide(intent) {
|
|
|
136
136
|
case 'ORDER_INTENT_SELL_SHORT':
|
|
137
137
|
return 'sell';
|
|
138
138
|
default:
|
|
139
|
-
|
|
139
|
+
throw new Error(`[polymarket_us] unknown order intent: ${String(intent)}`);
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
function intentToOutcomeId(intent, slug) {
|
|
@@ -378,6 +378,10 @@ class PolymarketUSNormalizer {
|
|
|
378
378
|
timestamp: parseTimeToMs(trade.createTime),
|
|
379
379
|
price: (0, price_1.fromAmount)(trade.price),
|
|
380
380
|
amount: parseFloat(trade.qty || '0'),
|
|
381
|
+
// The Polymarket US activity trade object (Trade$1) does not carry
|
|
382
|
+
// an intent/side field — only price, qty, and state are provided.
|
|
383
|
+
// Side cannot be derived from activity data; consumers must cross-reference
|
|
384
|
+
// with their own order history if side is required.
|
|
381
385
|
side: 'unknown',
|
|
382
386
|
outcomeId: undefined,
|
|
383
387
|
};
|
|
@@ -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-08T22:46:15.509Z
|
|
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-08T22:46:15.509Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.probableApiSpec = {
|
|
@@ -272,13 +272,14 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
async fetchOpenOrders(marketId) {
|
|
275
|
+
if (!marketId) {
|
|
276
|
+
throw new Error('[Probable] fetchOpenOrders requires a marketId: this exchange does not support fetching all orders across all markets');
|
|
277
|
+
}
|
|
275
278
|
try {
|
|
276
279
|
const auth = this.ensureAuth();
|
|
277
280
|
const client = auth.getClobClient();
|
|
278
281
|
const params = {};
|
|
279
|
-
|
|
280
|
-
params.eventId = marketId;
|
|
281
|
-
}
|
|
282
|
+
params.eventId = marketId;
|
|
282
283
|
const orders = await client.getOpenOrders(params);
|
|
283
284
|
const orderList = Array.isArray(orders) ? orders : orders?.data || [];
|
|
284
285
|
return orderList.map((o) => ({
|
|
@@ -330,7 +331,8 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
330
331
|
total = parseFloat(formatUnits(balance, 18));
|
|
331
332
|
}
|
|
332
333
|
catch (chainError) {
|
|
333
|
-
|
|
334
|
+
console.warn('[Probable] fetchBalance: on-chain USDT balance fetch failed:', chainError);
|
|
335
|
+
throw chainError;
|
|
334
336
|
}
|
|
335
337
|
// Calculate locked from open BUY orders
|
|
336
338
|
let locked = 0;
|
|
@@ -342,8 +344,9 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
342
344
|
}
|
|
343
345
|
}
|
|
344
346
|
}
|
|
345
|
-
catch {
|
|
346
|
-
|
|
347
|
+
catch (ordersError) {
|
|
348
|
+
console.warn('[Probable] fetchBalance: failed to fetch open orders for locked balance calculation:', ordersError);
|
|
349
|
+
throw ordersError;
|
|
347
350
|
}
|
|
348
351
|
return [{
|
|
349
352
|
currency: 'USDT',
|
|
@@ -313,8 +313,12 @@ class SmarketsFetcher {
|
|
|
313
313
|
});
|
|
314
314
|
return (data.volumes || []);
|
|
315
315
|
}
|
|
316
|
-
catch {
|
|
317
|
-
// Volumes are non-critical; return empty on failure
|
|
316
|
+
catch (err) {
|
|
317
|
+
// Volumes are non-critical; return empty on failure but log it.
|
|
318
|
+
console.warn('[smarkets] volume fetch failed for batch', {
|
|
319
|
+
marketIds: batch,
|
|
320
|
+
error: err instanceof Error ? err.message : String(err),
|
|
321
|
+
});
|
|
318
322
|
return [];
|
|
319
323
|
}
|
|
320
324
|
}));
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './types';
|
|
|
3
3
|
export * from './utils/math';
|
|
4
4
|
export { parseOpenApiSpec } from './utils/openapi';
|
|
5
5
|
export * from './errors';
|
|
6
|
+
export * from './exchanges/mock';
|
|
6
7
|
export * from './exchanges/polymarket';
|
|
7
8
|
export * from './exchanges/limitless';
|
|
8
9
|
export * from './exchanges/kalshi';
|
|
@@ -15,10 +16,12 @@ export * from './exchanges/metaculus';
|
|
|
15
16
|
export * from './exchanges/smarkets';
|
|
16
17
|
export * from './exchanges/polymarket_us';
|
|
17
18
|
export * from './exchanges/hyperliquid';
|
|
19
|
+
export * from './exchanges/gemini-titan';
|
|
18
20
|
export * from './router';
|
|
19
21
|
export * from './server/app';
|
|
20
22
|
export * from './server/utils/port-manager';
|
|
21
23
|
export * from './server/utils/lock-file';
|
|
24
|
+
import { MockExchange } from './exchanges/mock';
|
|
22
25
|
import { PolymarketExchange } from './exchanges/polymarket';
|
|
23
26
|
import { LimitlessExchange } from './exchanges/limitless';
|
|
24
27
|
import { KalshiExchange } from './exchanges/kalshi';
|
|
@@ -31,8 +34,10 @@ import { MetaculusExchange } from './exchanges/metaculus';
|
|
|
31
34
|
import { SmarketsExchange } from './exchanges/smarkets';
|
|
32
35
|
import { PolymarketUSExchange } from './exchanges/polymarket_us';
|
|
33
36
|
import { HyperliquidExchange } from './exchanges/hyperliquid';
|
|
37
|
+
import { GeminiTitanExchange } from './exchanges/gemini-titan';
|
|
34
38
|
import { Router } from './router';
|
|
35
39
|
declare const pmxt: {
|
|
40
|
+
Mock: typeof MockExchange;
|
|
36
41
|
Polymarket: typeof PolymarketExchange;
|
|
37
42
|
Limitless: typeof LimitlessExchange;
|
|
38
43
|
Kalshi: typeof KalshiExchange;
|
|
@@ -45,8 +50,10 @@ declare const pmxt: {
|
|
|
45
50
|
Smarkets: typeof SmarketsExchange;
|
|
46
51
|
PolymarketUS: typeof PolymarketUSExchange;
|
|
47
52
|
Hyperliquid: typeof HyperliquidExchange;
|
|
53
|
+
GeminiTitan: typeof GeminiTitanExchange;
|
|
48
54
|
Router: typeof Router;
|
|
49
55
|
};
|
|
56
|
+
export declare const Mock: typeof MockExchange;
|
|
50
57
|
export declare const Polymarket: typeof PolymarketExchange;
|
|
51
58
|
export declare const Limitless: typeof LimitlessExchange;
|
|
52
59
|
export declare const Kalshi: typeof KalshiExchange;
|
|
@@ -59,4 +66,5 @@ export declare const Metaculus: typeof MetaculusExchange;
|
|
|
59
66
|
export declare const Smarkets: typeof SmarketsExchange;
|
|
60
67
|
export declare const PolymarketUS: typeof PolymarketUSExchange;
|
|
61
68
|
export declare const Hyperliquid: typeof HyperliquidExchange;
|
|
69
|
+
export declare const GeminiTitan: typeof GeminiTitanExchange;
|
|
62
70
|
export default pmxt;
|
package/dist/index.js
CHANGED
|
@@ -14,13 +14,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.Hyperliquid = exports.PolymarketUS = exports.Smarkets = exports.Metaculus = exports.Opinion = exports.Myriad = exports.Baozi = exports.Probable = exports.KalshiDemo = exports.Kalshi = exports.Limitless = exports.Polymarket = exports.parseOpenApiSpec = void 0;
|
|
17
|
+
exports.GeminiTitan = exports.Hyperliquid = exports.PolymarketUS = exports.Smarkets = exports.Metaculus = exports.Opinion = exports.Myriad = exports.Baozi = exports.Probable = exports.KalshiDemo = exports.Kalshi = exports.Limitless = exports.Polymarket = exports.Mock = exports.parseOpenApiSpec = void 0;
|
|
18
18
|
__exportStar(require("./BaseExchange"), exports);
|
|
19
19
|
__exportStar(require("./types"), exports);
|
|
20
20
|
__exportStar(require("./utils/math"), exports);
|
|
21
21
|
var openapi_1 = require("./utils/openapi");
|
|
22
22
|
Object.defineProperty(exports, "parseOpenApiSpec", { enumerable: true, get: function () { return openapi_1.parseOpenApiSpec; } });
|
|
23
23
|
__exportStar(require("./errors"), exports);
|
|
24
|
+
__exportStar(require("./exchanges/mock"), exports);
|
|
24
25
|
__exportStar(require("./exchanges/polymarket"), exports);
|
|
25
26
|
__exportStar(require("./exchanges/limitless"), exports);
|
|
26
27
|
__exportStar(require("./exchanges/kalshi"), exports);
|
|
@@ -33,10 +34,12 @@ __exportStar(require("./exchanges/metaculus"), exports);
|
|
|
33
34
|
__exportStar(require("./exchanges/smarkets"), exports);
|
|
34
35
|
__exportStar(require("./exchanges/polymarket_us"), exports);
|
|
35
36
|
__exportStar(require("./exchanges/hyperliquid"), exports);
|
|
37
|
+
__exportStar(require("./exchanges/gemini-titan"), exports);
|
|
36
38
|
__exportStar(require("./router"), exports);
|
|
37
39
|
__exportStar(require("./server/app"), exports);
|
|
38
40
|
__exportStar(require("./server/utils/port-manager"), exports);
|
|
39
41
|
__exportStar(require("./server/utils/lock-file"), exports);
|
|
42
|
+
const mock_1 = require("./exchanges/mock");
|
|
40
43
|
const polymarket_1 = require("./exchanges/polymarket");
|
|
41
44
|
const limitless_1 = require("./exchanges/limitless");
|
|
42
45
|
const kalshi_1 = require("./exchanges/kalshi");
|
|
@@ -49,8 +52,10 @@ const metaculus_1 = require("./exchanges/metaculus");
|
|
|
49
52
|
const smarkets_1 = require("./exchanges/smarkets");
|
|
50
53
|
const polymarket_us_1 = require("./exchanges/polymarket_us");
|
|
51
54
|
const hyperliquid_1 = require("./exchanges/hyperliquid");
|
|
55
|
+
const gemini_titan_1 = require("./exchanges/gemini-titan");
|
|
52
56
|
const router_1 = require("./router");
|
|
53
57
|
const pmxt = {
|
|
58
|
+
Mock: mock_1.MockExchange,
|
|
54
59
|
Polymarket: polymarket_1.PolymarketExchange,
|
|
55
60
|
Limitless: limitless_1.LimitlessExchange,
|
|
56
61
|
Kalshi: kalshi_1.KalshiExchange,
|
|
@@ -63,8 +68,10 @@ const pmxt = {
|
|
|
63
68
|
Smarkets: smarkets_1.SmarketsExchange,
|
|
64
69
|
PolymarketUS: polymarket_us_1.PolymarketUSExchange,
|
|
65
70
|
Hyperliquid: hyperliquid_1.HyperliquidExchange,
|
|
71
|
+
GeminiTitan: gemini_titan_1.GeminiTitanExchange,
|
|
66
72
|
Router: router_1.Router,
|
|
67
73
|
};
|
|
74
|
+
exports.Mock = mock_1.MockExchange;
|
|
68
75
|
exports.Polymarket = polymarket_1.PolymarketExchange;
|
|
69
76
|
exports.Limitless = limitless_1.LimitlessExchange;
|
|
70
77
|
exports.Kalshi = kalshi_1.KalshiExchange;
|
|
@@ -77,4 +84,5 @@ exports.Metaculus = metaculus_1.MetaculusExchange;
|
|
|
77
84
|
exports.Smarkets = smarkets_1.SmarketsExchange;
|
|
78
85
|
exports.PolymarketUS = polymarket_us_1.PolymarketUSExchange;
|
|
79
86
|
exports.Hyperliquid = hyperliquid_1.HyperliquidExchange;
|
|
87
|
+
exports.GeminiTitan = gemini_titan_1.GeminiTitanExchange;
|
|
80
88
|
exports.default = pmxt;
|