pmxt-core 2.20.0 → 2.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/exchanges/baozi/fetcher.d.ts +40 -0
- package/dist/exchanges/baozi/fetcher.js +155 -0
- package/dist/exchanges/baozi/index.d.ts +2 -0
- package/dist/exchanges/baozi/index.js +60 -131
- package/dist/exchanges/baozi/normalizer.d.ts +14 -0
- package/dist/exchanges/baozi/normalizer.js +208 -0
- package/dist/exchanges/interfaces.d.ts +28 -0
- package/dist/exchanges/interfaces.js +2 -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 +126 -0
- package/dist/exchanges/kalshi/fetcher.js +313 -0
- package/dist/exchanges/kalshi/index.d.ts +6 -6
- package/dist/exchanges/kalshi/index.js +119 -202
- package/dist/exchanges/kalshi/normalizer.d.ts +25 -0
- package/dist/exchanges/kalshi/normalizer.js +294 -0
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/fetcher.d.ts +81 -0
- package/dist/exchanges/limitless/fetcher.js +238 -0
- package/dist/exchanges/limitless/index.d.ts +6 -9
- package/dist/exchanges/limitless/index.js +81 -79
- package/dist/exchanges/limitless/normalizer.d.ts +14 -0
- package/dist/exchanges/limitless/normalizer.js +117 -0
- package/dist/exchanges/limitless/websocket.d.ts +3 -0
- package/dist/exchanges/limitless/websocket.js +5 -4
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/fetcher.d.ts +73 -0
- package/dist/exchanges/myriad/fetcher.js +217 -0
- package/dist/exchanges/myriad/index.d.ts +2 -0
- package/dist/exchanges/myriad/index.js +40 -97
- package/dist/exchanges/myriad/normalizer.d.ts +14 -0
- package/dist/exchanges/myriad/normalizer.js +167 -0
- package/dist/exchanges/myriad/websocket.d.ts +3 -1
- package/dist/exchanges/myriad/websocket.js +4 -3
- 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/fetcher.d.ts +99 -0
- package/dist/exchanges/polymarket/fetcher.js +335 -0
- package/dist/exchanges/polymarket/index.d.ts +2 -0
- package/dist/exchanges/polymarket/index.js +80 -66
- package/dist/exchanges/polymarket/normalizer.d.ts +18 -0
- package/dist/exchanges/polymarket/normalizer.js +126 -0
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/fetcher.d.ts +106 -0
- package/dist/exchanges/probable/fetcher.js +357 -0
- package/dist/exchanges/probable/index.d.ts +3 -1
- package/dist/exchanges/probable/index.js +73 -105
- package/dist/exchanges/probable/normalizer.d.ts +14 -0
- package/dist/exchanges/probable/normalizer.js +109 -0
- package/dist/utils/error-mapper.d.ts +8 -0
- package/dist/utils/error-mapper.js +58 -26
- package/package.json +3 -3
- package/dist/exchanges/baozi/fetchEvents.d.ts +0 -8
- package/dist/exchanges/baozi/fetchEvents.js +0 -39
- package/dist/exchanges/baozi/fetchMarkets.d.ts +0 -5
- package/dist/exchanges/baozi/fetchMarkets.js +0 -160
- package/dist/exchanges/baozi/fetchOHLCV.d.ts +0 -6
- package/dist/exchanges/baozi/fetchOHLCV.js +0 -10
- package/dist/exchanges/baozi/fetchOrderBook.d.ts +0 -12
- package/dist/exchanges/baozi/fetchOrderBook.js +0 -36
- package/dist/exchanges/baozi/fetchTrades.d.ts +0 -6
- package/dist/exchanges/baozi/fetchTrades.js +0 -10
- package/dist/exchanges/kalshi/fetchEvents.d.ts +0 -5
- package/dist/exchanges/kalshi/fetchEvents.js +0 -196
- package/dist/exchanges/kalshi/fetchMarkets.d.ts +0 -6
- package/dist/exchanges/kalshi/fetchMarkets.js +0 -247
- package/dist/exchanges/kalshi/fetchOHLCV.d.ts +0 -3
- package/dist/exchanges/kalshi/fetchOHLCV.js +0 -97
- package/dist/exchanges/kalshi/fetchOrderBook.d.ts +0 -2
- package/dist/exchanges/kalshi/fetchOrderBook.js +0 -60
- package/dist/exchanges/kalshi/fetchTrades.d.ts +0 -3
- package/dist/exchanges/kalshi/fetchTrades.js +0 -33
- package/dist/exchanges/limitless/fetchEvents.d.ts +0 -4
- package/dist/exchanges/limitless/fetchEvents.js +0 -173
- package/dist/exchanges/limitless/fetchMarkets.d.ts +0 -3
- package/dist/exchanges/limitless/fetchMarkets.js +0 -152
- package/dist/exchanges/limitless/fetchOHLCV.d.ts +0 -7
- package/dist/exchanges/limitless/fetchOHLCV.js +0 -49
- package/dist/exchanges/limitless/fetchOrderBook.d.ts +0 -6
- package/dist/exchanges/limitless/fetchOrderBook.js +0 -41
- package/dist/exchanges/limitless/fetchTrades.d.ts +0 -8
- package/dist/exchanges/limitless/fetchTrades.js +0 -27
- package/dist/exchanges/myriad/fetchEvents.d.ts +0 -4
- package/dist/exchanges/myriad/fetchEvents.js +0 -48
- package/dist/exchanges/myriad/fetchMarkets.d.ts +0 -4
- package/dist/exchanges/myriad/fetchMarkets.js +0 -102
- package/dist/exchanges/myriad/fetchOHLCV.d.ts +0 -3
- package/dist/exchanges/myriad/fetchOHLCV.js +0 -83
- package/dist/exchanges/myriad/fetchOrderBook.d.ts +0 -2
- package/dist/exchanges/myriad/fetchOrderBook.js +0 -39
- package/dist/exchanges/polymarket/fetchEvents.d.ts +0 -4
- package/dist/exchanges/polymarket/fetchEvents.js +0 -135
- package/dist/exchanges/polymarket/fetchMarkets.d.ts +0 -4
- package/dist/exchanges/polymarket/fetchMarkets.js +0 -214
- package/dist/exchanges/polymarket/fetchOHLCV.d.ts +0 -7
- package/dist/exchanges/polymarket/fetchOHLCV.js +0 -98
- package/dist/exchanges/polymarket/fetchOrderBook.d.ts +0 -6
- package/dist/exchanges/polymarket/fetchOrderBook.js +0 -33
- package/dist/exchanges/polymarket/fetchTrades.d.ts +0 -9
- package/dist/exchanges/polymarket/fetchTrades.js +0 -43
- package/dist/exchanges/probable/fetchEvents.d.ts +0 -6
- package/dist/exchanges/probable/fetchEvents.js +0 -151
- package/dist/exchanges/probable/fetchMarkets.d.ts +0 -4
- package/dist/exchanges/probable/fetchMarkets.js +0 -239
- package/dist/exchanges/probable/fetchTrades.d.ts +0 -10
- package/dist/exchanges/probable/fetchTrades.js +0 -40
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaoziNormalizer = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
const fetcher_1 = require("./fetcher");
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Normalizer -- pure data mapping, no I/O
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
class BaoziNormalizer {
|
|
10
|
+
normalizeMarket(raw) {
|
|
11
|
+
if (!raw)
|
|
12
|
+
return null;
|
|
13
|
+
if ((0, fetcher_1.isRawBooleanMarket)(raw)) {
|
|
14
|
+
return (0, utils_1.mapBooleanToUnified)(raw.parsed, raw.pubkey);
|
|
15
|
+
}
|
|
16
|
+
if ((0, fetcher_1.isRawRaceMarket)(raw)) {
|
|
17
|
+
return (0, utils_1.mapRaceToUnified)(raw.parsed, raw.pubkey);
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
normalizeEvent(raw) {
|
|
22
|
+
const market = this.normalizeMarket(raw);
|
|
23
|
+
if (!market)
|
|
24
|
+
return null;
|
|
25
|
+
return {
|
|
26
|
+
id: market.marketId,
|
|
27
|
+
title: market.title,
|
|
28
|
+
description: market.description,
|
|
29
|
+
slug: market.marketId,
|
|
30
|
+
markets: [market],
|
|
31
|
+
volume24h: market.volume24h,
|
|
32
|
+
volume: market.volume,
|
|
33
|
+
url: market.url,
|
|
34
|
+
image: market.image,
|
|
35
|
+
category: market.category,
|
|
36
|
+
tags: market.tags,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
normalizeMarkets(rawMarkets, params) {
|
|
40
|
+
const markets = [];
|
|
41
|
+
for (const raw of rawMarkets) {
|
|
42
|
+
const m = this.normalizeMarket(raw);
|
|
43
|
+
if (m)
|
|
44
|
+
markets.push(m);
|
|
45
|
+
}
|
|
46
|
+
return applyFilters(markets, params);
|
|
47
|
+
}
|
|
48
|
+
normalizeEvents(rawMarkets, params) {
|
|
49
|
+
const markets = this.normalizeMarkets(rawMarkets, params);
|
|
50
|
+
return markets.map(m => ({
|
|
51
|
+
id: m.marketId,
|
|
52
|
+
title: m.title,
|
|
53
|
+
description: m.description,
|
|
54
|
+
slug: m.marketId,
|
|
55
|
+
markets: [m],
|
|
56
|
+
volume24h: m.volume24h,
|
|
57
|
+
volume: m.volume,
|
|
58
|
+
url: m.url,
|
|
59
|
+
image: m.image,
|
|
60
|
+
category: m.category,
|
|
61
|
+
tags: m.tags,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
normalizeOrderBook(raw, outcomeId) {
|
|
65
|
+
if (!raw) {
|
|
66
|
+
throw new Error(`Market not found for outcome: ${outcomeId}`);
|
|
67
|
+
}
|
|
68
|
+
const market = this.normalizeMarket(raw);
|
|
69
|
+
if (!market) {
|
|
70
|
+
throw new Error(`Could not parse market for outcome: ${outcomeId}`);
|
|
71
|
+
}
|
|
72
|
+
const outcome = market.outcomes.find(o => o.outcomeId === outcomeId);
|
|
73
|
+
const price = outcome?.price ?? 0.5;
|
|
74
|
+
const totalLiquidity = market.liquidity;
|
|
75
|
+
return {
|
|
76
|
+
bids: [{ price, size: totalLiquidity }],
|
|
77
|
+
asks: [{ price, size: totalLiquidity }],
|
|
78
|
+
timestamp: Date.now(),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
normalizeBooleanPositions(positions, marketLookup) {
|
|
82
|
+
const result = [];
|
|
83
|
+
for (const { parsed: pos } of positions) {
|
|
84
|
+
if (pos.claimed)
|
|
85
|
+
continue;
|
|
86
|
+
const marketPda = (0, utils_1.deriveMarketPda)(pos.marketId);
|
|
87
|
+
const marketPdaStr = marketPda.toString();
|
|
88
|
+
const market = marketLookup.get(marketPdaStr);
|
|
89
|
+
let currentYesPrice = 0;
|
|
90
|
+
let currentNoPrice = 0;
|
|
91
|
+
if (market) {
|
|
92
|
+
currentYesPrice = market.yes?.price ?? 0;
|
|
93
|
+
currentNoPrice = market.no?.price ?? 0;
|
|
94
|
+
}
|
|
95
|
+
const yesSOL = Number(pos.yesAmount) / utils_1.LAMPORTS_PER_SOL;
|
|
96
|
+
const noSOL = Number(pos.noAmount) / utils_1.LAMPORTS_PER_SOL;
|
|
97
|
+
if (yesSOL > 0) {
|
|
98
|
+
result.push({
|
|
99
|
+
marketId: marketPdaStr,
|
|
100
|
+
outcomeId: `${marketPdaStr}-YES`,
|
|
101
|
+
outcomeLabel: 'Yes',
|
|
102
|
+
size: yesSOL,
|
|
103
|
+
entryPrice: 0,
|
|
104
|
+
currentPrice: currentYesPrice,
|
|
105
|
+
unrealizedPnL: 0,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (noSOL > 0) {
|
|
109
|
+
result.push({
|
|
110
|
+
marketId: marketPdaStr,
|
|
111
|
+
outcomeId: `${marketPdaStr}-NO`,
|
|
112
|
+
outcomeLabel: 'No',
|
|
113
|
+
size: noSOL,
|
|
114
|
+
entryPrice: 0,
|
|
115
|
+
currentPrice: currentNoPrice,
|
|
116
|
+
unrealizedPnL: 0,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
normalizeRacePositions(positions, marketLookup) {
|
|
123
|
+
const result = [];
|
|
124
|
+
for (const { parsed: pos } of positions) {
|
|
125
|
+
if (pos.claimed)
|
|
126
|
+
continue;
|
|
127
|
+
const racePda = (0, utils_1.deriveRaceMarketPda)(pos.marketId);
|
|
128
|
+
const racePdaStr = racePda.toString();
|
|
129
|
+
const market = marketLookup.get(racePdaStr);
|
|
130
|
+
const outcomePrices = market ? market.outcomes.map(o => o.price) : [];
|
|
131
|
+
const outcomeLabels = market ? market.outcomes.map(o => o.label) : [];
|
|
132
|
+
for (let i = 0; i < pos.bets.length; i++) {
|
|
133
|
+
const betSOL = Number(pos.bets[i]) / utils_1.LAMPORTS_PER_SOL;
|
|
134
|
+
if (betSOL <= 0)
|
|
135
|
+
continue;
|
|
136
|
+
result.push({
|
|
137
|
+
marketId: racePdaStr,
|
|
138
|
+
outcomeId: `${racePdaStr}-${i}`,
|
|
139
|
+
outcomeLabel: outcomeLabels[i] || `Outcome ${i}`,
|
|
140
|
+
size: betSOL,
|
|
141
|
+
entryPrice: 0,
|
|
142
|
+
currentPrice: outcomePrices[i] ?? 0,
|
|
143
|
+
unrealizedPnL: 0,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
normalizeBalance(raw) {
|
|
150
|
+
const solBalance = raw.lamports / utils_1.LAMPORTS_PER_SOL;
|
|
151
|
+
return [{
|
|
152
|
+
currency: 'SOL',
|
|
153
|
+
total: solBalance,
|
|
154
|
+
available: solBalance,
|
|
155
|
+
locked: 0,
|
|
156
|
+
}];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exports.BaoziNormalizer = BaoziNormalizer;
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Filtering / sorting (pure, no I/O)
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
function applyFilters(markets, params) {
|
|
164
|
+
let result = [...markets];
|
|
165
|
+
// Status filter
|
|
166
|
+
const status = params?.status || 'active';
|
|
167
|
+
if (status !== 'all') {
|
|
168
|
+
const now = Date.now();
|
|
169
|
+
if (status === 'active') {
|
|
170
|
+
result = result.filter(m => m.resolutionDate.getTime() > now);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
result = result.filter(m => m.resolutionDate.getTime() <= now);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Text search
|
|
177
|
+
if (params?.query) {
|
|
178
|
+
const lowerQuery = params.query.toLowerCase();
|
|
179
|
+
const searchIn = params.searchIn || 'title';
|
|
180
|
+
result = result.filter(m => {
|
|
181
|
+
const titleMatch = m.title.toLowerCase().includes(lowerQuery);
|
|
182
|
+
const descMatch = (m.description || '').toLowerCase().includes(lowerQuery);
|
|
183
|
+
if (searchIn === 'title')
|
|
184
|
+
return titleMatch;
|
|
185
|
+
if (searchIn === 'description')
|
|
186
|
+
return descMatch;
|
|
187
|
+
return titleMatch || descMatch;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// Sort
|
|
191
|
+
if (params?.sort === 'volume') {
|
|
192
|
+
result.sort((a, b) => (b.volume || 0) - (a.volume || 0));
|
|
193
|
+
}
|
|
194
|
+
else if (params?.sort === 'liquidity') {
|
|
195
|
+
result.sort((a, b) => b.liquidity - a.liquidity);
|
|
196
|
+
}
|
|
197
|
+
else if (params?.sort === 'newest') {
|
|
198
|
+
result.sort((a, b) => b.resolutionDate.getTime() - a.resolutionDate.getTime());
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
result.sort((a, b) => (b.volume || 0) - (a.volume || 0));
|
|
202
|
+
}
|
|
203
|
+
// Pagination
|
|
204
|
+
const offset = params?.offset || 0;
|
|
205
|
+
const limit = params?.limit || 10000;
|
|
206
|
+
result = result.slice(offset, offset + limit);
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
import { MarketFilterParams, EventFetchParams, OHLCVParams, TradesParams, MyTradesParams } from '../BaseExchange';
|
|
3
|
+
import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Position, Balance } from '../types';
|
|
4
|
+
export interface FetcherContext {
|
|
5
|
+
readonly http: AxiosInstance;
|
|
6
|
+
callApi(operationId: string, params?: Record<string, any>): Promise<any>;
|
|
7
|
+
getHeaders(): Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
export interface IExchangeFetcher<TRawMarket = unknown, TRawEvent = unknown> {
|
|
10
|
+
fetchRawMarkets(params?: MarketFilterParams): Promise<TRawMarket[]>;
|
|
11
|
+
fetchRawEvents(params: EventFetchParams): Promise<TRawEvent[]>;
|
|
12
|
+
fetchRawOHLCV?(id: string, params: OHLCVParams): Promise<unknown>;
|
|
13
|
+
fetchRawOrderBook?(id: string): Promise<unknown>;
|
|
14
|
+
fetchRawTrades?(id: string, params: TradesParams): Promise<unknown[]>;
|
|
15
|
+
fetchRawMyTrades?(params: MyTradesParams, walletAddress: string): Promise<unknown[]>;
|
|
16
|
+
fetchRawPositions?(walletAddress: string): Promise<unknown[]>;
|
|
17
|
+
fetchRawBalance?(walletAddress: string): Promise<unknown>;
|
|
18
|
+
}
|
|
19
|
+
export interface IExchangeNormalizer<TRawMarket = unknown, TRawEvent = unknown> {
|
|
20
|
+
normalizeMarket(raw: TRawMarket): UnifiedMarket | null;
|
|
21
|
+
normalizeEvent(raw: TRawEvent): UnifiedEvent | null;
|
|
22
|
+
normalizeOHLCV?(raw: unknown, params: OHLCVParams): PriceCandle[];
|
|
23
|
+
normalizeOrderBook?(raw: unknown, id: string): OrderBook;
|
|
24
|
+
normalizeTrade?(raw: unknown, index: number): Trade;
|
|
25
|
+
normalizeUserTrade?(raw: unknown, index: number): UserTrade;
|
|
26
|
+
normalizePosition?(raw: unknown): Position;
|
|
27
|
+
normalizeBalance?(raw: unknown): Balance[];
|
|
28
|
+
}
|
|
@@ -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-03-
|
|
3
|
+
* Generated at: 2026-03-14T21:28:31.833Z
|
|
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-03-
|
|
6
|
+
* Generated at: 2026-03-14T21:28:31.833Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { MarketFilterParams, EventFetchParams, OHLCVParams, TradesParams, MyTradesParams } from '../../BaseExchange';
|
|
2
|
+
import { IExchangeFetcher, FetcherContext } from '../interfaces';
|
|
3
|
+
export interface KalshiRawMarket {
|
|
4
|
+
ticker: string;
|
|
5
|
+
last_price?: number;
|
|
6
|
+
yes_ask?: number;
|
|
7
|
+
yes_bid?: number;
|
|
8
|
+
subtitle?: string;
|
|
9
|
+
yes_sub_title?: string;
|
|
10
|
+
previous_price_dollars?: number;
|
|
11
|
+
last_price_dollars?: number;
|
|
12
|
+
rules_primary?: string;
|
|
13
|
+
rules_secondary?: string;
|
|
14
|
+
expiration_time: string;
|
|
15
|
+
volume_24h?: number;
|
|
16
|
+
volume?: number;
|
|
17
|
+
liquidity?: number;
|
|
18
|
+
open_interest?: number;
|
|
19
|
+
close_time?: string;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface KalshiRawEvent {
|
|
23
|
+
event_ticker: string;
|
|
24
|
+
title: string;
|
|
25
|
+
mututals_description?: string;
|
|
26
|
+
image_url?: string;
|
|
27
|
+
category?: string;
|
|
28
|
+
tags?: string[];
|
|
29
|
+
series_ticker?: string;
|
|
30
|
+
markets?: KalshiRawMarket[];
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface KalshiRawCandlestick {
|
|
34
|
+
end_period_ts: number;
|
|
35
|
+
volume?: number;
|
|
36
|
+
price?: {
|
|
37
|
+
open?: number;
|
|
38
|
+
high?: number;
|
|
39
|
+
low?: number;
|
|
40
|
+
close?: number;
|
|
41
|
+
previous?: number;
|
|
42
|
+
};
|
|
43
|
+
yes_ask?: {
|
|
44
|
+
open?: number;
|
|
45
|
+
high?: number;
|
|
46
|
+
low?: number;
|
|
47
|
+
close?: number;
|
|
48
|
+
};
|
|
49
|
+
yes_bid?: {
|
|
50
|
+
open?: number;
|
|
51
|
+
high?: number;
|
|
52
|
+
low?: number;
|
|
53
|
+
close?: number;
|
|
54
|
+
};
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
export interface KalshiRawOrderBookFp {
|
|
58
|
+
yes_dollars?: string[][];
|
|
59
|
+
no_dollars?: string[][];
|
|
60
|
+
}
|
|
61
|
+
export interface KalshiRawTrade {
|
|
62
|
+
trade_id: string;
|
|
63
|
+
created_time: string;
|
|
64
|
+
yes_price: number;
|
|
65
|
+
count: number;
|
|
66
|
+
taker_side: string;
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
}
|
|
69
|
+
export interface KalshiRawFill {
|
|
70
|
+
fill_id: string;
|
|
71
|
+
created_time: string;
|
|
72
|
+
yes_price: number;
|
|
73
|
+
count: number;
|
|
74
|
+
side: string;
|
|
75
|
+
order_id: string;
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
}
|
|
78
|
+
export interface KalshiRawOrder {
|
|
79
|
+
order_id: string;
|
|
80
|
+
ticker: string;
|
|
81
|
+
side: string;
|
|
82
|
+
type?: string;
|
|
83
|
+
yes_price?: number;
|
|
84
|
+
count: number;
|
|
85
|
+
remaining_count?: number;
|
|
86
|
+
status?: string;
|
|
87
|
+
created_time: string;
|
|
88
|
+
[key: string]: unknown;
|
|
89
|
+
}
|
|
90
|
+
export interface KalshiRawPosition {
|
|
91
|
+
ticker: string;
|
|
92
|
+
position: number;
|
|
93
|
+
total_cost: number;
|
|
94
|
+
market_price?: number;
|
|
95
|
+
market_exposure?: number;
|
|
96
|
+
realized_pnl?: number;
|
|
97
|
+
[key: string]: unknown;
|
|
98
|
+
}
|
|
99
|
+
export declare class KalshiFetcher implements IExchangeFetcher<KalshiRawEvent, KalshiRawEvent> {
|
|
100
|
+
private readonly ctx;
|
|
101
|
+
private cachedEvents;
|
|
102
|
+
private cachedSeriesMap;
|
|
103
|
+
private lastCacheTime;
|
|
104
|
+
constructor(ctx: FetcherContext);
|
|
105
|
+
resetCache(): void;
|
|
106
|
+
fetchRawMarkets(params?: MarketFilterParams): Promise<KalshiRawEvent[]>;
|
|
107
|
+
fetchRawEvents(params: EventFetchParams): Promise<KalshiRawEvent[]>;
|
|
108
|
+
fetchRawOHLCV(id: string, params: OHLCVParams): Promise<KalshiRawCandlestick[]>;
|
|
109
|
+
fetchRawOrderBook(id: string): Promise<{
|
|
110
|
+
orderbook_fp: KalshiRawOrderBookFp;
|
|
111
|
+
}>;
|
|
112
|
+
fetchRawTrades(id: string, params: TradesParams): Promise<KalshiRawTrade[]>;
|
|
113
|
+
fetchRawMyTrades(params: MyTradesParams): Promise<KalshiRawFill[]>;
|
|
114
|
+
fetchRawPositions(): Promise<KalshiRawPosition[]>;
|
|
115
|
+
fetchRawBalance(): Promise<{
|
|
116
|
+
balance: number;
|
|
117
|
+
portfolio_value: number;
|
|
118
|
+
}>;
|
|
119
|
+
fetchRawOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
|
|
120
|
+
fetchRawHistoricalOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
|
|
121
|
+
fetchRawSeriesMap(): Promise<Map<string, string[]>>;
|
|
122
|
+
fetchRawEventByTicker(eventTicker: string): Promise<KalshiRawEvent[]>;
|
|
123
|
+
private fetchRawEventsDefault;
|
|
124
|
+
private fetchActiveEvents;
|
|
125
|
+
private fetchAllWithStatus;
|
|
126
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KalshiFetcher = void 0;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
const validation_1 = require("../../utils/validation");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
// ----------------------------------------------------------------------------
|
|
8
|
+
// Fetcher
|
|
9
|
+
// ----------------------------------------------------------------------------
|
|
10
|
+
const BATCH_SIZE = 200;
|
|
11
|
+
const MAX_PAGES = 1000;
|
|
12
|
+
const CACHE_TTL = 5 * 60 * 1000;
|
|
13
|
+
class KalshiFetcher {
|
|
14
|
+
ctx;
|
|
15
|
+
// Instance-level cache (moved from module-level)
|
|
16
|
+
cachedEvents = null;
|
|
17
|
+
cachedSeriesMap = null;
|
|
18
|
+
lastCacheTime = 0;
|
|
19
|
+
constructor(ctx) {
|
|
20
|
+
this.ctx = ctx;
|
|
21
|
+
}
|
|
22
|
+
resetCache() {
|
|
23
|
+
this.cachedEvents = null;
|
|
24
|
+
this.cachedSeriesMap = null;
|
|
25
|
+
this.lastCacheTime = 0;
|
|
26
|
+
}
|
|
27
|
+
// -- Markets (returns raw events containing nested markets) ----------------
|
|
28
|
+
async fetchRawMarkets(params) {
|
|
29
|
+
try {
|
|
30
|
+
if (params?.marketId) {
|
|
31
|
+
return this.fetchRawEventByTicker(params.marketId);
|
|
32
|
+
}
|
|
33
|
+
if (params?.slug) {
|
|
34
|
+
return this.fetchRawEventByTicker(params.slug);
|
|
35
|
+
}
|
|
36
|
+
if (params?.outcomeId) {
|
|
37
|
+
const ticker = params.outcomeId.replace(/-NO$/, '');
|
|
38
|
+
return this.fetchRawEventByTicker(ticker);
|
|
39
|
+
}
|
|
40
|
+
if (params?.eventId) {
|
|
41
|
+
return this.fetchRawEventByTicker(params.eventId);
|
|
42
|
+
}
|
|
43
|
+
// Default + query cases: fetch all events, caller does filtering
|
|
44
|
+
return this.fetchRawEventsDefault(params);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// -- Events ---------------------------------------------------------------
|
|
51
|
+
async fetchRawEvents(params) {
|
|
52
|
+
try {
|
|
53
|
+
if (params.eventId) {
|
|
54
|
+
return this.fetchRawEventByTicker(params.eventId);
|
|
55
|
+
}
|
|
56
|
+
if (params.slug) {
|
|
57
|
+
return this.fetchRawEventByTicker(params.slug);
|
|
58
|
+
}
|
|
59
|
+
const status = params?.status || 'active';
|
|
60
|
+
if (status === 'all') {
|
|
61
|
+
const [openEvents, closedEvents, settledEvents] = await Promise.all([
|
|
62
|
+
this.fetchAllWithStatus('open'),
|
|
63
|
+
this.fetchAllWithStatus('closed'),
|
|
64
|
+
this.fetchAllWithStatus('settled'),
|
|
65
|
+
]);
|
|
66
|
+
return [...openEvents, ...closedEvents, ...settledEvents];
|
|
67
|
+
}
|
|
68
|
+
else if (status === 'closed' || status === 'inactive') {
|
|
69
|
+
const [closedEvents, settledEvents] = await Promise.all([
|
|
70
|
+
this.fetchAllWithStatus('closed'),
|
|
71
|
+
this.fetchAllWithStatus('settled'),
|
|
72
|
+
]);
|
|
73
|
+
return [...closedEvents, ...settledEvents];
|
|
74
|
+
}
|
|
75
|
+
return this.fetchAllWithStatus('open');
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// -- OHLCV ----------------------------------------------------------------
|
|
82
|
+
async fetchRawOHLCV(id, params) {
|
|
83
|
+
(0, validation_1.validateIdFormat)(id, 'OHLCV');
|
|
84
|
+
if (!params.resolution) {
|
|
85
|
+
throw new Error('fetchOHLCV requires a resolution parameter.');
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const cleanedId = id.replace(/-NO$/, '');
|
|
89
|
+
const normalizedId = cleanedId.toUpperCase();
|
|
90
|
+
const interval = (0, utils_1.mapIntervalToKalshi)(params.resolution);
|
|
91
|
+
const parts = normalizedId.split('-');
|
|
92
|
+
if (parts.length < 2) {
|
|
93
|
+
throw new Error(`Invalid Kalshi Ticker format: "${id}". Expected format like "FED-25JAN29-B4.75".`);
|
|
94
|
+
}
|
|
95
|
+
const seriesTicker = parts.slice(0, -1).join('-');
|
|
96
|
+
const now = Math.floor(Date.now() / 1000);
|
|
97
|
+
let startTs = now - 24 * 60 * 60;
|
|
98
|
+
let endTs = now;
|
|
99
|
+
const ensureDate = (d) => {
|
|
100
|
+
if (typeof d === 'string') {
|
|
101
|
+
if (!d.endsWith('Z') && !d.match(/[+-]\d{2}:\d{2}$/)) {
|
|
102
|
+
return new Date(d + 'Z');
|
|
103
|
+
}
|
|
104
|
+
return new Date(d);
|
|
105
|
+
}
|
|
106
|
+
return d;
|
|
107
|
+
};
|
|
108
|
+
const pStart = params.start ? ensureDate(params.start) : undefined;
|
|
109
|
+
const pEnd = params.end ? ensureDate(params.end) : undefined;
|
|
110
|
+
if (pStart)
|
|
111
|
+
startTs = Math.floor(pStart.getTime() / 1000);
|
|
112
|
+
if (pEnd) {
|
|
113
|
+
endTs = Math.floor(pEnd.getTime() / 1000);
|
|
114
|
+
if (!pStart)
|
|
115
|
+
startTs = endTs - 24 * 60 * 60;
|
|
116
|
+
}
|
|
117
|
+
const data = await this.ctx.callApi('GetMarketCandlesticks', {
|
|
118
|
+
series_ticker: seriesTicker,
|
|
119
|
+
ticker: normalizedId,
|
|
120
|
+
period_interval: interval,
|
|
121
|
+
start_ts: startTs,
|
|
122
|
+
end_ts: endTs,
|
|
123
|
+
});
|
|
124
|
+
return data.candlesticks || [];
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// -- OrderBook -------------------------------------------------------------
|
|
131
|
+
async fetchRawOrderBook(id) {
|
|
132
|
+
(0, validation_1.validateIdFormat)(id, 'OrderBook');
|
|
133
|
+
const ticker = id.replace(/-NO$/, '');
|
|
134
|
+
const data = await this.ctx.callApi('GetMarketOrderbook', { ticker });
|
|
135
|
+
return data;
|
|
136
|
+
}
|
|
137
|
+
// -- Trades ----------------------------------------------------------------
|
|
138
|
+
async fetchRawTrades(id, params) {
|
|
139
|
+
const ticker = id.replace(/-NO$/, '');
|
|
140
|
+
const data = await this.ctx.callApi('GetTrades', {
|
|
141
|
+
ticker,
|
|
142
|
+
limit: params.limit || 100,
|
|
143
|
+
});
|
|
144
|
+
return data.trades || [];
|
|
145
|
+
}
|
|
146
|
+
// -- User data -------------------------------------------------------------
|
|
147
|
+
async fetchRawMyTrades(params) {
|
|
148
|
+
const queryParams = {};
|
|
149
|
+
if (params?.outcomeId || params?.marketId) {
|
|
150
|
+
queryParams.ticker = (params.outcomeId || params.marketId).replace(/-NO$/, '');
|
|
151
|
+
}
|
|
152
|
+
if (params?.since)
|
|
153
|
+
queryParams.min_ts = Math.floor(params.since.getTime() / 1000);
|
|
154
|
+
if (params?.until)
|
|
155
|
+
queryParams.max_ts = Math.floor(params.until.getTime() / 1000);
|
|
156
|
+
if (params?.limit)
|
|
157
|
+
queryParams.limit = params.limit;
|
|
158
|
+
if (params?.cursor)
|
|
159
|
+
queryParams.cursor = params.cursor;
|
|
160
|
+
const data = await this.ctx.callApi('GetFills', queryParams);
|
|
161
|
+
return data.fills || [];
|
|
162
|
+
}
|
|
163
|
+
async fetchRawPositions() {
|
|
164
|
+
const data = await this.ctx.callApi('GetPositions');
|
|
165
|
+
return data.market_positions || [];
|
|
166
|
+
}
|
|
167
|
+
async fetchRawBalance() {
|
|
168
|
+
return this.ctx.callApi('GetBalance');
|
|
169
|
+
}
|
|
170
|
+
async fetchRawOrders(queryParams) {
|
|
171
|
+
const data = await this.ctx.callApi('GetOrders', queryParams);
|
|
172
|
+
return data.orders || [];
|
|
173
|
+
}
|
|
174
|
+
async fetchRawHistoricalOrders(queryParams) {
|
|
175
|
+
const data = await this.ctx.callApi('GetHistoricalOrders', queryParams);
|
|
176
|
+
return data.orders || [];
|
|
177
|
+
}
|
|
178
|
+
async fetchRawSeriesMap() {
|
|
179
|
+
try {
|
|
180
|
+
const data = await this.ctx.callApi('GetSeriesList');
|
|
181
|
+
const seriesList = data.series || [];
|
|
182
|
+
const map = new Map();
|
|
183
|
+
for (const series of seriesList) {
|
|
184
|
+
if (series.tags && series.tags.length > 0) {
|
|
185
|
+
map.set(series.ticker, series.tags);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return map;
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
throw errors_1.kalshiErrorMapper.mapError(e);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// -- Private helpers -------------------------------------------------------
|
|
195
|
+
async fetchRawEventByTicker(eventTicker) {
|
|
196
|
+
const normalizedTicker = eventTicker.toUpperCase();
|
|
197
|
+
const data = await this.ctx.callApi('GetEvent', {
|
|
198
|
+
event_ticker: normalizedTicker,
|
|
199
|
+
with_nested_markets: true,
|
|
200
|
+
});
|
|
201
|
+
const event = data.event;
|
|
202
|
+
if (!event)
|
|
203
|
+
return [];
|
|
204
|
+
// Enrich with series tags
|
|
205
|
+
if (event.series_ticker) {
|
|
206
|
+
try {
|
|
207
|
+
const seriesData = await this.ctx.callApi('GetSeries', {
|
|
208
|
+
series_ticker: event.series_ticker,
|
|
209
|
+
});
|
|
210
|
+
const series = seriesData.series;
|
|
211
|
+
if (series?.tags?.length > 0 && (!event.tags || event.tags.length === 0)) {
|
|
212
|
+
event.tags = series.tags;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Non-critical
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return [event];
|
|
220
|
+
}
|
|
221
|
+
async fetchRawEventsDefault(params) {
|
|
222
|
+
const limit = params?.limit || 250000;
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
const status = params?.status || 'active';
|
|
225
|
+
let apiStatus = 'open';
|
|
226
|
+
if (status === 'closed' || status === 'inactive')
|
|
227
|
+
apiStatus = 'closed';
|
|
228
|
+
const useCache = status === 'active' || !params?.status;
|
|
229
|
+
if (useCache && this.cachedEvents && this.cachedSeriesMap && now - this.lastCacheTime < CACHE_TTL) {
|
|
230
|
+
return this.cachedEvents;
|
|
231
|
+
}
|
|
232
|
+
const isSorted = params?.sort && (params.sort === 'volume' || params.sort === 'liquidity');
|
|
233
|
+
const fetchLimit = isSorted ? 1000 : limit;
|
|
234
|
+
const [allEvents, fetchedSeriesMap] = await Promise.all([
|
|
235
|
+
this.fetchActiveEvents(fetchLimit, apiStatus),
|
|
236
|
+
this.fetchRawSeriesMap(),
|
|
237
|
+
]);
|
|
238
|
+
// Enrich events with series tags
|
|
239
|
+
for (const event of allEvents) {
|
|
240
|
+
if (event.series_ticker && fetchedSeriesMap.has(event.series_ticker)) {
|
|
241
|
+
if (!event.tags || event.tags.length === 0) {
|
|
242
|
+
event.tags = fetchedSeriesMap.get(event.series_ticker);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (fetchLimit >= 1000 && useCache) {
|
|
247
|
+
this.cachedEvents = allEvents;
|
|
248
|
+
this.cachedSeriesMap = fetchedSeriesMap;
|
|
249
|
+
this.lastCacheTime = now;
|
|
250
|
+
}
|
|
251
|
+
return allEvents;
|
|
252
|
+
}
|
|
253
|
+
async fetchActiveEvents(targetMarketCount, status = 'open') {
|
|
254
|
+
let allEvents = [];
|
|
255
|
+
let totalMarketCount = 0;
|
|
256
|
+
let cursor = null;
|
|
257
|
+
let page = 0;
|
|
258
|
+
do {
|
|
259
|
+
try {
|
|
260
|
+
const queryParams = {
|
|
261
|
+
limit: BATCH_SIZE,
|
|
262
|
+
with_nested_markets: true,
|
|
263
|
+
status,
|
|
264
|
+
};
|
|
265
|
+
if (cursor)
|
|
266
|
+
queryParams.cursor = cursor;
|
|
267
|
+
const data = await this.ctx.callApi('GetEvents', queryParams);
|
|
268
|
+
const events = data.events || [];
|
|
269
|
+
if (events.length === 0)
|
|
270
|
+
break;
|
|
271
|
+
allEvents = allEvents.concat(events);
|
|
272
|
+
if (targetMarketCount) {
|
|
273
|
+
for (const event of events) {
|
|
274
|
+
totalMarketCount += (event.markets || []).length;
|
|
275
|
+
}
|
|
276
|
+
if (totalMarketCount >= targetMarketCount * 1.5)
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
cursor = data.cursor;
|
|
280
|
+
page++;
|
|
281
|
+
if (!targetMarketCount && page >= 10)
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
catch (e) {
|
|
285
|
+
throw errors_1.kalshiErrorMapper.mapError(e);
|
|
286
|
+
}
|
|
287
|
+
} while (cursor && page < MAX_PAGES);
|
|
288
|
+
return allEvents;
|
|
289
|
+
}
|
|
290
|
+
async fetchAllWithStatus(apiStatus) {
|
|
291
|
+
let allEvents = [];
|
|
292
|
+
let cursor = null;
|
|
293
|
+
let page = 0;
|
|
294
|
+
do {
|
|
295
|
+
const queryParams = {
|
|
296
|
+
limit: BATCH_SIZE,
|
|
297
|
+
with_nested_markets: true,
|
|
298
|
+
status: apiStatus,
|
|
299
|
+
};
|
|
300
|
+
if (cursor)
|
|
301
|
+
queryParams.cursor = cursor;
|
|
302
|
+
const data = await this.ctx.callApi('GetEvents', queryParams);
|
|
303
|
+
const events = data.events || [];
|
|
304
|
+
if (events.length === 0)
|
|
305
|
+
break;
|
|
306
|
+
allEvents = allEvents.concat(events);
|
|
307
|
+
cursor = data.cursor;
|
|
308
|
+
page++;
|
|
309
|
+
} while (cursor && page < MAX_PAGES);
|
|
310
|
+
return allEvents;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
exports.KalshiFetcher = KalshiFetcher;
|