pmxt-core 2.46.14 → 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/baozi/normalizer.js +2 -0
- package/dist/exchanges/baozi/utils.js +28 -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 +54 -0
- package/dist/exchanges/hyperliquid/index.d.ts +3 -0
- package/dist/exchanges/hyperliquid/index.js +7 -0
- package/dist/exchanges/hyperliquid/normalizer.js +20 -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 +35 -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/limitless/normalizer.js +10 -0
- package/dist/exchanges/limitless/utils.js +17 -0
- package/dist/exchanges/metaculus/fetchEvents.js +6 -0
- package/dist/exchanges/metaculus/index.d.ts +3 -0
- package/dist/exchanges/metaculus/index.js +3 -0
- package/dist/exchanges/metaculus/utils.d.ts +2 -1
- package/dist/exchanges/metaculus/utils.js +32 -3
- 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/myriad/normalizer.js +14 -0
- package/dist/exchanges/myriad/utils.js +14 -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 +42 -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 +48 -0
- package/dist/exchanges/polymarket/utils.js +28 -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 +42 -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/probable/utils.js +17 -0
- package/dist/exchanges/smarkets/index.d.ts +1 -0
- package/dist/exchanges/smarkets/index.js +5 -0
- package/dist/exchanges/smarkets/normalizer.js +20 -0
- package/dist/exchanges/suibets/index.d.ts +1 -0
- package/dist/exchanges/suibets/index.js +5 -0
- package/dist/exchanges/suibets/normalizer.js +20 -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 +132 -0
- package/dist/types.d.ts +35 -0
- package/dist/utils/metadata.d.ts +14 -0
- package/dist/utils/metadata.js +33 -0
- package/package.json +3 -3
|
@@ -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;
|
|
@@ -3,7 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.KalshiNormalizer = void 0;
|
|
4
4
|
exports.sortRawEvents = sortRawEvents;
|
|
5
5
|
const market_utils_1 = require("../../utils/market-utils");
|
|
6
|
+
const metadata_1 = require("../../utils/metadata");
|
|
6
7
|
const price_1 = require("./price");
|
|
8
|
+
// Raw Kalshi fields already promoted to first-class Unified columns — excluded
|
|
9
|
+
// from sourceMetadata so we capture only what the unified shape would drop.
|
|
10
|
+
const KALSHI_PROMOTED_EVENT_KEYS = [
|
|
11
|
+
'event_ticker', 'title', 'markets', 'category', 'image_url', 'tags',
|
|
12
|
+
];
|
|
13
|
+
const KALSHI_PROMOTED_SERIES_KEYS = [
|
|
14
|
+
'ticker', 'title', 'tags', 'frequency', 'category',
|
|
15
|
+
];
|
|
16
|
+
const KALSHI_PROMOTED_MARKET_KEYS = [
|
|
17
|
+
'ticker', 'title', 'rules_primary', 'rules_secondary', 'expiration_time',
|
|
18
|
+
'volume_24h_fp', 'volume_24h', 'volume', 'volume_fp',
|
|
19
|
+
'liquidity_dollars', 'liquidity', 'open_interest_fp', 'open_interest',
|
|
20
|
+
'status', 'last_price_dollars', 'previous_price_dollars',
|
|
21
|
+
'yes_ask_dollars', 'yes_bid_dollars', 'last_price', 'yes_ask', 'yes_bid',
|
|
22
|
+
];
|
|
7
23
|
class KalshiNormalizer {
|
|
8
24
|
normalizeMarket(raw) {
|
|
9
25
|
// This normalizes a single-market event. For multi-market events, use normalizeMarketsFromEvent.
|
|
@@ -92,6 +108,11 @@ class KalshiNormalizer {
|
|
|
92
108
|
category: event.category,
|
|
93
109
|
tags: unifiedTags,
|
|
94
110
|
status: this.cleanLabel(market.status) || undefined,
|
|
111
|
+
// series_ticker/series_title live on the parent event, not the raw
|
|
112
|
+
// market, and aren't promoted to a column — attach them here so
|
|
113
|
+
// markets are queryable by series. event_ticker is omitted (already
|
|
114
|
+
// promoted to eventId).
|
|
115
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, KALSHI_PROMOTED_MARKET_KEYS, { series_ticker: event.series_ticker, series_title: event.series_title }),
|
|
95
116
|
};
|
|
96
117
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
97
118
|
return um;
|
|
@@ -114,6 +135,20 @@ class KalshiNormalizer {
|
|
|
114
135
|
image: raw.image_url ?? undefined,
|
|
115
136
|
category: raw.category,
|
|
116
137
|
tags: raw.tags || [],
|
|
138
|
+
// Keeps non-promoted event fields (series_ticker, series_title,
|
|
139
|
+
// sub_title, strike_period, ...); raw markets array is promoted.
|
|
140
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, KALSHI_PROMOTED_EVENT_KEYS),
|
|
141
|
+
};
|
|
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),
|
|
117
152
|
};
|
|
118
153
|
}
|
|
119
154
|
normalizeOHLCV(rawCandles, params) {
|
|
@@ -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))
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LimitlessNormalizer = void 0;
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
|
+
const metadata_1 = require("../../utils/metadata");
|
|
6
|
+
// Raw Limitless event fields already promoted to first-class Unified columns —
|
|
7
|
+
// excluded from sourceMetadata so we capture only what the unified shape drops.
|
|
8
|
+
const LIMITLESS_PROMOTED_EVENT_KEYS = [
|
|
9
|
+
'slug', 'title', 'question', 'description',
|
|
10
|
+
'logo',
|
|
11
|
+
'categories', 'tags',
|
|
12
|
+
'markets',
|
|
13
|
+
];
|
|
5
14
|
// Limitless uses USDC with 6 decimals
|
|
6
15
|
const USDC_DECIMALS = 6;
|
|
7
16
|
const USDC_SCALE = Math.pow(10, USDC_DECIMALS);
|
|
@@ -47,6 +56,7 @@ class LimitlessNormalizer {
|
|
|
47
56
|
image: raw.logo || `https://limitless.exchange/api/og?slug=${raw.slug}`,
|
|
48
57
|
category: raw.categories?.[0],
|
|
49
58
|
tags: raw.tags || [],
|
|
59
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, LIMITLESS_PROMOTED_EVENT_KEYS),
|
|
50
60
|
};
|
|
51
61
|
}
|
|
52
62
|
normalizeOHLCV(rawPrices, params) {
|
|
@@ -5,7 +5,23 @@ exports.mapMarketToUnified = mapMarketToUnified;
|
|
|
5
5
|
exports.mapIntervalToFidelity = mapIntervalToFidelity;
|
|
6
6
|
exports.paginateLimitlessMarkets = paginateLimitlessMarkets;
|
|
7
7
|
const market_utils_1 = require("../../utils/market-utils");
|
|
8
|
+
const metadata_1 = require("../../utils/metadata");
|
|
8
9
|
exports.DEFAULT_LIMITLESS_API_URL = 'https://api.limitless.exchange';
|
|
10
|
+
// Raw Limitless market fields already promoted to first-class Unified columns —
|
|
11
|
+
// excluded from sourceMetadata so we capture only what the unified shape drops.
|
|
12
|
+
// Also excludes __pmxt* internal injection keys (not raw vendor data).
|
|
13
|
+
const LIMITLESS_PROMOTED_MARKET_KEYS = [
|
|
14
|
+
'slug', 'title', 'question', 'description',
|
|
15
|
+
'tokens', 'prices',
|
|
16
|
+
'expirationTimestamp',
|
|
17
|
+
'volumeFormatted', 'volume',
|
|
18
|
+
'logo',
|
|
19
|
+
'categories', 'tags',
|
|
20
|
+
'expired', 'status',
|
|
21
|
+
'markets',
|
|
22
|
+
'__pmxtEventId', '__pmxtEventTitle', '__pmxtEventDescription',
|
|
23
|
+
'__pmxtCategories', '__pmxtTags',
|
|
24
|
+
];
|
|
9
25
|
function mapMarketToUnified(market, context = {}) {
|
|
10
26
|
if (!market)
|
|
11
27
|
return null;
|
|
@@ -76,6 +92,7 @@ function mapMarketToUnified(market, context = {}) {
|
|
|
76
92
|
category: market.categories?.[0] || resolvedContext.categories?.[0],
|
|
77
93
|
tags: market.tags || resolvedContext.tags || [],
|
|
78
94
|
status,
|
|
95
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, LIMITLESS_PROMOTED_MARKET_KEYS),
|
|
79
96
|
};
|
|
80
97
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
81
98
|
return um;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.fetchEvents = fetchEvents;
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
|
+
const metadata_1 = require("../../utils/metadata");
|
|
6
7
|
const BATCH_SIZE = 100;
|
|
7
8
|
const MAX_PAGES = 200;
|
|
8
9
|
/**
|
|
@@ -72,6 +73,7 @@ function postToEvent(post) {
|
|
|
72
73
|
: post.projects.category[0]?.name
|
|
73
74
|
: undefined,
|
|
74
75
|
tags: markets[0]?.tags ?? [],
|
|
76
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(post, utils_1.METACULUS_PROMOTED_EVENT_KEYS),
|
|
75
77
|
};
|
|
76
78
|
}
|
|
77
79
|
/**
|
|
@@ -141,6 +143,10 @@ async function fetchEventBySlug(slug, callApi) {
|
|
|
141
143
|
return [];
|
|
142
144
|
}
|
|
143
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
|
+
}
|
|
144
150
|
try {
|
|
145
151
|
// Direct lookup by slug (post ID, tournament slug, or url_title)
|
|
146
152
|
if (params.slug) {
|
|
@@ -37,6 +37,9 @@ import { UnifiedMarket, UnifiedEvent, CreateOrderParams, Order } from "../../typ
|
|
|
37
37
|
* | Continuous/numeric/date | Yes (read-only HIGHER/LOWER) | No (requires 201-point CDF) |
|
|
38
38
|
*/
|
|
39
39
|
export declare class MetaculusExchange extends PredictionMarketExchange {
|
|
40
|
+
protected readonly capabilityOverrides: {
|
|
41
|
+
fetchSeries: false;
|
|
42
|
+
};
|
|
40
43
|
private readonly apiToken?;
|
|
41
44
|
private readonly baseUrl;
|
|
42
45
|
constructor(credentials?: ExchangeCredentials);
|
|
@@ -48,6 +48,9 @@ const cancelOrder_1 = require("./cancelOrder");
|
|
|
48
48
|
* | Continuous/numeric/date | Yes (read-only HIGHER/LOWER) | No (requires 201-point CDF) |
|
|
49
49
|
*/
|
|
50
50
|
class MetaculusExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
51
|
+
capabilityOverrides = {
|
|
52
|
+
fetchSeries: false,
|
|
53
|
+
};
|
|
51
54
|
apiToken;
|
|
52
55
|
baseUrl;
|
|
53
56
|
constructor(credentials) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { UnifiedMarket } from "../../types";
|
|
2
|
+
export declare const METACULUS_PROMOTED_EVENT_KEYS: readonly ["id", "slug", "url_title", "title", "question", "group_of_questions", "projects", "status"];
|
|
2
3
|
/**
|
|
3
4
|
* Base URL passed to parseOpenApiSpec to override the spec's servers[0].url.
|
|
4
5
|
* The generated api.ts already has "https://www.metaculus.com/api" as its server URL,
|
|
@@ -23,7 +24,7 @@ export declare function mapStatus(status: string): "active" | "closed";
|
|
|
23
24
|
* @param eventId Optional parent event ID (tournament slug) to override
|
|
24
25
|
* the value derived from post.projects.tournament.
|
|
25
26
|
*/
|
|
26
|
-
export declare function mapMarketToUnified(post: any, eventId?: string): UnifiedMarket | null;
|
|
27
|
+
export declare function mapMarketToUnified(post: any, eventId?: string, groupPostId?: number): UnifiedMarket | null;
|
|
27
28
|
/**
|
|
28
29
|
* Convert a raw Metaculus post into one or more `UnifiedMarket` objects.
|
|
29
30
|
*
|
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_BASE_URL = void 0;
|
|
3
|
+
exports.DEFAULT_BASE_URL = exports.METACULUS_PROMOTED_EVENT_KEYS = void 0;
|
|
4
4
|
exports.mapStatus = mapStatus;
|
|
5
5
|
exports.mapMarketToUnified = mapMarketToUnified;
|
|
6
6
|
exports.expandPost = expandPost;
|
|
7
7
|
const market_utils_1 = require("../../utils/market-utils");
|
|
8
|
+
const metadata_1 = require("../../utils/metadata");
|
|
9
|
+
// Raw Metaculus Post fields already promoted to first-class UnifiedMarket columns
|
|
10
|
+
// — excluded from sourceMetadata so we capture only what the unified shape drops.
|
|
11
|
+
const METACULUS_PROMOTED_MARKET_KEYS = [
|
|
12
|
+
// identity / slug
|
|
13
|
+
'id', 'slug', 'url_title',
|
|
14
|
+
// title
|
|
15
|
+
'title',
|
|
16
|
+
// description lives inside question / group_of_questions — those are excluded below
|
|
17
|
+
// resolution timing -> resolutionDate
|
|
18
|
+
'scheduled_resolve_time', 'scheduled_close_time', 'actual_close_time',
|
|
19
|
+
// forecaster count -> liquidity / openInterest
|
|
20
|
+
'nr_forecasters',
|
|
21
|
+
// child objects whose fields are promoted individually
|
|
22
|
+
'question', 'group_of_questions',
|
|
23
|
+
// project sub-tree fields that map to image / category / tags / eventId
|
|
24
|
+
'projects',
|
|
25
|
+
// status -> mapStatus
|
|
26
|
+
'status',
|
|
27
|
+
];
|
|
28
|
+
// Raw Metaculus Post fields already promoted to first-class UnifiedEvent columns.
|
|
29
|
+
exports.METACULUS_PROMOTED_EVENT_KEYS = [
|
|
30
|
+
'id', 'slug', 'url_title',
|
|
31
|
+
'title',
|
|
32
|
+
'question', 'group_of_questions',
|
|
33
|
+
'projects',
|
|
34
|
+
'status',
|
|
35
|
+
];
|
|
8
36
|
/**
|
|
9
37
|
* Base URL passed to parseOpenApiSpec to override the spec's servers[0].url.
|
|
10
38
|
* The generated api.ts already has "https://www.metaculus.com/api" as its server URL,
|
|
@@ -185,7 +213,7 @@ function buildOutcomes(question, postId, medianProb) {
|
|
|
185
213
|
* @param eventId Optional parent event ID (tournament slug) to override
|
|
186
214
|
* the value derived from post.projects.tournament.
|
|
187
215
|
*/
|
|
188
|
-
function mapMarketToUnified(post, eventId) {
|
|
216
|
+
function mapMarketToUnified(post, eventId, groupPostId) {
|
|
189
217
|
if (!post || !post.id)
|
|
190
218
|
return null;
|
|
191
219
|
// Group-of-questions posts have no top-level question -- they must be
|
|
@@ -240,6 +268,7 @@ function mapMarketToUnified(post, eventId) {
|
|
|
240
268
|
image: post.projects?.default_project?.header_image ?? undefined,
|
|
241
269
|
category,
|
|
242
270
|
tags,
|
|
271
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(post, METACULUS_PROMOTED_MARKET_KEYS, groupPostId !== undefined ? { group_post_id: groupPostId } : undefined),
|
|
243
272
|
};
|
|
244
273
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
245
274
|
return um;
|
|
@@ -281,7 +310,7 @@ function mapGroupPostToMarkets(post, eventId) {
|
|
|
281
310
|
actual_close_time: subQuestion.actual_close_time ?? post.actual_close_time,
|
|
282
311
|
status: post.status,
|
|
283
312
|
};
|
|
284
|
-
const market = mapMarketToUnified(syntheticPost, groupEventId);
|
|
313
|
+
const market = mapMarketToUnified(syntheticPost, groupEventId, Number(parentPostId));
|
|
285
314
|
if (market) {
|
|
286
315
|
// Tag each outcome with the parent group post ID for traceability
|
|
287
316
|
for (const outcome of market.outcomes) {
|
|
@@ -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-05-
|
|
3
|
+
* Generated at: 2026-05-30T13:35:15.586Z
|
|
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-05-
|
|
6
|
+
* Generated at: 2026-05-30T13:35:15.586Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.myriadApiSpec = {
|
|
@@ -23,6 +23,7 @@ class MyriadExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
23
23
|
fetchBalance: 'emulated',
|
|
24
24
|
watchOrderBook: 'emulated',
|
|
25
25
|
watchTrades: 'emulated',
|
|
26
|
+
fetchSeries: false,
|
|
26
27
|
};
|
|
27
28
|
auth;
|
|
28
29
|
ws;
|
|
@@ -76,6 +77,10 @@ class MyriadExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
76
77
|
.filter((m) => m !== null);
|
|
77
78
|
}
|
|
78
79
|
async fetchEventsImpl(params) {
|
|
80
|
+
// Venue does not expose a series concept; honoring `params.series` by
|
|
81
|
+
// returning [] rather than ignoring the filter.
|
|
82
|
+
if (params.series !== undefined)
|
|
83
|
+
return [];
|
|
79
84
|
const rawQuestions = await this.fetcher.fetchRawEvents(params);
|
|
80
85
|
return rawQuestions
|
|
81
86
|
.map((raw) => this.normalizer.normalizeEvent(raw))
|
|
@@ -2,8 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MyriadNormalizer = void 0;
|
|
4
4
|
const market_utils_1 = require("../../utils/market-utils");
|
|
5
|
+
const metadata_1 = require("../../utils/metadata");
|
|
5
6
|
const price_1 = require("./price");
|
|
6
7
|
const utils_1 = require("./utils");
|
|
8
|
+
// Raw Myriad fields already promoted to first-class Unified columns — excluded
|
|
9
|
+
// from sourceMetadata so we capture only what the unified shape would drop.
|
|
10
|
+
const MYRIAD_PROMOTED_MARKET_KEYS = [
|
|
11
|
+
'id', 'networkId', 'title', 'description', 'slug', 'imageUrl',
|
|
12
|
+
'expiresAt', 'volume24h', 'volume', 'liquidity', 'eventId',
|
|
13
|
+
'topics', 'outcomes', 'state',
|
|
14
|
+
];
|
|
15
|
+
const MYRIAD_PROMOTED_EVENT_KEYS = [
|
|
16
|
+
'id', 'title', 'markets',
|
|
17
|
+
];
|
|
7
18
|
function selectTimeframe(interval) {
|
|
8
19
|
switch (interval) {
|
|
9
20
|
case '1m':
|
|
@@ -45,6 +56,7 @@ class MyriadNormalizer {
|
|
|
45
56
|
image: raw.imageUrl,
|
|
46
57
|
tags: raw.topics || [],
|
|
47
58
|
status,
|
|
59
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, MYRIAD_PROMOTED_MARKET_KEYS),
|
|
48
60
|
};
|
|
49
61
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
50
62
|
return um;
|
|
@@ -97,6 +109,8 @@ class MyriadNormalizer {
|
|
|
97
109
|
? markets.reduce((sum, m) => sum + (m.volume ?? 0), 0)
|
|
98
110
|
: undefined,
|
|
99
111
|
url: `https://myriad.markets`,
|
|
112
|
+
// Keeps non-promoted question fields; raw markets array is promoted.
|
|
113
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, MYRIAD_PROMOTED_EVENT_KEYS),
|
|
100
114
|
};
|
|
101
115
|
}
|
|
102
116
|
normalizeOHLCV(raw, params, outcomeId) {
|
|
@@ -6,6 +6,17 @@ exports.mapStatusToMyriad = mapStatusToMyriad;
|
|
|
6
6
|
exports.mapMarketToUnified = mapMarketToUnified;
|
|
7
7
|
exports.mapQuestionToEvent = mapQuestionToEvent;
|
|
8
8
|
const market_utils_1 = require("../../utils/market-utils");
|
|
9
|
+
const metadata_1 = require("../../utils/metadata");
|
|
10
|
+
// Raw Myriad fields already promoted to first-class Unified columns — excluded
|
|
11
|
+
// from sourceMetadata so we capture only what the unified shape would drop.
|
|
12
|
+
const MYRIAD_PROMOTED_MARKET_KEYS = [
|
|
13
|
+
'id', 'networkId', 'title', 'description', 'slug', 'imageUrl',
|
|
14
|
+
'expiresAt', 'volume24h', 'volume', 'liquidity', 'eventId',
|
|
15
|
+
'topics', 'outcomes', 'state',
|
|
16
|
+
];
|
|
17
|
+
const MYRIAD_PROMOTED_EVENT_KEYS = [
|
|
18
|
+
'id', 'title', 'markets',
|
|
19
|
+
];
|
|
9
20
|
exports.DEFAULT_BASE_URL = 'https://api-v2.myriadprotocol.com';
|
|
10
21
|
// Mainnet network IDs
|
|
11
22
|
exports.NETWORKS = {
|
|
@@ -76,6 +87,7 @@ function mapMarketToUnified(market) {
|
|
|
76
87
|
url: `https://myriad.markets/markets/${market.slug || market.id}`,
|
|
77
88
|
image: market.imageUrl,
|
|
78
89
|
tags: market.topics || [],
|
|
90
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, MYRIAD_PROMOTED_MARKET_KEYS),
|
|
79
91
|
};
|
|
80
92
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
81
93
|
return um;
|
|
@@ -98,6 +110,8 @@ function mapQuestionToEvent(question) {
|
|
|
98
110
|
volume24h: markets.reduce((sum, m) => sum + m.volume24h, 0),
|
|
99
111
|
volume: markets.some(m => m.volume !== undefined) ? markets.reduce((sum, m) => sum + (m.volume ?? 0), 0) : undefined,
|
|
100
112
|
url: `https://myriad.markets`,
|
|
113
|
+
// Keeps non-promoted question fields; raw markets array is promoted.
|
|
114
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(question, MYRIAD_PROMOTED_EVENT_KEYS),
|
|
101
115
|
};
|
|
102
116
|
return unifiedEvent;
|
|
103
117
|
}
|
|
@@ -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-30T13:35:15.591Z
|
|
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-30T13:35:15.591Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.opinionApiSpec = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PredictionMarketExchange, MarketFilterParams, OHLCVParams, ExchangeCredentials, EventFetchParams, MyTradesParams, OrderHistoryParams } from '../../BaseExchange';
|
|
2
|
-
import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, Order, Position, UserTrade, CreateOrderParams, BuiltOrder } from '../../types';
|
|
1
|
+
import { PredictionMarketExchange, MarketFilterParams, OHLCVParams, ExchangeCredentials, EventFetchParams, SeriesFetchParams, MyTradesParams, OrderHistoryParams } from '../../BaseExchange';
|
|
2
|
+
import { UnifiedMarket, UnifiedEvent, UnifiedSeries, PriceCandle, OrderBook, Trade, Order, Position, UserTrade, CreateOrderParams, BuiltOrder } from '../../types';
|
|
3
3
|
import { OpinionWebSocketConfig } from './websocket';
|
|
4
4
|
export type { OpinionWebSocketConfig };
|
|
5
5
|
export interface OpinionExchangeOptions {
|
|
@@ -15,12 +15,16 @@ export declare class OpinionExchange extends PredictionMarketExchange {
|
|
|
15
15
|
private readonly normalizer;
|
|
16
16
|
private ws?;
|
|
17
17
|
private readonly outcomeToMarketId;
|
|
18
|
+
protected readonly capabilityOverrides: {
|
|
19
|
+
fetchSeries: "emulated";
|
|
20
|
+
};
|
|
18
21
|
constructor(options?: ExchangeCredentials | OpinionExchangeOptions);
|
|
19
22
|
get name(): string;
|
|
20
23
|
protected sign(_method: string, _path: string, _params: Record<string, any>): Record<string, string>;
|
|
21
24
|
protected mapImplicitApiError(error: any): any;
|
|
22
25
|
protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
|
|
23
26
|
protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
27
|
+
protected fetchSeriesImpl(params: SeriesFetchParams): Promise<UnifiedSeries[]>;
|
|
24
28
|
fetchOHLCV(outcomeId: string, params: OHLCVParams): Promise<PriceCandle[]>;
|
|
25
29
|
fetchOrderBook(outcomeId: string, _limit?: number, _params?: Record<string, any>): Promise<OrderBook>;
|
|
26
30
|
fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
|
|
@@ -60,6 +60,9 @@ class OpinionExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
60
60
|
ws;
|
|
61
61
|
// Maps outcomeId (token ID) → numeric marketId for WebSocket subscriptions
|
|
62
62
|
outcomeToMarketId = new Map();
|
|
63
|
+
capabilityOverrides = {
|
|
64
|
+
fetchSeries: 'emulated',
|
|
65
|
+
};
|
|
63
66
|
constructor(options) {
|
|
64
67
|
let credentials;
|
|
65
68
|
let walletAddress;
|
|
@@ -154,9 +157,15 @@ class OpinionExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
154
157
|
const rawEvents = await this.fetcher.fetchRawEvents(params);
|
|
155
158
|
const limit = params.limit || 250000;
|
|
156
159
|
const query = (params.query || '').toLowerCase();
|
|
157
|
-
|
|
160
|
+
let filtered = query
|
|
158
161
|
? rawEvents.filter((raw) => (raw.marketTitle || '').toLowerCase().includes(query))
|
|
159
162
|
: rawEvents;
|
|
163
|
+
// Client-side series filter: keep only events whose collection symbol
|
|
164
|
+
// matches the requested series identifier.
|
|
165
|
+
if (params.series) {
|
|
166
|
+
const seriesId = params.series;
|
|
167
|
+
filtered = filtered.filter((raw) => raw.collection?.symbol === seriesId);
|
|
168
|
+
}
|
|
160
169
|
const events = filtered
|
|
161
170
|
.map((raw) => this.normalizer.normalizeEvent(raw))
|
|
162
171
|
.filter((e) => e !== null)
|
|
@@ -165,6 +174,50 @@ class OpinionExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
165
174
|
await this.enrichPrices(allMarkets);
|
|
166
175
|
return events;
|
|
167
176
|
}
|
|
177
|
+
async fetchSeriesImpl(params) {
|
|
178
|
+
// Opinion has no dedicated /series endpoint. Derive the catalog by
|
|
179
|
+
// fetching all markets and grouping by collection.symbol.
|
|
180
|
+
const rawMarkets = await this.fetcher.fetchRawMarkets();
|
|
181
|
+
// Build a map from collection.symbol -> { collection, rawEvents[] }
|
|
182
|
+
const seriesMap = new Map();
|
|
183
|
+
for (const raw of rawMarkets) {
|
|
184
|
+
if (!raw.collection)
|
|
185
|
+
continue;
|
|
186
|
+
const sym = raw.collection.symbol;
|
|
187
|
+
const existing = seriesMap.get(sym);
|
|
188
|
+
if (existing) {
|
|
189
|
+
existing.raws.push(raw);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
seriesMap.set(sym, { collection: raw.collection, raws: [raw] });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
let entries = Array.from(seriesMap.values());
|
|
196
|
+
// Apply params.id filter
|
|
197
|
+
if (params.id) {
|
|
198
|
+
entries = entries.filter((e) => e.collection.symbol === params.id);
|
|
199
|
+
}
|
|
200
|
+
// Apply params.query filter (title match)
|
|
201
|
+
if (params.query) {
|
|
202
|
+
const lowerQuery = params.query.toLowerCase();
|
|
203
|
+
entries = entries.filter((e) => e.collection.title.toLowerCase().includes(lowerQuery));
|
|
204
|
+
}
|
|
205
|
+
// Apply params.recurrence filter
|
|
206
|
+
if (params.recurrence) {
|
|
207
|
+
const recurrence = params.recurrence;
|
|
208
|
+
entries = entries.filter((e) => e.collection.frequency === recurrence);
|
|
209
|
+
}
|
|
210
|
+
return entries.map((e) => {
|
|
211
|
+
let events;
|
|
212
|
+
// When fetching by id, populate the events field.
|
|
213
|
+
if (params.id) {
|
|
214
|
+
events = e.raws
|
|
215
|
+
.map((raw) => this.normalizer.normalizeEvent(raw))
|
|
216
|
+
.filter((ev) => ev !== null);
|
|
217
|
+
}
|
|
218
|
+
return this.normalizer.normalizeSeries(e.collection, events);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
168
221
|
async fetchOHLCV(outcomeId, params) {
|
|
169
222
|
const rawPoints = await this.fetcher.fetchRawOHLCV(outcomeId, params);
|
|
170
223
|
return this.normalizer.normalizeOHLCV({ history: rawPoints }, params);
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { OHLCVParams } from '../../BaseExchange';
|
|
2
|
-
import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Position, Order } from '../../types';
|
|
2
|
+
import { UnifiedMarket, UnifiedEvent, UnifiedSeries, PriceCandle, OrderBook, Trade, UserTrade, Position, Order } from '../../types';
|
|
3
3
|
import { IExchangeNormalizer } from '../interfaces';
|
|
4
|
-
import { OpinionRawMarket, OpinionRawOrderBook, OpinionRawPricePoint, OpinionRawLatestPrice, OpinionRawUserTrade, OpinionRawPosition, OpinionRawOrder } from './fetcher';
|
|
4
|
+
import { OpinionRawMarket, OpinionRawCollection, OpinionRawOrderBook, OpinionRawPricePoint, OpinionRawLatestPrice, OpinionRawUserTrade, OpinionRawPosition, OpinionRawOrder } from './fetcher';
|
|
5
5
|
export declare class OpinionNormalizer implements IExchangeNormalizer<OpinionRawMarket, OpinionRawMarket> {
|
|
6
6
|
normalizeMarket(raw: OpinionRawMarket): UnifiedMarket | null;
|
|
7
7
|
normalizeMarketsFromEvent(raw: OpinionRawMarket): UnifiedMarket[];
|
|
8
|
+
/**
|
|
9
|
+
* Produce a UnifiedSeries from an Opinion `collection` object.
|
|
10
|
+
* `id` is the canonical series identifier (collection.symbol).
|
|
11
|
+
* `events` is optionally injected by the caller when doing a single-id lookup.
|
|
12
|
+
*/
|
|
13
|
+
normalizeSeries(raw: OpinionRawCollection, events?: UnifiedEvent[]): UnifiedSeries;
|
|
8
14
|
normalizeEvent(raw: OpinionRawMarket): UnifiedEvent | null;
|
|
9
15
|
normalizeOHLCV(raw: {
|
|
10
16
|
history: OpinionRawPricePoint[];
|