Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions core/src/exchanges/kalshi/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ export interface KalshiRawMarket {
ticker: string;
title?: string;
status?: string;
last_price?: number;
yes_ask?: number;
yes_bid?: number;
subtitle?: string;
yes_sub_title?: string;
previous_price_dollars?: string;
Expand All @@ -39,10 +36,8 @@ export interface KalshiRawMarket {
rules_secondary?: string;
expiration_time: string;
volume_24h?: number;
volume?: number;
liquidity?: number;
liquidity_dollars?: string;
open_interest?: number;
volume_24h_fp?: string;
volume_fp?: string;
open_interest_fp?: string;
Expand Down
25 changes: 9 additions & 16 deletions core/src/exchanges/kalshi/normalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ const KALSHI_PROMOTED_SERIES_KEYS = [

const KALSHI_PROMOTED_MARKET_KEYS = [
'ticker', 'title', 'rules_primary', 'rules_secondary', 'expiration_time',
'volume_24h_fp', 'volume_24h', 'volume', 'volume_fp',
'liquidity_dollars', 'liquidity', 'open_interest_fp', 'open_interest',
'volume_24h_fp', 'volume_24h', 'volume_fp',
'liquidity_dollars', 'liquidity', 'open_interest_fp',
'status', 'last_price_dollars', 'previous_price_dollars',
'yes_ask_dollars', 'yes_bid_dollars', 'no_ask_dollars', 'no_bid_dollars',
'response_price_units', 'last_price', 'yes_ask', 'yes_bid',
'response_price_units',
] as const;

function parseKalshiPrice(value: string | number | undefined, responseUnits?: string): number | undefined {
Expand Down Expand Up @@ -54,8 +54,7 @@ export class KalshiNormalizer implements IExchangeNormalizer<KalshiRawEvent, Kal
normalizeRawMarket(event: KalshiRawEvent, market: KalshiRawMarket): UnifiedMarket | null {
if (!market) return null;

// Kalshi API v2 migrated from cent integers to FixedPointDollars strings.
// Prefer the _dollars fields; fall back to deprecated cent fields.
// Kalshi API v2 returns prices as FixedPointDollars strings.
const yesBid = parseKalshiPrice(market.yes_bid_dollars, market.response_price_units);
const yesAsk = parseKalshiPrice(market.yes_ask_dollars, market.response_price_units);
const noBid = parseKalshiPrice(market.no_bid_dollars, market.response_price_units);
Expand All @@ -67,12 +66,6 @@ export class KalshiNormalizer implements IExchangeNormalizer<KalshiRawEvent, Kal
price = (yesAsk + yesBid) / 2;
} else if (yesAsk != null) {
price = yesAsk;
} else if (market.last_price) {
price = fromKalshiCents(market.last_price);
} else if (market.yes_ask && market.yes_bid) {
price = (fromKalshiCents(market.yes_ask) + fromKalshiCents(market.yes_bid)) / 2;
} else if (market.yes_ask) {
price = fromKalshiCents(market.yes_ask);
}

const candidateName = this.deriveOutcomeLabel(market);
Expand Down Expand Up @@ -122,10 +115,10 @@ export class KalshiNormalizer implements IExchangeNormalizer<KalshiRawEvent, Kal
slug: market.ticker,
outcomes,
resolutionDate: new Date(market.expiration_time),
volume24h: parseFloat(market.volume_24h_fp ?? '') || Number(market.volume_24h || market.volume || 0),
volume: parseFloat(market.volume_fp ?? '') || Number(market.volume || 0),
volume24h: parseFloat(market.volume_24h_fp ?? '') || Number(market.volume_24h || 0),
volume: parseFloat(market.volume_fp ?? '') || 0,
liquidity: parseFloat(String(market.liquidity_dollars || market.liquidity || '0')) || 0,
openInterest: parseFloat(market.open_interest_fp ?? '') || Number(market.open_interest || 0),
openInterest: parseFloat(market.open_interest_fp ?? '') || 0,
url: `https://kalshi.com/events/${event.event_ticker}`,
category: event.category,
tags: unifiedTags,
Expand Down Expand Up @@ -632,11 +625,11 @@ export class KalshiNormalizer implements IExchangeNormalizer<KalshiRawEvent, Kal
// -- Event sorting utility (exported for fetchEvents) -------------------------

function eventVolume(event: KalshiRawEvent): number {
return (event.markets || []).reduce((sum: number, m: KalshiRawMarket) => sum + (parseFloat(m.volume_fp ?? '') || Number(m.volume || 0)), 0);
return (event.markets || []).reduce((sum: number, m: KalshiRawMarket) => sum + (parseFloat(m.volume_fp ?? '') || 0), 0);
}

function eventLiquidity(event: KalshiRawEvent): number {
return (event.markets || []).reduce((sum: number, m: KalshiRawMarket) => sum + (parseFloat(m.open_interest_fp ?? '') || parseFloat(String(m.liquidity_dollars || m.open_interest || m.liquidity || '0')) || 0), 0);
return (event.markets || []).reduce((sum: number, m: KalshiRawMarket) => sum + (parseFloat(m.open_interest_fp ?? '') || parseFloat(String(m.liquidity_dollars || m.liquidity || '0')) || 0), 0);
}

function eventNewest(event: KalshiRawEvent): number {
Expand Down
18 changes: 0 additions & 18 deletions core/test/normalizers/exchange-normalizers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,11 +388,9 @@ describe('KalshiNormalizer', () => {
rules_secondary: 'Data source: Federal Reserve',
expiration_time: '2025-01-29T18:00:00Z',
volume_24h: 12000,
volume: 350000,
volume_24h_fp: '12000.00',
volume_fp: '350000.00',
open_interest_fp: '8500.00',
open_interest: 8500,
liquidity: 5000,
close_time: '2025-01-29T18:00:00Z',
});
Expand Down Expand Up @@ -702,22 +700,6 @@ describe('KalshiNormalizer', () => {
expect(result!.outcomes[0].price).toBeCloseTo(0.55);
});

it('falls back to legacy cent fields when dollar fields are absent', () => {
const marketCents: KalshiRawMarket = {
ticker: 'TEST-CENTS',
expiration_time: '2025-06-01T00:00:00Z',
last_price: 65,
};
const eventCents: KalshiRawEvent = {
event_ticker: 'TEST-CENTS-EVT',
title: 'Cent Market',
markets: [marketCents],
};
const result = normalizer.normalizeRawMarket(eventCents, marketCents);
expect(result).not.toBeNull();
expect(result!.outcomes[0].price).toBeCloseTo(0.65);
});

it('maps zero price when no price fields are present', () => {
const marketNoPrice: KalshiRawMarket = {
ticker: 'TEST-NOPRICE',
Expand Down