pmxt-core 2.12.1 → 2.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/exchanges/kalshi/api.d.ts +7 -1
- package/dist/exchanges/kalshi/api.js +11 -2
- package/dist/exchanges/kalshi/config.d.ts +103 -0
- package/dist/exchanges/kalshi/config.js +144 -0
- package/dist/exchanges/kalshi/fetchEvents.d.ts +2 -2
- package/dist/exchanges/kalshi/fetchEvents.js +19 -16
- package/dist/exchanges/kalshi/fetchMarkets.d.ts +2 -2
- package/dist/exchanges/kalshi/fetchMarkets.js +36 -25
- package/dist/exchanges/kalshi/fetchOHLCV.d.ts +2 -2
- package/dist/exchanges/kalshi/fetchOHLCV.js +20 -17
- package/dist/exchanges/kalshi/fetchOrderBook.d.ts +2 -0
- package/dist/exchanges/kalshi/fetchOrderBook.js +60 -0
- package/dist/exchanges/kalshi/fetchTrades.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchTrades.js +32 -0
- package/dist/exchanges/kalshi/index.d.ts +8 -3
- package/dist/exchanges/kalshi/index.js +82 -70
- package/dist/exchanges/kalshi/kalshi.test.js +294 -292
- package/dist/exchanges/kalshi/utils.d.ts +1 -3
- package/dist/exchanges/kalshi/utils.js +14 -16
- package/dist/exchanges/kalshi/websocket.d.ts +4 -3
- package/dist/exchanges/kalshi/websocket.js +87 -61
- package/dist/exchanges/kalshi-demo/index.d.ts +10 -0
- package/dist/exchanges/kalshi-demo/index.js +23 -0
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
- package/dist/exchanges/polymarket/api-clob.js +1 -1
- package/dist/exchanges/polymarket/api-data.d.ts +1 -1
- package/dist/exchanges/polymarket/api-data.js +1 -1
- package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
- package/dist/exchanges/polymarket/api-gamma.js +1 -1
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -1
- package/dist/server/app.js +56 -48
- package/package.json +5 -4
|
@@ -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-02-
|
|
3
|
+
* Generated at: 2026-02-23T07:41:05.138Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const kalshiApiSpec: {
|
|
@@ -11,6 +11,12 @@ export declare const kalshiApiSpec: {
|
|
|
11
11
|
};
|
|
12
12
|
servers: {
|
|
13
13
|
url: string;
|
|
14
|
+
variables: {
|
|
15
|
+
env: {
|
|
16
|
+
default: string;
|
|
17
|
+
enum: string[];
|
|
18
|
+
};
|
|
19
|
+
};
|
|
14
20
|
}[];
|
|
15
21
|
paths: {
|
|
16
22
|
"/historical/cutoff": {
|
|
@@ -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-02-
|
|
6
|
+
* Generated at: 2026-02-23T07:41:05.138Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -14,7 +14,16 @@ exports.kalshiApiSpec = {
|
|
|
14
14
|
},
|
|
15
15
|
"servers": [
|
|
16
16
|
{
|
|
17
|
-
"url": "https://
|
|
17
|
+
"url": "https://{env}.elections.kalshi.com/trade-api/v2",
|
|
18
|
+
"variables": {
|
|
19
|
+
"env": {
|
|
20
|
+
"default": "api",
|
|
21
|
+
"enum": [
|
|
22
|
+
"api",
|
|
23
|
+
"demo-api"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
18
27
|
}
|
|
19
28
|
],
|
|
20
29
|
"paths": {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kalshi runtime configuration.
|
|
3
|
+
*
|
|
4
|
+
* This file is hand-authored and is the single source of truth for:
|
|
5
|
+
* - Base URL constants for production and demo environments
|
|
6
|
+
* - API path constants (KALSHI_PATHS)
|
|
7
|
+
* - URL helper functions used by fetch functions and the exchange class
|
|
8
|
+
*
|
|
9
|
+
* The OpenAPI spec lives in core/specs/kalshi/Kalshi.yaml and is compiled
|
|
10
|
+
* into core/src/exchanges/kalshi/api.ts by `npm run fetch:openapi`.
|
|
11
|
+
* Do NOT put runtime config into api.ts — it will be overwritten.
|
|
12
|
+
*
|
|
13
|
+
* Environment mapping (aligns with the `{env}` server variable in Kalshi.yaml):
|
|
14
|
+
* env = "api" → production: https://api.elections.kalshi.com
|
|
15
|
+
* env = "demo-api" → demo/paper: https://demo-api.elections.kalshi.com
|
|
16
|
+
*/
|
|
17
|
+
export declare const KALSHI_PROD_API_URL = "https://api.elections.kalshi.com";
|
|
18
|
+
export declare const KALSHI_DEMO_API_URL = "https://demo-api.kalshi.co";
|
|
19
|
+
export declare const KALSHI_PROD_WS_URL = "wss://api.elections.kalshi.com/trade-api/ws/v2";
|
|
20
|
+
export declare const KALSHI_DEMO_WS_URL = "wss://demo-api.kalshi.co/trade-api/ws/v2";
|
|
21
|
+
export declare const KALSHI_PATHS: {
|
|
22
|
+
TRADE_API: string;
|
|
23
|
+
EVENTS: string;
|
|
24
|
+
SERIES: string;
|
|
25
|
+
PORTFOLIO: string;
|
|
26
|
+
MARKETS: string;
|
|
27
|
+
BALANCE: string;
|
|
28
|
+
ORDERS: string;
|
|
29
|
+
POSITIONS: string;
|
|
30
|
+
};
|
|
31
|
+
export interface KalshiApiConfig {
|
|
32
|
+
/** Base REST API URL — production or demo */
|
|
33
|
+
apiUrl: string;
|
|
34
|
+
/** WebSocket URL — production or demo */
|
|
35
|
+
wsUrl?: string;
|
|
36
|
+
/** Whether the demo environment is active */
|
|
37
|
+
demoMode: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Return a typed config object for the requested environment.
|
|
41
|
+
*
|
|
42
|
+
* @param demoMode - Pass `true` to target demo-api.elections.kalshi.com.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const config = getKalshiConfig(true);
|
|
47
|
+
* // config.apiUrl === "https://demo-api.elections.kalshi.com"
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function getKalshiConfig(demoMode?: boolean): KalshiApiConfig;
|
|
51
|
+
/**
|
|
52
|
+
* Build the full path (including `/trade-api/v2` prefix) for use in
|
|
53
|
+
* `KalshiAuth.getHeaders()` signing.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* getApiPath("/portfolio/balance")
|
|
58
|
+
* // → "/trade-api/v2/portfolio/balance"
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function getApiPath(operationPath: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* Build the full URL for the events endpoint.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* getEventsUrl(baseUrl) // .../events
|
|
68
|
+
* getEventsUrl(baseUrl, ['FED-25']) // .../events/FED-25
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function getEventsUrl(baseUrl: string, pathSegments?: string[]): string;
|
|
72
|
+
/**
|
|
73
|
+
* Build the full URL for the series endpoint, with optional nested segments.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* getSeriesUrl(baseUrl) // .../series
|
|
78
|
+
* getSeriesUrl(baseUrl, 'FED') // .../series/FED
|
|
79
|
+
* getSeriesUrl(baseUrl, 'FED', ['markets', 'FED-B4.75', 'candlesticks'])
|
|
80
|
+
* // .../series/FED/markets/FED-B4.75/candlesticks
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function getSeriesUrl(baseUrl: string, seriesTicker?: string, pathSegments?: string[]): string;
|
|
84
|
+
/**
|
|
85
|
+
* Build the full URL for the portfolio endpoint.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* getPortfolioUrl(baseUrl, '/balance') // .../portfolio/balance
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function getPortfolioUrl(baseUrl: string, subPath?: string): string;
|
|
93
|
+
/**
|
|
94
|
+
* Build the full URL for the markets endpoint, with optional nested segments.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* getMarketsUrl(baseUrl) // .../markets
|
|
99
|
+
* getMarketsUrl(baseUrl, 'FED-B4.75') // .../markets/FED-B4.75
|
|
100
|
+
* getMarketsUrl(baseUrl, 'FED-B4.75', ['orderbook']) // .../markets/FED-B4.75/orderbook
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export declare function getMarketsUrl(baseUrl: string, marketId?: string, pathSegments?: string[]): string;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Kalshi runtime configuration.
|
|
4
|
+
*
|
|
5
|
+
* This file is hand-authored and is the single source of truth for:
|
|
6
|
+
* - Base URL constants for production and demo environments
|
|
7
|
+
* - API path constants (KALSHI_PATHS)
|
|
8
|
+
* - URL helper functions used by fetch functions and the exchange class
|
|
9
|
+
*
|
|
10
|
+
* The OpenAPI spec lives in core/specs/kalshi/Kalshi.yaml and is compiled
|
|
11
|
+
* into core/src/exchanges/kalshi/api.ts by `npm run fetch:openapi`.
|
|
12
|
+
* Do NOT put runtime config into api.ts — it will be overwritten.
|
|
13
|
+
*
|
|
14
|
+
* Environment mapping (aligns with the `{env}` server variable in Kalshi.yaml):
|
|
15
|
+
* env = "api" → production: https://api.elections.kalshi.com
|
|
16
|
+
* env = "demo-api" → demo/paper: https://demo-api.elections.kalshi.com
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.KALSHI_PATHS = exports.KALSHI_DEMO_WS_URL = exports.KALSHI_PROD_WS_URL = exports.KALSHI_DEMO_API_URL = exports.KALSHI_PROD_API_URL = void 0;
|
|
20
|
+
exports.getKalshiConfig = getKalshiConfig;
|
|
21
|
+
exports.getApiPath = getApiPath;
|
|
22
|
+
exports.getEventsUrl = getEventsUrl;
|
|
23
|
+
exports.getSeriesUrl = getSeriesUrl;
|
|
24
|
+
exports.getPortfolioUrl = getPortfolioUrl;
|
|
25
|
+
exports.getMarketsUrl = getMarketsUrl;
|
|
26
|
+
// ── Base URL constants ────────────────────────────────────────────────────────
|
|
27
|
+
exports.KALSHI_PROD_API_URL = "https://api.elections.kalshi.com";
|
|
28
|
+
exports.KALSHI_DEMO_API_URL = "https://demo-api.kalshi.co";
|
|
29
|
+
exports.KALSHI_PROD_WS_URL = "wss://api.elections.kalshi.com/trade-api/ws/v2";
|
|
30
|
+
exports.KALSHI_DEMO_WS_URL = "wss://demo-api.kalshi.co/trade-api/ws/v2";
|
|
31
|
+
// ── Path constants ────────────────────────────────────────────────────────────
|
|
32
|
+
exports.KALSHI_PATHS = {
|
|
33
|
+
TRADE_API: "/trade-api/v2",
|
|
34
|
+
EVENTS: "/events",
|
|
35
|
+
SERIES: "/series",
|
|
36
|
+
PORTFOLIO: "/portfolio",
|
|
37
|
+
MARKETS: "/markets",
|
|
38
|
+
BALANCE: "/balance",
|
|
39
|
+
ORDERS: "/orders",
|
|
40
|
+
POSITIONS: "/positions",
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Return a typed config object for the requested environment.
|
|
44
|
+
*
|
|
45
|
+
* @param demoMode - Pass `true` to target demo-api.elections.kalshi.com.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const config = getKalshiConfig(true);
|
|
50
|
+
* // config.apiUrl === "https://demo-api.elections.kalshi.com"
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
function getKalshiConfig(demoMode = false) {
|
|
54
|
+
return {
|
|
55
|
+
apiUrl: demoMode ? exports.KALSHI_DEMO_API_URL : exports.KALSHI_PROD_API_URL,
|
|
56
|
+
wsUrl: demoMode ? exports.KALSHI_DEMO_WS_URL : exports.KALSHI_PROD_WS_URL,
|
|
57
|
+
demoMode,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// ── URL builder helpers ───────────────────────────────────────────────────────
|
|
61
|
+
/**
|
|
62
|
+
* Build a full URL from a base and an arbitrary list of path segments.
|
|
63
|
+
* Empty segments are filtered out; leading/trailing slashes are normalised.
|
|
64
|
+
*/
|
|
65
|
+
function buildApiUrl(baseUrl, ...segments) {
|
|
66
|
+
const flatSegments = segments.flat().filter(Boolean);
|
|
67
|
+
const path = flatSegments.map((s) => s.replace(/^\/+|\/+$/g, "")).join("/");
|
|
68
|
+
return path ? `${baseUrl}/${path}` : baseUrl;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Build the full path (including `/trade-api/v2` prefix) for use in
|
|
72
|
+
* `KalshiAuth.getHeaders()` signing.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* getApiPath("/portfolio/balance")
|
|
77
|
+
* // → "/trade-api/v2/portfolio/balance"
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
function getApiPath(operationPath) {
|
|
81
|
+
return exports.KALSHI_PATHS.TRADE_API + operationPath;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Build the full URL for the events endpoint.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* getEventsUrl(baseUrl) // .../events
|
|
89
|
+
* getEventsUrl(baseUrl, ['FED-25']) // .../events/FED-25
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
function getEventsUrl(baseUrl, pathSegments = []) {
|
|
93
|
+
return buildApiUrl(baseUrl, exports.KALSHI_PATHS.TRADE_API, exports.KALSHI_PATHS.EVENTS, pathSegments);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Build the full URL for the series endpoint, with optional nested segments.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* getSeriesUrl(baseUrl) // .../series
|
|
101
|
+
* getSeriesUrl(baseUrl, 'FED') // .../series/FED
|
|
102
|
+
* getSeriesUrl(baseUrl, 'FED', ['markets', 'FED-B4.75', 'candlesticks'])
|
|
103
|
+
* // .../series/FED/markets/FED-B4.75/candlesticks
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
function getSeriesUrl(baseUrl, seriesTicker, pathSegments = []) {
|
|
107
|
+
const segments = [
|
|
108
|
+
exports.KALSHI_PATHS.TRADE_API,
|
|
109
|
+
exports.KALSHI_PATHS.SERIES,
|
|
110
|
+
seriesTicker || "",
|
|
111
|
+
...pathSegments,
|
|
112
|
+
];
|
|
113
|
+
return buildApiUrl(baseUrl, ...segments);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Build the full URL for the portfolio endpoint.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* getPortfolioUrl(baseUrl, '/balance') // .../portfolio/balance
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
function getPortfolioUrl(baseUrl, subPath) {
|
|
124
|
+
return buildApiUrl(baseUrl, exports.KALSHI_PATHS.TRADE_API, exports.KALSHI_PATHS.PORTFOLIO, subPath || "");
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Build the full URL for the markets endpoint, with optional nested segments.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* getMarketsUrl(baseUrl) // .../markets
|
|
132
|
+
* getMarketsUrl(baseUrl, 'FED-B4.75') // .../markets/FED-B4.75
|
|
133
|
+
* getMarketsUrl(baseUrl, 'FED-B4.75', ['orderbook']) // .../markets/FED-B4.75/orderbook
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
function getMarketsUrl(baseUrl, marketId, pathSegments = []) {
|
|
137
|
+
const segments = [
|
|
138
|
+
exports.KALSHI_PATHS.TRADE_API,
|
|
139
|
+
exports.KALSHI_PATHS.MARKETS,
|
|
140
|
+
marketId || "",
|
|
141
|
+
...pathSegments,
|
|
142
|
+
];
|
|
143
|
+
return buildApiUrl(baseUrl, ...segments);
|
|
144
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EventFetchParams } from
|
|
2
|
-
import { UnifiedEvent } from
|
|
1
|
+
import { EventFetchParams } from "../../BaseExchange";
|
|
2
|
+
import { UnifiedEvent } from "../../types";
|
|
3
3
|
type CallApi = (operationId: string, params?: Record<string, any>) => Promise<any>;
|
|
4
4
|
export declare function fetchEvents(params: EventFetchParams, callApi: CallApi): Promise<UnifiedEvent[]>;
|
|
5
5
|
export {};
|
|
@@ -5,7 +5,10 @@ const utils_1 = require("./utils");
|
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
6
|
async function fetchEventByTicker(eventTicker, callApi) {
|
|
7
7
|
const normalizedTicker = eventTicker.toUpperCase();
|
|
8
|
-
const data = await callApi(
|
|
8
|
+
const data = await callApi("GetEvent", {
|
|
9
|
+
event_ticker: normalizedTicker,
|
|
10
|
+
with_nested_markets: true,
|
|
11
|
+
});
|
|
9
12
|
const event = data.event;
|
|
10
13
|
if (!event)
|
|
11
14
|
return [];
|
|
@@ -27,7 +30,7 @@ async function fetchEventByTicker(eventTicker, callApi) {
|
|
|
27
30
|
url: `https://kalshi.com/events/${event.event_ticker}`,
|
|
28
31
|
image: event.image_url,
|
|
29
32
|
category: event.category,
|
|
30
|
-
tags: event.tags || []
|
|
33
|
+
tags: event.tags || [],
|
|
31
34
|
};
|
|
32
35
|
return [unifiedEvent];
|
|
33
36
|
}
|
|
@@ -41,9 +44,9 @@ async function fetchEvents(params, callApi) {
|
|
|
41
44
|
if (params.slug) {
|
|
42
45
|
return await fetchEventByTicker(params.slug, callApi);
|
|
43
46
|
}
|
|
44
|
-
const status = params?.status ||
|
|
47
|
+
const status = params?.status || "active";
|
|
45
48
|
const limit = params?.limit || 10000;
|
|
46
|
-
const query = (params?.query ||
|
|
49
|
+
const query = (params?.query || "").toLowerCase();
|
|
47
50
|
const fetchAllWithStatus = async (apiStatus) => {
|
|
48
51
|
let allEvents = [];
|
|
49
52
|
let cursor = null;
|
|
@@ -54,11 +57,11 @@ async function fetchEvents(params, callApi) {
|
|
|
54
57
|
const queryParams = {
|
|
55
58
|
limit: BATCH_SIZE,
|
|
56
59
|
with_nested_markets: true,
|
|
57
|
-
status: apiStatus
|
|
60
|
+
status: apiStatus,
|
|
58
61
|
};
|
|
59
62
|
if (cursor)
|
|
60
63
|
queryParams.cursor = cursor;
|
|
61
|
-
const data = await callApi(
|
|
64
|
+
const data = await callApi("GetEvents", queryParams);
|
|
62
65
|
const events = data.events || [];
|
|
63
66
|
if (events.length === 0)
|
|
64
67
|
break;
|
|
@@ -73,26 +76,26 @@ async function fetchEvents(params, callApi) {
|
|
|
73
76
|
return allEvents;
|
|
74
77
|
};
|
|
75
78
|
let events = [];
|
|
76
|
-
if (status ===
|
|
79
|
+
if (status === "all") {
|
|
77
80
|
const [openEvents, closedEvents, settledEvents] = await Promise.all([
|
|
78
|
-
fetchAllWithStatus(
|
|
79
|
-
fetchAllWithStatus(
|
|
80
|
-
fetchAllWithStatus(
|
|
81
|
+
fetchAllWithStatus("open"),
|
|
82
|
+
fetchAllWithStatus("closed"),
|
|
83
|
+
fetchAllWithStatus("settled"),
|
|
81
84
|
]);
|
|
82
85
|
events = [...openEvents, ...closedEvents, ...settledEvents];
|
|
83
86
|
}
|
|
84
|
-
else if (status ===
|
|
87
|
+
else if (status === "closed" || status === "inactive") {
|
|
85
88
|
const [closedEvents, settledEvents] = await Promise.all([
|
|
86
|
-
fetchAllWithStatus(
|
|
87
|
-
fetchAllWithStatus(
|
|
89
|
+
fetchAllWithStatus("closed"),
|
|
90
|
+
fetchAllWithStatus("settled"),
|
|
88
91
|
]);
|
|
89
92
|
events = [...closedEvents, ...settledEvents];
|
|
90
93
|
}
|
|
91
94
|
else {
|
|
92
|
-
events = await fetchAllWithStatus(
|
|
95
|
+
events = await fetchAllWithStatus("open");
|
|
93
96
|
}
|
|
94
97
|
const filtered = events.filter((event) => {
|
|
95
|
-
return (event.title ||
|
|
98
|
+
return (event.title || "").toLowerCase().includes(query);
|
|
96
99
|
});
|
|
97
100
|
const unifiedEvents = filtered.map((event) => {
|
|
98
101
|
const markets = [];
|
|
@@ -113,7 +116,7 @@ async function fetchEvents(params, callApi) {
|
|
|
113
116
|
url: `https://kalshi.com/events/${event.event_ticker}`,
|
|
114
117
|
image: event.image_url,
|
|
115
118
|
category: event.category,
|
|
116
|
-
tags: event.tags || []
|
|
119
|
+
tags: event.tags || [],
|
|
117
120
|
};
|
|
118
121
|
return unifiedEvent;
|
|
119
122
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MarketFetchParams } from
|
|
2
|
-
import { UnifiedMarket } from
|
|
1
|
+
import { MarketFetchParams } from "../../BaseExchange";
|
|
2
|
+
import { UnifiedMarket } from "../../types";
|
|
3
3
|
type CallApi = (operationId: string, params?: Record<string, any>) => Promise<any>;
|
|
4
4
|
export declare function resetCache(): void;
|
|
5
5
|
export declare function fetchMarkets(params: MarketFetchParams | undefined, callApi: CallApi): Promise<UnifiedMarket[]>;
|
|
@@ -4,7 +4,7 @@ exports.resetCache = resetCache;
|
|
|
4
4
|
exports.fetchMarkets = fetchMarkets;
|
|
5
5
|
const utils_1 = require("./utils");
|
|
6
6
|
const errors_1 = require("./errors");
|
|
7
|
-
async function fetchActiveEvents(callApi, targetMarketCount, status =
|
|
7
|
+
async function fetchActiveEvents(callApi, targetMarketCount, status = "open") {
|
|
8
8
|
let allEvents = [];
|
|
9
9
|
let totalMarketCount = 0;
|
|
10
10
|
let cursor = null;
|
|
@@ -19,11 +19,11 @@ async function fetchActiveEvents(callApi, targetMarketCount, status = 'open') {
|
|
|
19
19
|
const queryParams = {
|
|
20
20
|
limit: BATCH_SIZE,
|
|
21
21
|
with_nested_markets: true,
|
|
22
|
-
status: status // Filter by status (default 'open')
|
|
22
|
+
status: status, // Filter by status (default 'open')
|
|
23
23
|
};
|
|
24
24
|
if (cursor)
|
|
25
25
|
queryParams.cursor = cursor;
|
|
26
|
-
const data = await callApi(
|
|
26
|
+
const data = await callApi("GetEvents", queryParams);
|
|
27
27
|
const events = data.events || [];
|
|
28
28
|
if (events.length === 0)
|
|
29
29
|
break;
|
|
@@ -54,7 +54,7 @@ async function fetchActiveEvents(callApi, targetMarketCount, status = 'open') {
|
|
|
54
54
|
}
|
|
55
55
|
async function fetchSeriesMap(callApi) {
|
|
56
56
|
try {
|
|
57
|
-
const data = await callApi(
|
|
57
|
+
const data = await callApi("GetSeriesList");
|
|
58
58
|
const seriesList = data.series || [];
|
|
59
59
|
const map = new Map();
|
|
60
60
|
for (const series of seriesList) {
|
|
@@ -91,7 +91,7 @@ async function fetchMarkets(params, callApi) {
|
|
|
91
91
|
}
|
|
92
92
|
// Handle outcomeId lookup (strip -NO suffix, use as ticker)
|
|
93
93
|
if (params?.outcomeId) {
|
|
94
|
-
const ticker = params.outcomeId.replace(/-NO$/,
|
|
94
|
+
const ticker = params.outcomeId.replace(/-NO$/, "");
|
|
95
95
|
return await fetchMarketsBySlug(ticker, callApi);
|
|
96
96
|
}
|
|
97
97
|
// Handle eventId lookup (event ticker works the same way)
|
|
@@ -112,14 +112,19 @@ async function fetchMarkets(params, callApi) {
|
|
|
112
112
|
async function fetchMarketsBySlug(eventTicker, callApi) {
|
|
113
113
|
// Kalshi API expects uppercase tickers, but URLs use lowercase
|
|
114
114
|
const normalizedTicker = eventTicker.toUpperCase();
|
|
115
|
-
const data = await callApi(
|
|
115
|
+
const data = await callApi("GetEvent", {
|
|
116
|
+
event_ticker: normalizedTicker,
|
|
117
|
+
with_nested_markets: true,
|
|
118
|
+
});
|
|
116
119
|
const event = data.event;
|
|
117
120
|
if (!event)
|
|
118
121
|
return [];
|
|
119
122
|
// Enrichment: Fetch series tags if they exist
|
|
120
123
|
if (event.series_ticker) {
|
|
121
124
|
try {
|
|
122
|
-
const seriesData = await callApi(
|
|
125
|
+
const seriesData = await callApi("GetSeries", {
|
|
126
|
+
series_ticker: event.series_ticker,
|
|
127
|
+
});
|
|
123
128
|
const series = seriesData.series;
|
|
124
129
|
if (series && series.tags && series.tags.length > 0) {
|
|
125
130
|
if (!event.tags || event.tags.length === 0) {
|
|
@@ -146,13 +151,15 @@ async function searchMarkets(query, params, callApi) {
|
|
|
146
151
|
const searchLimit = 250000;
|
|
147
152
|
const markets = await fetchMarketsDefault({ ...params, limit: searchLimit }, callApi);
|
|
148
153
|
const lowerQuery = query.toLowerCase();
|
|
149
|
-
const searchIn = params?.searchIn ||
|
|
150
|
-
const filtered = markets.filter(market => {
|
|
151
|
-
const titleMatch = (market.title ||
|
|
152
|
-
const descMatch = (market.description ||
|
|
153
|
-
|
|
154
|
+
const searchIn = params?.searchIn || "title"; // Default to title-only search
|
|
155
|
+
const filtered = markets.filter((market) => {
|
|
156
|
+
const titleMatch = (market.title || "").toLowerCase().includes(lowerQuery);
|
|
157
|
+
const descMatch = (market.description || "")
|
|
158
|
+
.toLowerCase()
|
|
159
|
+
.includes(lowerQuery);
|
|
160
|
+
if (searchIn === "title")
|
|
154
161
|
return titleMatch;
|
|
155
|
-
if (searchIn ===
|
|
162
|
+
if (searchIn === "description")
|
|
156
163
|
return descMatch;
|
|
157
164
|
return titleMatch || descMatch; // 'both'
|
|
158
165
|
});
|
|
@@ -163,21 +170,24 @@ async function fetchMarketsDefault(params, callApi) {
|
|
|
163
170
|
const limit = params?.limit || 250000;
|
|
164
171
|
const offset = params?.offset || 0;
|
|
165
172
|
const now = Date.now();
|
|
166
|
-
const status = params?.status ||
|
|
173
|
+
const status = params?.status || "active"; // Default to 'active'
|
|
167
174
|
// Map 'active' -> 'open', 'closed' -> 'closed'
|
|
168
175
|
// Kalshi statuses: 'open', 'closed', 'settled'
|
|
169
|
-
let apiStatus =
|
|
170
|
-
if (status ===
|
|
171
|
-
apiStatus =
|
|
172
|
-
else if (status ===
|
|
173
|
-
apiStatus =
|
|
176
|
+
let apiStatus = "open";
|
|
177
|
+
if (status === "closed" || status === "inactive")
|
|
178
|
+
apiStatus = "closed";
|
|
179
|
+
else if (status === "all")
|
|
180
|
+
apiStatus = "open"; // Fallback for all? Or loop? For now default to open.
|
|
174
181
|
try {
|
|
175
182
|
let events;
|
|
176
183
|
let seriesMap;
|
|
177
184
|
// Check if we have valid cached data
|
|
178
185
|
// Only use global cache for the default 'active'/'open' case
|
|
179
|
-
const useCache =
|
|
180
|
-
if (useCache &&
|
|
186
|
+
const useCache = status === "active" || !params?.status;
|
|
187
|
+
if (useCache &&
|
|
188
|
+
cachedEvents &&
|
|
189
|
+
cachedSeriesMap &&
|
|
190
|
+
now - lastCacheTime < CACHE_TTL) {
|
|
181
191
|
events = cachedEvents;
|
|
182
192
|
seriesMap = cachedSeriesMap;
|
|
183
193
|
}
|
|
@@ -185,11 +195,12 @@ async function fetchMarketsDefault(params, callApi) {
|
|
|
185
195
|
// Optimize fetch limit based on request parameters
|
|
186
196
|
// If sorting is required (e.g. by volume), we need to fetch a larger set (or all) to sort correctly.
|
|
187
197
|
// If no sorting is requested, we only need to fetch enough to satisfy the limit.
|
|
188
|
-
const isSorted = params?.sort &&
|
|
198
|
+
const isSorted = params?.sort &&
|
|
199
|
+
(params.sort === "volume" || params.sort === "liquidity");
|
|
189
200
|
const fetchLimit = isSorted ? 1000 : limit;
|
|
190
201
|
const [allEvents, fetchedSeriesMap] = await Promise.all([
|
|
191
202
|
fetchActiveEvents(callApi, fetchLimit, apiStatus),
|
|
192
|
-
fetchSeriesMap(callApi)
|
|
203
|
+
fetchSeriesMap(callApi),
|
|
193
204
|
]);
|
|
194
205
|
events = allEvents;
|
|
195
206
|
seriesMap = fetchedSeriesMap;
|
|
@@ -222,10 +233,10 @@ async function fetchMarketsDefault(params, callApi) {
|
|
|
222
233
|
}
|
|
223
234
|
}
|
|
224
235
|
// Sort by 24h volume
|
|
225
|
-
if (params?.sort ===
|
|
236
|
+
if (params?.sort === "volume") {
|
|
226
237
|
allMarkets.sort((a, b) => b.volume24h - a.volume24h);
|
|
227
238
|
}
|
|
228
|
-
else if (params?.sort ===
|
|
239
|
+
else if (params?.sort === "liquidity") {
|
|
229
240
|
allMarkets.sort((a, b) => b.liquidity - a.liquidity);
|
|
230
241
|
}
|
|
231
242
|
return allMarkets.slice(offset, offset + limit);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { HistoryFilterParams, OHLCVParams } from
|
|
2
|
-
import { PriceCandle } from
|
|
1
|
+
import { HistoryFilterParams, OHLCVParams } from "../../BaseExchange";
|
|
2
|
+
import { PriceCandle } from "../../types";
|
|
3
3
|
export declare function fetchOHLCV(id: string, params: OHLCVParams | HistoryFilterParams, callApi: (operationId: string, params?: Record<string, any>) => Promise<any>): Promise<PriceCandle[]>;
|
|
@@ -5,34 +5,34 @@ const utils_1 = require("./utils");
|
|
|
5
5
|
const validation_1 = require("../../utils/validation");
|
|
6
6
|
const errors_1 = require("./errors");
|
|
7
7
|
async function fetchOHLCV(id, params, callApi) {
|
|
8
|
-
(0, validation_1.validateIdFormat)(id,
|
|
8
|
+
(0, validation_1.validateIdFormat)(id, "OHLCV");
|
|
9
9
|
// Validate resolution is provided
|
|
10
10
|
if (!params.resolution) {
|
|
11
|
-
throw new Error(
|
|
11
|
+
throw new Error("fetchOHLCV requires a resolution parameter. Use OHLCVParams with resolution specified.");
|
|
12
12
|
}
|
|
13
13
|
try {
|
|
14
14
|
// Kalshi API expects uppercase tickers
|
|
15
15
|
// Handle virtual "-NO" suffix by stripping it (fetching the underlying market history)
|
|
16
|
-
const cleanedId = id.replace(/-NO$/,
|
|
16
|
+
const cleanedId = id.replace(/-NO$/, "");
|
|
17
17
|
const normalizedId = cleanedId.toUpperCase();
|
|
18
18
|
const interval = (0, utils_1.mapIntervalToKalshi)(params.resolution);
|
|
19
19
|
// Heuristic for series_ticker
|
|
20
|
-
const parts = normalizedId.split(
|
|
20
|
+
const parts = normalizedId.split("-");
|
|
21
21
|
if (parts.length < 2) {
|
|
22
22
|
throw new Error(`Invalid Kalshi Ticker format: "${id}". Expected format like "FED-25JAN29-B4.75".`);
|
|
23
23
|
}
|
|
24
|
-
const seriesTicker = parts.slice(0, -1).join(
|
|
24
|
+
const seriesTicker = parts.slice(0, -1).join("-");
|
|
25
25
|
const now = Math.floor(Date.now() / 1000);
|
|
26
|
-
let startTs = now -
|
|
26
|
+
let startTs = now - 24 * 60 * 60;
|
|
27
27
|
let endTs = now;
|
|
28
28
|
// Helper to handle string dates (from JSON)
|
|
29
29
|
// IMPORTANT: Python sends naive datetimes as ISO strings without 'Z' suffix.
|
|
30
30
|
// We must treat these as UTC, not local time.
|
|
31
31
|
const ensureDate = (d) => {
|
|
32
|
-
if (typeof d ===
|
|
32
|
+
if (typeof d === "string") {
|
|
33
33
|
// If string doesn't end with 'Z' and doesn't have timezone offset, append 'Z'
|
|
34
|
-
if (!d.endsWith(
|
|
35
|
-
return new Date(d +
|
|
34
|
+
if (!d.endsWith("Z") && !d.match(/[+-]\d{2}:\d{2}$/)) {
|
|
35
|
+
return new Date(d + "Z");
|
|
36
36
|
}
|
|
37
37
|
return new Date(d);
|
|
38
38
|
}
|
|
@@ -46,10 +46,10 @@ async function fetchOHLCV(id, params, callApi) {
|
|
|
46
46
|
if (pEnd) {
|
|
47
47
|
endTs = Math.floor(pEnd.getTime() / 1000);
|
|
48
48
|
if (!pStart) {
|
|
49
|
-
startTs = endTs -
|
|
49
|
+
startTs = endTs - 24 * 60 * 60;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
const data = await callApi(
|
|
52
|
+
const data = await callApi("GetMarketCandlesticks", {
|
|
53
53
|
series_ticker: seriesTicker,
|
|
54
54
|
ticker: normalizedId,
|
|
55
55
|
period_interval: interval,
|
|
@@ -68,18 +68,21 @@ async function fetchOHLCV(id, params, callApi) {
|
|
|
68
68
|
const getVal = (field) => {
|
|
69
69
|
if (p[field] !== null && p[field] !== undefined)
|
|
70
70
|
return p[field];
|
|
71
|
-
if (ask[field] !== null &&
|
|
71
|
+
if (ask[field] !== null &&
|
|
72
|
+
bid[field] !== null &&
|
|
73
|
+
ask[field] !== undefined &&
|
|
74
|
+
bid[field] !== undefined) {
|
|
72
75
|
return (ask[field] + bid[field]) / 2;
|
|
73
76
|
}
|
|
74
77
|
return p.previous || 0;
|
|
75
78
|
};
|
|
76
79
|
return {
|
|
77
80
|
timestamp: c.end_period_ts * 1000,
|
|
78
|
-
open: getVal(
|
|
79
|
-
high: getVal(
|
|
80
|
-
low: getVal(
|
|
81
|
-
close: getVal(
|
|
82
|
-
volume: c.volume || 0
|
|
81
|
+
open: getVal("open") / 100,
|
|
82
|
+
high: getVal("high") / 100,
|
|
83
|
+
low: getVal("low") / 100,
|
|
84
|
+
close: getVal("close") / 100,
|
|
85
|
+
volume: c.volume || 0,
|
|
83
86
|
};
|
|
84
87
|
});
|
|
85
88
|
if (params.limit && mappedCandles.length > params.limit) {
|