mirror of
https://github.com/multipleof4/KalBot.git
synced 2026-03-16 21:41:02 +00:00
131 lines
3.7 KiB
JavaScript
131 lines
3.7 KiB
JavaScript
import { signRequest, KALSHI_API_BASE } from './auth.js';
|
|
|
|
const OPEN_EVENT_STATUSES = new Set(['open', 'active', 'initialized', 'trading']);
|
|
|
|
async function kalshiFetch(method, path, body = null) {
|
|
const headers = signRequest(method, path);
|
|
const opts = { method, headers };
|
|
if (body) opts.body = JSON.stringify(body);
|
|
|
|
const res = await fetch(`${KALSHI_API_BASE}${path}`, opts);
|
|
if (!res.ok) {
|
|
const text = await res.text();
|
|
throw new Error(`Kalshi API ${method} ${path} → ${res.status}: ${text}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
function getTimeMs(value) {
|
|
if (!value) return null;
|
|
const ts = new Date(value).getTime();
|
|
return Number.isFinite(ts) ? ts : null;
|
|
}
|
|
|
|
function getEventCloseTimeMs(event) {
|
|
return (
|
|
getTimeMs(event?.close_time) ||
|
|
getTimeMs(event?.expiration_time) ||
|
|
getTimeMs(event?.settlement_time) ||
|
|
getTimeMs(event?.end_date) ||
|
|
null
|
|
);
|
|
}
|
|
|
|
function pickBestEvent(events = []) {
|
|
const now = Date.now();
|
|
|
|
const ranked = events
|
|
.filter(Boolean)
|
|
.map((event) => {
|
|
const status = String(event.status || '').toLowerCase();
|
|
const closeTs = getEventCloseTimeMs(event);
|
|
const openLike = OPEN_EVENT_STATUSES.has(status);
|
|
const notClearlyExpired = closeTs == null || closeTs > now - 60_000;
|
|
return { event, openLike, closeTs, notClearlyExpired };
|
|
})
|
|
.filter((x) => x.openLike || x.notClearlyExpired);
|
|
|
|
if (!ranked.length) return events[0] || null;
|
|
|
|
ranked.sort((a, b) => {
|
|
if (a.openLike !== b.openLike) return a.openLike ? -1 : 1;
|
|
const aTs = a.closeTs ?? Number.MAX_SAFE_INTEGER;
|
|
const bTs = b.closeTs ?? Number.MAX_SAFE_INTEGER;
|
|
return aTs - bTs;
|
|
});
|
|
|
|
return ranked[0].event;
|
|
}
|
|
|
|
async function fetchEvents(series, query) {
|
|
const path = `/trade-api/v2/events?series_ticker=${encodeURIComponent(series)}&${query}`;
|
|
const data = await kalshiFetch('GET', path);
|
|
return Array.isArray(data.events) ? data.events : [];
|
|
}
|
|
|
|
/**
|
|
* Get events for the BTC 15-min series.
|
|
* Returns the currently active event + its markets.
|
|
*/
|
|
export async function getActiveBTCEvent() {
|
|
const configuredSeries = process.env.KALSHI_SERIES_TICKER || 'KXBTC15M';
|
|
const seriesCandidates = [...new Set([configuredSeries, configuredSeries.toUpperCase(), configuredSeries.toLowerCase()])];
|
|
const eventMap = new Map();
|
|
|
|
for (const series of seriesCandidates) {
|
|
for (const query of ['status=open&limit=5', 'limit=25']) {
|
|
try {
|
|
const events = await fetchEvents(series, query);
|
|
for (const event of events) {
|
|
if (event?.event_ticker) eventMap.set(event.event_ticker, event);
|
|
}
|
|
} catch (e) {
|
|
console.error(`[Kalshi] Event fetch failed (${series}, ${query}):`, e.message);
|
|
}
|
|
}
|
|
if (eventMap.size) break;
|
|
}
|
|
|
|
return pickBestEvent([...eventMap.values()]);
|
|
}
|
|
|
|
/**
|
|
* Get markets for a specific event ticker.
|
|
*/
|
|
export async function getEventMarkets(eventTicker) {
|
|
const data = await kalshiFetch('GET', `/trade-api/v2/events/${eventTicker}`);
|
|
return data.event?.markets || [];
|
|
}
|
|
|
|
/**
|
|
* Get orderbook for a specific market ticker.
|
|
*/
|
|
export async function getOrderbook(ticker) {
|
|
const data = await kalshiFetch('GET', `/trade-api/v2/markets/${ticker}/orderbook`);
|
|
return data.orderbook || data;
|
|
}
|
|
|
|
/**
|
|
* Get single market details.
|
|
*/
|
|
export async function getMarket(ticker) {
|
|
const data = await kalshiFetch('GET', `/trade-api/v2/markets/${ticker}`);
|
|
return data.market || data;
|
|
}
|
|
|
|
/**
|
|
* Place a real order on Kalshi. NOT used in paper mode.
|
|
*/
|
|
export async function placeOrder(params) {
|
|
return kalshiFetch('POST', '/trade-api/v2/portfolio/orders', params);
|
|
}
|
|
|
|
/**
|
|
* Get wallet balance.
|
|
*/
|
|
export async function getBalance() {
|
|
return kalshiFetch('GET', '/trade-api/v2/portfolio/balance');
|
|
}
|
|
|
|
export { kalshiFetch };
|