kalshi-trading-bot-cli 2.1.2 → 2.1.4
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/README.md +205 -9
- package/data/themes_seo.json +159 -0
- package/env.example +5 -2
- package/package.json +1 -1
- package/src/backtest/renderer.ts +0 -2
- package/src/cli.ts +88 -0
- package/src/commands/basket.ts +646 -0
- package/src/commands/catalysts.ts +121 -0
- package/src/commands/clusters.ts +153 -0
- package/src/commands/correlate.ts +112 -0
- package/src/commands/dispatch.ts +238 -7
- package/src/commands/editorial-themes.ts +494 -0
- package/src/commands/events.ts +140 -0
- package/src/commands/help.ts +299 -14
- package/src/commands/index.ts +137 -6
- package/src/commands/parse-args.ts +204 -1
- package/src/commands/peers.ts +87 -0
- package/src/commands/search-remote.ts +90 -0
- package/src/commands/series.ts +386 -0
- package/src/commands/similar.ts +97 -0
- package/src/commands/status.ts +2 -2
- package/src/components/intro.ts +9 -0
- package/src/db/editorial-themes.ts +111 -0
- package/src/db/event-index.ts +109 -31
- package/src/db/schema.ts +18 -0
- package/src/gateway/commands/handler.ts +6 -2
- package/src/model/llm.ts +5 -5
- package/src/scan/octagon-events-api.ts +55 -0
- package/src/scan/octagon-kalshi-api.ts +564 -0
- package/src/utils/env.ts +1 -1
- package/src/utils/model.ts +1 -1
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed wrappers over Octagon's Kalshi search/clusters/correlation/basket API.
|
|
3
|
+
* Endpoints live under https://api.octagonai.co/v1/prediction-markets/kalshi/*.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the pattern in octagon-events-api.ts:
|
|
6
|
+
* - Fetch + Authorization: Bearer ${OCTAGON_API_KEY}
|
|
7
|
+
* - 60s AbortController timeout per request
|
|
8
|
+
* - Non-2xx → Error with status + body excerpt
|
|
9
|
+
*
|
|
10
|
+
* All endpoints are stateless from the CLI's perspective — no SQLite caching.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const KALSHI_API_BASE = 'https://api.octagonai.co/v1/prediction-markets/kalshi';
|
|
14
|
+
const TIMEOUT_MS = 60_000;
|
|
15
|
+
|
|
16
|
+
function buildQuery(params?: object): string {
|
|
17
|
+
if (!params) return '';
|
|
18
|
+
const usp = new URLSearchParams();
|
|
19
|
+
for (const [k, v] of Object.entries(params)) {
|
|
20
|
+
if (v === undefined || v === null || v === '') continue;
|
|
21
|
+
usp.set(k, String(v));
|
|
22
|
+
}
|
|
23
|
+
const s = usp.toString();
|
|
24
|
+
return s ? `?${s}` : '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function kalshiApi<T>(
|
|
28
|
+
method: 'GET' | 'POST',
|
|
29
|
+
path: string,
|
|
30
|
+
opts?: {
|
|
31
|
+
params?: object;
|
|
32
|
+
body?: unknown;
|
|
33
|
+
},
|
|
34
|
+
): Promise<T> {
|
|
35
|
+
const apiKey = process.env.OCTAGON_API_KEY;
|
|
36
|
+
if (!apiKey) {
|
|
37
|
+
throw new Error('OCTAGON_API_KEY not set. Get one at https://app.octagonai.co');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const url = `${KALSHI_API_BASE}${path}${method === 'GET' ? buildQuery(opts?.params) : ''}`;
|
|
41
|
+
const controller = new AbortController();
|
|
42
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
43
|
+
|
|
44
|
+
let resp: Response;
|
|
45
|
+
try {
|
|
46
|
+
resp = await fetch(url, {
|
|
47
|
+
method,
|
|
48
|
+
headers: {
|
|
49
|
+
Authorization: `Bearer ${apiKey}`,
|
|
50
|
+
...(method === 'POST' ? { 'Content-Type': 'application/json' } : {}),
|
|
51
|
+
},
|
|
52
|
+
...(method === 'POST' && opts?.body !== undefined ? { body: JSON.stringify(opts.body) } : {}),
|
|
53
|
+
signal: controller.signal,
|
|
54
|
+
});
|
|
55
|
+
} finally {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!resp.ok) {
|
|
60
|
+
const body = await resp.text().catch(() => '');
|
|
61
|
+
let detail = body.slice(0, 300);
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(body) as { detail?: unknown };
|
|
64
|
+
if (typeof parsed.detail === 'string') detail = parsed.detail;
|
|
65
|
+
} catch {
|
|
66
|
+
// body wasn't JSON — fall through with text excerpt
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`Octagon Kalshi API ${resp.status} (${method} ${path}): ${detail}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (await resp.json()) as T;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─── Response shapes ────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
export interface KalshiMarketRow {
|
|
77
|
+
market_ticker: string;
|
|
78
|
+
event_ticker: string;
|
|
79
|
+
series_ticker?: string | null;
|
|
80
|
+
title: string;
|
|
81
|
+
subtitle?: string | null;
|
|
82
|
+
yes_subtitle?: string | null;
|
|
83
|
+
no_subtitle?: string | null;
|
|
84
|
+
status: string;
|
|
85
|
+
close_time: string | null;
|
|
86
|
+
last_price?: number | null;
|
|
87
|
+
yes_bid?: number | null;
|
|
88
|
+
yes_ask?: number | null;
|
|
89
|
+
no_bid?: number | null;
|
|
90
|
+
no_ask?: number | null;
|
|
91
|
+
volume?: number | null;
|
|
92
|
+
volume_24h?: number | null;
|
|
93
|
+
liquidity?: number | null;
|
|
94
|
+
open_interest?: number | null;
|
|
95
|
+
category?: string | null;
|
|
96
|
+
event_name?: string | null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface PagedResult<T> {
|
|
100
|
+
data: T[];
|
|
101
|
+
next_cursor: string | null;
|
|
102
|
+
has_more: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface SimilarMarketRow extends KalshiMarketRow {
|
|
106
|
+
distance: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface ClusterRow {
|
|
110
|
+
cluster_id: number;
|
|
111
|
+
label: string;
|
|
112
|
+
description: string;
|
|
113
|
+
size: number;
|
|
114
|
+
sample_titles: string[];
|
|
115
|
+
created_at: string;
|
|
116
|
+
mean_daily_return?: number;
|
|
117
|
+
daily_volatility?: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ClusterMembership {
|
|
121
|
+
market_ticker: string;
|
|
122
|
+
thematic: {
|
|
123
|
+
cluster_id: number;
|
|
124
|
+
label: string;
|
|
125
|
+
description: string;
|
|
126
|
+
size: number;
|
|
127
|
+
} | null;
|
|
128
|
+
behavioral: {
|
|
129
|
+
cluster_id: number;
|
|
130
|
+
label: string;
|
|
131
|
+
size: number;
|
|
132
|
+
mean_daily_return?: number;
|
|
133
|
+
daily_volatility?: number;
|
|
134
|
+
} | null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ClusterPeersResponse {
|
|
138
|
+
market_ticker: string;
|
|
139
|
+
kind: 'thematic' | 'behavioral';
|
|
140
|
+
cluster: {
|
|
141
|
+
cluster_id: number;
|
|
142
|
+
label: string;
|
|
143
|
+
description: string;
|
|
144
|
+
size: number;
|
|
145
|
+
};
|
|
146
|
+
data: SimilarMarketRow[];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface CorrelationResponse {
|
|
150
|
+
tickers: string[];
|
|
151
|
+
matrix: (number | null)[][];
|
|
152
|
+
ranked_pairs: { ticker_a: string; ticker_b: string; correlation: number }[];
|
|
153
|
+
window_days: number;
|
|
154
|
+
interval: string;
|
|
155
|
+
missing: string[];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface BasketCandle {
|
|
159
|
+
time: number;
|
|
160
|
+
open: number;
|
|
161
|
+
high: number;
|
|
162
|
+
low: number;
|
|
163
|
+
close: number;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface BasketCandlesResponse {
|
|
167
|
+
timeframe: string;
|
|
168
|
+
interval_source: string;
|
|
169
|
+
candles: BasketCandle[];
|
|
170
|
+
tickers: string[];
|
|
171
|
+
missing: string[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface BasketSummary {
|
|
175
|
+
total_return: number;
|
|
176
|
+
annualized_return: number;
|
|
177
|
+
sharpe: number | null;
|
|
178
|
+
max_drawdown: number;
|
|
179
|
+
win_rate: number;
|
|
180
|
+
first_nav: number;
|
|
181
|
+
final_nav: number;
|
|
182
|
+
observation_count: number;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface BasketBacktestResponse extends BasketCandlesResponse {
|
|
186
|
+
summary: BasketSummary;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface BasketSizeLeg {
|
|
190
|
+
market_ticker: string;
|
|
191
|
+
side: 'yes' | 'no';
|
|
192
|
+
model_probability: number;
|
|
193
|
+
price: number;
|
|
194
|
+
edge_pp: number;
|
|
195
|
+
kelly_fraction: number;
|
|
196
|
+
weight: number;
|
|
197
|
+
notional_usd: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface BasketSizeResponse {
|
|
201
|
+
bankroll_usd: number;
|
|
202
|
+
kelly_multiplier: number;
|
|
203
|
+
total_notional: number;
|
|
204
|
+
legs: BasketSizeLeg[];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface BasketBuildLeg {
|
|
208
|
+
market_ticker: string;
|
|
209
|
+
title: string;
|
|
210
|
+
category: string | null;
|
|
211
|
+
cluster_id: number | null;
|
|
212
|
+
cluster_label: string | null;
|
|
213
|
+
volume_24h: number | null;
|
|
214
|
+
price: number | null;
|
|
215
|
+
side: 'yes' | 'no';
|
|
216
|
+
model_probability: number | null;
|
|
217
|
+
kelly_fraction: number | null;
|
|
218
|
+
weight: number | null;
|
|
219
|
+
notional_usd: number | null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface BasketBuildResponse {
|
|
223
|
+
legs: BasketBuildLeg[];
|
|
224
|
+
realized_max_pairwise_correlation: number | null;
|
|
225
|
+
cluster_breakdown: Record<string, number>;
|
|
226
|
+
dropped: { market_ticker: string; reason: string }[];
|
|
227
|
+
universe_size: number;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface RankedClusterRow {
|
|
231
|
+
cluster_id: number;
|
|
232
|
+
label: string;
|
|
233
|
+
description: string;
|
|
234
|
+
size: number;
|
|
235
|
+
basket_tickers: string[];
|
|
236
|
+
summary: BasketSummary;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface RankedClustersResponse {
|
|
240
|
+
timeframe: string;
|
|
241
|
+
kind: 'thematic' | 'behavioral';
|
|
242
|
+
top_n_per_cluster: number;
|
|
243
|
+
min_return: number;
|
|
244
|
+
data: RankedClusterRow[];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface MarketsWithEdgeRow {
|
|
248
|
+
event_ticker: string;
|
|
249
|
+
market_ticker?: string | null;
|
|
250
|
+
title: string;
|
|
251
|
+
series_category: string | null;
|
|
252
|
+
model_probability: number; // 0-100 (live API returns percentage, not fraction)
|
|
253
|
+
market_probability: number; // 0-100
|
|
254
|
+
edge_pp: number; // already in percentage points
|
|
255
|
+
expected_return: number;
|
|
256
|
+
confidence_score: number;
|
|
257
|
+
total_volume: number;
|
|
258
|
+
total_open_interest: number;
|
|
259
|
+
captured_at?: string;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export interface MarketsWithEdgeResponse {
|
|
263
|
+
run_id: string;
|
|
264
|
+
captured_at: string | null;
|
|
265
|
+
sort_by: string;
|
|
266
|
+
data: MarketsWithEdgeRow[];
|
|
267
|
+
next_cursor: string | null;
|
|
268
|
+
has_more: boolean;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ─── Group A — Primitives ───────────────────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
export interface SearchMarketsParams {
|
|
274
|
+
q?: string;
|
|
275
|
+
category?: string;
|
|
276
|
+
series_ticker?: string;
|
|
277
|
+
series_prefix?: string;
|
|
278
|
+
event_ticker?: string;
|
|
279
|
+
close_before?: string;
|
|
280
|
+
min_volume_24h?: number;
|
|
281
|
+
sort_by?: 'volume_24h' | 'close_time' | 'last_price';
|
|
282
|
+
limit?: number;
|
|
283
|
+
cursor?: string;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function searchKalshiMarkets(params: SearchMarketsParams): Promise<PagedResult<KalshiMarketRow>> {
|
|
287
|
+
return kalshiApi<PagedResult<KalshiMarketRow>>('GET', '/markets', { params });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export interface SimilarParams {
|
|
291
|
+
anchor_ticker?: string;
|
|
292
|
+
q?: string;
|
|
293
|
+
top_k?: number;
|
|
294
|
+
category?: string;
|
|
295
|
+
min_volume_24h?: number;
|
|
296
|
+
close_before?: string;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export interface SimilarResponse {
|
|
300
|
+
anchor_ticker: string | null;
|
|
301
|
+
anchor_query: string | null;
|
|
302
|
+
data: SimilarMarketRow[];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function findSimilarMarkets(params: SimilarParams): Promise<SimilarResponse> {
|
|
306
|
+
return kalshiApi<SimilarResponse>('GET', '/markets/similar', { params });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export interface ListClustersParams {
|
|
310
|
+
limit?: number;
|
|
311
|
+
sample_titles?: number;
|
|
312
|
+
label_contains?: string;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function listClusters(params: ListClustersParams = {}): Promise<{ data: ClusterRow[] }> {
|
|
316
|
+
return kalshiApi<{ data: ClusterRow[] }>('GET', '/clusters', { params });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export function listBehavioralClusters(params: ListClustersParams = {}): Promise<{ data: ClusterRow[] }> {
|
|
320
|
+
return kalshiApi<{ data: ClusterRow[] }>('GET', '/behavioral-clusters', { params });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function getClusterMarkets(clusterId: number, params: { limit?: number; cursor?: string } = {}): Promise<PagedResult<SimilarMarketRow>> {
|
|
324
|
+
return kalshiApi<PagedResult<SimilarMarketRow>>('GET', `/clusters/${clusterId}/markets`, { params });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function getBehavioralClusterMarkets(clusterId: number, params: { limit?: number; cursor?: string } = {}): Promise<PagedResult<SimilarMarketRow>> {
|
|
328
|
+
return kalshiApi<PagedResult<SimilarMarketRow>>('GET', `/behavioral-clusters/${clusterId}/markets`, { params });
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function getMarketClusterMembership(marketTicker: string): Promise<ClusterMembership> {
|
|
332
|
+
return kalshiApi<ClusterMembership>('GET', `/markets/${encodeURIComponent(marketTicker)}/clusters`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function getClusterPeers(marketTicker: string, params: { kind?: 'thematic' | 'behavioral'; limit?: number } = {}): Promise<ClusterPeersResponse> {
|
|
336
|
+
return kalshiApi<ClusterPeersResponse>('GET', `/markets/${encodeURIComponent(marketTicker)}/cluster-peers`, { params });
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export interface CorrelationsBody {
|
|
340
|
+
market_tickers: string[];
|
|
341
|
+
sides?: ('yes' | 'no')[];
|
|
342
|
+
include_cell_detail?: boolean;
|
|
343
|
+
window_days?: number;
|
|
344
|
+
interval?: '1h' | '1d';
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface CorrelationCellDetail {
|
|
348
|
+
ticker_a: string;
|
|
349
|
+
ticker_b: string;
|
|
350
|
+
correlation: number | null;
|
|
351
|
+
overlap_count: number;
|
|
352
|
+
reason: 'ok' | 'insufficient_overlap' | 'zero_variance';
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export interface CorrelationResponseWithSides extends CorrelationResponse {
|
|
356
|
+
sides?: ('yes' | 'no')[];
|
|
357
|
+
cells_detail?: CorrelationCellDetail[] | null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function getCorrelations(body: CorrelationsBody): Promise<CorrelationResponseWithSides> {
|
|
361
|
+
return kalshiApi<CorrelationResponseWithSides>('POST', '/markets/correlations', { body });
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export interface BasketCandlesBody {
|
|
365
|
+
market_tickers: string[];
|
|
366
|
+
weights?: number[];
|
|
367
|
+
timeframe?: '1w' | '1m' | '3m' | '6m' | '1y';
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export function getBasketCandles(body: BasketCandlesBody): Promise<BasketCandlesResponse> {
|
|
371
|
+
return kalshiApi<BasketCandlesResponse>('POST', '/baskets/candles', { body });
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ─── Group B — Composites ───────────────────────────────────────────────────
|
|
375
|
+
|
|
376
|
+
export interface BasketSizeBody {
|
|
377
|
+
bankroll_usd: number;
|
|
378
|
+
kelly_multiplier: number;
|
|
379
|
+
legs: { market_ticker: string; side: 'yes' | 'no'; model_probability: number }[];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function getBasketSize(body: BasketSizeBody): Promise<BasketSizeResponse> {
|
|
383
|
+
return kalshiApi<BasketSizeResponse>('POST', '/baskets/size', { body });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export interface BasketBuildUniverse {
|
|
387
|
+
q?: string;
|
|
388
|
+
anchor_ticker?: string;
|
|
389
|
+
market_tickers?: string[]; // explicit candidate pool (1–200); takes precedence over q/anchor
|
|
390
|
+
category?: string;
|
|
391
|
+
series_ticker?: string;
|
|
392
|
+
min_volume_24h?: number;
|
|
393
|
+
close_before?: string;
|
|
394
|
+
label_contains_any?: string[];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export interface BasketBuildSizing {
|
|
398
|
+
strategy: 'equal' | 'kelly';
|
|
399
|
+
bankroll_usd?: number;
|
|
400
|
+
kelly_multiplier?: number;
|
|
401
|
+
leg_probabilities?: Record<string, number>;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export interface BasketBuildBody {
|
|
405
|
+
universe: BasketBuildUniverse;
|
|
406
|
+
n: number;
|
|
407
|
+
max_per_cluster?: number;
|
|
408
|
+
max_pairwise_correlation?: number;
|
|
409
|
+
candidate_pool_size?: number;
|
|
410
|
+
correlation_window_days?: number;
|
|
411
|
+
sizing: BasketBuildSizing;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function buildBasket(body: BasketBuildBody): Promise<BasketBuildResponse> {
|
|
415
|
+
return kalshiApi<BasketBuildResponse>('POST', '/baskets/build', { body });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function backtestBasket(body: BasketCandlesBody): Promise<BasketBacktestResponse> {
|
|
419
|
+
return kalshiApi<BasketBacktestResponse>('POST', '/baskets/backtest', { body });
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export interface RankedClustersParams {
|
|
423
|
+
timeframe?: '1w' | '1m' | '3m' | '6m' | '1y';
|
|
424
|
+
min_return?: number;
|
|
425
|
+
top_n_per_cluster?: number;
|
|
426
|
+
kind?: 'thematic' | 'behavioral';
|
|
427
|
+
max_clusters?: number;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export function getClustersRankedByReturn(params: RankedClustersParams = {}): Promise<RankedClustersResponse> {
|
|
431
|
+
return kalshiApi<RankedClustersResponse>('GET', '/clusters/ranked-by-return', { params });
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export interface MarketsWithEdgeParams {
|
|
435
|
+
run_id?: string;
|
|
436
|
+
category?: string;
|
|
437
|
+
edge_pp_min?: number;
|
|
438
|
+
edge_pp_max?: number;
|
|
439
|
+
expected_return_min?: number;
|
|
440
|
+
total_volume_min?: number;
|
|
441
|
+
model_probability_min?: number;
|
|
442
|
+
sort_by?: 'edge_pp' | 'expected_return' | 'total_volume' | 'model_probability';
|
|
443
|
+
limit?: number;
|
|
444
|
+
cursor?: string;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export function getMarketsWithEdge(params: MarketsWithEdgeParams = {}): Promise<MarketsWithEdgeResponse> {
|
|
448
|
+
return kalshiApi<MarketsWithEdgeResponse>('GET', '/markets-with-edge', { params });
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ─── Endpoints added in subsequent sessions ─────────────────────────────────
|
|
452
|
+
|
|
453
|
+
export interface PerTickerEdgeRow {
|
|
454
|
+
input_ticker: string;
|
|
455
|
+
market_ticker: string | null;
|
|
456
|
+
event_ticker: string | null;
|
|
457
|
+
title: string | null;
|
|
458
|
+
series_category: string | null;
|
|
459
|
+
model_probability: number | null; // 0-1 fraction per the new endpoint doc
|
|
460
|
+
market_probability: number | null;
|
|
461
|
+
edge_pp: number | null;
|
|
462
|
+
expected_return: number | null;
|
|
463
|
+
confidence_score: number | null;
|
|
464
|
+
total_volume: number | null;
|
|
465
|
+
total_open_interest: number | null;
|
|
466
|
+
status: 'scored' | 'unscored';
|
|
467
|
+
captured_at: string | null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export interface PerTickerEdgeResponse {
|
|
471
|
+
run_id: string;
|
|
472
|
+
captured_at: string;
|
|
473
|
+
data: PerTickerEdgeRow[];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export function getMarketsEdge(body: { tickers: string[]; run_id?: string }): Promise<PerTickerEdgeResponse> {
|
|
477
|
+
return kalshiApi<PerTickerEdgeResponse>('POST', '/markets/edge', { body });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export function getEventMarkets(
|
|
481
|
+
eventTicker: string,
|
|
482
|
+
params: { limit?: number; cursor?: string; min_volume_24h?: number } = {},
|
|
483
|
+
): Promise<{ event_ticker: string; data: KalshiMarketRow[]; next_cursor?: string | null; has_more?: boolean }> {
|
|
484
|
+
return kalshiApi('GET', `/events/${encodeURIComponent(eventTicker)}/markets`, { params });
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export interface SeriesRollupRow {
|
|
488
|
+
series_ticker: string;
|
|
489
|
+
series_title: string | null;
|
|
490
|
+
market_count: number;
|
|
491
|
+
active_count: number;
|
|
492
|
+
total_volume_24h: number;
|
|
493
|
+
dominant_category: string | null;
|
|
494
|
+
categories: string[];
|
|
495
|
+
last_seen_at: string;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export interface SeriesListParams {
|
|
499
|
+
series_prefix?: string;
|
|
500
|
+
category?: string;
|
|
501
|
+
min_volume_24h?: number;
|
|
502
|
+
sort_by?: 'total_volume_24h' | 'market_count' | 'active_count';
|
|
503
|
+
limit?: number;
|
|
504
|
+
cursor?: string;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export function listKalshiSeries(params: SeriesListParams = {}): Promise<PagedResult<SeriesRollupRow>> {
|
|
508
|
+
return kalshiApi<PagedResult<SeriesRollupRow>>('GET', '/series', { params });
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export interface SeriesEventRow {
|
|
512
|
+
event_ticker: string;
|
|
513
|
+
series_ticker: string;
|
|
514
|
+
title: string;
|
|
515
|
+
sub_title?: string | null;
|
|
516
|
+
category?: string | null;
|
|
517
|
+
mutually_exclusive?: boolean | null;
|
|
518
|
+
available_on_brokers?: boolean | null;
|
|
519
|
+
last_updated_ts?: string | null;
|
|
520
|
+
kalshi_url?: string | null;
|
|
521
|
+
kalshi_image_url?: string | null;
|
|
522
|
+
has_report?: boolean;
|
|
523
|
+
close_time?: string | null;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function getSeriesEvents(
|
|
527
|
+
seriesTicker: string,
|
|
528
|
+
params: { limit?: number; cursor?: string; q?: string } = {},
|
|
529
|
+
): Promise<{ series_ticker: string; data: SeriesEventRow[]; next_cursor?: string | null; has_more?: boolean }> {
|
|
530
|
+
return kalshiApi('GET', `/series/${encodeURIComponent(seriesTicker)}/events`, { params });
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export interface ValidateBasketLeg {
|
|
534
|
+
market_ticker: string;
|
|
535
|
+
side: 'yes' | 'no';
|
|
536
|
+
stake_usd: number;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export interface ValidateBasketBody {
|
|
540
|
+
legs: ValidateBasketLeg[];
|
|
541
|
+
bankroll_usd?: number;
|
|
542
|
+
correlation_window_days?: number;
|
|
543
|
+
correlation_interval?: '1h' | '1d';
|
|
544
|
+
max_pairwise_correlation?: number;
|
|
545
|
+
calendar_clash_window_days?: number;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export interface BasketValidateResponse {
|
|
549
|
+
total_stake_usd: number;
|
|
550
|
+
bankroll_usd: number | null;
|
|
551
|
+
max_leg_pct: number;
|
|
552
|
+
cluster_breakdown_thematic: Record<string, string[]>;
|
|
553
|
+
cluster_breakdown_behavioral: Record<string, string[]>;
|
|
554
|
+
unassigned_market_tickers: string[];
|
|
555
|
+
max_pairwise_correlation: number | null;
|
|
556
|
+
pairwise_correlations: { ticker_a: string; ticker_b: string; correlation: number }[];
|
|
557
|
+
calendar_clashes: { window_start: string; window_end: string; market_tickers: string[] }[];
|
|
558
|
+
duplicate_underliers: { event_ticker: string; market_tickers: string[] }[];
|
|
559
|
+
warnings: string[];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export function validateBasket(body: ValidateBasketBody): Promise<BasketValidateResponse> {
|
|
563
|
+
return kalshiApi<BasketValidateResponse>('POST', '/baskets/validate', { body });
|
|
564
|
+
}
|
package/src/utils/env.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
3
|
import { config } from 'dotenv';
|
|
4
|
-
import { getProviderById } from '
|
|
4
|
+
import { getProviderById } from '../providers.js';
|
|
5
5
|
import { appPath, getAppDir } from './paths.js';
|
|
6
6
|
|
|
7
7
|
// Resolve .env from a CWD override (dev workflow) or the home config dir
|
package/src/utils/model.ts
CHANGED