pmxt-core 2.47.0 → 2.48.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 +42 -1
- package/dist/BaseExchange.js +29 -2
- package/dist/exchanges/baozi/index.d.ts +1 -0
- package/dist/exchanges/baozi/index.js +5 -0
- package/dist/exchanges/gemini-titan/index.d.ts +6 -2
- package/dist/exchanges/gemini-titan/index.js +81 -1
- package/dist/exchanges/gemini-titan/normalizer.d.ts +7 -1
- package/dist/exchanges/gemini-titan/normalizer.js +23 -0
- package/dist/exchanges/hyperliquid/index.d.ts +3 -0
- package/dist/exchanges/hyperliquid/index.js +7 -0
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/kalshi/fetcher.d.ts +10 -0
- package/dist/exchanges/kalshi/fetcher.js +36 -0
- package/dist/exchanges/kalshi/index.d.ts +3 -2
- package/dist/exchanges/kalshi/index.js +33 -0
- package/dist/exchanges/kalshi/normalizer.d.ts +3 -2
- package/dist/exchanges/kalshi/normalizer.js +14 -0
- 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/metaculus/fetchEvents.js +4 -0
- package/dist/exchanges/metaculus/index.d.ts +3 -0
- package/dist/exchanges/metaculus/index.js +3 -0
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/index.d.ts +1 -0
- package/dist/exchanges/myriad/index.js +5 -0
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +1 -1
- package/dist/exchanges/opinion/index.d.ts +6 -2
- package/dist/exchanges/opinion/index.js +54 -1
- package/dist/exchanges/opinion/normalizer.d.ts +8 -2
- package/dist/exchanges/opinion/normalizer.js +16 -0
- package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
- package/dist/exchanges/polymarket/api-clob.js +1 -1
- package/dist/exchanges/polymarket/api-data.d.ts +1 -1
- package/dist/exchanges/polymarket/api-data.js +1 -1
- package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
- package/dist/exchanges/polymarket/api-gamma.js +1 -1
- package/dist/exchanges/polymarket/index.d.ts +13 -2
- package/dist/exchanges/polymarket/index.js +49 -0
- package/dist/exchanges/polymarket/normalizer.d.ts +2 -1
- package/dist/exchanges/polymarket/normalizer.js +33 -0
- package/dist/exchanges/polymarket_us/index.d.ts +3 -2
- package/dist/exchanges/polymarket_us/index.js +57 -0
- package/dist/exchanges/polymarket_us/normalizer.d.ts +9 -2
- package/dist/exchanges/polymarket_us/normalizer.js +27 -0
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/index.d.ts +3 -0
- package/dist/exchanges/probable/index.js +7 -0
- package/dist/exchanges/smarkets/index.d.ts +1 -0
- package/dist/exchanges/smarkets/index.js +5 -0
- package/dist/exchanges/suibets/index.d.ts +1 -0
- package/dist/exchanges/suibets/index.js +5 -0
- package/dist/router/Router.d.ts +29 -2
- package/dist/router/Router.js +145 -0
- package/dist/router/index.d.ts +1 -0
- package/dist/router/index.js +1 -0
- package/dist/router/series-map.d.ts +32 -0
- package/dist/router/series-map.js +146 -0
- package/dist/server/method-verbs.json +10 -0
- package/dist/server/openapi.yaml +118 -0
- package/dist/types.d.ts +31 -0
- package/package.json +3 -3
package/dist/BaseExchange.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AxiosInstance } from 'axios';
|
|
2
2
|
import { SubscribedAddressSnapshot, SubscriptionOption } from './subscriber/base';
|
|
3
|
-
import { Balance, BuiltOrder, CandleInterval, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from './types';
|
|
3
|
+
import { Balance, BuiltOrder, CandleInterval, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UnifiedSeries, UserTrade } from './types';
|
|
4
4
|
import { ExecutionPriceResult } from './utils/math';
|
|
5
5
|
import type { FetchMarketMatchesParams, FetchMatchesParams, FetchEventMatchesParams, FetchArbitrageParams, FetchMatchedMarketsParams, FetchMatchedPricesParams, MatchResult, EventMatchResult, PriceComparison, ArbitrageOpportunity, MatchedMarketPair, MatchedPricePair } from './router/types';
|
|
6
6
|
export interface ApiEndpoint {
|
|
@@ -74,6 +74,8 @@ export interface EventFetchParams {
|
|
|
74
74
|
searchIn?: 'title' | 'description' | 'both';
|
|
75
75
|
eventId?: string;
|
|
76
76
|
slug?: string;
|
|
77
|
+
/** Filter events by their parent series. Accepts the venue-native series id / ticker / slug (e.g. Kalshi `"KXATPMATCH"`, Polymarket `"wta"`). Passed through to the vendor where supported, otherwise applied to `sourceMetadata` after fetch. */
|
|
78
|
+
series?: string;
|
|
77
79
|
/** Optional client-side filter applied after fetching */
|
|
78
80
|
filter?: EventFilterCriteria;
|
|
79
81
|
/** Filter by category. Each event belongs to a venue-assigned category such as "Sports", "Politics", "Crypto", "Bitcoin", "Soccer", "Economic Policy" (Polymarket) or "Sports", "Mentions" (Kalshi). */
|
|
@@ -81,6 +83,24 @@ export interface EventFetchParams {
|
|
|
81
83
|
/** Filter by tags. Returns events matching ANY of the provided tags. Tags are more specific than categories -- for example a "Politics" event might carry tags ["Politics", "Geopolitics", "Middle East", "Iran"]. Common tags include "Crypto", "Elections", "Fed Rates", "FIFA World Cup", "Trump". */
|
|
82
84
|
tags?: string[];
|
|
83
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Parameters for `fetchSeries`. Venues that don't expose a series concept
|
|
88
|
+
* return an empty array regardless of the filters.
|
|
89
|
+
*/
|
|
90
|
+
export interface SeriesFetchParams {
|
|
91
|
+
/** Direct lookup by venue-native series id (e.g. "KXATPMATCH" on Kalshi, "atp" or "1" on Polymarket Gamma). When set, the result is the matching series with its events populated where the venue supports it. */
|
|
92
|
+
id?: string;
|
|
93
|
+
/** Lookup by series slug (e.g. "wta", "nfl"). */
|
|
94
|
+
slug?: string;
|
|
95
|
+
/** Keyword search across series title / description. */
|
|
96
|
+
query?: string;
|
|
97
|
+
/** Filter by recurrence cadence ('daily', 'weekly', 'annual', ...). */
|
|
98
|
+
recurrence?: string;
|
|
99
|
+
/** Maximum number of results to return. */
|
|
100
|
+
limit?: number;
|
|
101
|
+
/** Pagination offset. */
|
|
102
|
+
offset?: number;
|
|
103
|
+
}
|
|
84
104
|
/**
|
|
85
105
|
* Deprecated - use OHLCVParams or TradesParams instead. Resolution is optional for backward compatibility.
|
|
86
106
|
*/
|
|
@@ -219,6 +239,8 @@ export interface ExchangeHas {
|
|
|
219
239
|
fetchMarkets: ExchangeCapability;
|
|
220
240
|
/** Whether this exchange supports fetching events. */
|
|
221
241
|
fetchEvents: ExchangeCapability;
|
|
242
|
+
/** Whether this exchange exposes a recurring-series concept (Series -> Event -> Market -> Outcome). Venues without one return `false` and an empty array from `fetchSeries`. */
|
|
243
|
+
fetchSeries: ExchangeCapability;
|
|
222
244
|
/** Whether this exchange supports fetching OHLCV candles. */
|
|
223
245
|
fetchOHLCV: ExchangeCapability;
|
|
224
246
|
/** Whether this exchange supports fetching the order book. */
|
|
@@ -451,6 +473,18 @@ export declare abstract class PredictionMarketExchange {
|
|
|
451
473
|
* @note Some exchanges (like Limitless) may only support status 'active' for search results.
|
|
452
474
|
*/
|
|
453
475
|
fetchEvents(params?: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
476
|
+
/**
|
|
477
|
+
* Fetch the recurring series (fourth tier above Event -> Market -> Outcome)
|
|
478
|
+
* that this venue exposes. Returns an empty array on venues without a
|
|
479
|
+
* series concept (Limitless, Smarkets, Probable, Metaculus, Baozi,
|
|
480
|
+
* Hyperliquid, SuiBets, Polymarket US).
|
|
481
|
+
*
|
|
482
|
+
* - `params.id` -> a single matching series with its events populated where supported.
|
|
483
|
+
* - no params -> the full list, typically without nested events for payload size.
|
|
484
|
+
*
|
|
485
|
+
* @returns Array of unified series. Always an array, including the singular-lookup case.
|
|
486
|
+
*/
|
|
487
|
+
fetchSeries(params?: SeriesFetchParams): Promise<UnifiedSeries[]>;
|
|
454
488
|
/**
|
|
455
489
|
* Fetch a single market by lookup parameters.
|
|
456
490
|
* Convenience wrapper around fetchMarkets() that returns a single result or throws MarketNotFound.
|
|
@@ -782,6 +816,13 @@ export declare abstract class PredictionMarketExchange {
|
|
|
782
816
|
* Implementation for searching events by keyword.
|
|
783
817
|
*/
|
|
784
818
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
819
|
+
/**
|
|
820
|
+
* @internal
|
|
821
|
+
* Implementation for fetching recurring series. Override in venue adapters
|
|
822
|
+
* that expose a series concept; the default returns an empty array so
|
|
823
|
+
* venues without one are silently a no-op.
|
|
824
|
+
*/
|
|
825
|
+
protected fetchSeriesImpl(_params: SeriesFetchParams): Promise<UnifiedSeries[]>;
|
|
785
826
|
/**
|
|
786
827
|
* Call an implicit API method by its operationId (or auto-generated name).
|
|
787
828
|
* Provides a typed entry point so unified methods can delegate to the implicit API
|
package/dist/BaseExchange.js
CHANGED
|
@@ -361,6 +361,23 @@ class PredictionMarketExchange {
|
|
|
361
361
|
const start = offset ?? 0;
|
|
362
362
|
return limit !== undefined ? events.slice(start, start + limit) : events.slice(start);
|
|
363
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Fetch the recurring series (fourth tier above Event -> Market -> Outcome)
|
|
366
|
+
* that this venue exposes. Returns an empty array on venues without a
|
|
367
|
+
* series concept (Limitless, Smarkets, Probable, Metaculus, Baozi,
|
|
368
|
+
* Hyperliquid, SuiBets, Polymarket US).
|
|
369
|
+
*
|
|
370
|
+
* - `params.id` -> a single matching series with its events populated where supported.
|
|
371
|
+
* - no params -> the full list, typically without nested events for payload size.
|
|
372
|
+
*
|
|
373
|
+
* @returns Array of unified series. Always an array, including the singular-lookup case.
|
|
374
|
+
*/
|
|
375
|
+
async fetchSeries(params) {
|
|
376
|
+
const { limit, offset, ...venueParams } = params ?? {};
|
|
377
|
+
const series = await this.fetchSeriesImpl(venueParams);
|
|
378
|
+
const start = offset ?? 0;
|
|
379
|
+
return limit !== undefined ? series.slice(start, start + limit) : series.slice(start);
|
|
380
|
+
}
|
|
364
381
|
/**
|
|
365
382
|
* Fetch a single market by lookup parameters.
|
|
366
383
|
* Convenience wrapper around fetchMarkets() that returns a single result or throws MarketNotFound.
|
|
@@ -1056,6 +1073,15 @@ class PredictionMarketExchange {
|
|
|
1056
1073
|
async fetchEventsImpl(params) {
|
|
1057
1074
|
throw new Error("Method fetchEventsImpl not implemented.");
|
|
1058
1075
|
}
|
|
1076
|
+
/**
|
|
1077
|
+
* @internal
|
|
1078
|
+
* Implementation for fetching recurring series. Override in venue adapters
|
|
1079
|
+
* that expose a series concept; the default returns an empty array so
|
|
1080
|
+
* venues without one are silently a no-op.
|
|
1081
|
+
*/
|
|
1082
|
+
async fetchSeriesImpl(_params) {
|
|
1083
|
+
return [];
|
|
1084
|
+
}
|
|
1059
1085
|
/**
|
|
1060
1086
|
* Call an implicit API method by its operationId (or auto-generated name).
|
|
1061
1087
|
* Provides a typed entry point so unified methods can delegate to the implicit API
|
|
@@ -1163,7 +1189,7 @@ class PredictionMarketExchange {
|
|
|
1163
1189
|
// ----------------------------------------------------------------------------
|
|
1164
1190
|
/** All keys that appear in ExchangeHas -- kept in sync via the exhaustive check below. */
|
|
1165
1191
|
static _capabilityKeys = [
|
|
1166
|
-
'fetchMarkets', 'fetchEvents', 'fetchOHLCV', 'fetchOrderBook', 'fetchOrderBooks',
|
|
1192
|
+
'fetchMarkets', 'fetchEvents', 'fetchSeries', 'fetchOHLCV', 'fetchOrderBook', 'fetchOrderBooks',
|
|
1167
1193
|
'fetchTrades', 'createOrder', 'cancelOrder', 'fetchOrder',
|
|
1168
1194
|
'fetchOpenOrders', 'fetchPositions', 'fetchBalance',
|
|
1169
1195
|
'watchAddress', 'unwatchAddress', 'watchOrderBook', 'watchOrderBooks',
|
|
@@ -1175,7 +1201,7 @@ class PredictionMarketExchange {
|
|
|
1175
1201
|
// Compile-time exhaustiveness check: fails tsc if a key exists in
|
|
1176
1202
|
// ExchangeHas but is missing from _capabilityKeys above.
|
|
1177
1203
|
static _exhaustiveCheck = {
|
|
1178
|
-
fetchMarkets: true, fetchEvents: true, fetchOHLCV: true,
|
|
1204
|
+
fetchMarkets: true, fetchEvents: true, fetchSeries: true, fetchOHLCV: true,
|
|
1179
1205
|
fetchOrderBook: true, fetchOrderBooks: true, fetchTrades: true, createOrder: true,
|
|
1180
1206
|
cancelOrder: true, fetchOrder: true, fetchOpenOrders: true,
|
|
1181
1207
|
fetchPositions: true, fetchBalance: true, watchAddress: true,
|
|
@@ -1194,6 +1220,7 @@ class PredictionMarketExchange {
|
|
|
1194
1220
|
static _capabilityDelegates = {
|
|
1195
1221
|
fetchMarkets: 'fetchMarketsImpl',
|
|
1196
1222
|
fetchEvents: 'fetchEventsImpl',
|
|
1223
|
+
fetchSeries: 'fetchSeriesImpl',
|
|
1197
1224
|
watchOrderBooks: 'watchOrderBook',
|
|
1198
1225
|
fetchMatches: 'fetchMarketMatches',
|
|
1199
1226
|
fetchHedges: 'fetchRelatedMarkets',
|
|
@@ -19,6 +19,7 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
19
19
|
fetchOpenOrders: 'emulated',
|
|
20
20
|
cancelOrder: false,
|
|
21
21
|
watchTrades: false,
|
|
22
|
+
fetchSeries: false,
|
|
22
23
|
};
|
|
23
24
|
auth;
|
|
24
25
|
connection;
|
|
@@ -59,6 +60,10 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
59
60
|
return this.normalizer.normalizeMarkets(rawMarkets, params);
|
|
60
61
|
}
|
|
61
62
|
async fetchEventsImpl(params) {
|
|
63
|
+
// Venue does not expose a series concept; honoring `params.series` by returning [] rather than ignoring the filter.
|
|
64
|
+
if (params.series !== undefined) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
62
67
|
const rawMarkets = await this.fetcher.fetchRawEvents(params);
|
|
63
68
|
return this.normalizer.normalizeEvents(rawMarkets, {
|
|
64
69
|
query: params.query,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PredictionMarketExchange, MarketFilterParams, EventFetchParams, ExchangeCredentials } from '../../BaseExchange';
|
|
2
|
-
import { UnifiedMarket, UnifiedEvent, OrderBook, Trade, Order, Position, CreateOrderParams, BuiltOrder } from '../../types';
|
|
1
|
+
import { PredictionMarketExchange, MarketFilterParams, EventFetchParams, SeriesFetchParams, ExchangeCredentials } from '../../BaseExchange';
|
|
2
|
+
import { UnifiedMarket, UnifiedEvent, UnifiedSeries, OrderBook, Trade, Order, Position, CreateOrderParams, BuiltOrder } from '../../types';
|
|
3
3
|
export interface GeminiTitanExchangeOptions {
|
|
4
4
|
credentials?: ExchangeCredentials;
|
|
5
5
|
sandbox?: boolean;
|
|
@@ -10,11 +10,15 @@ export declare class GeminiTitanExchange extends PredictionMarketExchange {
|
|
|
10
10
|
private readonly normalizer;
|
|
11
11
|
private readonly geminiAuth?;
|
|
12
12
|
private geminiWs?;
|
|
13
|
+
protected readonly capabilityOverrides: {
|
|
14
|
+
fetchSeries: "emulated";
|
|
15
|
+
};
|
|
13
16
|
constructor(credentials?: ExchangeCredentials | GeminiTitanExchangeOptions);
|
|
14
17
|
get name(): string;
|
|
15
18
|
private requireAuth;
|
|
16
19
|
protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
|
|
17
20
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
21
|
+
protected fetchSeriesImpl(params: SeriesFetchParams): Promise<UnifiedSeries[]>;
|
|
18
22
|
fetchOrderBook(outcomeId: string, _limit?: number, _params?: Record<string, any>): Promise<OrderBook>;
|
|
19
23
|
buildOrder(params: CreateOrderParams): Promise<BuiltOrder>;
|
|
20
24
|
submitOrder(built: BuiltOrder): Promise<Order>;
|
|
@@ -16,6 +16,9 @@ class GeminiTitanExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
16
16
|
normalizer;
|
|
17
17
|
geminiAuth;
|
|
18
18
|
geminiWs;
|
|
19
|
+
capabilityOverrides = {
|
|
20
|
+
fetchSeries: 'emulated',
|
|
21
|
+
};
|
|
19
22
|
constructor(credentials) {
|
|
20
23
|
const opts = credentials && 'credentials' in credentials
|
|
21
24
|
? credentials
|
|
@@ -63,10 +66,87 @@ class GeminiTitanExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
63
66
|
}
|
|
64
67
|
async fetchEventsImpl(params) {
|
|
65
68
|
const rawEvents = await this.fetcher.fetchRawEvents(params);
|
|
66
|
-
|
|
69
|
+
let filtered = rawEvents;
|
|
70
|
+
// Client-side series filter: keep only events whose series id matches.
|
|
71
|
+
if (params.series) {
|
|
72
|
+
const seriesId = params.series;
|
|
73
|
+
filtered = rawEvents.filter((e) => {
|
|
74
|
+
if (e.series == null)
|
|
75
|
+
return false;
|
|
76
|
+
const s = e.series;
|
|
77
|
+
const id = String(s['id'] ?? s['ticker'] ?? s['symbol'] ?? '');
|
|
78
|
+
return id === seriesId;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return filtered
|
|
67
82
|
.map(e => this.normalizer.normalizeEventWithMarkets(e))
|
|
68
83
|
.filter((e) => e !== null);
|
|
69
84
|
}
|
|
85
|
+
async fetchSeriesImpl(params) {
|
|
86
|
+
// Gemini-Titan has no dedicated /series endpoint. Derive the catalog by
|
|
87
|
+
// fetching all events and grouping by the series id in event.series.
|
|
88
|
+
const rawEvents = await this.fetcher.fetchRawEvents({});
|
|
89
|
+
// Build a map from series id -> { rawSeries, rawEvents[] }
|
|
90
|
+
const seriesMap = new Map();
|
|
91
|
+
for (const event of rawEvents) {
|
|
92
|
+
if (event.series == null)
|
|
93
|
+
continue;
|
|
94
|
+
const s = event.series;
|
|
95
|
+
const id = String(s['id'] ?? s['ticker'] ?? s['symbol'] ?? '');
|
|
96
|
+
if (!id)
|
|
97
|
+
continue;
|
|
98
|
+
const existing = seriesMap.get(id);
|
|
99
|
+
if (existing) {
|
|
100
|
+
existing.raws.push(event);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
seriesMap.set(id, { raw: s, raws: [event] });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
let entries = Array.from(seriesMap.entries()).map(([id, v]) => ({
|
|
107
|
+
id,
|
|
108
|
+
raw: v.raw,
|
|
109
|
+
raws: v.raws,
|
|
110
|
+
}));
|
|
111
|
+
// Apply params.id filter
|
|
112
|
+
if (params.id) {
|
|
113
|
+
entries = entries.filter((e) => e.id === params.id);
|
|
114
|
+
}
|
|
115
|
+
// Apply params.slug filter (treat slug same as id for Gemini series)
|
|
116
|
+
if (params.slug) {
|
|
117
|
+
const slug = params.slug;
|
|
118
|
+
entries = entries.filter((e) => {
|
|
119
|
+
const rawSlug = e.raw['slug'] != null ? String(e.raw['slug']) : e.id;
|
|
120
|
+
return rawSlug === slug;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Apply params.query filter (title match)
|
|
124
|
+
if (params.query) {
|
|
125
|
+
const lowerQuery = params.query.toLowerCase();
|
|
126
|
+
entries = entries.filter((e) => {
|
|
127
|
+
const title = String(e.raw['title'] ?? e.raw['name'] ?? '');
|
|
128
|
+
return title.toLowerCase().includes(lowerQuery);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// Apply params.recurrence filter
|
|
132
|
+
if (params.recurrence) {
|
|
133
|
+
const recurrence = params.recurrence;
|
|
134
|
+
entries = entries.filter((e) => {
|
|
135
|
+
const freq = e.raw['frequency'] ?? e.raw['recurrence'];
|
|
136
|
+
return freq != null && String(freq) === recurrence;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return entries.map((e) => {
|
|
140
|
+
let events;
|
|
141
|
+
// When fetching by id, populate the events field.
|
|
142
|
+
if (params.id) {
|
|
143
|
+
events = e.raws
|
|
144
|
+
.map((raw) => this.normalizer.normalizeEventWithMarkets(raw))
|
|
145
|
+
.filter((ev) => ev !== null);
|
|
146
|
+
}
|
|
147
|
+
return this.normalizer.normalizeSeries(e.raw, events);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
70
150
|
async fetchOrderBook(outcomeId, _limit, _params) {
|
|
71
151
|
const resolved = await this.resolveOutcomeAlias(outcomeId, _params);
|
|
72
152
|
outcomeId = resolved.outcomeId;
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import { UnifiedMarket, UnifiedEvent, OrderBook, Order, Position } from '../../types';
|
|
1
|
+
import { UnifiedMarket, UnifiedEvent, UnifiedSeries, OrderBook, Order, Position } from '../../types';
|
|
2
2
|
import { IExchangeNormalizer } from '../interfaces';
|
|
3
3
|
import { GeminiRawEvent, GeminiRawContract, GeminiRawOrder, GeminiRawPosition, GeminiRawOrderBook } from './types';
|
|
4
4
|
export declare class GeminiNormalizer implements IExchangeNormalizer<GeminiRawEvent, GeminiRawEvent> {
|
|
5
5
|
normalizeMarket(raw: GeminiRawEvent): UnifiedMarket | null;
|
|
6
6
|
normalizeEvent(raw: GeminiRawEvent): UnifiedEvent | null;
|
|
7
7
|
normalizeEventWithMarkets(raw: GeminiRawEvent): UnifiedEvent | null;
|
|
8
|
+
/**
|
|
9
|
+
* Produce a UnifiedSeries from a Gemini `series` object (raw Record).
|
|
10
|
+
* The series id is taken from the first non-null of: id, ticker, symbol.
|
|
11
|
+
* `events` is optionally injected by the caller when doing a single-id lookup.
|
|
12
|
+
*/
|
|
13
|
+
normalizeSeries(raw: Record<string, unknown>, events?: UnifiedEvent[]): UnifiedSeries;
|
|
8
14
|
normalizeMarketsFromEvent(raw: GeminiRawEvent): UnifiedMarket[];
|
|
9
15
|
normalizeContract(contract: GeminiRawContract, event: GeminiRawEvent): UnifiedMarket | null;
|
|
10
16
|
normalizeOrderBook(raw: GeminiRawOrderBook, _outcomeId: string): OrderBook;
|
|
@@ -141,6 +141,29 @@ class GeminiNormalizer {
|
|
|
141
141
|
volume24h: markets.reduce((sum, m) => sum + m.volume24h, 0),
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Produce a UnifiedSeries from a Gemini `series` object (raw Record).
|
|
146
|
+
* The series id is taken from the first non-null of: id, ticker, symbol.
|
|
147
|
+
* `events` is optionally injected by the caller when doing a single-id lookup.
|
|
148
|
+
*/
|
|
149
|
+
normalizeSeries(raw, events) {
|
|
150
|
+
const id = String(raw['id'] ?? raw['ticker'] ?? raw['symbol'] ?? '');
|
|
151
|
+
const ticker = raw['ticker'] != null ? String(raw['ticker']) : undefined;
|
|
152
|
+
const title = String(raw['title'] ?? raw['name'] ?? id);
|
|
153
|
+
const recurrence = raw['frequency'] != null
|
|
154
|
+
? String(raw['frequency'])
|
|
155
|
+
: raw['recurrence'] != null
|
|
156
|
+
? String(raw['recurrence'])
|
|
157
|
+
: null;
|
|
158
|
+
return {
|
|
159
|
+
id,
|
|
160
|
+
ticker,
|
|
161
|
+
title,
|
|
162
|
+
recurrence,
|
|
163
|
+
events,
|
|
164
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, ['id', 'ticker', 'title', 'name', 'frequency', 'recurrence']),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
144
167
|
normalizeMarketsFromEvent(raw) {
|
|
145
168
|
const markets = [];
|
|
146
169
|
for (const contract of raw.contracts) {
|
|
@@ -5,6 +5,9 @@ export interface HyperliquidExchangeOptions {
|
|
|
5
5
|
testnet?: boolean;
|
|
6
6
|
}
|
|
7
7
|
export declare class HyperliquidExchange extends PredictionMarketExchange {
|
|
8
|
+
protected readonly capabilityOverrides: {
|
|
9
|
+
fetchSeries: false;
|
|
10
|
+
};
|
|
8
11
|
private readonly config;
|
|
9
12
|
private readonly fetcher;
|
|
10
13
|
private readonly normalizer;
|
|
@@ -9,6 +9,9 @@ const normalizer_1 = require("./normalizer");
|
|
|
9
9
|
const auth_1 = require("./auth");
|
|
10
10
|
const errors_2 = require("./errors");
|
|
11
11
|
class HyperliquidExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
12
|
+
capabilityOverrides = {
|
|
13
|
+
fetchSeries: false,
|
|
14
|
+
};
|
|
12
15
|
config;
|
|
13
16
|
fetcher;
|
|
14
17
|
normalizer;
|
|
@@ -69,6 +72,10 @@ class HyperliquidExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
69
72
|
.filter((m) => m !== null);
|
|
70
73
|
}
|
|
71
74
|
async fetchEventsImpl(params) {
|
|
75
|
+
// Venue does not expose a series concept; honoring `params.series` by returning [] rather than ignoring the filter.
|
|
76
|
+
if (params.series !== undefined) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
72
79
|
const [rawQuestions, meta, mids] = await Promise.all([
|
|
73
80
|
this.fetcher.fetchRawEvents(params),
|
|
74
81
|
this.fetcher.fetchOutcomeMeta(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-30T13:35:15.520Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const kalshiApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.kalshiApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-30T13:35:15.520Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -44,6 +44,14 @@ interface KalshiSeriesInfo {
|
|
|
44
44
|
title?: string;
|
|
45
45
|
tags?: string[];
|
|
46
46
|
}
|
|
47
|
+
export interface KalshiRawSeries {
|
|
48
|
+
ticker: string;
|
|
49
|
+
title?: string;
|
|
50
|
+
tags?: string[];
|
|
51
|
+
frequency?: string;
|
|
52
|
+
category?: string;
|
|
53
|
+
[key: string]: unknown;
|
|
54
|
+
}
|
|
47
55
|
export interface KalshiRawEventPage {
|
|
48
56
|
events: KalshiRawEvent[];
|
|
49
57
|
cursor?: string | null;
|
|
@@ -153,6 +161,7 @@ export declare class KalshiFetcher implements IExchangeFetcher<KalshiRawEvent, K
|
|
|
153
161
|
}>;
|
|
154
162
|
fetchRawOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
|
|
155
163
|
fetchRawHistoricalOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
|
|
164
|
+
fetchRawSeriesList(): Promise<KalshiRawSeries[]>;
|
|
156
165
|
fetchRawSeriesMap(): Promise<Map<string, KalshiSeriesInfo>>;
|
|
157
166
|
fetchRawEventByTicker(eventTicker: string): Promise<KalshiRawEvent[]>;
|
|
158
167
|
private fetchRawEventsDefault;
|
|
@@ -160,6 +169,7 @@ export declare class KalshiFetcher implements IExchangeFetcher<KalshiRawEvent, K
|
|
|
160
169
|
private enrichEventsWithSeriesMap;
|
|
161
170
|
private fetchActiveEvents;
|
|
162
171
|
private fetchAllWithStatus;
|
|
172
|
+
private fetchAllWithSeriesTicker;
|
|
163
173
|
private fetchPageWithStatus;
|
|
164
174
|
}
|
|
165
175
|
export {};
|
|
@@ -58,6 +58,11 @@ class KalshiFetcher {
|
|
|
58
58
|
if (params.slug) {
|
|
59
59
|
return this.fetchRawEventByTicker(params.slug);
|
|
60
60
|
}
|
|
61
|
+
// When a series filter is present, delegate to server-side filtering so
|
|
62
|
+
// only events belonging to that series are returned — no double-filtering.
|
|
63
|
+
if (params.series) {
|
|
64
|
+
return this.fetchAllWithSeriesTicker(params.series);
|
|
65
|
+
}
|
|
61
66
|
const status = params?.status || 'active';
|
|
62
67
|
if (status === 'all') {
|
|
63
68
|
const [openEvents, closedEvents, settledEvents] = await Promise.all([
|
|
@@ -213,6 +218,15 @@ class KalshiFetcher {
|
|
|
213
218
|
const data = await this.ctx.callApi('GetHistoricalOrders', queryParams);
|
|
214
219
|
return data.orders || [];
|
|
215
220
|
}
|
|
221
|
+
async fetchRawSeriesList() {
|
|
222
|
+
try {
|
|
223
|
+
const data = await this.ctx.callApi('GetSeriesList');
|
|
224
|
+
return (data.series || []);
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
throw errors_1.kalshiErrorMapper.mapError(e);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
216
230
|
async fetchRawSeriesMap() {
|
|
217
231
|
try {
|
|
218
232
|
const data = await this.ctx.callApi('GetSeriesList');
|
|
@@ -381,6 +395,28 @@ class KalshiFetcher {
|
|
|
381
395
|
} while (cursor && page < MAX_PAGES);
|
|
382
396
|
return allEvents;
|
|
383
397
|
}
|
|
398
|
+
async fetchAllWithSeriesTicker(seriesTicker) {
|
|
399
|
+
let allEvents = [];
|
|
400
|
+
let cursor = null;
|
|
401
|
+
let page = 0;
|
|
402
|
+
do {
|
|
403
|
+
const queryParams = {
|
|
404
|
+
series_ticker: seriesTicker,
|
|
405
|
+
with_nested_markets: true,
|
|
406
|
+
limit: BATCH_SIZE,
|
|
407
|
+
};
|
|
408
|
+
if (cursor)
|
|
409
|
+
queryParams.cursor = cursor;
|
|
410
|
+
const data = await this.ctx.callApi('GetEvents', queryParams);
|
|
411
|
+
const events = data.events || [];
|
|
412
|
+
if (events.length === 0)
|
|
413
|
+
break;
|
|
414
|
+
allEvents = [...allEvents, ...events];
|
|
415
|
+
cursor = data.cursor;
|
|
416
|
+
page++;
|
|
417
|
+
} while (cursor && page < MAX_PAGES);
|
|
418
|
+
return allEvents;
|
|
419
|
+
}
|
|
384
420
|
async fetchPageWithStatus(apiStatus, maxEvents, initialCursor) {
|
|
385
421
|
let allEvents = [];
|
|
386
422
|
let cursor = initialCursor || null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EventFetchParams, ExchangeCredentials, HistoryFilterParams, MarketFilterParams, MyTradesParams, OHLCVParams, OrderHistoryParams, PredictionMarketExchange, TradesParams } from '../../BaseExchange';
|
|
2
|
-
import { Balance, BuiltOrder, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from '../../types';
|
|
1
|
+
import { EventFetchParams, ExchangeCredentials, HistoryFilterParams, MarketFilterParams, MyTradesParams, OHLCVParams, OrderHistoryParams, PredictionMarketExchange, SeriesFetchParams, TradesParams } from '../../BaseExchange';
|
|
2
|
+
import { Balance, BuiltOrder, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UnifiedSeries, UserTrade } from '../../types';
|
|
3
3
|
import { KalshiWebSocketConfig } from './websocket';
|
|
4
4
|
export type { KalshiWebSocketConfig };
|
|
5
5
|
export interface KalshiExchangeOptions {
|
|
@@ -23,6 +23,7 @@ export declare class KalshiExchange extends PredictionMarketExchange {
|
|
|
23
23
|
private ensureAuth;
|
|
24
24
|
protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
|
|
25
25
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
26
|
+
protected fetchSeriesImpl(params: SeriesFetchParams): Promise<UnifiedSeries[]>;
|
|
26
27
|
fetchEventsPage(params?: EventFetchParams): Promise<{
|
|
27
28
|
events: UnifiedEvent[];
|
|
28
29
|
cursor: string | null;
|
|
@@ -118,6 +118,39 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
118
118
|
.filter((e) => e !== null)
|
|
119
119
|
.slice(0, limit);
|
|
120
120
|
}
|
|
121
|
+
async fetchSeriesImpl(params) {
|
|
122
|
+
const rawList = await this.fetcher.fetchRawSeriesList();
|
|
123
|
+
// If a specific series id is requested, fetch its events and attach them.
|
|
124
|
+
// We still iterate the full list so filters apply uniformly.
|
|
125
|
+
let eventsById = null;
|
|
126
|
+
if (params.id) {
|
|
127
|
+
const rawEvents = await this.fetcher.fetchRawEvents({ series: params.id });
|
|
128
|
+
const normalizedEvents = rawEvents
|
|
129
|
+
.map((raw) => this.normalizer.normalizeEvent(raw))
|
|
130
|
+
.filter((e) => e !== null);
|
|
131
|
+
eventsById = new Map([[params.id, normalizedEvents]]);
|
|
132
|
+
}
|
|
133
|
+
const normalized = rawList.map((raw) => {
|
|
134
|
+
const events = eventsById?.get(raw.ticker);
|
|
135
|
+
return this.normalizer.normalizeSeries(raw, events);
|
|
136
|
+
});
|
|
137
|
+
// Client-side filters
|
|
138
|
+
let filtered = normalized;
|
|
139
|
+
if (params.id) {
|
|
140
|
+
filtered = filtered.filter((s) => s.id === params.id);
|
|
141
|
+
}
|
|
142
|
+
else if (params.slug) {
|
|
143
|
+
filtered = filtered.filter((s) => s.ticker === params.slug || s.slug === params.slug);
|
|
144
|
+
}
|
|
145
|
+
if (params.query) {
|
|
146
|
+
const lowerQuery = params.query.toLowerCase();
|
|
147
|
+
filtered = filtered.filter((s) => s.title.toLowerCase().includes(lowerQuery));
|
|
148
|
+
}
|
|
149
|
+
if (params.recurrence) {
|
|
150
|
+
filtered = filtered.filter((s) => s.recurrence === params.recurrence);
|
|
151
|
+
}
|
|
152
|
+
return filtered;
|
|
153
|
+
}
|
|
121
154
|
async fetchEventsPage(params = {}) {
|
|
122
155
|
const page = await this.fetcher.fetchRawEventPage(params);
|
|
123
156
|
const query = (params?.query || '').toLowerCase();
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { OHLCVParams } from '../../BaseExchange';
|
|
2
|
-
import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Position, Balance } from '../../types';
|
|
2
|
+
import { UnifiedMarket, UnifiedEvent, UnifiedSeries, PriceCandle, OrderBook, Trade, UserTrade, Position, Balance } from '../../types';
|
|
3
3
|
import { IExchangeNormalizer } from '../interfaces';
|
|
4
|
-
import { KalshiRawEvent, KalshiRawMarket, KalshiRawCandlestick, KalshiRawTrade, KalshiRawFill, KalshiRawOrder, KalshiRawPosition, KalshiRawOrderBookFp } from './fetcher';
|
|
4
|
+
import { KalshiRawEvent, KalshiRawMarket, KalshiRawCandlestick, KalshiRawTrade, KalshiRawFill, KalshiRawOrder, KalshiRawPosition, KalshiRawOrderBookFp, KalshiRawSeries } from './fetcher';
|
|
5
5
|
export declare class KalshiNormalizer implements IExchangeNormalizer<KalshiRawEvent, KalshiRawEvent> {
|
|
6
6
|
normalizeMarket(raw: KalshiRawEvent): UnifiedMarket | null;
|
|
7
7
|
normalizeMarketsFromEvent(rawEvent: KalshiRawEvent): UnifiedMarket[];
|
|
8
8
|
normalizeRawMarket(event: KalshiRawEvent, market: KalshiRawMarket): UnifiedMarket | null;
|
|
9
9
|
normalizeEvent(raw: KalshiRawEvent): UnifiedEvent | null;
|
|
10
|
+
normalizeSeries(raw: KalshiRawSeries, events?: UnifiedEvent[]): UnifiedSeries;
|
|
10
11
|
normalizeOHLCV(rawCandles: KalshiRawCandlestick[], params: OHLCVParams): PriceCandle[];
|
|
11
12
|
normalizeOrderBook(raw: {
|
|
12
13
|
orderbook_fp: KalshiRawOrderBookFp;
|
|
@@ -10,6 +10,9 @@ const price_1 = require("./price");
|
|
|
10
10
|
const KALSHI_PROMOTED_EVENT_KEYS = [
|
|
11
11
|
'event_ticker', 'title', 'markets', 'category', 'image_url', 'tags',
|
|
12
12
|
];
|
|
13
|
+
const KALSHI_PROMOTED_SERIES_KEYS = [
|
|
14
|
+
'ticker', 'title', 'tags', 'frequency', 'category',
|
|
15
|
+
];
|
|
13
16
|
const KALSHI_PROMOTED_MARKET_KEYS = [
|
|
14
17
|
'ticker', 'title', 'rules_primary', 'rules_secondary', 'expiration_time',
|
|
15
18
|
'volume_24h_fp', 'volume_24h', 'volume', 'volume_fp',
|
|
@@ -137,6 +140,17 @@ class KalshiNormalizer {
|
|
|
137
140
|
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, KALSHI_PROMOTED_EVENT_KEYS),
|
|
138
141
|
};
|
|
139
142
|
}
|
|
143
|
+
normalizeSeries(raw, events) {
|
|
144
|
+
return {
|
|
145
|
+
id: raw.ticker,
|
|
146
|
+
ticker: raw.ticker,
|
|
147
|
+
title: (typeof raw.title === 'string' && raw.title.trim()) ? raw.title.trim() : raw.ticker,
|
|
148
|
+
recurrence: (typeof raw.frequency === 'string' && raw.frequency.trim()) ? raw.frequency.trim() : null,
|
|
149
|
+
url: `https://kalshi.com/events?series=${raw.ticker}`,
|
|
150
|
+
events: events ?? undefined,
|
|
151
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, KALSHI_PROMOTED_SERIES_KEYS),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
140
154
|
normalizeOHLCV(rawCandles, params) {
|
|
141
155
|
const candles = rawCandles.map((c) => {
|
|
142
156
|
const p = c.price || {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
|
|
3
|
-
* Generated at: 2026-05-
|
|
3
|
+
* Generated at: 2026-05-30T13:35:15.569Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const limitlessApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.limitlessApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
|
|
6
|
-
* Generated at: 2026-05-
|
|
6
|
+
* Generated at: 2026-05-30T13:35:15.569Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.limitlessApiSpec = {
|
|
@@ -22,6 +22,7 @@ const logger_1 = require("../../utils/logger");
|
|
|
22
22
|
class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
23
23
|
capabilityOverrides = {
|
|
24
24
|
fetchOrder: false,
|
|
25
|
+
fetchSeries: false,
|
|
25
26
|
};
|
|
26
27
|
auth;
|
|
27
28
|
client;
|
|
@@ -146,6 +147,10 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
146
147
|
return marketsAfterOffset.slice(0, limit);
|
|
147
148
|
}
|
|
148
149
|
async fetchEventsImpl(params) {
|
|
150
|
+
// Venue does not expose a series concept; honoring `params.series` by
|
|
151
|
+
// returning [] rather than ignoring the filter.
|
|
152
|
+
if (params.series !== undefined)
|
|
153
|
+
return [];
|
|
149
154
|
const rawEvents = await this.fetcher.fetchRawEvents(params);
|
|
150
155
|
return rawEvents
|
|
151
156
|
.map((raw) => this.normalizer.normalizeEvent(raw))
|
|
@@ -143,6 +143,10 @@ async function fetchEventBySlug(slug, callApi) {
|
|
|
143
143
|
return [];
|
|
144
144
|
}
|
|
145
145
|
async function fetchEvents(params, callApi) {
|
|
146
|
+
// Venue does not expose a series concept; honoring `params.series` by returning [] rather than ignoring the filter.
|
|
147
|
+
if (params.series !== undefined) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
146
150
|
try {
|
|
147
151
|
// Direct lookup by slug (post ID, tournament slug, or url_title)
|
|
148
152
|
if (params.slug) {
|