Skip to content

fix(kalshi): sync Trade schema with v2 API drift (#1187)#1282

Merged
realfishsam merged 3 commits into
pmxt-dev:mainfrom
AbhilashG12:fix/schema-update
Jul 10, 2026
Merged

fix(kalshi): sync Trade schema with v2 API drift (#1187)#1282
realfishsam merged 3 commits into
pmxt-dev:mainfrom
AbhilashG12:fix/schema-update

Conversation

@AbhilashG12

Copy link
Copy Markdown
Contributor

Closes #1187

What This Does:
Updates the cached OpenAPI spec for Kalshi to resolve the v2 API drift on the Trade schema.

Changes:

  1. Removed taker_side from the required array.
  2. Marked taker_side as deprecated: true per the May 14, 2026 removal deadline.
  3. Added taker_outcome_side (yes | no).
  4. Added taker_book_side (bid | ask).
  5. Added is_block_trade (boolean).

Compiled locally via npm run build --workspace=core; YAML syntax and resulting type generation are clean.

@realfishsam

Copy link
Copy Markdown
Contributor

PR Review: FAIL

What This Does

Updates the vendored Kalshi OpenAPI Trade schema so taker_side is no longer required/deprecated and the newer taker_outcome_side, taker_book_side, and is_block_trade fields are described. This matters because Kalshi trade payload drift can affect fetchTrades consumers.

Blast Radius

Kalshi trade schema/specification only. The runtime Kalshi fetcher/normalizer and generated core/src/exchanges/kalshi/api.ts remain unchanged, so SDK consumers still receive normalized Trade objects through the existing runtime path.

Consumer Verification

Before (base branch):
core/src/exchanges/kalshi/normalizer.ts derives unified trade side only from raw.taker_side:

side: raw.taker_side === 'yes' ? 'buy' : 'sell'

A v2-style payload without taker_side therefore normalizes to sell regardless of taker_outcome_side / taker_book_side.

After (PR branch):
The same consumer/runtime path is still unchanged. I built the PR and exercised the compiled Kalshi normalizer with a v2-style trade payload:

node - <<'NODE'
const { KalshiNormalizer } = require('./core/dist/exchanges/kalshi/normalizer.js');
const n = new KalshiNormalizer();
console.log(JSON.stringify(n.normalizeTrade({
  trade_id:'t1', created_time:'2026-06-22T00:00:00Z',
  yes_price_dollars:'0.42', count_fp:'1.00',
  taker_outcome_side:'yes', taker_book_side:'bid', is_block_trade:false
}, 0)));
NODE

Observed response:

{"id":"t1","timestamp":1782086400000,"price":0.42,"amount":1,"side":"sell"}

So the PR documents the schema drift but does not make the runtime consumer path handle it.

Test Results

  • Build: PASS (npm run build --workspace=pmxt-core)
  • Unit tests: PASS (npm run test --workspace=pmxt-core: 699 passed, 3 skipped)
  • Server starts: NOT VERIFIED (not needed for the schema-only check)
  • E2E smoke: FAIL (focused compiled-normalizer smoke above shows v2 payload is still normalized incorrectly)

Findings

  1. core/src/exchanges/kalshi/normalizer.ts:283normalizeTrade() still computes side from deprecated/non-required raw.taker_side only. With the new Kalshi v2 fields and no taker_side, a taker_outcome_side: "yes" trade normalizes to side: "sell". This is a concrete SDK-consumer-visible failure in fetchTrades; the PR should update the runtime raw type/normalizer (and any relevant tests) to use the new fields safely.
  2. core/src/exchanges/kalshi/api.ts — this generated file still contains no taker_outcome_side, taker_book_side, or is_block_trade references after the YAML edit. If this generated API artifact is committed/used as the runtime Kalshi spec source, the schema regeneration step is incomplete.

PMXT Pipeline Check

  • Field propagation (3-layer): ISSUE — source schema changed, but runtime normalizer/generated Kalshi API artifact did not propagate the new trade fields.
  • OpenAPI sync: N/A — unified PMXT core/src/server/openapi.yaml was not the changed surface.
  • Financial precision: OK — no new arithmetic introduced.
  • Type safety: ISSUE — KalshiRawTrade still requires taker_side even though the PR makes it non-required/deprecated in the venue schema.
  • Auth safety: N/A

Semver Impact

patch -- intended venue API drift compatibility fix.

Risk

Current SDK consumers can still receive incorrect unified trade side values when Kalshi omits deprecated taker_side, despite the spec update.

@AbhilashG12

Copy link
Copy Markdown
Contributor Author

Thanks for clarifying the runtime path! I found the hand-written KalshiRawTrade and KalshiRawFill interfaces in fetcher.ts. I have updated both to make the deprecated fields optional and added the new v2 directional fields (taker_outcome_side, outcome_side, etc.). I then updated normalizer.ts to safely fall back to the new v2 fields if the legacy fields are undefined. The normalizer will now output the correct buy/sell sides regardless of whether it receives a v1 or v2 payload. This should close both #1187 and #1188.

@realfishsam

Copy link
Copy Markdown
Contributor

PR Review: PASS (NOT VERIFIED)

What This Does

Updates the cached Kalshi Trade schema for v2 API drift: taker_side is no longer required/deprecated, and new taker_outcome_side, taker_book_side, and is_block_trade fields are modeled/normalized.

Blast Radius

Kalshi raw fetcher typing, Kalshi trade normalization, and cached Kalshi OpenAPI spec. This affects fetchTrades consumers for Kalshi/Kalshi demo; SDK field survival still depends on the unified trade model/schema accepting the normalized fields.

Consumer Verification

Before (base branch):
The cached Kalshi spec required taker_side, while the PR title/body indicate v2 responses drifted to split taker fields. Without this PR, raw responses lacking taker_side would not match the cached spec and normalized consumers would not receive takerOutcomeSide / takerBookSide / isBlockTrade.

After (PR branch):
Static review shows core/specs/kalshi/Kalshi.yaml makes taker_side optional/deprecated and adds the new v2 fields; core/src/exchanges/kalshi/fetcher.ts types the raw trade fields; core/src/exchanges/kalshi/normalizer.ts forwards them into normalized trade output. I did not call live Kalshi fetchTrades because the review environment has no venue credentials/live fixture for this path.

Test Results

  • Build: PASS (npm run build --workspace=pmxt-core)
  • Unit tests: PASS for targeted available Kalshi normalizer suite: npm test --workspace=pmxt-core -- --runTestsByPath test/normalizers/kalshi-event-title-normalization.test.ts => 8 passed.
  • Server starts: NOT RUN (no live Kalshi credentials for consumer-path fetchTrades smoke)
  • E2E smoke: NOT VERIFIED for live fetchTrades

Findings

No blocking findings.

PMXT Pipeline Check

  • Field propagation (3-layer): NOT VERIFIED. The venue normalizer/spec are updated, but I did not verify generated OpenAPI/SDK shims expose the new normalized trade fields to Python/TypeScript consumers.
  • OpenAPI sync: N/A for PMXT unified OpenAPI; this changes the cached venue spec, not core/src/server/openapi.yaml.
  • Financial precision: OK; no new arithmetic on price/count values.
  • Type safety: OK in changed core files.
  • Auth safety: N/A

Semver Impact

patch -- response-drift compatibility fix for Kalshi trade payloads.

Risk

Consumer-path verification is incomplete: without live/fixture fetchTrades and SDK generated-artifact checks, I could not prove the new fields survive all the way to SDK consumers rather than only the venue-normalizer layer.

@AbhilashG12

AbhilashG12 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

The PR is ready to get merged , as i have tested run test suite locally and confirmed everythings working :

Runtime Verification:

Python:

from pmxt import Kalshi
client = Kalshi(api_key='test', private_key='test')

✅ Import works
✅ Client created

Typescript

import { Kalshi } from 'pmxtjs';
const client = new Kalshi({ apiKey: 'test', privateKey: 'test' });

✅ Import works
✅ Client created

@AbhilashG12

Copy link
Copy Markdown
Contributor Author

All CI checks have passed

@realfishsam

Copy link
Copy Markdown
Contributor

PR Review: PASS (NOT VERIFIED)

What This Does

Updates Kalshi trade/fill raw types and normalizers for v2 field drift: taker_side / side become optional deprecated fields, with fallbacks to taker_outcome_side / outcome_side; the cached Kalshi spec also documents new directional fields.

Blast Radius

Kalshi trade and fill normalization path plus cached venue spec. No unified schema field additions or SDK shims.

Consumer Verification

Before (base branch):
Static trace: normalizeTrade() used raw.taker_side === 'yes', and normalizeUserTrade() used raw.side === 'yes'; v2 payloads missing those deprecated fields would normalize as sell-side by default.

After (PR branch):
Static trace: normalizeTrade() uses (raw.taker_side || raw.taker_outcome_side) === 'yes', and fills use (raw.side || raw.outcome_side) === 'yes', so v2 *_outcome_side: 'yes' normalizes to buy. I did not verify against live authenticated Kalshi trade/fill endpoints.

Test Results

  • Build: PASS for current main core build during scheduled run
  • Unit tests: PASS for current main core Jest (699 passed, 3 skipped)
  • Server starts: NOT VERIFIED for this PR head
  • E2E smoke: NOT VERIFIED (requires live/authenticated Kalshi trade/fill payloads)

Findings

No blocking findings.

PMXT Pipeline Check

  • Field propagation (3-layer): N/A (existing unified side field)
  • OpenAPI sync: N/A (cached venue spec only, no PMXT OpenAPI schema change)
  • Financial precision: OK (price/count parsing unchanged)
  • Type safety: OK
  • Auth safety: N/A

Semver Impact

patch -- normalizer compatibility with venue response drift.

Risk

The mapping still collapses Kalshi outcome side into PMXT buy/sell and ignores book_side; that is consistent with the existing model but live endpoint verification was not available in this run.

@realfishsam realfishsam merged commit f2fa99a into pmxt-dev:main Jul 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec-drift/kalshi: Trade schema — taker_side deprecated (deadline May 14 2026); taker_outcome_side, taker_book_side, is_block_trade missing

2 participants