mirror of
https://github.com/multipleof4/KalBot.git
synced 2026-03-16 21:41:02 +00:00
Feat: Export getPositions + getBalance for live
This commit is contained in:
@@ -8,20 +8,17 @@ const DEFAULT_HTTP_RETRIES = Math.max(0, Number(process.env.KALSHI_HTTP_RETRIES
|
||||
const BASE_BACKOFF_MS = Math.max(100, Number(process.env.KALSHI_HTTP_BACKOFF_MS || 350));
|
||||
|
||||
const EVENTS_CACHE_TTL_MS = Math.max(1000, Number(process.env.KALSHI_EVENTS_CACHE_TTL_MS || 5000));
|
||||
const eventsCache = new Map(); // key -> { expiresAt, data }
|
||||
const inflightEvents = new Map(); // key -> Promise<events>
|
||||
const eventsCache = new Map();
|
||||
const inflightEvents = new Map();
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
function parseRetryAfterMs(value) {
|
||||
if (!value) return null;
|
||||
|
||||
const asSeconds = Number(value);
|
||||
if (Number.isFinite(asSeconds) && asSeconds >= 0) return asSeconds * 1000;
|
||||
|
||||
const asDate = new Date(value).getTime();
|
||||
if (Number.isFinite(asDate)) return Math.max(0, asDate - Date.now());
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -55,11 +52,7 @@ async function kalshiFetch(method, path, body = null, opts = {}) {
|
||||
if (res.status === 204) return {};
|
||||
const text = await res.text();
|
||||
if (!text) return {};
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
try { return JSON.parse(text); } catch { return {}; }
|
||||
}
|
||||
|
||||
const text = await res.text();
|
||||
@@ -95,20 +88,16 @@ function getEventCloseTimeMs(event) {
|
||||
|
||||
function rankEvents(events = []) {
|
||||
const now = Date.now();
|
||||
|
||||
return events
|
||||
.filter(Boolean)
|
||||
.map((event) => {
|
||||
const status = String(event.status || '').toLowerCase();
|
||||
const closeTs = getEventCloseTimeMs(event);
|
||||
|
||||
const openLike = OPEN_EVENT_STATUSES.has(status);
|
||||
const tradableLike = TRADABLE_EVENT_STATUSES.has(status);
|
||||
const notClearlyExpired = closeTs == null || closeTs > now - 60_000;
|
||||
|
||||
const delta = closeTs == null ? Number.MAX_SAFE_INTEGER : closeTs - now;
|
||||
const closenessScore = delta < 0 ? Math.abs(delta) + 3_600_000 : delta;
|
||||
|
||||
return { event, openLike, tradableLike, notClearlyExpired, closenessScore };
|
||||
})
|
||||
.filter((x) => x.openLike || x.notClearlyExpired)
|
||||
@@ -124,18 +113,12 @@ function rankEvents(events = []) {
|
||||
function getCachedEvents(key) {
|
||||
const hit = eventsCache.get(key);
|
||||
if (!hit) return null;
|
||||
if (Date.now() > hit.expiresAt) {
|
||||
eventsCache.delete(key);
|
||||
return null;
|
||||
}
|
||||
if (Date.now() > hit.expiresAt) { eventsCache.delete(key); return null; }
|
||||
return hit.data;
|
||||
}
|
||||
|
||||
function setCachedEvents(key, events) {
|
||||
eventsCache.set(key, {
|
||||
expiresAt: Date.now() + EVENTS_CACHE_TTL_MS,
|
||||
data: events
|
||||
});
|
||||
eventsCache.set(key, { expiresAt: Date.now() + EVENTS_CACHE_TTL_MS, data: events });
|
||||
}
|
||||
|
||||
async function fetchEvents(series, query) {
|
||||
@@ -165,22 +148,16 @@ async function fetchEvents(series, query) {
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return ranked candidate events for BTC 15m.
|
||||
*/
|
||||
export async function getActiveBTCEvents(limit = 12) {
|
||||
const seriesCandidates = [SERIES_TICKER];
|
||||
const eventMap = new Map();
|
||||
|
||||
for (const series of seriesCandidates) {
|
||||
try {
|
||||
// Use only known-good filter to avoid 400s from unsupported statuses.
|
||||
const openEvents = await fetchEvents(series, 'status=open&limit=25');
|
||||
for (const event of openEvents) {
|
||||
if (event?.event_ticker) eventMap.set(event.event_ticker, event);
|
||||
}
|
||||
|
||||
// Fallback if endpoint returns empty.
|
||||
if (!openEvents.length) {
|
||||
const fallbackEvents = await fetchEvents(series, 'limit=25');
|
||||
for (const event of fallbackEvents) {
|
||||
@@ -195,51 +172,60 @@ export async function getActiveBTCEvents(limit = 12) {
|
||||
return rankEvents([...eventMap.values()]).slice(0, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backward-compatible: return single best candidate event.
|
||||
*/
|
||||
export async function getActiveBTCEvent() {
|
||||
const events = await getActiveBTCEvents(1);
|
||||
return events[0] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get markets for a specific event ticker.
|
||||
*/
|
||||
export async function getEventMarkets(eventTicker) {
|
||||
const data = await kalshiFetch('GET', `/trade-api/v2/events/${eventTicker}`);
|
||||
const markets = data?.event?.markets ?? data?.markets ?? data?.event_markets ?? [];
|
||||
return Array.isArray(markets) ? 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 async function getPositions(params = {}) {
|
||||
let path = '/trade-api/v2/portfolio/positions';
|
||||
const qs = [];
|
||||
if (params.settlement_status) qs.push(`settlement_status=${params.settlement_status}`);
|
||||
if (params.limit) qs.push(`limit=${params.limit}`);
|
||||
if (params.event_ticker) qs.push(`event_ticker=${params.event_ticker}`);
|
||||
if (qs.length) path += `?${qs.join('&')}`;
|
||||
return kalshiFetch('GET', path);
|
||||
}
|
||||
|
||||
export async function getOrder(orderId) {
|
||||
return kalshiFetch('GET', `/trade-api/v2/portfolio/orders/${orderId}`);
|
||||
}
|
||||
|
||||
export async function cancelOrder(orderId) {
|
||||
return kalshiFetch('DELETE', `/trade-api/v2/portfolio/orders/${orderId}`);
|
||||
}
|
||||
|
||||
export async function getFills(params = {}) {
|
||||
let path = '/trade-api/v2/portfolio/fills';
|
||||
const qs = [];
|
||||
if (params.ticker) qs.push(`ticker=${params.ticker}`);
|
||||
if (params.limit) qs.push(`limit=${params.limit}`);
|
||||
if (qs.length) path += `?${qs.join('&')}`;
|
||||
return kalshiFetch('GET', path);
|
||||
}
|
||||
|
||||
export { kalshiFetch };
|
||||
|
||||
Reference in New Issue
Block a user