pmxt-core 2.33.5 → 2.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import { AxiosInstance } from 'axios';
2
2
  import { SubscribedAddressSnapshot, SubscriptionOption } from './subscriber/base';
3
3
  import { Balance, BuiltOrder, CandleInterval, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from './types';
4
4
  import { ExecutionPriceResult } from './utils/math';
5
- import type { FetchMatchesParams, FetchEventMatchesParams, FetchArbitrageParams, MatchResult, EventMatchResult, PriceComparison, ArbitrageOpportunity } from './router/types';
5
+ import type { FetchMarketMatchesParams, FetchMatchesParams, FetchEventMatchesParams, FetchArbitrageParams, MatchResult, EventMatchResult, PriceComparison, ArbitrageOpportunity } from './router/types';
6
6
  export interface ApiEndpoint {
7
7
  /** HTTP verb for the endpoint (e.g. GET, POST). */
8
8
  method: string;
@@ -229,6 +229,8 @@ export interface ExchangeHas {
229
229
  /** Whether this exchange supports submitting a pre-built order. */
230
230
  submitOrder: ExchangeCapability;
231
231
  /** Whether this exchange supports fetching cross-venue market matches. */
232
+ fetchMarketMatches: ExchangeCapability;
233
+ /** @deprecated Use {@link fetchMarketMatches} instead. */
232
234
  fetchMatches: ExchangeCapability;
233
235
  /** Whether this exchange supports fetching cross-venue event matches. */
234
236
  fetchEventMatches: ExchangeCapability;
@@ -576,6 +578,10 @@ export declare abstract class PredictionMarketExchange {
576
578
  * @param params - Match filter parameters (marketId, relation, minConfidence, etc.)
577
579
  * @returns Array of matched markets with relation and confidence
578
580
  */
581
+ fetchMarketMatches(params: FetchMarketMatchesParams): Promise<MatchResult[]>;
582
+ /**
583
+ * @deprecated Use {@link fetchMarketMatches} instead.
584
+ */
579
585
  fetchMatches(params: FetchMatchesParams): Promise<MatchResult[]>;
580
586
  /**
581
587
  * Find the same or related event on other venues. Given an event on one venue, discover semantically equivalent events across every other venue PMXT ingests — including market-level match details for each child market.
@@ -769,8 +769,15 @@ class PredictionMarketExchange {
769
769
  * @param params - Match filter parameters (marketId, relation, minConfidence, etc.)
770
770
  * @returns Array of matched markets with relation and confidence
771
771
  */
772
+ async fetchMarketMatches(params) {
773
+ throw new Error("Method fetchMarketMatches not implemented.");
774
+ }
775
+ /**
776
+ * @deprecated Use {@link fetchMarketMatches} instead.
777
+ */
772
778
  async fetchMatches(params) {
773
- throw new Error("Method fetchMatches not implemented.");
779
+ console.warn('[pmxt] fetchMatches is deprecated, use fetchMarketMatches instead');
780
+ return this.fetchMarketMatches(params);
774
781
  }
775
782
  /**
776
783
  * Find the same or related event on other venues. Given an event on one venue, discover semantically equivalent events across every other venue PMXT ingests — including market-level match details for each child market.
@@ -934,7 +941,7 @@ class PredictionMarketExchange {
934
941
  'watchAddress', 'unwatchAddress', 'watchOrderBook',
935
942
  'unwatchOrderBook', 'watchTrades', 'fetchMyTrades',
936
943
  'fetchClosedOrders', 'fetchAllOrders', 'buildOrder', 'submitOrder',
937
- 'fetchMatches', 'fetchEventMatches', 'compareMarketPrices',
944
+ 'fetchMarketMatches', 'fetchMatches', 'fetchEventMatches', 'compareMarketPrices',
938
945
  'fetchHedges', 'fetchArbitrage',
939
946
  ];
940
947
  // Compile-time exhaustiveness check: fails tsc if a key exists in
@@ -947,7 +954,7 @@ class PredictionMarketExchange {
947
954
  unwatchAddress: true, watchOrderBook: true, unwatchOrderBook: true,
948
955
  watchTrades: true, fetchMyTrades: true, fetchClosedOrders: true,
949
956
  fetchAllOrders: true, buildOrder: true, submitOrder: true,
950
- fetchMatches: true, fetchEventMatches: true, compareMarketPrices: true,
957
+ fetchMarketMatches: true, fetchMatches: true, fetchEventMatches: true, compareMarketPrices: true,
951
958
  fetchHedges: true, fetchArbitrage: true,
952
959
  };
953
960
  /**
@@ -959,6 +966,7 @@ class PredictionMarketExchange {
959
966
  static _capabilityDelegates = {
960
967
  fetchMarkets: 'fetchMarketsImpl',
961
968
  fetchEvents: 'fetchEventsImpl',
969
+ fetchMatches: 'fetchMarketMatches',
962
970
  };
963
971
  /**
964
972
  * Derive the capability map by comparing this instance's prototype chain
@@ -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-04-23T11:21:13.069Z
3
+ * Generated at: 2026-04-23T20:31:35.050Z
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-04-23T11:21:13.069Z
6
+ * Generated at: 2026-04-23T20:31:35.050Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -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-04-23T11:21:13.107Z
3
+ * Generated at: 2026-04-23T20:31:35.096Z
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-04-23T11:21:13.107Z
6
+ * Generated at: 2026-04-23T20:31:35.096Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -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-04-23T11:21:13.118Z
3
+ * Generated at: 2026-04-23T20:31:35.107Z
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-04-23T11:21:13.118Z
6
+ * Generated at: 2026-04-23T20:31:35.107Z
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-04-23T11:21:13.123Z
3
+ * Generated at: 2026-04-23T20:31:35.112Z
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-04-23T11:21:13.123Z
6
+ * Generated at: 2026-04-23T20:31:35.112Z
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-04-23T11:21:13.076Z
3
+ * Generated at: 2026-04-23T20:31:35.057Z
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-04-23T11:21:13.076Z
6
+ * Generated at: 2026-04-23T20:31:35.057Z
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-04-23T11:21:13.090Z
3
+ * Generated at: 2026-04-23T20:31:35.073Z
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-04-23T11:21:13.090Z
6
+ * Generated at: 2026-04-23T20:31:35.073Z
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-04-23T11:21:13.088Z
3
+ * Generated at: 2026-04-23T20:31:35.070Z
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-04-23T11:21:13.088Z
6
+ * Generated at: 2026-04-23T20:31:35.070Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -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-04-23T11:21:13.111Z
3
+ * Generated at: 2026-04-23T20:31:35.101Z
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-04-23T11:21:13.111Z
6
+ * Generated at: 2026-04-23T20:31:35.101Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -38,7 +38,18 @@ function mapMarketToUnified(market, event) {
38
38
  volume: Number(market.volume || 0),
39
39
  liquidity: Number(market.liquidity || 0),
40
40
  openInterest: 0,
41
- url: `https://probable.markets/markets/${market.market_slug || market.slug || market.id}`,
41
+ url: (() => {
42
+ const eventSlug = event?.slug || market._parentEvent?.slug;
43
+ const marketId = market.id;
44
+ if (eventSlug) {
45
+ return `https://probable.markets/event/${eventSlug}?market=${marketId}`;
46
+ }
47
+ const eventId = event?.id || market.event_id;
48
+ if (eventId) {
49
+ return `https://probable.markets/event/${eventId}?market=${marketId}`;
50
+ }
51
+ return `https://probable.markets/event/?market=${marketId}`;
52
+ })(),
42
53
  image: market.icon || event?.icon || event?.image || undefined,
43
54
  category: event?.category || market.category || undefined,
44
55
  tags: market.tags || event?.tags || [],
@@ -65,7 +76,7 @@ function mapEventToUnified(event) {
65
76
  markets,
66
77
  volume24h: markets.reduce((sum, m) => sum + m.volume24h, 0),
67
78
  volume: markets.some(m => m.volume !== undefined) ? markets.reduce((sum, m) => sum + (m.volume ?? 0), 0) : undefined,
68
- url: `https://probable.markets/events/${event.slug || event.id}`,
79
+ url: `https://probable.markets/event/${event.slug || event.id}`,
69
80
  image: event.icon || event.image || undefined,
70
81
  category: event.category || undefined,
71
82
  tags: event.tags || [],
@@ -1,15 +1,25 @@
1
1
  import { PredictionMarketExchange, type MarketFetchParams, type EventFetchParams } from '../BaseExchange';
2
2
  import type { UnifiedMarket, UnifiedEvent } from '../types';
3
- import type { RouterOptions, MatchResult, EventMatchResult, PriceComparison, ArbitrageOpportunity, FetchMatchesParams, FetchEventMatchesParams, FetchArbitrageParams } from './types';
3
+ import type { RouterOptions, MatchResult, EventMatchResult, PriceComparison, ArbitrageOpportunity, FetchMarketMatchesParams, FetchMatchesParams, FetchEventMatchesParams, FetchArbitrageParams } from './types';
4
4
  export declare class Router extends PredictionMarketExchange {
5
5
  private readonly client;
6
6
  constructor(options: RouterOptions);
7
7
  get name(): string;
8
8
  protected fetchMarketsImpl(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
9
9
  protected fetchEventsImpl(params?: EventFetchParams): Promise<UnifiedEvent[]>;
10
+ fetchMarketMatches(params: FetchMarketMatchesParams): Promise<MatchResult[]>;
11
+ /** @deprecated Use {@link fetchMarketMatches} instead. */
10
12
  fetchMatches(params: FetchMatchesParams): Promise<MatchResult[]>;
11
13
  fetchEventMatches(params: FetchEventMatchesParams): Promise<EventMatchResult[]>;
12
- compareMarketPrices(params: FetchMatchesParams): Promise<PriceComparison[]>;
13
- fetchHedges(params: FetchMatchesParams): Promise<PriceComparison[]>;
14
+ compareMarketPrices(params: FetchMarketMatchesParams): Promise<PriceComparison[]>;
15
+ fetchHedges(params: FetchMarketMatchesParams): Promise<PriceComparison[]>;
14
16
  fetchArbitrage(params?: FetchArbitrageParams): Promise<ArbitrageOpportunity[]>;
17
+ /**
18
+ * Bulk arbitrage via `GET /v0/arbitrage`. One round-trip.
19
+ */
20
+ private fetchArbitrageBulk;
21
+ /**
22
+ * Legacy N+1 fallback: fetch markets, then fetch matches per-market.
23
+ */
24
+ private fetchArbitrageFallback;
15
25
  }
@@ -38,7 +38,10 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
38
38
  // -----------------------------------------------------------------------
39
39
  // Cross-exchange market matches
40
40
  // -----------------------------------------------------------------------
41
- async fetchMatches(params) {
41
+ async fetchMarketMatches(params) {
42
+ if (params.market && !params.marketId) {
43
+ params = { ...params, marketId: params.market.marketId };
44
+ }
42
45
  const response = await this.client.getMarketMatches(params);
43
46
  const matches = response.matches ?? [];
44
47
  return matches.map((m) => ({
@@ -50,10 +53,18 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
50
53
  bestAsk: m.market?.bestAsk ?? null,
51
54
  }));
52
55
  }
56
+ /** @deprecated Use {@link fetchMarketMatches} instead. */
57
+ async fetchMatches(params) {
58
+ console.warn('[pmxt] fetchMatches is deprecated, use fetchMarketMatches instead');
59
+ return this.fetchMarketMatches(params);
60
+ }
53
61
  // -----------------------------------------------------------------------
54
62
  // Cross-exchange event matches
55
63
  // -----------------------------------------------------------------------
56
64
  async fetchEventMatches(params) {
65
+ if (params.event && !params.eventId) {
66
+ params = { ...params, eventId: params.event.id };
67
+ }
57
68
  const response = await this.client.getEventMatches(params);
58
69
  return response.matches ?? [];
59
70
  }
@@ -61,7 +72,10 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
61
72
  // Price comparison: identity matches with live prices
62
73
  // -----------------------------------------------------------------------
63
74
  async compareMarketPrices(params) {
64
- const matches = await this.fetchMatches({
75
+ if (params.market && !params.marketId) {
76
+ params = { ...params, marketId: params.market.marketId };
77
+ }
78
+ const matches = await this.fetchMarketMatches({
65
79
  ...params,
66
80
  relation: 'identity',
67
81
  includePrices: true,
@@ -80,7 +94,10 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
80
94
  // Hedging: subset/superset matches with live prices
81
95
  // -----------------------------------------------------------------------
82
96
  async fetchHedges(params) {
83
- const matches = await this.fetchMatches({
97
+ if (params.market && !params.marketId) {
98
+ params = { ...params, marketId: params.market.marketId };
99
+ }
100
+ const matches = await this.fetchMarketMatches({
84
101
  ...params,
85
102
  includePrices: true,
86
103
  });
@@ -97,57 +114,104 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
97
114
  }));
98
115
  }
99
116
  // -----------------------------------------------------------------------
100
- // Arbitrage: scan identity matches for price spreads
117
+ // Arbitrage: scan matches for price spreads
101
118
  // -----------------------------------------------------------------------
102
119
  async fetchArbitrage(params) {
120
+ // Try the dedicated bulk endpoint first (single DB query).
121
+ try {
122
+ return await this.fetchArbitrageBulk(params);
123
+ }
124
+ catch {
125
+ // Dedicated endpoint not available — fall back to N+1 approach.
126
+ return this.fetchArbitrageFallback(params);
127
+ }
128
+ }
129
+ /**
130
+ * Bulk arbitrage via `GET /v0/arbitrage`. One round-trip.
131
+ */
132
+ async fetchArbitrageBulk(params) {
133
+ const query = {};
134
+ const relations = params?.relations ?? ['identity'];
135
+ query.relations = relations.join(',');
136
+ if (params?.minSpread !== undefined)
137
+ query.minSpread = String(params.minSpread);
138
+ if (params?.category)
139
+ query.category = params.category;
140
+ if (params?.limit !== undefined)
141
+ query.limit = String(params.limit);
142
+ const res = await this.client.getArbitrage(query);
143
+ const items = res.data ?? [];
144
+ return items.map((r) => ({
145
+ marketA: r.marketA,
146
+ marketB: r.marketB,
147
+ spread: r.spread ?? 0,
148
+ buyVenue: r.buyVenue ?? '',
149
+ sellVenue: r.sellVenue ?? '',
150
+ buyPrice: r.buyPrice ?? 0,
151
+ sellPrice: r.sellPrice ?? 0,
152
+ relation: r.relation,
153
+ confidence: r.confidence,
154
+ }));
155
+ }
156
+ /**
157
+ * Legacy N+1 fallback: fetch markets, then fetch matches per-market.
158
+ */
159
+ async fetchArbitrageFallback(params) {
103
160
  const minSpread = params?.minSpread ?? 0;
104
161
  const limit = params?.limit ?? 50;
162
+ const relations = params?.relations ?? ['identity'];
105
163
  const markets = await this.fetchMarkets({
106
164
  category: params?.category,
107
165
  limit,
108
166
  });
109
167
  const opportunities = [];
110
168
  for (const market of markets) {
111
- const matches = await this.fetchMatches({
112
- marketId: market.marketId,
113
- relation: 'identity',
114
- includePrices: true,
115
- });
116
- if (matches.length === 0)
117
- continue;
118
- const sourceAsk = market.outcomes[0]?.price ?? null;
119
- const sourceBid = sourceAsk;
120
- const sourceVenue = market.sourceExchange ?? '';
121
- for (const match of matches) {
122
- const matchBid = match.bestBid;
123
- const matchAsk = match.bestAsk;
124
- const matchVenue = match.market.sourceExchange ?? '';
125
- if (sourceAsk !== null && matchBid !== null) {
126
- const spread = matchBid - sourceAsk;
127
- if (spread >= minSpread) {
128
- opportunities.push({
129
- marketA: market,
130
- marketB: match.market,
131
- spread,
132
- buyVenue: sourceVenue,
133
- sellVenue: matchVenue,
134
- buyPrice: sourceAsk,
135
- sellPrice: matchBid,
136
- });
169
+ for (const relation of relations) {
170
+ const matches = await this.fetchMarketMatches({
171
+ marketId: market.marketId,
172
+ relation,
173
+ includePrices: true,
174
+ });
175
+ if (matches.length === 0)
176
+ continue;
177
+ const sourceAsk = market.outcomes[0]?.price ?? null;
178
+ const sourceBid = sourceAsk;
179
+ const sourceVenue = market.sourceExchange ?? '';
180
+ for (const match of matches) {
181
+ const matchBid = match.bestBid;
182
+ const matchAsk = match.bestAsk;
183
+ const matchVenue = match.market.sourceExchange ?? '';
184
+ if (sourceAsk !== null && matchBid !== null) {
185
+ const spread = matchBid - sourceAsk;
186
+ if (spread >= minSpread) {
187
+ opportunities.push({
188
+ marketA: market,
189
+ marketB: match.market,
190
+ spread,
191
+ buyVenue: sourceVenue,
192
+ sellVenue: matchVenue,
193
+ buyPrice: sourceAsk,
194
+ sellPrice: matchBid,
195
+ relation: match.relation,
196
+ confidence: match.confidence,
197
+ });
198
+ }
137
199
  }
138
- }
139
- if (matchAsk !== null && sourceBid !== null) {
140
- const spread = sourceBid - matchAsk;
141
- if (spread >= minSpread) {
142
- opportunities.push({
143
- marketA: match.market,
144
- marketB: market,
145
- spread,
146
- buyVenue: matchVenue,
147
- sellVenue: sourceVenue,
148
- buyPrice: matchAsk,
149
- sellPrice: sourceBid,
150
- });
200
+ if (matchAsk !== null && sourceBid !== null) {
201
+ const spread = sourceBid - matchAsk;
202
+ if (spread >= minSpread) {
203
+ opportunities.push({
204
+ marketA: match.market,
205
+ marketB: market,
206
+ spread,
207
+ buyVenue: matchVenue,
208
+ sellVenue: sourceVenue,
209
+ buyPrice: matchAsk,
210
+ sellPrice: sourceBid,
211
+ relation: match.relation,
212
+ confidence: match.confidence,
213
+ });
214
+ }
151
215
  }
152
216
  }
153
217
  }
@@ -21,7 +21,7 @@ describe('Router', () => {
21
21
  expect(() => new Router_1.Router({ apiKey: 'key' })).not.toThrow();
22
22
  });
23
23
  });
24
- describe('fetchMatches', () => {
24
+ describe('fetchMarketMatches', () => {
25
25
  it('returns matches from the API using marketId', async () => {
26
26
  const mockApiResponse = [
27
27
  {
@@ -32,7 +32,7 @@ describe('Router', () => {
32
32
  },
33
33
  ];
34
34
  clientInstance.getMarketMatches = jest.fn().mockResolvedValue({ matches: mockApiResponse });
35
- const result = await router.fetchMatches({ marketId: 'm1', relation: 'identity' });
35
+ const result = await router.fetchMarketMatches({ marketId: 'm1', relation: 'identity' });
36
36
  expect(clientInstance.getMarketMatches).toHaveBeenCalledWith({ marketId: 'm1', relation: 'identity' });
37
37
  expect(result[0].confidence).toBe(0.95);
38
38
  expect(result[0].bestBid).toBe(0.60);
@@ -40,15 +40,33 @@ describe('Router', () => {
40
40
  });
41
41
  it('accepts slug as identifier', async () => {
42
42
  clientInstance.getMarketMatches = jest.fn().mockResolvedValue({ matches: [] });
43
- await router.fetchMatches({ slug: 'btc-100k' });
43
+ await router.fetchMarketMatches({ slug: 'btc-100k' });
44
44
  expect(clientInstance.getMarketMatches).toHaveBeenCalledWith({ slug: 'btc-100k' });
45
45
  });
46
46
  it('returns empty array when no matches', async () => {
47
47
  clientInstance.getMarketMatches = jest.fn().mockResolvedValue({});
48
- const result = await router.fetchMatches({ marketId: 'm1' });
48
+ const result = await router.fetchMarketMatches({ marketId: 'm1' });
49
49
  expect(result).toEqual([]);
50
50
  });
51
51
  });
52
+ describe('fetchMatches (deprecated)', () => {
53
+ it('delegates to fetchMarketMatches and logs deprecation warning', async () => {
54
+ const mockApiResponse = [
55
+ {
56
+ market: { marketId: 'k1', sourceExchange: 'kalshi', bestBid: 0.60, bestAsk: 0.65 },
57
+ relation: 'identity',
58
+ confidence: 0.95,
59
+ reasoning: 'Same resolution condition.',
60
+ },
61
+ ];
62
+ clientInstance.getMarketMatches = jest.fn().mockResolvedValue({ matches: mockApiResponse });
63
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
64
+ const result = await router.fetchMatches({ marketId: 'm1' });
65
+ expect(warnSpy).toHaveBeenCalledWith('[pmxt] fetchMatches is deprecated, use fetchMarketMatches instead');
66
+ expect(result[0].confidence).toBe(0.95);
67
+ warnSpy.mockRestore();
68
+ });
69
+ });
52
70
  describe('fetchEventMatches', () => {
53
71
  it('returns event matches from the API', async () => {
54
72
  const mockMatches = [
@@ -139,6 +157,7 @@ describe('Router', () => {
139
157
  });
140
158
  describe('capabilities', () => {
141
159
  it('reports matching methods as supported', () => {
160
+ expect(router.has.fetchMarketMatches).toBe(true);
142
161
  expect(router.has.fetchMatches).toBe(true);
143
162
  expect(router.has.fetchEventMatches).toBe(true);
144
163
  expect(router.has.compareMarketPrices).toBe(true);
@@ -6,6 +6,7 @@ export declare class PmxtApiClient {
6
6
  getEventMatches(params: FetchEventMatchesParams): Promise<any>;
7
7
  searchMarkets(params?: RouterMarketSearchParams): Promise<any>;
8
8
  searchEvents(params?: RouterEventSearchParams): Promise<any>;
9
+ getArbitrage(query?: Record<string, string>): Promise<any>;
9
10
  private request;
10
11
  private mapError;
11
12
  }
@@ -85,6 +85,10 @@ class PmxtApiClient {
85
85
  const res = await this.request('GET', '/v0/events', query);
86
86
  return res.data;
87
87
  }
88
+ async getArbitrage(query) {
89
+ const res = await this.request('GET', '/v0/arbitrage', query);
90
+ return res.data;
91
+ }
88
92
  // -----------------------------------------------------------------------
89
93
  // Internal
90
94
  // -----------------------------------------------------------------------
@@ -33,8 +33,14 @@ export interface ArbitrageOpportunity {
33
33
  sellVenue: string;
34
34
  buyPrice: number;
35
35
  sellPrice: number;
36
+ /** The set-theoretic relation between the two markets (e.g. identity, subset). */
37
+ relation?: MatchRelation;
38
+ /** Match confidence score (0.0 to 1.0). */
39
+ confidence?: number;
36
40
  }
37
- export interface FetchMatchesParams {
41
+ export interface FetchMarketMatchesParams {
42
+ /** Pass a UnifiedMarket directly instead of marketId/slug/url. */
43
+ market?: UnifiedMarket;
38
44
  marketId?: string;
39
45
  slug?: string;
40
46
  url?: string;
@@ -43,7 +49,11 @@ export interface FetchMatchesParams {
43
49
  limit?: number;
44
50
  includePrices?: boolean;
45
51
  }
52
+ /** @deprecated Use {@link FetchMarketMatchesParams} instead. */
53
+ export type FetchMatchesParams = FetchMarketMatchesParams;
46
54
  export interface FetchEventMatchesParams {
55
+ /** Pass a UnifiedEvent directly instead of eventId/slug. */
56
+ event?: UnifiedEvent;
47
57
  eventId?: string;
48
58
  slug?: string;
49
59
  relation?: MatchRelation;
@@ -55,6 +65,8 @@ export interface FetchArbitrageParams {
55
65
  minSpread?: number;
56
66
  category?: string;
57
67
  limit?: number;
68
+ /** Comma-separated relation types to include (default: 'identity'). */
69
+ relations?: MatchRelation[];
58
70
  }
59
71
  export interface RouterMarketSearchParams {
60
72
  query?: string;
@@ -358,6 +358,16 @@
358
358
  "verb": "post",
359
359
  "args": []
360
360
  },
361
+ "fetchMarketMatches": {
362
+ "verb": "get",
363
+ "args": [
364
+ {
365
+ "name": "params",
366
+ "kind": "object",
367
+ "optional": false
368
+ }
369
+ ]
370
+ },
361
371
  "fetchMatches": {
362
372
  "verb": "get",
363
373
  "args": [
@@ -1464,12 +1464,19 @@ paths:
1464
1464
  description: >-
1465
1465
  Close all WebSocket connections and clean up resources. Call this when you're done streaming to properly release
1466
1466
  connections.
1467
- '/api/{exchange}/fetchMatches':
1467
+ '/api/{exchange}/fetchMarketMatches':
1468
1468
  get:
1469
1469
  summary: Find Similar Markets
1470
- operationId: fetchMatches
1470
+ operationId: fetchMarketMatches
1471
1471
  parameters:
1472
1472
  - $ref: '#/components/parameters/ExchangeParam'
1473
+ - in: query
1474
+ name: market
1475
+ required: false
1476
+ schema:
1477
+ allOf:
1478
+ - $ref: '#/components/schemas/UnifiedMarket'
1479
+ description: Pass a UnifiedMarket directly instead of marketId/slug/url.
1473
1480
  - in: query
1474
1481
  name: marketId
1475
1482
  required: false
@@ -1529,12 +1536,88 @@ paths:
1529
1536
  Find the same or related market on other venues. Given a market on one venue, discover semantically equivalent
1530
1537
  markets across every other venue PMXT ingests — each with a relation type (identity, subset, superset, overlap,
1531
1538
  disjoint), confidence score, and reasoning.
1539
+ '/api/{exchange}/fetchMatches':
1540
+ get:
1541
+ summary: Find Similar Markets (Deprecated)
1542
+ operationId: fetchMatches
1543
+ parameters:
1544
+ - $ref: '#/components/parameters/ExchangeParam'
1545
+ - in: query
1546
+ name: market
1547
+ required: false
1548
+ schema:
1549
+ allOf:
1550
+ - $ref: '#/components/schemas/UnifiedMarket'
1551
+ description: Pass a UnifiedMarket directly instead of marketId/slug/url.
1552
+ - in: query
1553
+ name: marketId
1554
+ required: false
1555
+ schema:
1556
+ type: string
1557
+ - in: query
1558
+ name: slug
1559
+ required: false
1560
+ schema:
1561
+ type: string
1562
+ - in: query
1563
+ name: url
1564
+ required: false
1565
+ schema:
1566
+ type: string
1567
+ - in: query
1568
+ name: relation
1569
+ required: false
1570
+ schema:
1571
+ type: string
1572
+ enum:
1573
+ - identity
1574
+ - subset
1575
+ - superset
1576
+ - overlap
1577
+ - disjoint
1578
+ - in: query
1579
+ name: minConfidence
1580
+ required: false
1581
+ schema:
1582
+ type: number
1583
+ - in: query
1584
+ name: limit
1585
+ required: false
1586
+ schema:
1587
+ type: number
1588
+ - in: query
1589
+ name: includePrices
1590
+ required: false
1591
+ schema:
1592
+ type: boolean
1593
+ responses:
1594
+ '200':
1595
+ description: Find Similar Markets (Deprecated) response
1596
+ content:
1597
+ application/json:
1598
+ schema:
1599
+ allOf:
1600
+ - $ref: '#/components/schemas/BaseResponse'
1601
+ - type: object
1602
+ properties:
1603
+ data:
1604
+ type: array
1605
+ items:
1606
+ $ref: '#/components/schemas/MatchResult'
1607
+ deprecated: true
1532
1608
  '/api/{exchange}/fetchEventMatches':
1533
1609
  get:
1534
1610
  summary: Find Similar Events
1535
1611
  operationId: fetchEventMatches
1536
1612
  parameters:
1537
1613
  - $ref: '#/components/parameters/ExchangeParam'
1614
+ - in: query
1615
+ name: event
1616
+ required: false
1617
+ schema:
1618
+ allOf:
1619
+ - $ref: '#/components/schemas/UnifiedEvent'
1620
+ description: Pass a UnifiedEvent directly instead of eventId/slug.
1538
1621
  - in: query
1539
1622
  name: eventId
1540
1623
  required: false
@@ -1605,7 +1688,7 @@ paths:
1605
1688
  type: array
1606
1689
  maxItems: 1
1607
1690
  items:
1608
- $ref: '#/components/schemas/FetchMatchesParams'
1691
+ $ref: '#/components/schemas/FetchMarketMatchesParams'
1609
1692
  minItems: 1
1610
1693
  credentials:
1611
1694
  $ref: '#/components/schemas/ExchangeCredentials'
@@ -1634,6 +1717,13 @@ paths:
1634
1717
  operationId: fetchHedges
1635
1718
  parameters:
1636
1719
  - $ref: '#/components/parameters/ExchangeParam'
1720
+ - in: query
1721
+ name: market
1722
+ required: false
1723
+ schema:
1724
+ allOf:
1725
+ - $ref: '#/components/schemas/UnifiedMarket'
1726
+ description: Pass a UnifiedMarket directly instead of marketId/slug/url.
1637
1727
  - in: query
1638
1728
  name: marketId
1639
1729
  required: false
@@ -1713,6 +1803,20 @@ paths:
1713
1803
  required: false
1714
1804
  schema:
1715
1805
  type: number
1806
+ - in: query
1807
+ name: relations
1808
+ required: false
1809
+ schema:
1810
+ type: array
1811
+ items:
1812
+ type: string
1813
+ enum:
1814
+ - identity
1815
+ - subset
1816
+ - superset
1817
+ - overlap
1818
+ - disjoint
1819
+ description: 'Comma-separated relation types to include (default: ''identity'').'
1716
1820
  responses:
1717
1821
  '200':
1718
1822
  description: Find Arbitrage Opportunities response
@@ -1842,6 +1946,9 @@ components:
1842
1946
  contractAddress:
1843
1947
  type: string
1844
1948
  description: 'On-chain contract / condition identifier where applicable (Polymarket conditionId, etc.).'
1949
+ sourceExchange:
1950
+ type: string
1951
+ description: 'The exchange/venue this market originates from (e.g. ''polymarket'', ''kalshi''). Populated by the Router.'
1845
1952
  'yes':
1846
1953
  allOf:
1847
1954
  - $ref: '#/components/schemas/MarketOutcome'
@@ -1934,6 +2041,9 @@ components:
1934
2041
  items:
1935
2042
  type: string
1936
2043
  description: Optional list of tags associated with the event.
2044
+ sourceExchange:
2045
+ type: string
2046
+ description: 'The exchange/venue this event originates from (e.g. ''polymarket'', ''kalshi''). Populated by the Router.'
1937
2047
  required:
1938
2048
  - id
1939
2049
  - title
@@ -2639,9 +2749,13 @@ components:
2639
2749
  max:
2640
2750
  type: number
2641
2751
  description: Sum of market volumes
2642
- FetchMatchesParams:
2752
+ FetchMarketMatchesParams:
2643
2753
  type: object
2644
2754
  properties:
2755
+ market:
2756
+ allOf:
2757
+ - $ref: '#/components/schemas/UnifiedMarket'
2758
+ description: Pass a UnifiedMarket directly instead of marketId/slug/url.
2645
2759
  marketId:
2646
2760
  type: string
2647
2761
  slug:
@@ -2665,6 +2779,10 @@ components:
2665
2779
  FetchEventMatchesParams:
2666
2780
  type: object
2667
2781
  properties:
2782
+ event:
2783
+ allOf:
2784
+ - $ref: '#/components/schemas/UnifiedEvent'
2785
+ description: Pass a UnifiedEvent directly instead of eventId/slug.
2668
2786
  eventId:
2669
2787
  type: string
2670
2788
  slug:
@@ -2692,6 +2810,17 @@ components:
2692
2810
  type: string
2693
2811
  limit:
2694
2812
  type: number
2813
+ relations:
2814
+ type: array
2815
+ items:
2816
+ type: string
2817
+ enum:
2818
+ - identity
2819
+ - subset
2820
+ - superset
2821
+ - overlap
2822
+ - disjoint
2823
+ description: 'Comma-separated relation types to include (default: ''identity'').'
2695
2824
  MatchResult:
2696
2825
  type: object
2697
2826
  properties:
@@ -2792,6 +2921,18 @@ components:
2792
2921
  type: number
2793
2922
  sellPrice:
2794
2923
  type: number
2924
+ relation:
2925
+ type: string
2926
+ enum:
2927
+ - identity
2928
+ - subset
2929
+ - superset
2930
+ - overlap
2931
+ - disjoint
2932
+ description: 'The set-theoretic relation between the two markets (e.g. identity, subset).'
2933
+ confidence:
2934
+ type: number
2935
+ description: Match confidence score (0.0 to 1.0).
2795
2936
  required:
2796
2937
  - marketA
2797
2938
  - marketB
package/dist/types.d.ts CHANGED
@@ -37,6 +37,8 @@ export interface UnifiedEvent {
37
37
  category?: string;
38
38
  /** Optional list of tags associated with the event. */
39
39
  tags?: string[];
40
+ /** The exchange/venue this event originates from (e.g. 'polymarket', 'kalshi'). Populated by the Router. */
41
+ sourceExchange?: string;
40
42
  }
41
43
  export interface UnifiedMarket {
42
44
  /** The unique identifier for this market */
@@ -73,6 +75,8 @@ export interface UnifiedMarket {
73
75
  status?: string;
74
76
  /** On-chain contract / condition identifier where applicable (Polymarket conditionId, etc.). */
75
77
  contractAddress?: string;
78
+ /** The exchange/venue this market originates from (e.g. 'polymarket', 'kalshi'). Populated by the Router. */
79
+ sourceExchange?: string;
76
80
  /** Convenience accessor for the YES outcome on a binary market. */
77
81
  yes?: MarketOutcome;
78
82
  /** Convenience accessor for the NO outcome on a binary market. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxt-core",
3
- "version": "2.33.5",
3
+ "version": "2.34.1",
4
4
  "description": "pmxt is a unified prediction market data API. The ccxt for prediction markets.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,8 +29,8 @@
29
29
  "test": "jest -c jest.config.js",
30
30
  "server": "tsx watch src/server/index.ts",
31
31
  "server:prod": "node dist/server/index.js",
32
- "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.33.5,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.33.5,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.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.34.1,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.34.1,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",