chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+457
View File
@@ -0,0 +1,457 @@
import type { KalshiAuthParams, KalshiOrder } from '@/tools/kalshi/types'
import { buildKalshiAuthHeaders, buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiAmendOrderParams extends KalshiAuthParams {
orderId: string // Order ID to amend (required)
ticker: string // Market ticker (required)
side: string // 'yes' or 'no' (required)
action: string // 'buy' or 'sell' (required)
clientOrderId: string // Original client order ID (required)
updatedClientOrderId: string // New client order ID (required)
count?: string // Updated quantity
yesPrice?: string // Updated yes price in cents (1-99)
noPrice?: string // Updated no price in cents (1-99)
yesPriceDollars?: string // Updated yes price in dollars
noPriceDollars?: string // Updated no price in dollars
}
export interface KalshiAmendOrderResponse {
success: boolean
output: {
order: KalshiOrder
}
}
export const kalshiAmendOrderTool: ToolConfig<KalshiAmendOrderParams, KalshiAmendOrderResponse> = {
id: 'kalshi_amend_order',
name: 'Amend Order on Kalshi',
description: 'Modify the price or quantity of an existing order on Kalshi',
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Order ID to amend (e.g., "abc123-def456-ghi789")',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
side: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Side of the order: "yes" or "no"',
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Action type: "buy" or "sell"',
},
clientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Original client-specified order ID',
},
updatedClientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New client-specified order ID after amendment',
},
count: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated quantity for the order (e.g., "10", "100")',
},
yesPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated yes price in cents (1-99)',
},
noPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated no price in cents (1-99)',
},
yesPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated yes price in dollars (e.g., "0.56")',
},
noPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated no price in dollars (e.g., "0.56")',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId.trim()}/amend`),
method: 'POST',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId.trim()}/amend`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'POST', path)
},
body: (params) => {
const body: Record<string, any> = {
ticker: params.ticker,
side: params.side.toLowerCase(),
action: params.action.toLowerCase(),
client_order_id: params.clientOrderId,
updated_client_order_id: params.updatedClientOrderId,
}
if (params.count) body.count = Number.parseInt(params.count, 10)
if (params.yesPrice) body.yes_price = Number.parseInt(params.yesPrice, 10)
if (params.noPrice) body.no_price = Number.parseInt(params.noPrice, 10)
if (params.yesPriceDollars) body.yes_price_dollars = params.yesPriceDollars
if (params.noPriceDollars) body.no_price_dollars = params.noPriceDollars
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'amend_order')
}
return {
success: true,
output: {
order: data.order,
},
}
},
outputs: {
order: {
type: 'object',
description: 'The amended order object',
},
},
}
export interface KalshiAmendOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to amend (required)
ticker: string // Market ticker (required)
side: string // 'yes' or 'no' (required)
action: string // 'buy' or 'sell' (required)
clientOrderId?: string // Original client order ID (optional in V2)
updatedClientOrderId?: string // New client order ID (optional in V2)
count?: string // Updated quantity
yesPrice?: string // Updated yes price in cents (1-99)
noPrice?: string // Updated no price in cents (1-99)
yesPriceDollars?: string // Updated yes price in dollars
noPriceDollars?: string // Updated no price in dollars
countFp?: string // Count in fixed-point for fractional contracts
}
interface KalshiAmendOrderV2Order {
order_id: string
user_id: string | null
ticker: string
event_ticker: string
status: string
side: string
type: string
yes_price: number | null
no_price: number | null
action: string
count: number
remaining_count: number
created_time: string
expiration_time: string | null
order_group_id: string | null
client_order_id: string | null
place_count: number | null
decrease_count: number | null
queue_position: number | null
maker_fill_count: number | null
taker_fill_count: number | null
maker_fees: number | null
taker_fees: number | null
last_update_time: string | null
take_profit_order_id: string | null
stop_loss_order_id: string | null
amend_count: number | null
amend_taker_fill_count: number | null
}
export interface KalshiAmendOrderV2Response {
success: boolean
output: {
old_order: KalshiAmendOrderV2Order
order: KalshiAmendOrderV2Order
}
}
export const kalshiAmendOrderV2Tool: ToolConfig<
KalshiAmendOrderV2Params,
KalshiAmendOrderV2Response
> = {
id: 'kalshi_amend_order_v2',
name: 'Amend Order on Kalshi V2',
description:
'Modify the price or quantity of an existing order on Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Order ID to amend (e.g., "abc123-def456-ghi789")',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
side: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Side of the order: "yes" or "no"',
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Action type: "buy" or "sell"',
},
clientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Original client-specified order ID',
},
updatedClientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New client-specified order ID after amendment',
},
count: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated quantity for the order (e.g., "10", "100")',
},
yesPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated yes price in cents (1-99)',
},
noPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated no price in cents (1-99)',
},
yesPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated yes price in dollars (e.g., "0.56")',
},
noPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated no price in dollars (e.g., "0.56")',
},
countFp: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Count in fixed-point for fractional contracts',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId.trim()}/amend`),
method: 'POST',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId.trim()}/amend`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'POST', path)
},
body: (params) => {
const body: Record<string, any> = {
ticker: params.ticker,
side: params.side.toLowerCase(),
action: params.action.toLowerCase(),
}
if (params.clientOrderId) body.client_order_id = params.clientOrderId
if (params.updatedClientOrderId) body.updated_client_order_id = params.updatedClientOrderId
if (params.count) body.count = Number.parseInt(params.count, 10)
if (params.yesPrice) body.yes_price = Number.parseInt(params.yesPrice, 10)
if (params.noPrice) body.no_price = Number.parseInt(params.noPrice, 10)
if (params.yesPriceDollars) body.yes_price_dollars = params.yesPriceDollars
if (params.noPriceDollars) body.no_price_dollars = params.noPriceDollars
if (params.countFp) body.count_fp = params.countFp
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'amend_order_v2')
}
const mapOrder = (order: any): KalshiAmendOrderV2Order => ({
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
ticker: order.ticker ?? null,
event_ticker: order.event_ticker ?? null,
status: order.status ?? null,
side: order.side ?? null,
type: order.type ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
action: order.action ?? null,
count: order.count ?? null,
remaining_count: order.remaining_count ?? null,
created_time: order.created_time ?? null,
expiration_time: order.expiration_time ?? null,
order_group_id: order.order_group_id ?? null,
client_order_id: order.client_order_id ?? null,
place_count: order.place_count ?? null,
decrease_count: order.decrease_count ?? null,
queue_position: order.queue_position ?? null,
maker_fill_count: order.maker_fill_count ?? null,
taker_fill_count: order.taker_fill_count ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees: order.taker_fees ?? null,
last_update_time: order.last_update_time ?? null,
take_profit_order_id: order.take_profit_order_id ?? null,
stop_loss_order_id: order.stop_loss_order_id ?? null,
amend_count: order.amend_count ?? null,
amend_taker_fill_count: order.amend_taker_fill_count ?? null,
})
return {
success: true,
output: {
old_order: mapOrder(data.old_order || {}),
order: mapOrder(data.order || {}),
},
}
},
outputs: {
old_order: {
type: 'object',
description: 'The original order object before amendment',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
status: { type: 'string', description: 'Order status' },
side: { type: 'string', description: 'Order side (yes/no)' },
type: { type: 'string', description: 'Order type (limit/market)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
action: { type: 'string', description: 'Action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
created_time: { type: 'string', description: 'Order creation time' },
expiration_time: { type: 'string', description: 'Order expiration time' },
order_group_id: { type: 'string', description: 'Order group ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
place_count: { type: 'number', description: 'Place count' },
decrease_count: { type: 'number', description: 'Decrease count' },
queue_position: { type: 'number', description: 'Queue position' },
maker_fill_count: { type: 'number', description: 'Maker fill count' },
taker_fill_count: { type: 'number', description: 'Taker fill count' },
maker_fees: { type: 'number', description: 'Maker fees' },
taker_fees: { type: 'number', description: 'Taker fees' },
last_update_time: { type: 'string', description: 'Last update time' },
take_profit_order_id: { type: 'string', description: 'Take profit order ID' },
stop_loss_order_id: { type: 'string', description: 'Stop loss order ID' },
amend_count: { type: 'number', description: 'Amend count' },
amend_taker_fill_count: { type: 'number', description: 'Amend taker fill count' },
},
},
order: {
type: 'object',
description: 'The amended order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
status: { type: 'string', description: 'Order status' },
side: { type: 'string', description: 'Order side (yes/no)' },
type: { type: 'string', description: 'Order type (limit/market)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
action: { type: 'string', description: 'Action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
created_time: { type: 'string', description: 'Order creation time' },
expiration_time: { type: 'string', description: 'Order expiration time' },
order_group_id: { type: 'string', description: 'Order group ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
place_count: { type: 'number', description: 'Place count' },
decrease_count: { type: 'number', description: 'Decrease count' },
queue_position: { type: 'number', description: 'Queue position' },
maker_fill_count: { type: 'number', description: 'Maker fill count' },
taker_fill_count: { type: 'number', description: 'Taker fill count' },
maker_fees: { type: 'number', description: 'Maker fees' },
taker_fees: { type: 'number', description: 'Taker fees' },
last_update_time: { type: 'string', description: 'Last update time' },
take_profit_order_id: { type: 'string', description: 'Take profit order ID' },
stop_loss_order_id: { type: 'string', description: 'Stop loss order ID' },
amend_count: { type: 'number', description: 'Amend count' },
amend_taker_fill_count: { type: 'number', description: 'Amend taker fill count' },
},
},
},
}
+270
View File
@@ -0,0 +1,270 @@
import type { KalshiAuthParams, KalshiOrder } from '@/tools/kalshi/types'
import { buildKalshiAuthHeaders, buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiCancelOrderParams extends KalshiAuthParams {
orderId: string // Order ID to cancel (required)
}
export interface KalshiCancelOrderResponse {
success: boolean
output: {
order: KalshiOrder
reducedBy: number
}
}
export const kalshiCancelOrderTool: ToolConfig<KalshiCancelOrderParams, KalshiCancelOrderResponse> =
{
id: 'kalshi_cancel_order',
name: 'Cancel Order on Kalshi',
description: 'Cancel an existing order on Kalshi',
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Order ID to cancel (e.g., "abc123-def456-ghi789")',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId.trim()}`),
method: 'DELETE',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId.trim()}`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'DELETE', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'cancel_order')
}
return {
success: true,
output: {
order: data.order,
reducedBy: data.reduced_by || 0,
},
}
},
outputs: {
order: {
type: 'object',
description: 'The canceled order object',
},
reducedBy: {
type: 'number',
description: 'Number of contracts canceled',
},
},
}
export interface KalshiCancelOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to cancel (required)
}
export interface KalshiCancelOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
reduced_by: number
reduced_by_fp: string | null
}
}
export const kalshiCancelOrderV2Tool: ToolConfig<
KalshiCancelOrderV2Params,
KalshiCancelOrderV2Response
> = {
id: 'kalshi_cancel_order_v2',
name: 'Cancel Order on Kalshi V2',
description: 'Cancel an existing order on Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Order ID to cancel (e.g., "abc123-def456-ghi789")',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId.trim()}`),
method: 'DELETE',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId.trim()}`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'DELETE', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'cancel_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
reduced_by: data.reduced_by ?? 0,
reduced_by_fp: data.reduced_by_fp ?? null,
},
}
},
outputs: {
order: {
type: 'object',
description: 'The canceled order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
reduced_by: {
type: 'number',
description: 'Number of contracts canceled',
},
reduced_by_fp: {
type: 'string',
description: 'Number of contracts canceled in fixed-point format',
},
},
}
+552
View File
@@ -0,0 +1,552 @@
import type { KalshiAuthParams, KalshiOrder } from '@/tools/kalshi/types'
import { buildKalshiAuthHeaders, buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiCreateOrderParams extends KalshiAuthParams {
ticker: string // Market ticker (required)
side: string // 'yes' or 'no' (required)
action: string // 'buy' or 'sell' (required)
count: string // Number of contracts (required)
type?: string // 'limit' or 'market' (default: limit)
yesPrice?: string // Yes price in cents (1-99)
noPrice?: string // No price in cents (1-99)
yesPriceDollars?: string // Yes price in dollars (e.g., "0.56")
noPriceDollars?: string // No price in dollars (e.g., "0.56")
clientOrderId?: string // Custom order identifier
expirationTs?: string // Unix timestamp expiration
timeInForce?: string // 'fill_or_kill', 'good_till_canceled', 'immediate_or_cancel'
buyMaxCost?: string // Maximum cost in cents
postOnly?: string // 'true' or 'false' - maker-only orders
reduceOnly?: string // 'true' or 'false' - position reduction only
selfTradePreventionType?: string // 'taker_at_cross' or 'maker'
orderGroupId?: string // Associated order group
}
export interface KalshiCreateOrderResponse {
success: boolean
output: {
order: KalshiOrder
}
}
export const kalshiCreateOrderTool: ToolConfig<KalshiCreateOrderParams, KalshiCreateOrderResponse> =
{
id: 'kalshi_create_order',
name: 'Create Order on Kalshi',
description: 'Create a new order on a Kalshi prediction market',
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
side: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Side of the order: "yes" or "no"',
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Action type: "buy" or "sell"',
},
count: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Number of contracts to trade (e.g., "10", "100")',
},
type: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Order type: "limit" or "market" (default: "limit")',
},
yesPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Yes price in cents (1-99)',
},
noPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'No price in cents (1-99)',
},
yesPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Yes price in dollars (e.g., "0.56")',
},
noPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'No price in dollars (e.g., "0.56")',
},
clientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom order identifier',
},
expirationTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Unix timestamp for order expiration',
},
timeInForce: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Time in force: 'fill_or_kill', 'good_till_canceled', 'immediate_or_cancel'",
},
buyMaxCost: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum cost in cents (auto-enables fill_or_kill)',
},
postOnly: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' for maker-only orders",
},
reduceOnly: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' for position reduction only",
},
selfTradePreventionType: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Self-trade prevention: 'taker_at_cross' or 'maker'",
},
orderGroupId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Associated order group ID',
},
},
request: {
url: () => buildKalshiUrl('/portfolio/orders'),
method: 'POST',
headers: (params) => {
const path = '/trade-api/v2/portfolio/orders'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'POST', path)
},
body: (params) => {
const body: Record<string, any> = {
ticker: params.ticker,
side: params.side.toLowerCase(),
action: params.action.toLowerCase(),
count: Number.parseInt(params.count, 10),
}
if (params.type) body.type = params.type.toLowerCase()
if (params.yesPrice) body.yes_price = Number.parseInt(params.yesPrice, 10)
if (params.noPrice) body.no_price = Number.parseInt(params.noPrice, 10)
if (params.yesPriceDollars) body.yes_price_dollars = params.yesPriceDollars
if (params.noPriceDollars) body.no_price_dollars = params.noPriceDollars
if (params.clientOrderId) body.client_order_id = params.clientOrderId
if (params.expirationTs) body.expiration_ts = Number.parseInt(params.expirationTs, 10)
if (params.timeInForce) body.time_in_force = params.timeInForce
if (params.buyMaxCost) body.buy_max_cost = Number.parseInt(params.buyMaxCost, 10)
if (params.postOnly) body.post_only = params.postOnly === 'true'
if (params.reduceOnly) body.reduce_only = params.reduceOnly === 'true'
if (params.selfTradePreventionType)
body.self_trade_prevention_type = params.selfTradePreventionType
if (params.orderGroupId) body.order_group_id = params.orderGroupId
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'create_order')
}
return {
success: true,
output: {
order: data.order,
},
}
},
outputs: {
order: {
type: 'object',
description: 'The created order object',
},
},
}
export interface KalshiCreateOrderV2Params extends KalshiAuthParams {
ticker: string // Market ticker (required)
side: string // 'yes' or 'no' (required)
action: string // 'buy' or 'sell' (required)
count?: string // Number of contracts (optional - provide count or countFp)
type?: string // 'limit' or 'market' (default: limit)
yesPrice?: string // Yes price in cents (1-99)
noPrice?: string // No price in cents (1-99)
yesPriceDollars?: string // Yes price in dollars (e.g., "0.56")
noPriceDollars?: string // No price in dollars (e.g., "0.56")
clientOrderId?: string // Custom order identifier
expirationTs?: string // Unix timestamp expiration
timeInForce?: string // 'fill_or_kill', 'good_till_canceled', 'immediate_or_cancel'
buyMaxCost?: string // Maximum cost in cents
postOnly?: string // 'true' or 'false' - maker-only orders
reduceOnly?: string // 'true' or 'false' - position reduction only
selfTradePreventionType?: string // 'taker_at_cross' or 'maker'
orderGroupId?: string // Associated order group
countFp?: string // Count in fixed-point (for fractional contracts)
cancelOrderOnPause?: string // 'true' or 'false' - cancel on market pause
subaccount?: string // Subaccount to use for the order
}
export interface KalshiCreateOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
}
}
export const kalshiCreateOrderV2Tool: ToolConfig<
KalshiCreateOrderV2Params,
KalshiCreateOrderV2Response
> = {
id: 'kalshi_create_order_v2',
name: 'Create Order on Kalshi V2',
description: 'Create a new order on a Kalshi prediction market (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
side: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Side of the order: "yes" or "no"',
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Action type: "buy" or "sell"',
},
count: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of contracts to trade (e.g., "10", "100"). Provide count or countFp',
},
type: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Order type: "limit" or "market" (default: "limit")',
},
yesPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Yes price in cents (1-99)',
},
noPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'No price in cents (1-99)',
},
yesPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Yes price in dollars (e.g., "0.56")',
},
noPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'No price in dollars (e.g., "0.56")',
},
clientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom order identifier',
},
expirationTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Unix timestamp for order expiration',
},
timeInForce: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Time in force: 'fill_or_kill', 'good_till_canceled', 'immediate_or_cancel'",
},
buyMaxCost: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum cost in cents (auto-enables fill_or_kill)',
},
postOnly: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' for maker-only orders",
},
reduceOnly: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' for position reduction only",
},
selfTradePreventionType: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Self-trade prevention: 'taker_at_cross' or 'maker'",
},
orderGroupId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Associated order group ID',
},
countFp: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Count in fixed-point for fractional contracts',
},
cancelOrderOnPause: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' to cancel order on market pause",
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to use for the order',
},
},
request: {
url: () => buildKalshiUrl('/portfolio/orders'),
method: 'POST',
headers: (params) => {
const path = '/trade-api/v2/portfolio/orders'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'POST', path)
},
body: (params) => {
const body: Record<string, any> = {
ticker: params.ticker,
side: params.side.toLowerCase(),
action: params.action.toLowerCase(),
}
// count or count_fp must be provided (but not both required)
if (params.count) body.count = Number.parseInt(params.count, 10)
if (params.countFp) body.count_fp = params.countFp
if (params.type) body.type = params.type.toLowerCase()
if (params.yesPrice) body.yes_price = Number.parseInt(params.yesPrice, 10)
if (params.noPrice) body.no_price = Number.parseInt(params.noPrice, 10)
if (params.yesPriceDollars) body.yes_price_dollars = params.yesPriceDollars
if (params.noPriceDollars) body.no_price_dollars = params.noPriceDollars
if (params.clientOrderId) body.client_order_id = params.clientOrderId
if (params.expirationTs) body.expiration_ts = Number.parseInt(params.expirationTs, 10)
if (params.timeInForce) body.time_in_force = params.timeInForce
if (params.buyMaxCost) body.buy_max_cost = Number.parseInt(params.buyMaxCost, 10)
if (params.postOnly) body.post_only = params.postOnly === 'true'
if (params.reduceOnly) body.reduce_only = params.reduceOnly === 'true'
if (params.selfTradePreventionType)
body.self_trade_prevention_type = params.selfTradePreventionType
if (params.orderGroupId) body.order_group_id = params.orderGroupId
if (params.cancelOrderOnPause)
body.cancel_order_on_pause = params.cancelOrderOnPause === 'true'
if (params.subaccount) body.subaccount = params.subaccount
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'create_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
},
}
},
outputs: {
order: {
type: 'object',
description: 'The created order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
},
}
+143
View File
@@ -0,0 +1,143 @@
import type { KalshiAuthParams } from '@/tools/kalshi/types'
import { buildKalshiAuthHeaders, buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetBalanceParams extends KalshiAuthParams {}
export interface KalshiGetBalanceResponse {
success: boolean
output: {
balance: number // In cents
portfolioValue: number // In cents
}
}
export const kalshiGetBalanceTool: ToolConfig<KalshiGetBalanceParams, KalshiGetBalanceResponse> = {
id: 'kalshi_get_balance',
name: 'Get Balance from Kalshi',
description: 'Retrieve your account balance and portfolio value from Kalshi',
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
},
request: {
url: () => buildKalshiUrl('/portfolio/balance'),
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/balance'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_balance')
}
const balance = data.balance ?? 0
const portfolioValue = data.portfolio_value ?? 0
return {
success: true,
output: {
balance,
portfolioValue,
},
}
},
outputs: {
balance: { type: 'number', description: 'Account balance in cents' },
portfolioValue: { type: 'number', description: 'Portfolio value in cents' },
},
}
/**
* V2 Params for Get Balance
*/
export interface KalshiGetBalanceV2Params extends KalshiAuthParams {}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetBalanceV2Response {
success: boolean
output: {
balance: number
portfolio_value: number
updated_ts: number | null
}
}
export const kalshiGetBalanceV2Tool: ToolConfig<
KalshiGetBalanceV2Params,
KalshiGetBalanceV2Response
> = {
id: 'kalshi_get_balance_v2',
name: 'Get Balance from Kalshi V2',
description:
'Retrieve your account balance and portfolio value from Kalshi (V2 - exact API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
},
request: {
url: () => buildKalshiUrl('/portfolio/balance'),
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/balance'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_balance_v2')
}
return {
success: true,
output: {
balance: data.balance ?? 0,
portfolio_value: data.portfolio_value ?? 0,
updated_ts: data.updated_ts ?? null,
},
}
},
outputs: {
balance: { type: 'number', description: 'Account balance in cents' },
portfolio_value: { type: 'number', description: 'Portfolio value in cents' },
updated_ts: { type: 'number', description: 'Unix timestamp of last update (seconds)' },
},
}
+347
View File
@@ -0,0 +1,347 @@
import type { KalshiCandlestick } from '@/tools/kalshi/types'
import { buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetCandlesticksParams {
seriesTicker: string
ticker: string
startTs: number
endTs: number
periodInterval: number // 1, 60, or 1440 (1min, 1hour, 1day)
}
export interface KalshiGetCandlesticksResponse {
success: boolean
output: {
candlesticks: KalshiCandlestick[]
}
}
export const kalshiGetCandlesticksTool: ToolConfig<
KalshiGetCandlesticksParams,
KalshiGetCandlesticksResponse
> = {
id: 'kalshi_get_candlesticks',
name: 'Get Market Candlesticks from Kalshi',
description: 'Retrieve OHLC candlestick data for a specific market',
version: '1.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker identifier (e.g., "KXBTC", "INX", "FED-RATE")',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
startTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Start timestamp in Unix seconds (e.g., 1704067200)',
},
endTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'End timestamp in Unix seconds (e.g., 1704153600)',
},
periodInterval: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Period interval: 1 (1 minute), 60 (1 hour), or 1440 (1 day)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('start_ts', params.startTs.toString())
queryParams.append('end_ts', params.endTs.toString())
queryParams.append('period_interval', params.periodInterval.toString())
const query = queryParams.toString()
const url = buildKalshiUrl(
`/series/${params.seriesTicker.trim()}/markets/${params.ticker.trim()}/candlesticks`
)
return `${url}?${query}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_candlesticks')
}
const candlesticks = data.candlesticks || []
return {
success: true,
output: {
candlesticks,
},
}
},
outputs: {
candlesticks: {
type: 'array',
description: 'Array of OHLC candlestick data',
},
},
}
/**
* BidAskDistribution - OHLC data for yes_bid and yes_ask
*/
interface BidAskDistribution {
open: number | null
open_dollars: string | null
low: number | null
low_dollars: string | null
high: number | null
high_dollars: string | null
close: number | null
close_dollars: string | null
}
/**
* PriceDistribution - Extended OHLC data for price field
*/
interface PriceDistribution {
open: number | null
open_dollars: string | null
low: number | null
low_dollars: string | null
high: number | null
high_dollars: string | null
close: number | null
close_dollars: string | null
mean: number | null
mean_dollars: string | null
previous: number | null
previous_dollars: string | null
min: number | null
min_dollars: string | null
max: number | null
max_dollars: string | null
}
/**
* V2 Get Candlesticks Tool - Returns exact Kalshi API response structure
*/
export interface KalshiGetCandlesticksV2Response {
success: boolean
output: {
ticker: string
candlesticks: Array<{
end_period_ts: number | null
yes_bid: BidAskDistribution
yes_ask: BidAskDistribution
price: PriceDistribution
volume: number | null
volume_fp: string | null
open_interest: number | null
open_interest_fp: string | null
}>
}
}
export const kalshiGetCandlesticksV2Tool: ToolConfig<
KalshiGetCandlesticksParams,
KalshiGetCandlesticksV2Response
> = {
id: 'kalshi_get_candlesticks_v2',
name: 'Get Market Candlesticks from Kalshi V2',
description: 'Retrieve OHLC candlestick data for a specific market (V2 - full API response)',
version: '2.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker identifier (e.g., "KXBTC", "INX", "FED-RATE")',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
startTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Start timestamp in Unix seconds (e.g., 1704067200)',
},
endTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'End timestamp in Unix seconds (e.g., 1704153600)',
},
periodInterval: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Period interval: 1 (1 minute), 60 (1 hour), or 1440 (1 day)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('start_ts', params.startTs.toString())
queryParams.append('end_ts', params.endTs.toString())
queryParams.append('period_interval', params.periodInterval.toString())
const query = queryParams.toString()
const url = buildKalshiUrl(
`/series/${params.seriesTicker.trim()}/markets/${params.ticker.trim()}/candlesticks`
)
return `${url}?${query}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_candlesticks_v2')
}
const mapBidAsk = (obj: Record<string, unknown> | null): BidAskDistribution => ({
open: (obj?.open as number) ?? null,
open_dollars: (obj?.open_dollars as string) ?? null,
low: (obj?.low as number) ?? null,
low_dollars: (obj?.low_dollars as string) ?? null,
high: (obj?.high as number) ?? null,
high_dollars: (obj?.high_dollars as string) ?? null,
close: (obj?.close as number) ?? null,
close_dollars: (obj?.close_dollars as string) ?? null,
})
const mapPrice = (obj: Record<string, unknown> | null): PriceDistribution => ({
open: (obj?.open as number) ?? null,
open_dollars: (obj?.open_dollars as string) ?? null,
low: (obj?.low as number) ?? null,
low_dollars: (obj?.low_dollars as string) ?? null,
high: (obj?.high as number) ?? null,
high_dollars: (obj?.high_dollars as string) ?? null,
close: (obj?.close as number) ?? null,
close_dollars: (obj?.close_dollars as string) ?? null,
mean: (obj?.mean as number) ?? null,
mean_dollars: (obj?.mean_dollars as string) ?? null,
previous: (obj?.previous as number) ?? null,
previous_dollars: (obj?.previous_dollars as string) ?? null,
min: (obj?.min as number) ?? null,
min_dollars: (obj?.min_dollars as string) ?? null,
max: (obj?.max as number) ?? null,
max_dollars: (obj?.max_dollars as string) ?? null,
})
const candlesticks = (data.candlesticks || []).map((c: Record<string, unknown>) => ({
end_period_ts: (c.end_period_ts as number) ?? null,
yes_bid: mapBidAsk(c.yes_bid as Record<string, unknown> | null),
yes_ask: mapBidAsk(c.yes_ask as Record<string, unknown> | null),
price: mapPrice(c.price as Record<string, unknown> | null),
volume: (c.volume as number) ?? null,
volume_fp: (c.volume_fp as string) ?? null,
open_interest: (c.open_interest as number) ?? null,
open_interest_fp: (c.open_interest_fp as string) ?? null,
}))
return {
success: true,
output: {
ticker: data.ticker ?? null,
candlesticks,
},
}
},
outputs: {
ticker: {
type: 'string',
description: 'Market ticker',
},
candlesticks: {
type: 'array',
description: 'Array of OHLC candlestick data with nested bid/ask/price objects',
properties: {
end_period_ts: { type: 'number', description: 'End period timestamp (Unix)' },
yes_bid: {
type: 'object',
description: 'Yes bid OHLC data',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
},
},
yes_ask: {
type: 'object',
description: 'Yes ask OHLC data',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
},
},
price: {
type: 'object',
description: 'Trade price OHLC data with additional statistics',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
mean: { type: 'number', description: 'Mean price (cents)' },
mean_dollars: { type: 'string', description: 'Mean price (dollars)' },
previous: { type: 'number', description: 'Previous price (cents)' },
previous_dollars: { type: 'string', description: 'Previous price (dollars)' },
min: { type: 'number', description: 'Min price (cents)' },
min_dollars: { type: 'string', description: 'Min price (dollars)' },
max: { type: 'number', description: 'Max price (cents)' },
max_dollars: { type: 'string', description: 'Max price (dollars)' },
},
},
volume: { type: 'number', description: 'Volume (contracts)' },
volume_fp: { type: 'string', description: 'Volume (fixed-point string)' },
open_interest: { type: 'number', description: 'Open interest (contracts)' },
open_interest_fp: { type: 'string', description: 'Open interest (fixed-point string)' },
},
},
},
}
+251
View File
@@ -0,0 +1,251 @@
import type { KalshiEvent } from '@/tools/kalshi/types'
import { buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetEventParams {
eventTicker: string // Event ticker
withNestedMarkets?: string // 'true' or 'false'
}
export interface KalshiGetEventResponse {
success: boolean
output: {
event: KalshiEvent
}
}
export const kalshiGetEventTool: ToolConfig<KalshiGetEventParams, KalshiGetEventResponse> = {
id: 'kalshi_get_event',
name: 'Get Event from Kalshi',
description: 'Retrieve details of a specific event by ticker',
version: '1.0.0',
params: {
eventTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Event ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03")',
},
withNestedMarkets: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include nested markets in response (true/false)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.withNestedMarkets)
queryParams.append('with_nested_markets', params.withNestedMarkets)
const query = queryParams.toString()
const url = buildKalshiUrl(`/events/${params.eventTicker.trim()}`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_event')
}
return {
success: true,
output: {
event: data.event,
},
}
},
outputs: {
event: {
type: 'object',
description: 'Event object with details',
},
},
}
/**
* V2 Params for Get Event
*/
export interface KalshiGetEventV2Params {
eventTicker: string
withNestedMarkets?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetEventV2Response {
success: boolean
output: {
event: {
event_ticker: string
series_ticker: string
title: string
sub_title: string | null
mutually_exclusive: boolean
category: string
collateral_return_type: string | null
strike_date: string | null
strike_period: string | null
available_on_brokers: boolean | null
product_metadata: Record<string, unknown> | null
markets: Array<{
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string
close_time: string
expiration_time: string
status: string
yes_bid: number
yes_ask: number
no_bid: number
no_ask: number
last_price: number
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number
volume_24h: number
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
}> | null
}
}
}
export const kalshiGetEventV2Tool: ToolConfig<KalshiGetEventV2Params, KalshiGetEventV2Response> = {
id: 'kalshi_get_event_v2',
name: 'Get Event from Kalshi V2',
description: 'Retrieve details of a specific event by ticker (V2 - exact API response)',
version: '2.0.0',
params: {
eventTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Event ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03")',
},
withNestedMarkets: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include nested markets in response (true/false)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.withNestedMarkets)
queryParams.append('with_nested_markets', params.withNestedMarkets)
const query = queryParams.toString()
const url = buildKalshiUrl(`/events/${params.eventTicker.trim()}`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_event_v2')
}
const event = data.event || {}
const markets =
event.markets?.map((m: Record<string, unknown>) => ({
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expiration_time: m.expiration_time ?? null,
status: m.status ?? null,
yes_bid: m.yes_bid ?? 0,
yes_ask: m.yes_ask ?? 0,
no_bid: m.no_bid ?? 0,
no_ask: m.no_ask ?? 0,
last_price: m.last_price ?? 0,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? 0,
volume_24h: m.volume_24h ?? 0,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
})) ?? null
return {
success: true,
output: {
event: {
event_ticker: event.event_ticker ?? null,
series_ticker: event.series_ticker ?? null,
title: event.title ?? null,
sub_title: event.sub_title ?? null,
mutually_exclusive: event.mutually_exclusive ?? false,
category: event.category ?? null,
collateral_return_type: event.collateral_return_type ?? null,
strike_date: event.strike_date ?? null,
strike_period: event.strike_period ?? null,
available_on_brokers: event.available_on_brokers ?? null,
product_metadata: event.product_metadata ?? null,
markets,
},
},
}
},
outputs: {
event: {
type: 'object',
description: 'Event object with full details matching Kalshi API response',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
series_ticker: { type: 'string', description: 'Series ticker' },
title: { type: 'string', description: 'Event title' },
sub_title: { type: 'string', description: 'Event subtitle' },
mutually_exclusive: { type: 'boolean', description: 'Mutually exclusive markets' },
category: { type: 'string', description: 'Event category' },
collateral_return_type: { type: 'string', description: 'Collateral return type' },
strike_date: { type: 'string', description: 'Strike date' },
strike_period: { type: 'string', description: 'Strike period' },
available_on_brokers: { type: 'boolean', description: 'Available on brokers' },
product_metadata: { type: 'object', description: 'Product metadata' },
markets: { type: 'array', description: 'Nested markets (if requested)' },
},
},
},
}
@@ -0,0 +1,289 @@
import { buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetEventCandlesticksParams {
seriesTicker: string
eventTicker: string
startTs: number
endTs: number
periodInterval: number // 1, 60, or 1440 (1min, 1hour, 1day)
}
export interface KalshiGetEventCandlesticksResponse {
success: boolean
output: {
market_candlesticks: Array<Record<string, unknown>>
}
}
export const kalshiGetEventCandlesticksTool: ToolConfig<
KalshiGetEventCandlesticksParams,
KalshiGetEventCandlesticksResponse
> = {
id: 'kalshi_get_event_candlesticks',
name: 'Get Event Candlesticks from Kalshi',
description: 'Retrieve OHLC candlestick data aggregated across all markets in an event',
version: '1.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker identifier (e.g., "KXBTC", "INX", "FED-RATE")',
},
eventTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Event ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03")',
},
startTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Start timestamp in Unix seconds (e.g., 1704067200)',
},
endTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'End timestamp in Unix seconds (e.g., 1704153600)',
},
periodInterval: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Period interval: 1 (1 minute), 60 (1 hour), or 1440 (1 day)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('start_ts', params.startTs.toString())
queryParams.append('end_ts', params.endTs.toString())
queryParams.append('period_interval', params.periodInterval.toString())
const url = buildKalshiUrl(
`/series/${params.seriesTicker.trim()}/events/${params.eventTicker.trim()}/candlesticks`
)
return `${url}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_event_candlesticks')
}
return {
success: true,
output: {
market_candlesticks: data.market_candlesticks || [],
},
}
},
outputs: {
market_candlesticks: {
type: 'array',
description: 'Array of event-level aggregated OHLC candlestick data',
},
},
}
/**
* BidAskDistribution - OHLC data for yes_bid and yes_ask
*/
interface BidAskDistribution {
open: number | null
open_dollars: string | null
low: number | null
low_dollars: string | null
high: number | null
high_dollars: string | null
close: number | null
close_dollars: string | null
}
/**
* PriceDistribution - Extended OHLC data for the price field
*/
interface PriceDistribution {
open: number | null
open_dollars: string | null
low: number | null
low_dollars: string | null
high: number | null
high_dollars: string | null
close: number | null
close_dollars: string | null
mean: number | null
mean_dollars: string | null
previous: number | null
previous_dollars: string | null
}
/**
* V2 Get Event Candlesticks Tool - Returns exact Kalshi API response structure
*/
export interface KalshiGetEventCandlesticksV2Response {
success: boolean
output: {
market_tickers: string[] | null
adjusted_end_ts: number | null
market_candlesticks: Array<{
end_period_ts: number | null
yes_bid: BidAskDistribution
yes_ask: BidAskDistribution
price: PriceDistribution
volume_fp: string | null
open_interest_fp: string | null
}>
}
}
export const kalshiGetEventCandlesticksV2Tool: ToolConfig<
KalshiGetEventCandlesticksParams,
KalshiGetEventCandlesticksV2Response
> = {
id: 'kalshi_get_event_candlesticks_v2',
name: 'Get Event Candlesticks from Kalshi V2',
description:
'Retrieve OHLC candlestick data aggregated across all markets in an event (V2 - full API response)',
version: '2.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker identifier (e.g., "KXBTC", "INX", "FED-RATE")',
},
eventTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Event ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03")',
},
startTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Start timestamp in Unix seconds (e.g., 1704067200)',
},
endTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'End timestamp in Unix seconds (e.g., 1704153600)',
},
periodInterval: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Period interval: 1 (1 minute), 60 (1 hour), or 1440 (1 day)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('start_ts', params.startTs.toString())
queryParams.append('end_ts', params.endTs.toString())
queryParams.append('period_interval', params.periodInterval.toString())
const url = buildKalshiUrl(
`/series/${params.seriesTicker.trim()}/events/${params.eventTicker.trim()}/candlesticks`
)
return `${url}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_event_candlesticks_v2')
}
const mapBidAsk = (obj: Record<string, unknown> | null): BidAskDistribution => ({
open: (obj?.open as number) ?? null,
open_dollars: (obj?.open_dollars as string) ?? null,
low: (obj?.low as number) ?? null,
low_dollars: (obj?.low_dollars as string) ?? null,
high: (obj?.high as number) ?? null,
high_dollars: (obj?.high_dollars as string) ?? null,
close: (obj?.close as number) ?? null,
close_dollars: (obj?.close_dollars as string) ?? null,
})
const mapPrice = (obj: Record<string, unknown> | null): PriceDistribution => ({
open: (obj?.open as number) ?? null,
open_dollars: (obj?.open_dollars as string) ?? null,
low: (obj?.low as number) ?? null,
low_dollars: (obj?.low_dollars as string) ?? null,
high: (obj?.high as number) ?? null,
high_dollars: (obj?.high_dollars as string) ?? null,
close: (obj?.close as number) ?? null,
close_dollars: (obj?.close_dollars as string) ?? null,
mean: (obj?.mean as number) ?? null,
mean_dollars: (obj?.mean_dollars as string) ?? null,
previous: (obj?.previous as number) ?? null,
previous_dollars: (obj?.previous_dollars as string) ?? null,
})
const candlesticks = (data.market_candlesticks || []).map((c: Record<string, unknown>) => ({
end_period_ts: (c.end_period_ts as number) ?? null,
yes_bid: mapBidAsk(c.yes_bid as Record<string, unknown> | null),
yes_ask: mapBidAsk(c.yes_ask as Record<string, unknown> | null),
price: mapPrice(c.price as Record<string, unknown> | null),
volume_fp: (c.volume_fp as string) ?? null,
open_interest_fp: (c.open_interest_fp as string) ?? null,
}))
return {
success: true,
output: {
market_tickers: data.market_tickers ?? null,
adjusted_end_ts: data.adjusted_end_ts ?? null,
market_candlesticks: candlesticks,
},
}
},
outputs: {
market_tickers: {
type: 'array',
description: 'Market tickers included in the aggregated candlesticks',
},
adjusted_end_ts: {
type: 'number',
description: 'Adjusted end timestamp used for the candlestick range (Unix seconds)',
},
market_candlesticks: {
type: 'array',
description:
'Array of event-level aggregated OHLC candlestick data with nested bid/ask/price',
properties: {
end_period_ts: { type: 'number', description: 'End period timestamp (Unix)' },
yes_bid: { type: 'object', description: 'Yes bid OHLC data' },
yes_ask: { type: 'object', description: 'Yes ask OHLC data' },
price: { type: 'object', description: 'Trade price OHLC data with statistics' },
volume_fp: { type: 'string', description: 'Volume (fixed-point string)' },
open_interest_fp: { type: 'string', description: 'Open interest (fixed-point string)' },
},
},
},
}
+321
View File
@@ -0,0 +1,321 @@
import type { KalshiEvent, KalshiPaginationParams, KalshiPagingInfo } from '@/tools/kalshi/types'
import {
buildKalshiUrl,
handleKalshiError,
KALSHI_EVENT_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetEventsParams extends KalshiPaginationParams {
status?: string // open, closed, settled
seriesTicker?: string
withNestedMarkets?: string // 'true' or 'false'
}
export interface KalshiGetEventsResponse {
success: boolean
output: {
events: KalshiEvent[]
paging?: KalshiPagingInfo
}
}
export const kalshiGetEventsTool: ToolConfig<KalshiGetEventsParams, KalshiGetEventsResponse> = {
id: 'kalshi_get_events',
name: 'Get Events from Kalshi',
description: 'Retrieve a list of events from Kalshi with optional filtering',
version: '1.0.0',
params: {
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event status: "open", "closed", or "settled"',
},
seriesTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by series ticker (e.g., "KXBTC", "INX", "FED-RATE")',
},
withNestedMarkets: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include nested markets in response: "true" or "false"',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-200, default: 200)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.append('status', params.status)
if (params.seriesTicker) queryParams.append('series_ticker', params.seriesTicker)
if (params.withNestedMarkets)
queryParams.append('with_nested_markets', params.withNestedMarkets)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/events')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_events')
}
const events = data.events || []
return {
success: true,
output: {
events,
paging: {
cursor: data.cursor || null,
},
},
}
},
outputs: {
events: {
type: 'array',
description: 'Array of event objects',
items: {
type: 'object',
properties: KALSHI_EVENT_OUTPUT_PROPERTIES,
},
},
paging: {
type: 'object',
description: 'Pagination cursor for fetching more results',
},
},
}
/**
* V2 Params for Get Events
*/
export interface KalshiGetEventsV2Params extends KalshiPaginationParams {
status?: string
seriesTicker?: string
withNestedMarkets?: string
withMilestones?: string
minCloseTs?: number
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetEventsV2Response {
success: boolean
output: {
events: Array<{
event_ticker: string
series_ticker: string
title: string
sub_title: string | null
mutually_exclusive: boolean
category: string
collateral_return_type: string | null
strike_date: string | null
strike_period: string | null
available_on_brokers: boolean | null
product_metadata: Record<string, unknown> | null
markets: Array<Record<string, unknown>> | null
}>
milestones: Array<{
id: string | null
category: string | null
type: string | null
title: string | null
start_date: string | null
end_date: string | null
notification_message: string | null
primary_event_tickers: string[] | null
related_event_tickers: string[] | null
last_updated_ts: string | null
}> | null
cursor: string | null
}
}
export const kalshiGetEventsV2Tool: ToolConfig<KalshiGetEventsV2Params, KalshiGetEventsV2Response> =
{
id: 'kalshi_get_events_v2',
name: 'Get Events from Kalshi V2',
description:
'Retrieve a list of events from Kalshi with optional filtering (V2 - exact API response)',
version: '2.0.0',
params: {
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event status: "open", "closed", or "settled"',
},
seriesTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by series ticker (e.g., "KXBTC", "INX", "FED-RATE")',
},
withNestedMarkets: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include nested markets in response: "true" or "false"',
},
withMilestones: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include milestones in response: "true" or "false"',
},
minCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum close timestamp in Unix seconds (e.g., 1704067200)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-200, default: 200)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.append('status', params.status)
if (params.seriesTicker) queryParams.append('series_ticker', params.seriesTicker)
if (params.withNestedMarkets)
queryParams.append('with_nested_markets', params.withNestedMarkets)
if (params.withMilestones) queryParams.append('with_milestones', params.withMilestones)
if (params.minCloseTs !== undefined)
queryParams.append('min_close_ts', params.minCloseTs.toString())
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/events')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_events_v2')
}
const events = (data.events || []).map((e: Record<string, unknown>) => ({
event_ticker: e.event_ticker ?? null,
series_ticker: e.series_ticker ?? null,
title: e.title ?? null,
sub_title: e.sub_title ?? null,
mutually_exclusive: e.mutually_exclusive ?? false,
category: e.category ?? null,
collateral_return_type: e.collateral_return_type ?? null,
strike_date: e.strike_date ?? null,
strike_period: e.strike_period ?? null,
available_on_brokers: e.available_on_brokers ?? null,
product_metadata: e.product_metadata ?? null,
markets: e.markets ?? null,
}))
const milestones = data.milestones
? (data.milestones as Array<Record<string, unknown>>).map((m) => ({
id: (m.id as string) ?? null,
category: (m.category as string) ?? null,
type: (m.type as string) ?? null,
title: (m.title as string | null) ?? null,
start_date: (m.start_date as string) ?? null,
end_date: (m.end_date as string) ?? null,
notification_message: (m.notification_message as string | null) ?? null,
primary_event_tickers: (m.primary_event_tickers as string[]) ?? null,
related_event_tickers: (m.related_event_tickers as string[]) ?? null,
last_updated_ts: (m.last_updated_ts as string) ?? null,
}))
: null
return {
success: true,
output: {
events,
milestones,
cursor: data.cursor ?? null,
},
}
},
outputs: {
events: {
type: 'array',
description: 'Array of event objects',
items: {
type: 'object',
properties: KALSHI_EVENT_OUTPUT_PROPERTIES,
},
},
milestones: {
type: 'array',
description: 'Array of milestone objects (if requested)',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Milestone ID' },
category: { type: 'string', description: 'Milestone category' },
type: { type: 'string', description: 'Milestone type' },
title: { type: 'string', description: 'Milestone title' },
start_date: { type: 'string', description: 'Milestone start date (ISO 8601)' },
end_date: { type: 'string', description: 'Milestone end date (ISO 8601)' },
notification_message: { type: 'string', description: 'Notification message' },
primary_event_tickers: { type: 'array', description: 'Primary event tickers' },
related_event_tickers: { type: 'array', description: 'Related event tickers' },
last_updated_ts: { type: 'string', description: 'Last updated time (ISO 8601)' },
},
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}
@@ -0,0 +1,129 @@
import {
buildKalshiUrl,
handleKalshiError,
KALSHI_ANNOUNCEMENT_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export type KalshiGetExchangeAnnouncementsParams = Record<string, never>
export interface KalshiGetExchangeAnnouncementsResponse {
success: boolean
output: {
announcements: Array<Record<string, unknown>>
}
}
export const kalshiGetExchangeAnnouncementsTool: ToolConfig<
KalshiGetExchangeAnnouncementsParams,
KalshiGetExchangeAnnouncementsResponse
> = {
id: 'kalshi_get_exchange_announcements',
name: 'Get Exchange Announcements from Kalshi',
description: 'Retrieve exchange-wide announcements from Kalshi',
version: '1.0.0',
params: {},
request: {
url: () => buildKalshiUrl('/exchange/announcements'),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_exchange_announcements')
}
return {
success: true,
output: {
announcements: data.announcements || [],
},
}
},
outputs: {
announcements: {
type: 'array',
description: 'Array of exchange announcement objects',
items: {
type: 'object',
properties: KALSHI_ANNOUNCEMENT_OUTPUT_PROPERTIES,
},
},
},
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetExchangeAnnouncementsV2Response {
success: boolean
output: {
announcements: Array<{
type: string | null
message: string | null
delivery_time: string | null
status: string | null
}>
}
}
export const kalshiGetExchangeAnnouncementsV2Tool: ToolConfig<
KalshiGetExchangeAnnouncementsParams,
KalshiGetExchangeAnnouncementsV2Response
> = {
id: 'kalshi_get_exchange_announcements_v2',
name: 'Get Exchange Announcements from Kalshi V2',
description: 'Retrieve exchange-wide announcements from Kalshi (V2 - exact API response)',
version: '2.0.0',
params: {},
request: {
url: () => buildKalshiUrl('/exchange/announcements'),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_exchange_announcements_v2')
}
const announcements = (data.announcements || []).map((a: Record<string, unknown>) => ({
type: a.type ?? null,
message: a.message ?? null,
delivery_time: a.delivery_time ?? null,
status: a.status ?? null,
}))
return {
success: true,
output: {
announcements,
},
}
},
outputs: {
announcements: {
type: 'array',
description: 'Array of exchange announcement objects',
items: {
type: 'object',
properties: KALSHI_ANNOUNCEMENT_OUTPUT_PROPERTIES,
},
},
},
}
@@ -0,0 +1,133 @@
import { buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export type KalshiGetExchangeScheduleParams = Record<string, never>
export interface KalshiGetExchangeScheduleResponse {
success: boolean
output: {
schedule: Record<string, unknown>
}
}
export const kalshiGetExchangeScheduleTool: ToolConfig<
KalshiGetExchangeScheduleParams,
KalshiGetExchangeScheduleResponse
> = {
id: 'kalshi_get_exchange_schedule',
name: 'Get Exchange Schedule from Kalshi',
description: 'Retrieve the Kalshi exchange trading schedule and maintenance windows',
version: '1.0.0',
params: {},
request: {
url: () => buildKalshiUrl('/exchange/schedule'),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_exchange_schedule')
}
return {
success: true,
output: {
schedule: data.schedule || {},
},
}
},
outputs: {
schedule: {
type: 'object',
description: 'Exchange schedule with standard_hours and maintenance_windows',
},
},
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetExchangeScheduleV2Response {
success: boolean
output: {
schedule: {
standard_hours: Array<Record<string, unknown>>
maintenance_windows: Array<{
start_datetime: string | null
end_datetime: string | null
}>
}
}
}
export const kalshiGetExchangeScheduleV2Tool: ToolConfig<
KalshiGetExchangeScheduleParams,
KalshiGetExchangeScheduleV2Response
> = {
id: 'kalshi_get_exchange_schedule_v2',
name: 'Get Exchange Schedule from Kalshi V2',
description:
'Retrieve the Kalshi exchange trading schedule and maintenance windows (V2 - exact API response)',
version: '2.0.0',
params: {},
request: {
url: () => buildKalshiUrl('/exchange/schedule'),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_exchange_schedule_v2')
}
const schedule = data.schedule || {}
const maintenanceWindows = (schedule.maintenance_windows || []).map(
(w: Record<string, unknown>) => ({
start_datetime: (w.start_datetime as string) ?? null,
end_datetime: (w.end_datetime as string) ?? null,
})
)
return {
success: true,
output: {
schedule: {
standard_hours: schedule.standard_hours ?? [],
maintenance_windows: maintenanceWindows,
},
},
}
},
outputs: {
schedule: {
type: 'object',
description: 'Exchange schedule (all times in ET)',
properties: {
standard_hours: {
type: 'array',
description: 'Weekly schedules with per-day open/close trading sessions',
},
maintenance_windows: {
type: 'array',
description: 'Scheduled maintenance windows with start_datetime and end_datetime',
},
},
},
},
}
@@ -0,0 +1,132 @@
import type { KalshiExchangeStatus } from '@/tools/kalshi/types'
import { buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export type KalshiGetExchangeStatusParams = Record<string, never>
export interface KalshiGetExchangeStatusResponse {
success: boolean
output: {
status: KalshiExchangeStatus
}
}
export const kalshiGetExchangeStatusTool: ToolConfig<
KalshiGetExchangeStatusParams,
KalshiGetExchangeStatusResponse
> = {
id: 'kalshi_get_exchange_status',
name: 'Get Exchange Status from Kalshi',
description: 'Retrieve the current status of the Kalshi exchange (trading and exchange activity)',
version: '1.0.0',
params: {},
request: {
url: () => {
return buildKalshiUrl('/exchange/status')
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_exchange_status')
}
const status = {
trading_active: data.trading_active ?? false,
exchange_active: data.exchange_active ?? false,
}
return {
success: true,
output: {
status,
},
}
},
outputs: {
status: {
type: 'object',
description: 'Exchange status with trading_active and exchange_active flags',
},
},
}
/**
* V2 Params for Get Exchange Status
*/
export type KalshiGetExchangeStatusV2Params = Record<string, never>
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetExchangeStatusV2Response {
success: boolean
output: {
exchange_active: boolean
trading_active: boolean
exchange_estimated_resume_time: string | null
}
}
export const kalshiGetExchangeStatusV2Tool: ToolConfig<
KalshiGetExchangeStatusV2Params,
KalshiGetExchangeStatusV2Response
> = {
id: 'kalshi_get_exchange_status_v2',
name: 'Get Exchange Status from Kalshi V2',
description: 'Retrieve the current status of the Kalshi exchange (V2 - exact API response)',
version: '2.0.0',
params: {},
request: {
url: () => {
return buildKalshiUrl('/exchange/status')
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_exchange_status_v2')
}
return {
success: true,
output: {
exchange_active: data.exchange_active ?? false,
trading_active: data.trading_active ?? false,
exchange_estimated_resume_time: data.exchange_estimated_resume_time ?? null,
},
}
},
outputs: {
exchange_active: {
type: 'boolean',
description: 'Whether the exchange is active',
},
trading_active: {
type: 'boolean',
description: 'Whether trading is active',
},
exchange_estimated_resume_time: {
type: 'string',
description: 'Estimated time when exchange will resume (if inactive)',
},
},
}
+321
View File
@@ -0,0 +1,321 @@
import type {
KalshiAuthParams,
KalshiFill,
KalshiPaginationParams,
KalshiPagingInfo,
} from '@/tools/kalshi/types'
import {
buildKalshiAuthHeaders,
buildKalshiUrl,
handleKalshiError,
KALSHI_FILL_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetFillsParams extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
orderId?: string
minTs?: number
maxTs?: number
}
export interface KalshiGetFillsResponse {
success: boolean
output: {
fills: KalshiFill[]
paging?: KalshiPagingInfo
}
}
export const kalshiGetFillsTool: ToolConfig<KalshiGetFillsParams, KalshiGetFillsResponse> = {
id: 'kalshi_get_fills',
name: 'Get Fills from Kalshi',
description: "Retrieve your portfolio's fills/trades from Kalshi",
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
orderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by order ID (e.g., "abc123-def456-ghi789")',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp in Unix seconds (e.g., 1704067200)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp in Unix seconds (e.g., 1704153600)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.orderId) queryParams.append('order_id', params.orderId)
if (params.minTs !== undefined) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs !== undefined) queryParams.append('max_ts', params.maxTs.toString())
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/fills')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/fills'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_fills')
}
const fills = data.fills || []
return {
success: true,
output: {
fills,
paging: {
cursor: data.cursor || null,
},
},
}
},
outputs: {
fills: {
type: 'array',
description: 'Array of fill/trade objects',
items: {
type: 'object',
properties: KALSHI_FILL_OUTPUT_PROPERTIES,
},
},
paging: {
type: 'object',
description: 'Pagination cursor for fetching more results',
},
},
}
/**
* V2 Params for Get Fills - fixes limit max to 200, adds subaccount
*/
export interface KalshiGetFillsV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
orderId?: string
minTs?: number
maxTs?: number
subaccount?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetFillsV2Response {
success: boolean
output: {
fills: Array<{
fill_id: string
trade_id: string
order_id: string
client_order_id: string | null
ticker: string
market_ticker: string
side: string
action: string
count: number
count_fp: string | null
price: number | null
yes_price: number
no_price: number
yes_price_fixed: string | null
no_price_fixed: string | null
is_taker: boolean
created_time: string | null
ts: number | null
}>
cursor: string | null
}
}
export const kalshiGetFillsV2Tool: ToolConfig<KalshiGetFillsV2Params, KalshiGetFillsV2Response> = {
id: 'kalshi_get_fills_v2',
name: 'Get Fills from Kalshi V2',
description: "Retrieve your portfolio's fills/trades from Kalshi (V2 - exact API response)",
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
orderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by order ID (e.g., "abc123-def456-ghi789")',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp in Unix seconds (e.g., 1704067200)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp in Unix seconds (e.g., 1704153600)',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount identifier to get fills for',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.orderId) queryParams.append('order_id', params.orderId)
if (params.minTs !== undefined) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs !== undefined) queryParams.append('max_ts', params.maxTs.toString())
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/fills')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/fills'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_fills_v2')
}
const fills = (data.fills || []).map((f: Record<string, unknown>) => ({
fill_id: f.fill_id ?? null,
trade_id: f.trade_id ?? null,
order_id: f.order_id ?? null,
client_order_id: f.client_order_id ?? null,
ticker: f.ticker ?? null,
market_ticker: f.market_ticker ?? null,
side: f.side ?? null,
action: f.action ?? null,
count: f.count ?? 0,
count_fp: f.count_fp ?? null,
price: f.price ?? null,
yes_price: f.yes_price ?? 0,
no_price: f.no_price ?? 0,
yes_price_fixed: f.yes_price_fixed ?? null,
no_price_fixed: f.no_price_fixed ?? null,
is_taker: f.is_taker ?? false,
created_time: f.created_time ?? null,
ts: f.ts ?? null,
}))
return {
success: true,
output: {
fills,
cursor: data.cursor ?? null,
},
}
},
outputs: {
fills: {
type: 'array',
description: 'Array of fill/trade objects with all API fields',
items: {
type: 'object',
properties: KALSHI_FILL_OUTPUT_PROPERTIES,
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}
+276
View File
@@ -0,0 +1,276 @@
import type { KalshiMarket } from '@/tools/kalshi/types'
import { buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetMarketParams {
ticker: string // Market ticker
}
export interface KalshiGetMarketResponse {
success: boolean
output: {
market: KalshiMarket
}
}
export const kalshiGetMarketTool: ToolConfig<KalshiGetMarketParams, KalshiGetMarketResponse> = {
id: 'kalshi_get_market',
name: 'Get Market from Kalshi',
description: 'Retrieve details of a specific prediction market by ticker',
version: '1.0.0',
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
},
request: {
url: (params) => buildKalshiUrl(`/markets/${params.ticker.trim()}`),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_market')
}
return {
success: true,
output: {
market: data.market,
},
}
},
outputs: {
market: {
type: 'object',
description: 'Market object with details',
},
},
}
/**
* V2 Get Market Tool - Returns exact Kalshi API response structure
*/
export interface KalshiGetMarketV2Response {
success: boolean
output: {
market: {
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string | null
close_time: string | null
expected_expiration_time: string | null
expiration_time: string | null
latest_expiration_time: string | null
settlement_timer_seconds: number | null
status: string
response_price_units: string | null
notional_value: number | null
tick_size: number | null
yes_bid: number | null
yes_ask: number | null
no_bid: number | null
no_ask: number | null
last_price: number | null
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number | null
volume_24h: number | null
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
can_close_early: boolean | null
expiration_value: string | null
category: string | null
risk_limit_cents: number | null
strike_type: string | null
rules_primary: string | null
rules_secondary: string | null
settlement_source_url: string | null
custom_strike: object | null
underlying: string | null
settlement_value: number | null
cfd_contract_size: number | null
yes_fee_fp: number | null
no_fee_fp: number | null
last_price_fp: number | null
yes_bid_fp: number | null
yes_ask_fp: number | null
no_bid_fp: number | null
no_ask_fp: number | null
}
}
}
export const kalshiGetMarketV2Tool: ToolConfig<KalshiGetMarketParams, KalshiGetMarketV2Response> = {
id: 'kalshi_get_market_v2',
name: 'Get Market from Kalshi V2',
description:
'Retrieve details of a specific prediction market by ticker (V2 - full API response)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
},
request: {
url: (params) => buildKalshiUrl(`/markets/${params.ticker.trim()}`),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_market_v2')
}
const m = data.market || {}
return {
success: true,
output: {
market: {
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expected_expiration_time: m.expected_expiration_time ?? null,
expiration_time: m.expiration_time ?? null,
latest_expiration_time: m.latest_expiration_time ?? null,
settlement_timer_seconds: m.settlement_timer_seconds ?? null,
status: m.status ?? null,
response_price_units: m.response_price_units ?? null,
notional_value: m.notional_value ?? null,
tick_size: m.tick_size ?? null,
yes_bid: m.yes_bid ?? null,
yes_ask: m.yes_ask ?? null,
no_bid: m.no_bid ?? null,
no_ask: m.no_ask ?? null,
last_price: m.last_price ?? null,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? null,
volume_24h: m.volume_24h ?? null,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
can_close_early: m.can_close_early ?? null,
expiration_value: m.expiration_value ?? null,
category: m.category ?? null,
risk_limit_cents: m.risk_limit_cents ?? null,
strike_type: m.strike_type ?? null,
rules_primary: m.rules_primary ?? null,
rules_secondary: m.rules_secondary ?? null,
settlement_source_url: m.settlement_source_url ?? null,
custom_strike: m.custom_strike ?? null,
underlying: m.underlying ?? null,
settlement_value: m.settlement_value ?? null,
cfd_contract_size: m.cfd_contract_size ?? null,
yes_fee_fp: m.yes_fee_fp ?? null,
no_fee_fp: m.no_fee_fp ?? null,
last_price_fp: m.last_price_fp ?? null,
yes_bid_fp: m.yes_bid_fp ?? null,
yes_ask_fp: m.yes_ask_fp ?? null,
no_bid_fp: m.no_bid_fp ?? null,
no_ask_fp: m.no_ask_fp ?? null,
},
},
}
},
outputs: {
market: {
type: 'object',
description: 'Market object with all API fields',
properties: {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
market_type: { type: 'string', description: 'Market type' },
title: { type: 'string', description: 'Market title' },
subtitle: { type: 'string', description: 'Market subtitle' },
yes_sub_title: { type: 'string', description: 'Yes outcome subtitle' },
no_sub_title: { type: 'string', description: 'No outcome subtitle' },
open_time: { type: 'string', description: 'Market open time' },
close_time: { type: 'string', description: 'Market close time' },
expected_expiration_time: { type: 'string', description: 'Expected expiration time' },
expiration_time: { type: 'string', description: 'Expiration time' },
latest_expiration_time: { type: 'string', description: 'Latest expiration time' },
settlement_timer_seconds: { type: 'number', description: 'Settlement timer in seconds' },
status: { type: 'string', description: 'Market status' },
response_price_units: { type: 'string', description: 'Response price units' },
notional_value: { type: 'number', description: 'Notional value' },
tick_size: { type: 'number', description: 'Tick size' },
yes_bid: { type: 'number', description: 'Current yes bid price' },
yes_ask: { type: 'number', description: 'Current yes ask price' },
no_bid: { type: 'number', description: 'Current no bid price' },
no_ask: { type: 'number', description: 'Current no ask price' },
last_price: { type: 'number', description: 'Last trade price' },
previous_yes_bid: { type: 'number', description: 'Previous yes bid' },
previous_yes_ask: { type: 'number', description: 'Previous yes ask' },
previous_price: { type: 'number', description: 'Previous price' },
volume: { type: 'number', description: 'Total volume' },
volume_24h: { type: 'number', description: '24-hour volume' },
liquidity: { type: 'number', description: 'Market liquidity' },
open_interest: { type: 'number', description: 'Open interest' },
result: { type: 'string', description: 'Market result' },
cap_strike: { type: 'number', description: 'Cap strike' },
floor_strike: { type: 'number', description: 'Floor strike' },
can_close_early: { type: 'boolean', description: 'Can close early' },
expiration_value: { type: 'string', description: 'Expiration value' },
category: { type: 'string', description: 'Market category' },
risk_limit_cents: { type: 'number', description: 'Risk limit in cents' },
strike_type: { type: 'string', description: 'Strike type' },
rules_primary: { type: 'string', description: 'Primary rules' },
rules_secondary: { type: 'string', description: 'Secondary rules' },
settlement_source_url: { type: 'string', description: 'Settlement source URL' },
custom_strike: { type: 'object', description: 'Custom strike object' },
underlying: { type: 'string', description: 'Underlying asset' },
settlement_value: { type: 'number', description: 'Settlement value' },
cfd_contract_size: { type: 'number', description: 'CFD contract size' },
yes_fee_fp: { type: 'number', description: 'Yes fee (fixed-point)' },
no_fee_fp: { type: 'number', description: 'No fee (fixed-point)' },
last_price_fp: { type: 'number', description: 'Last price (fixed-point)' },
yes_bid_fp: { type: 'number', description: 'Yes bid (fixed-point)' },
yes_ask_fp: { type: 'number', description: 'Yes ask (fixed-point)' },
no_bid_fp: { type: 'number', description: 'No bid (fixed-point)' },
no_ask_fp: { type: 'number', description: 'No ask (fixed-point)' },
},
},
},
}
+406
View File
@@ -0,0 +1,406 @@
import type { KalshiMarket, KalshiPaginationParams, KalshiPagingInfo } from '@/tools/kalshi/types'
import {
buildKalshiUrl,
handleKalshiError,
KALSHI_MARKET_OUTPUT_PROPERTIES,
KALSHI_PAGING_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetMarketsParams extends KalshiPaginationParams {
status?: string // unopened, open, closed, settled
seriesTicker?: string
eventTicker?: string
}
export interface KalshiGetMarketsResponse {
success: boolean
output: {
markets: KalshiMarket[]
paging?: KalshiPagingInfo
}
}
export const kalshiGetMarketsTool: ToolConfig<KalshiGetMarketsParams, KalshiGetMarketsResponse> = {
id: 'kalshi_get_markets',
name: 'Get Markets from Kalshi',
description: 'Retrieve a list of prediction markets from Kalshi with optional filtering',
version: '1.0.0',
params: {
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market status: "unopened", "open", "closed", or "settled"',
},
seriesTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by series ticker (e.g., "KXBTC", "INX", "FED-RATE")',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker (e.g., "KXBTC-24DEC31", "INX-25JAN03")',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.append('status', params.status)
if (params.seriesTicker) queryParams.append('series_ticker', params.seriesTicker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/markets')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_markets')
}
const markets = data.markets || []
return {
success: true,
output: {
markets,
paging: {
cursor: data.cursor || null,
},
},
}
},
outputs: {
markets: {
type: 'array',
description: 'Array of market objects',
items: {
type: 'object',
properties: KALSHI_MARKET_OUTPUT_PROPERTIES,
},
},
paging: {
type: 'object',
description: 'Pagination cursor for fetching more results',
properties: KALSHI_PAGING_OUTPUT_PROPERTIES,
},
},
}
/**
* V2 Get Markets Tool - Returns exact Kalshi API response structure with all params
*/
export interface KalshiGetMarketsV2Params extends KalshiPaginationParams {
status?: string // unopened, open, closed, settled
seriesTicker?: string
eventTicker?: string
minCreatedTs?: number
maxCreatedTs?: number
minUpdatedTs?: number
minCloseTs?: number
maxCloseTs?: number
minSettledTs?: number
maxSettledTs?: number
tickers?: string // comma-separated list
mveFilter?: string // only or exclude
}
export interface KalshiGetMarketsV2Response {
success: boolean
output: {
markets: Array<{
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string | null
close_time: string | null
expected_expiration_time: string | null
expiration_time: string | null
latest_expiration_time: string | null
settlement_timer_seconds: number | null
status: string
response_price_units: string | null
notional_value: number | null
tick_size: number | null
yes_bid: number | null
yes_ask: number | null
no_bid: number | null
no_ask: number | null
last_price: number | null
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number | null
volume_24h: number | null
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
can_close_early: boolean | null
expiration_value: string | null
category: string | null
risk_limit_cents: number | null
strike_type: string | null
rules_primary: string | null
rules_secondary: string | null
settlement_source_url: string | null
custom_strike: object | null
underlying: string | null
settlement_value: number | null
cfd_contract_size: number | null
yes_fee_fp: number | null
no_fee_fp: number | null
last_price_fp: number | null
yes_bid_fp: number | null
yes_ask_fp: number | null
no_bid_fp: number | null
no_ask_fp: number | null
}>
cursor: string | null
}
}
export const kalshiGetMarketsV2Tool: ToolConfig<
KalshiGetMarketsV2Params,
KalshiGetMarketsV2Response
> = {
id: 'kalshi_get_markets_v2',
name: 'Get Markets from Kalshi V2',
description:
'Retrieve a list of prediction markets from Kalshi with all filtering options (V2 - full API response)',
version: '2.0.0',
params: {
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market status: "unopened", "open", "closed", or "settled"',
},
seriesTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by series ticker (e.g., "KXBTC", "INX", "FED-RATE")',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker (e.g., "KXBTC-24DEC31", "INX-25JAN03")',
},
minCreatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum created timestamp in Unix seconds (e.g., 1704067200)',
},
maxCreatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum created timestamp in Unix seconds (e.g., 1704153600)',
},
minUpdatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum updated timestamp in Unix seconds (e.g., 1704067200)',
},
minCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum close timestamp in Unix seconds (e.g., 1704067200)',
},
maxCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum close timestamp in Unix seconds (e.g., 1704153600)',
},
minSettledTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum settled timestamp in Unix seconds (e.g., 1704067200)',
},
maxSettledTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum settled timestamp in Unix seconds (e.g., 1704153600)',
},
tickers: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of tickers (e.g., "KXBTC-24DEC31,INX-25JAN03")',
},
mveFilter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Multivariate event filter: "only" or "exclude"',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.append('status', params.status)
if (params.seriesTicker) queryParams.append('series_ticker', params.seriesTicker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.minCreatedTs) queryParams.append('min_created_ts', params.minCreatedTs.toString())
if (params.maxCreatedTs) queryParams.append('max_created_ts', params.maxCreatedTs.toString())
if (params.minUpdatedTs) queryParams.append('min_updated_ts', params.minUpdatedTs.toString())
if (params.minCloseTs) queryParams.append('min_close_ts', params.minCloseTs.toString())
if (params.maxCloseTs) queryParams.append('max_close_ts', params.maxCloseTs.toString())
if (params.minSettledTs) queryParams.append('min_settled_ts', params.minSettledTs.toString())
if (params.maxSettledTs) queryParams.append('max_settled_ts', params.maxSettledTs.toString())
if (params.tickers) queryParams.append('tickers', params.tickers)
if (params.mveFilter) queryParams.append('mve_filter', params.mveFilter)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/markets')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_markets_v2')
}
const markets = (data.markets || []).map((m: Record<string, unknown>) => ({
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expected_expiration_time: m.expected_expiration_time ?? null,
expiration_time: m.expiration_time ?? null,
latest_expiration_time: m.latest_expiration_time ?? null,
settlement_timer_seconds: m.settlement_timer_seconds ?? null,
status: m.status ?? null,
response_price_units: m.response_price_units ?? null,
notional_value: m.notional_value ?? null,
tick_size: m.tick_size ?? null,
yes_bid: m.yes_bid ?? null,
yes_ask: m.yes_ask ?? null,
no_bid: m.no_bid ?? null,
no_ask: m.no_ask ?? null,
last_price: m.last_price ?? null,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? null,
volume_24h: m.volume_24h ?? null,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
can_close_early: m.can_close_early ?? null,
expiration_value: m.expiration_value ?? null,
category: m.category ?? null,
risk_limit_cents: m.risk_limit_cents ?? null,
strike_type: m.strike_type ?? null,
rules_primary: m.rules_primary ?? null,
rules_secondary: m.rules_secondary ?? null,
settlement_source_url: m.settlement_source_url ?? null,
custom_strike: m.custom_strike ?? null,
underlying: m.underlying ?? null,
settlement_value: m.settlement_value ?? null,
cfd_contract_size: m.cfd_contract_size ?? null,
yes_fee_fp: m.yes_fee_fp ?? null,
no_fee_fp: m.no_fee_fp ?? null,
last_price_fp: m.last_price_fp ?? null,
yes_bid_fp: m.yes_bid_fp ?? null,
yes_ask_fp: m.yes_ask_fp ?? null,
no_bid_fp: m.no_bid_fp ?? null,
no_ask_fp: m.no_ask_fp ?? null,
}))
return {
success: true,
output: {
markets,
cursor: data.cursor ?? null,
},
}
},
outputs: {
markets: {
type: 'array',
description: 'Array of market objects with all API fields',
items: {
type: 'object',
properties: KALSHI_MARKET_OUTPUT_PROPERTIES,
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}
+248
View File
@@ -0,0 +1,248 @@
import type { KalshiAuthParams, KalshiOrder } from '@/tools/kalshi/types'
import { buildKalshiAuthHeaders, buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetOrderParams extends KalshiAuthParams {
orderId: string // Order ID to retrieve (required)
}
export interface KalshiGetOrderResponse {
success: boolean
output: {
order: KalshiOrder
}
}
export const kalshiGetOrderTool: ToolConfig<KalshiGetOrderParams, KalshiGetOrderResponse> = {
id: 'kalshi_get_order',
name: 'Get Order from Kalshi',
description: 'Retrieve details of a specific order by ID from Kalshi',
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Order ID to retrieve (e.g., "abc123-def456-ghi789")',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId.trim()}`),
method: 'GET',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId.trim()}`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_order')
}
return {
success: true,
output: {
order: data.order,
},
}
},
outputs: {
order: {
type: 'object',
description: 'Order object with details',
},
},
}
export interface KalshiGetOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to retrieve (required)
}
export interface KalshiGetOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
}
}
export const kalshiGetOrderV2Tool: ToolConfig<KalshiGetOrderV2Params, KalshiGetOrderV2Response> = {
id: 'kalshi_get_order_v2',
name: 'Get Order from Kalshi V2',
description: 'Retrieve details of a specific order by ID from Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Order ID to retrieve (e.g., "abc123-def456-ghi789")',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId.trim()}`),
method: 'GET',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId.trim()}`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
},
}
},
outputs: {
order: {
type: 'object',
description: 'Order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
},
}
+197
View File
@@ -0,0 +1,197 @@
import type { KalshiOrderbook } from '@/tools/kalshi/types'
import { buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetOrderbookParams {
ticker: string
}
export interface KalshiGetOrderbookResponse {
success: boolean
output: {
orderbook: KalshiOrderbook
}
}
export const kalshiGetOrderbookTool: ToolConfig<
KalshiGetOrderbookParams,
KalshiGetOrderbookResponse
> = {
id: 'kalshi_get_orderbook',
name: 'Get Market Orderbook from Kalshi',
description: 'Retrieve the orderbook (yes and no bids) for a specific market',
version: '1.0.0',
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
},
request: {
url: (params) => buildKalshiUrl(`/markets/${params.ticker.trim()}/orderbook`),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_orderbook')
}
const orderbook = data.orderbook || { yes: [], no: [] }
return {
success: true,
output: {
orderbook,
},
}
},
outputs: {
orderbook: {
type: 'object',
description: 'Orderbook with yes/no bids and asks',
},
},
}
/**
* V2 Get Orderbook Tool - Returns exact Kalshi API response structure with depth param
* API returns tuple arrays: [price, count] for orderbook, [dollars_string, count] for _dollars variants
*/
export interface KalshiGetOrderbookV2Params {
ticker: string
depth?: number // Number of price levels to return
}
export interface KalshiGetOrderbookV2Response {
success: boolean
output: {
orderbook: {
yes: Array<[number, number]> // [price_in_cents, count]
no: Array<[number, number]> // [price_in_cents, count]
yes_dollars: Array<[string, number]> // [dollars_string, count]
no_dollars: Array<[string, number]> // [dollars_string, count]
}
orderbook_fp: {
yes_dollars: Array<[string, string]> // [dollars_string, fp_count_string]
no_dollars: Array<[string, string]> // [dollars_string, fp_count_string]
}
}
}
export const kalshiGetOrderbookV2Tool: ToolConfig<
KalshiGetOrderbookV2Params,
KalshiGetOrderbookV2Response
> = {
id: 'kalshi_get_orderbook_v2',
name: 'Get Market Orderbook from Kalshi V2',
description:
'Retrieve the orderbook (yes and no bids) for a specific market (V2 - includes depth and fp fields)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker identifier (e.g., "KXBTC-24DEC31", "INX-25JAN03-T4485.99")',
},
depth: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of price levels to return (e.g., 10, 20). Default: all levels',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.depth) queryParams.append('depth', params.depth.toString())
const query = queryParams.toString()
const url = buildKalshiUrl(`/markets/${params.ticker.trim()}/orderbook`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_orderbook_v2')
}
const orderbook = data.orderbook || {}
const orderbookFp = data.orderbook_fp || {}
return {
success: true,
output: {
orderbook: {
yes: orderbook.yes ?? [],
no: orderbook.no ?? [],
yes_dollars: orderbook.yes_dollars ?? [],
no_dollars: orderbook.no_dollars ?? [],
},
orderbook_fp: {
yes_dollars: orderbookFp.yes_dollars ?? [],
no_dollars: orderbookFp.no_dollars ?? [],
},
},
}
},
outputs: {
orderbook: {
type: 'object',
description: 'Orderbook with yes/no bids (legacy integer counts)',
properties: {
yes: {
type: 'array',
description: 'Yes side bids as tuples [price_cents, count]',
},
no: {
type: 'array',
description: 'No side bids as tuples [price_cents, count]',
},
yes_dollars: {
type: 'array',
description: 'Yes side bids as tuples [dollars_string, count]',
},
no_dollars: {
type: 'array',
description: 'No side bids as tuples [dollars_string, count]',
},
},
},
orderbook_fp: {
type: 'object',
description: 'Orderbook with fixed-point counts (preferred)',
properties: {
yes_dollars: {
type: 'array',
description: 'Yes side bids as tuples [dollars_string, fp_count_string]',
},
no_dollars: {
type: 'array',
description: 'No side bids as tuples [dollars_string, fp_count_string]',
},
},
},
},
}
+352
View File
@@ -0,0 +1,352 @@
import type {
KalshiAuthParams,
KalshiOrder,
KalshiPaginationParams,
KalshiPagingInfo,
} from '@/tools/kalshi/types'
import {
buildKalshiAuthHeaders,
buildKalshiUrl,
handleKalshiError,
KALSHI_ORDER_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetOrdersParams extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
status?: string // resting, canceled, executed
}
export interface KalshiGetOrdersResponse {
success: boolean
output: {
orders: KalshiOrder[]
paging?: KalshiPagingInfo
}
}
export const kalshiGetOrdersTool: ToolConfig<KalshiGetOrdersParams, KalshiGetOrdersResponse> = {
id: 'kalshi_get_orders',
name: 'Get Orders from Kalshi',
description: 'Retrieve your orders from Kalshi with optional filtering',
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by event ticker, max 10 comma-separated (e.g., "KXBTC-24DEC31,INX-25JAN03")',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by order status: "resting", "canceled", or "executed"',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.status) queryParams.append('status', params.status)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/orders')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/orders'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_orders')
}
const orders = data.orders || []
return {
success: true,
output: {
orders,
paging: {
cursor: data.cursor || null,
},
},
}
},
outputs: {
orders: {
type: 'array',
description: 'Array of order objects',
items: {
type: 'object',
properties: KALSHI_ORDER_OUTPUT_PROPERTIES,
},
},
paging: {
type: 'object',
description: 'Pagination cursor for fetching more results',
},
},
}
export interface KalshiGetOrdersV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
status?: string // resting, canceled, executed
minTs?: string // Minimum timestamp filter (Unix timestamp)
maxTs?: string // Maximum timestamp filter (Unix timestamp)
subaccount?: string // Subaccount to filter orders
}
interface KalshiOrderV2 {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
export interface KalshiGetOrdersV2Response {
success: boolean
output: {
orders: KalshiOrderV2[]
cursor: string | null
}
}
export const kalshiGetOrdersV2Tool: ToolConfig<KalshiGetOrdersV2Params, KalshiGetOrdersV2Response> =
{
id: 'kalshi_get_orders_v2',
name: 'Get Orders from Kalshi V2',
description:
'Retrieve your orders from Kalshi with optional filtering (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by event ticker, max 10 comma-separated (e.g., "KXBTC-24DEC31,INX-25JAN03")',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by order status: "resting", "canceled", or "executed"',
},
minTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp filter (Unix timestamp, e.g., "1704067200")',
},
maxTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp filter (Unix timestamp, e.g., "1704153600")',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount identifier to filter orders',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.status) queryParams.append('status', params.status)
if (params.minTs) queryParams.append('min_ts', params.minTs)
if (params.maxTs) queryParams.append('max_ts', params.maxTs)
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/orders')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/orders'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_orders_v2')
}
const rawOrders = data.orders || []
const orders: KalshiOrderV2[] = rawOrders.map((order: any) => ({
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
}))
return {
success: true,
output: {
orders,
cursor: data.cursor ?? null,
},
}
},
outputs: {
orders: {
type: 'array',
description: 'Array of order objects with full API response fields',
items: {
type: 'object',
properties: KALSHI_ORDER_OUTPUT_PROPERTIES,
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}
+315
View File
@@ -0,0 +1,315 @@
import type {
KalshiAuthParams,
KalshiPaginationParams,
KalshiPagingInfo,
KalshiPosition,
} from '@/tools/kalshi/types'
import {
buildKalshiAuthHeaders,
buildKalshiUrl,
handleKalshiError,
KALSHI_EVENT_POSITION_OUTPUT_PROPERTIES,
KALSHI_POSITION_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetPositionsParams extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
}
export interface KalshiGetPositionsResponse {
success: boolean
output: {
positions: KalshiPosition[]
paging?: KalshiPagingInfo
}
}
export const kalshiGetPositionsTool: ToolConfig<
KalshiGetPositionsParams,
KalshiGetPositionsResponse
> = {
id: 'kalshi_get_positions',
name: 'Get Positions from Kalshi',
description: 'Retrieve your open positions from Kalshi',
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by event ticker, max 10 comma-separated (e.g., "KXBTC-24DEC31,INX-25JAN03")',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/positions')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/positions'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_positions')
}
const positions = data.market_positions || data.positions || []
return {
success: true,
output: {
positions,
paging: {
cursor: data.cursor || null,
},
},
}
},
outputs: {
positions: {
type: 'array',
description: 'Array of position objects',
items: {
type: 'object',
properties: KALSHI_POSITION_OUTPUT_PROPERTIES,
},
},
paging: {
type: 'object',
description: 'Pagination cursor for fetching more results',
},
},
}
/**
* V2 Params for Get Positions - removes invalid settlementStatus, adds countFilter and subaccount
*/
export interface KalshiGetPositionsV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
countFilter?: string
subaccount?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetPositionsV2Response {
success: boolean
output: {
market_positions: Array<{
ticker: string
event_ticker: string
event_title: string | null
market_title: string | null
position: number
market_exposure: number | null
realized_pnl: number | null
total_traded: number | null
resting_orders_count: number | null
fees_paid: number | null
}>
event_positions: Array<{
event_ticker: string
event_exposure: number
realized_pnl: number | null
total_cost: number | null
}> | null
cursor: string | null
}
}
export const kalshiGetPositionsV2Tool: ToolConfig<
KalshiGetPositionsV2Params,
KalshiGetPositionsV2Response
> = {
id: 'kalshi_get_positions_v2',
name: 'Get Positions from Kalshi V2',
description: 'Retrieve your open positions from Kalshi (V2 - exact API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by event ticker, max 10 comma-separated (e.g., "KXBTC-24DEC31,INX-25JAN03")',
},
countFilter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Restrict to positions with non-zero values for the given fields (comma-separated): "position", "total_traded"',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount identifier to get positions for',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.countFilter) queryParams.append('count_filter', params.countFilter)
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/positions')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/positions'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_positions_v2')
}
const marketPositions = (data.market_positions || []).map((p: Record<string, unknown>) => ({
ticker: p.ticker ?? null,
event_ticker: p.event_ticker ?? null,
event_title: p.event_title ?? null,
market_title: p.market_title ?? null,
position: p.position ?? 0,
market_exposure: p.market_exposure ?? null,
realized_pnl: p.realized_pnl ?? null,
total_traded: p.total_traded ?? null,
resting_orders_count: p.resting_orders_count ?? null,
fees_paid: p.fees_paid ?? null,
}))
const eventPositions = data.event_positions
? (data.event_positions as Array<Record<string, unknown>>).map((p) => ({
event_ticker: (p.event_ticker as string) ?? '',
event_exposure: (p.event_exposure as number) ?? 0,
realized_pnl: (p.realized_pnl as number | null) ?? null,
total_cost: (p.total_cost as number | null) ?? null,
}))
: null
return {
success: true,
output: {
market_positions: marketPositions,
event_positions: eventPositions,
cursor: data.cursor ?? null,
},
}
},
outputs: {
market_positions: {
type: 'array',
description: 'Array of market position objects',
items: {
type: 'object',
properties: KALSHI_POSITION_OUTPUT_PROPERTIES,
},
},
event_positions: {
type: 'array',
description: 'Array of event position objects',
items: {
type: 'object',
properties: KALSHI_EVENT_POSITION_OUTPUT_PROPERTIES,
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}
@@ -0,0 +1,205 @@
import type { KalshiSeries } from '@/tools/kalshi/types'
import { buildKalshiUrl, handleKalshiError } from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetSeriesByTickerParams {
seriesTicker: string
}
export interface KalshiGetSeriesByTickerResponse {
success: boolean
output: {
series: KalshiSeries
}
}
export const kalshiGetSeriesByTickerTool: ToolConfig<
KalshiGetSeriesByTickerParams,
KalshiGetSeriesByTickerResponse
> = {
id: 'kalshi_get_series_by_ticker',
name: 'Get Series by Ticker from Kalshi',
description: 'Retrieve details of a specific market series by ticker',
version: '1.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker identifier (e.g., "KXBTC", "INX", "FED-RATE")',
},
},
request: {
url: (params) => {
return buildKalshiUrl(`/series/${params.seriesTicker.trim()}`)
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_series_by_ticker')
}
const series = data.series || data
return {
success: true,
output: {
series,
},
}
},
outputs: {
series: {
type: 'object',
description: 'Series object with details',
},
},
}
/**
* V2 Params for Get Series by Ticker
*/
export interface KalshiGetSeriesByTickerV2Params {
seriesTicker: string
includeVolume?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetSeriesByTickerV2Response {
success: boolean
output: {
series: {
ticker: string
title: string
frequency: string
category: string
tags: string[] | null
settlement_sources: Array<{
name: string
url: string
}> | null
contract_url: string | null
contract_terms_url: string | null
fee_type: string | null
fee_multiplier: number | null
additional_prohibitions: string[] | null
product_metadata: Record<string, unknown> | null
volume: number | null
volume_fp: number | null
}
}
}
export const kalshiGetSeriesByTickerV2Tool: ToolConfig<
KalshiGetSeriesByTickerV2Params,
KalshiGetSeriesByTickerV2Response
> = {
id: 'kalshi_get_series_by_ticker_v2',
name: 'Get Series by Ticker from Kalshi V2',
description: 'Retrieve details of a specific market series by ticker (V2 - exact API response)',
version: '2.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker identifier (e.g., "KXBTC", "INX", "FED-RATE")',
},
includeVolume: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include volume data in response (true/false)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.includeVolume) queryParams.append('include_volume', params.includeVolume)
const query = queryParams.toString()
const url = buildKalshiUrl(`/series/${params.seriesTicker.trim()}`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_series_by_ticker_v2')
}
const series = data.series || data
const settlementSources = series.settlement_sources
? (series.settlement_sources as Array<Record<string, unknown>>).map((s) => ({
name: (s.name as string) ?? null,
url: (s.url as string) ?? null,
}))
: null
return {
success: true,
output: {
series: {
ticker: series.ticker ?? null,
title: series.title ?? null,
frequency: series.frequency ?? null,
category: series.category ?? null,
tags: series.tags ?? null,
settlement_sources: settlementSources,
contract_url: series.contract_url ?? null,
contract_terms_url: series.contract_terms_url ?? null,
fee_type: series.fee_type ?? null,
fee_multiplier: series.fee_multiplier ?? null,
additional_prohibitions: series.additional_prohibitions ?? null,
product_metadata: series.product_metadata ?? null,
volume: series.volume ?? null,
volume_fp: series.volume_fp ?? null,
},
},
}
},
outputs: {
series: {
type: 'object',
description: 'Series object with full details matching Kalshi API response',
properties: {
ticker: { type: 'string', description: 'Series ticker' },
title: { type: 'string', description: 'Series title' },
frequency: { type: 'string', description: 'Event frequency' },
category: { type: 'string', description: 'Series category' },
tags: { type: 'array', description: 'Series tags' },
settlement_sources: { type: 'array', description: 'Settlement sources' },
contract_url: { type: 'string', description: 'Contract URL' },
contract_terms_url: { type: 'string', description: 'Contract terms URL' },
fee_type: { type: 'string', description: 'Fee type' },
fee_multiplier: { type: 'number', description: 'Fee multiplier' },
additional_prohibitions: { type: 'array', description: 'Additional prohibitions' },
product_metadata: { type: 'object', description: 'Product metadata' },
volume: { type: 'number', description: 'Series volume' },
volume_fp: { type: 'number', description: 'Volume (fixed-point)' },
},
},
},
}
+239
View File
@@ -0,0 +1,239 @@
import type { KalshiSeries } from '@/tools/kalshi/types'
import {
buildKalshiUrl,
handleKalshiError,
KALSHI_SERIES_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetSeriesListParams {
category?: string
tags?: string
}
export interface KalshiGetSeriesListResponse {
success: boolean
output: {
series: KalshiSeries[]
}
}
export const kalshiGetSeriesListTool: ToolConfig<
KalshiGetSeriesListParams,
KalshiGetSeriesListResponse
> = {
id: 'kalshi_get_series_list',
name: 'Get Series List from Kalshi',
description: 'Retrieve a list of market series from Kalshi with optional filtering',
version: '1.0.0',
params: {
category: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by category (e.g., "Economics", "Politics", "Crypto")',
},
tags: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by comma-separated tags',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.category) queryParams.append('category', params.category)
if (params.tags) queryParams.append('tags', params.tags)
const query = queryParams.toString()
const url = buildKalshiUrl('/series')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_series_list')
}
return {
success: true,
output: {
series: data.series || [],
},
}
},
outputs: {
series: {
type: 'array',
description: 'Array of series objects',
items: {
type: 'object',
properties: KALSHI_SERIES_OUTPUT_PROPERTIES,
},
},
},
}
/**
* V2 Params for Get Series List - adds metadata/volume flags and exact response mapping
*/
export interface KalshiGetSeriesListV2Params {
category?: string
tags?: string
includeProductMetadata?: string
includeVolume?: string
minUpdatedTs?: number
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetSeriesListV2Response {
success: boolean
output: {
series: Array<{
ticker: string
frequency: string
title: string
category: string
tags: string[] | null
settlement_sources: Array<{ name: string; url: string }> | null
contract_url: string | null
contract_terms_url: string | null
fee_type: string | null
fee_multiplier: number | null
additional_prohibitions: string[] | null
product_metadata: Record<string, unknown> | null
volume_fp: string | null
last_updated_ts: string | null
}>
}
}
export const kalshiGetSeriesListV2Tool: ToolConfig<
KalshiGetSeriesListV2Params,
KalshiGetSeriesListV2Response
> = {
id: 'kalshi_get_series_list_v2',
name: 'Get Series List from Kalshi V2',
description:
'Retrieve a list of market series from Kalshi with optional filtering (V2 - exact API response)',
version: '2.0.0',
params: {
category: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by category (e.g., "Economics", "Politics", "Crypto")',
},
tags: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by comma-separated tags',
},
includeProductMetadata: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include product metadata in response (true/false)',
},
includeVolume: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include volume data in response (true/false)',
},
minUpdatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum updated timestamp in Unix seconds (e.g., 1704067200)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.category) queryParams.append('category', params.category)
if (params.tags) queryParams.append('tags', params.tags)
if (params.includeProductMetadata)
queryParams.append('include_product_metadata', params.includeProductMetadata)
if (params.includeVolume) queryParams.append('include_volume', params.includeVolume)
if (params.minUpdatedTs !== undefined)
queryParams.append('min_updated_ts', params.minUpdatedTs.toString())
const query = queryParams.toString()
const url = buildKalshiUrl('/series')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_series_list_v2')
}
const series = (data.series || []).map((s: Record<string, unknown>) => {
const settlementSources = s.settlement_sources
? (s.settlement_sources as Array<Record<string, unknown>>).map((src) => ({
name: (src.name as string) ?? null,
url: (src.url as string) ?? null,
}))
: null
return {
ticker: s.ticker ?? null,
frequency: s.frequency ?? null,
title: s.title ?? null,
category: s.category ?? null,
tags: s.tags ?? null,
settlement_sources: settlementSources,
contract_url: s.contract_url ?? null,
contract_terms_url: s.contract_terms_url ?? null,
fee_type: s.fee_type ?? null,
fee_multiplier: s.fee_multiplier ?? null,
additional_prohibitions: s.additional_prohibitions ?? null,
product_metadata: s.product_metadata ?? null,
volume_fp: s.volume_fp ?? null,
last_updated_ts: s.last_updated_ts ?? null,
}
})
return {
success: true,
output: {
series,
},
}
},
outputs: {
series: {
type: 'array',
description: 'Array of series objects with all API fields',
items: {
type: 'object',
properties: KALSHI_SERIES_OUTPUT_PROPERTIES,
},
},
},
}
+310
View File
@@ -0,0 +1,310 @@
import type {
KalshiAuthParams,
KalshiPaginationParams,
KalshiPagingInfo,
} from '@/tools/kalshi/types'
import {
buildKalshiAuthHeaders,
buildKalshiUrl,
handleKalshiError,
KALSHI_SETTLEMENT_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetSettlementsParams extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
minTs?: number
maxTs?: number
}
export interface KalshiGetSettlementsResponse {
success: boolean
output: {
settlements: Array<Record<string, unknown>>
paging?: KalshiPagingInfo
}
}
export const kalshiGetSettlementsTool: ToolConfig<
KalshiGetSettlementsParams,
KalshiGetSettlementsResponse
> = {
id: 'kalshi_get_settlements',
name: 'Get Settlements from Kalshi',
description: 'Retrieve your portfolio settlement history from Kalshi',
version: '1.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker (e.g., "KXBTC-24DEC31")',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum settled timestamp in Unix seconds (e.g., 1704067200)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum settled timestamp in Unix seconds (e.g., 1704153600)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.minTs !== undefined) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs !== undefined) queryParams.append('max_ts', params.maxTs.toString())
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/settlements')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/settlements'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_settlements')
}
return {
success: true,
output: {
settlements: data.settlements || [],
paging: {
cursor: data.cursor || null,
},
},
}
},
outputs: {
settlements: {
type: 'array',
description: 'Array of settlement objects',
items: {
type: 'object',
properties: KALSHI_SETTLEMENT_OUTPUT_PROPERTIES,
},
},
paging: {
type: 'object',
description: 'Pagination cursor for fetching more results',
},
},
}
/**
* V2 Params for Get Settlements - adds subaccount and exact response mapping
*/
export interface KalshiGetSettlementsV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
minTs?: number
maxTs?: number
subaccount?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetSettlementsV2Response {
success: boolean
output: {
settlements: Array<{
ticker: string
event_ticker: string
market_result: string | null
yes_count_fp: string | null
yes_total_cost_dollars: string | null
no_count_fp: string | null
no_total_cost_dollars: string | null
revenue: number | null
settled_time: string | null
fee_cost: string | null
value: number | null
}>
cursor: string | null
}
}
export const kalshiGetSettlementsV2Tool: ToolConfig<
KalshiGetSettlementsV2Params,
KalshiGetSettlementsV2Response
> = {
id: 'kalshi_get_settlements_v2',
name: 'Get Settlements from Kalshi V2',
description: 'Retrieve your portfolio settlement history from Kalshi (V2 - exact API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker (e.g., "KXBTC-24DEC31")',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum settled timestamp in Unix seconds (e.g., 1704067200)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum settled timestamp in Unix seconds (e.g., 1704153600)',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount number (0 for primary, 1-63 for subaccounts)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.minTs !== undefined) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs !== undefined) queryParams.append('max_ts', params.maxTs.toString())
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/settlements')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/settlements'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_settlements_v2')
}
const settlements = (data.settlements || []).map((s: Record<string, unknown>) => ({
ticker: s.ticker ?? null,
event_ticker: s.event_ticker ?? null,
market_result: s.market_result ?? null,
yes_count_fp: s.yes_count_fp ?? null,
yes_total_cost_dollars: s.yes_total_cost_dollars ?? null,
no_count_fp: s.no_count_fp ?? null,
no_total_cost_dollars: s.no_total_cost_dollars ?? null,
revenue: s.revenue ?? null,
settled_time: s.settled_time ?? null,
fee_cost: s.fee_cost ?? null,
value: s.value ?? null,
}))
return {
success: true,
output: {
settlements,
cursor: data.cursor ?? null,
},
}
},
outputs: {
settlements: {
type: 'array',
description: 'Array of settlement objects with all API fields',
items: {
type: 'object',
properties: KALSHI_SETTLEMENT_OUTPUT_PROPERTIES,
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}
+219
View File
@@ -0,0 +1,219 @@
import type { KalshiPaginationParams, KalshiPagingInfo, KalshiTrade } from '@/tools/kalshi/types'
import {
buildKalshiUrl,
handleKalshiError,
KALSHI_TRADE_OUTPUT_PROPERTIES,
} from '@/tools/kalshi/types'
import type { ToolConfig } from '@/tools/types'
export interface KalshiGetTradesParams extends KalshiPaginationParams {}
export interface KalshiGetTradesResponse {
success: boolean
output: {
trades: KalshiTrade[]
paging?: KalshiPagingInfo
}
}
export const kalshiGetTradesTool: ToolConfig<KalshiGetTradesParams, KalshiGetTradesResponse> = {
id: 'kalshi_get_trades',
name: 'Get Trades from Kalshi',
description: 'Retrieve recent trades across all markets',
version: '1.0.0',
params: {
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/markets/trades')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_trades')
}
const trades = data.trades || []
return {
success: true,
output: {
trades,
paging: {
cursor: data.cursor || null,
},
},
}
},
outputs: {
trades: {
type: 'array',
description: 'Array of trade objects',
items: {
type: 'object',
properties: KALSHI_TRADE_OUTPUT_PROPERTIES,
},
},
paging: {
type: 'object',
description: 'Pagination cursor for fetching more results',
},
},
}
/**
* V2 Get Trades Tool - Returns exact Kalshi API response structure with additional params
*/
export interface KalshiGetTradesV2Params extends KalshiPaginationParams {
ticker?: string // Filter by market ticker
minTs?: number // Minimum timestamp (Unix seconds)
maxTs?: number // Maximum timestamp (Unix seconds)
}
export interface KalshiGetTradesV2Response {
success: boolean
output: {
trades: Array<{
trade_id: string | null
ticker: string
yes_price: number | null
no_price: number | null
count: number | null
count_fp: number | null
created_time: string | null
taker_side: string | null
}>
cursor: string | null
}
}
export const kalshiGetTradesV2Tool: ToolConfig<KalshiGetTradesV2Params, KalshiGetTradesV2Response> =
{
id: 'kalshi_get_trades_v2',
name: 'Get Trades from Kalshi V2',
description:
'Retrieve recent trades with additional filtering options (V2 - includes trade_id and count_fp)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker (e.g., "KXBTC-24DEC31")',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp in Unix seconds (e.g., 1704067200)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp in Unix seconds (e.g., 1704153600)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from previous response for fetching next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.minTs) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs) queryParams.append('max_ts', params.maxTs.toString())
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/markets/trades')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_trades_v2')
}
const trades = (data.trades || []).map((t: Record<string, unknown>) => ({
trade_id: t.trade_id ?? null,
ticker: t.ticker ?? null,
yes_price: t.yes_price ?? null,
no_price: t.no_price ?? null,
count: t.count ?? null,
count_fp: t.count_fp ?? null,
created_time: t.created_time ?? null,
taker_side: t.taker_side ?? null,
}))
return {
success: true,
output: {
trades,
cursor: data.cursor ?? null,
},
}
},
outputs: {
trades: {
type: 'array',
description: 'Array of trade objects with trade_id and count_fp',
items: {
type: 'object',
properties: KALSHI_TRADE_OUTPUT_PROPERTIES,
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}
+31
View File
@@ -0,0 +1,31 @@
export { kalshiAmendOrderTool, kalshiAmendOrderV2Tool } from './amend_order'
export { kalshiCancelOrderTool, kalshiCancelOrderV2Tool } from './cancel_order'
export { kalshiCreateOrderTool, kalshiCreateOrderV2Tool } from './create_order'
export { kalshiGetBalanceTool, kalshiGetBalanceV2Tool } from './get_balance'
export { kalshiGetCandlesticksTool, kalshiGetCandlesticksV2Tool } from './get_candlesticks'
export { kalshiGetEventTool, kalshiGetEventV2Tool } from './get_event'
export {
kalshiGetEventCandlesticksTool,
kalshiGetEventCandlesticksV2Tool,
} from './get_event_candlesticks'
export { kalshiGetEventsTool, kalshiGetEventsV2Tool } from './get_events'
export {
kalshiGetExchangeAnnouncementsTool,
kalshiGetExchangeAnnouncementsV2Tool,
} from './get_exchange_announcements'
export {
kalshiGetExchangeScheduleTool,
kalshiGetExchangeScheduleV2Tool,
} from './get_exchange_schedule'
export { kalshiGetExchangeStatusTool, kalshiGetExchangeStatusV2Tool } from './get_exchange_status'
export { kalshiGetFillsTool, kalshiGetFillsV2Tool } from './get_fills'
export { kalshiGetMarketTool, kalshiGetMarketV2Tool } from './get_market'
export { kalshiGetMarketsTool, kalshiGetMarketsV2Tool } from './get_markets'
export { kalshiGetOrderTool, kalshiGetOrderV2Tool } from './get_order'
export { kalshiGetOrderbookTool, kalshiGetOrderbookV2Tool } from './get_orderbook'
export { kalshiGetOrdersTool, kalshiGetOrdersV2Tool } from './get_orders'
export { kalshiGetPositionsTool, kalshiGetPositionsV2Tool } from './get_positions'
export { kalshiGetSeriesByTickerTool, kalshiGetSeriesByTickerV2Tool } from './get_series_by_ticker'
export { kalshiGetSeriesListTool, kalshiGetSeriesListV2Tool } from './get_series_list'
export { kalshiGetSettlementsTool, kalshiGetSettlementsV2Tool } from './get_settlements'
export { kalshiGetTradesTool, kalshiGetTradesV2Tool } from './get_trades'
+630
View File
@@ -0,0 +1,630 @@
import crypto from 'crypto'
import type { OutputProperty } from '@/tools/types'
// Base URL for Kalshi API
export const KALSHI_BASE_URL = 'https://api.elections.kalshi.com/trade-api/v2'
/**
* Output property definitions for Kalshi Trade API responses.
* @see https://trading-api.readme.io/reference/introduction
*/
/**
* Output definition for market objects.
* @see https://trading-api.readme.io/reference/getmarkets
*/
export const KALSHI_MARKET_OUTPUT_PROPERTIES = {
ticker: { type: 'string', description: 'Unique market ticker identifier' },
event_ticker: { type: 'string', description: 'Parent event ticker' },
market_type: { type: 'string', description: 'Market type (binary, etc.)' },
title: { type: 'string', description: 'Market title/question' },
subtitle: { type: 'string', description: 'Market subtitle', optional: true },
yes_sub_title: { type: 'string', description: 'Yes outcome subtitle', optional: true },
no_sub_title: { type: 'string', description: 'No outcome subtitle', optional: true },
open_time: { type: 'string', description: 'Market open time (ISO 8601)', optional: true },
close_time: { type: 'string', description: 'Market close time (ISO 8601)', optional: true },
expiration_time: { type: 'string', description: 'Contract expiration time', optional: true },
status: { type: 'string', description: 'Market status (open, closed, settled, etc.)' },
yes_bid: { type: 'number', description: 'Current best yes bid price in cents', optional: true },
yes_ask: { type: 'number', description: 'Current best yes ask price in cents', optional: true },
no_bid: { type: 'number', description: 'Current best no bid price in cents', optional: true },
no_ask: { type: 'number', description: 'Current best no ask price in cents', optional: true },
last_price: { type: 'number', description: 'Last trade price in cents', optional: true },
previous_yes_bid: { type: 'number', description: 'Previous yes bid', optional: true },
previous_yes_ask: { type: 'number', description: 'Previous yes ask', optional: true },
previous_price: { type: 'number', description: 'Previous last price', optional: true },
volume: { type: 'number', description: 'Total volume (contracts traded)', optional: true },
volume_24h: { type: 'number', description: '24-hour trading volume', optional: true },
liquidity: { type: 'number', description: 'Market liquidity measure', optional: true },
open_interest: {
type: 'number',
description: 'Open interest (outstanding contracts)',
optional: true,
},
result: { type: 'string', description: 'Settlement result (yes, no, null)', optional: true },
cap_strike: { type: 'number', description: 'Cap strike for ranged markets', optional: true },
floor_strike: { type: 'number', description: 'Floor strike for ranged markets', optional: true },
category: { type: 'string', description: 'Market category', optional: true },
} as const satisfies Record<string, OutputProperty>
/**
* Complete market output definition
*/
export const KALSHI_MARKET_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi market object',
properties: KALSHI_MARKET_OUTPUT_PROPERTIES,
}
/**
* Output definition for event objects.
* @see https://trading-api.readme.io/reference/getevents
*/
export const KALSHI_EVENT_OUTPUT_PROPERTIES = {
event_ticker: { type: 'string', description: 'Unique event ticker identifier' },
series_ticker: { type: 'string', description: 'Parent series ticker' },
title: { type: 'string', description: 'Event title' },
sub_title: { type: 'string', description: 'Event subtitle', optional: true },
mutually_exclusive: { type: 'boolean', description: 'Whether markets are mutually exclusive' },
category: { type: 'string', description: 'Event category' },
strike_date: { type: 'string', description: 'Strike/settlement date', optional: true },
status: { type: 'string', description: 'Event status', optional: true },
} as const satisfies Record<string, OutputProperty>
/**
* Complete event output definition
*/
export const KALSHI_EVENT_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi event object',
properties: KALSHI_EVENT_OUTPUT_PROPERTIES,
}
/**
* Output definition for order objects.
* @see https://trading-api.readme.io/reference/getorders
*/
export const KALSHI_ORDER_OUTPUT_PROPERTIES = {
order_id: { type: 'string', description: 'Unique order identifier' },
user_id: { type: 'string', description: 'User ID', optional: true },
client_order_id: { type: 'string', description: 'Client-provided order ID', optional: true },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Order action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting, canceled, executed)' },
yes_price: { type: 'number', description: 'Yes price in cents', optional: true },
no_price: { type: 'number', description: 'No price in cents', optional: true },
fill_count: { type: 'number', description: 'Number of contracts filled', optional: true },
remaining_count: { type: 'number', description: 'Remaining contracts to fill', optional: true },
initial_count: { type: 'number', description: 'Initial order size', optional: true },
taker_fees: { type: 'number', description: 'Taker fees paid in cents', optional: true },
maker_fees: { type: 'number', description: 'Maker fees paid in cents', optional: true },
created_time: { type: 'string', description: 'Order creation time (ISO 8601)', optional: true },
expiration_time: { type: 'string', description: 'Order expiration time', optional: true },
last_update_time: { type: 'string', description: 'Last order update time', optional: true },
} as const satisfies Record<string, OutputProperty>
/**
* Complete order output definition
*/
export const KALSHI_ORDER_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi order object',
properties: KALSHI_ORDER_OUTPUT_PROPERTIES,
}
/**
* Output definition for position objects.
* @see https://trading-api.readme.io/reference/getpositions
*/
export const KALSHI_POSITION_OUTPUT_PROPERTIES = {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
event_title: { type: 'string', description: 'Event title', optional: true },
market_title: { type: 'string', description: 'Market title', optional: true },
position: { type: 'number', description: 'Net position (positive=yes, negative=no)' },
market_exposure: {
type: 'number',
description: 'Maximum potential loss in cents',
optional: true,
},
realized_pnl: { type: 'number', description: 'Realized profit/loss in cents', optional: true },
total_traded: { type: 'number', description: 'Total contracts traded', optional: true },
resting_orders_count: { type: 'number', description: 'Number of resting orders', optional: true },
fees_paid: { type: 'number', description: 'Total fees paid in cents', optional: true },
} as const satisfies Record<string, OutputProperty>
/**
* Complete position output definition
*/
export const KALSHI_POSITION_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi market position object',
properties: KALSHI_POSITION_OUTPUT_PROPERTIES,
}
/**
* Output definition for event position objects.
* @see https://trading-api.readme.io/reference/getpositions
*/
export const KALSHI_EVENT_POSITION_OUTPUT_PROPERTIES = {
event_ticker: { type: 'string', description: 'Event ticker' },
event_exposure: { type: 'number', description: 'Event-level exposure in cents' },
realized_pnl: { type: 'number', description: 'Realized P&L in cents', optional: true },
total_cost: { type: 'number', description: 'Total cost basis in cents', optional: true },
} as const satisfies Record<string, OutputProperty>
/**
* Complete event position output definition
*/
export const KALSHI_EVENT_POSITION_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi event position object',
properties: KALSHI_EVENT_POSITION_OUTPUT_PROPERTIES,
}
/**
* Output definition for fill/trade objects.
* @see https://trading-api.readme.io/reference/getfills
*/
export const KALSHI_FILL_OUTPUT_PROPERTIES = {
trade_id: { type: 'string', description: 'Unique trade identifier' },
order_id: { type: 'string', description: 'Associated order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Trade side (yes/no)' },
action: { type: 'string', description: 'Trade action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
is_taker: { type: 'boolean', description: 'Whether this was a taker trade' },
created_time: { type: 'string', description: 'Trade execution time (ISO 8601)' },
} as const satisfies Record<string, OutputProperty>
/**
* Complete fill output definition
*/
export const KALSHI_FILL_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi trade fill object',
properties: KALSHI_FILL_OUTPUT_PROPERTIES,
}
/**
* Output definition for trade objects (public trades).
* @see https://trading-api.readme.io/reference/gettrades
*/
export const KALSHI_TRADE_OUTPUT_PROPERTIES = {
ticker: { type: 'string', description: 'Market ticker' },
yes_price: { type: 'number', description: 'Trade price for yes in cents' },
no_price: { type: 'number', description: 'Trade price for no in cents' },
count: { type: 'number', description: 'Number of contracts traded' },
taker_side: { type: 'string', description: 'Taker side (yes/no)' },
created_time: { type: 'string', description: 'Trade time (ISO 8601)' },
} as const satisfies Record<string, OutputProperty>
/**
* Complete trade output definition
*/
export const KALSHI_TRADE_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi public trade object',
properties: KALSHI_TRADE_OUTPUT_PROPERTIES,
}
/**
* Output definition for candlestick/OHLC objects.
* @see https://trading-api.readme.io/reference/getmarketshistory
*/
export const KALSHI_CANDLESTICK_OUTPUT_PROPERTIES = {
open_time: { type: 'string', description: 'Candle open time (ISO 8601)' },
close_time: { type: 'string', description: 'Candle close time (ISO 8601)' },
open: { type: 'number', description: 'Opening price in cents' },
high: { type: 'number', description: 'High price in cents' },
low: { type: 'number', description: 'Low price in cents' },
close: { type: 'number', description: 'Closing price in cents' },
volume: { type: 'number', description: 'Volume during period' },
} as const satisfies Record<string, OutputProperty>
/**
* Complete candlestick output definition
*/
export const KALSHI_CANDLESTICK_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi price candlestick/OHLC data',
properties: KALSHI_CANDLESTICK_OUTPUT_PROPERTIES,
}
/**
* Output definition for orderbook level objects.
* @see https://trading-api.readme.io/reference/getmarketorderbook
*/
export const KALSHI_ORDERBOOK_LEVEL_OUTPUT_PROPERTIES = {
price: { type: 'number', description: 'Price level in cents' },
quantity: { type: 'number', description: 'Quantity at this price level' },
} as const satisfies Record<string, OutputProperty>
/**
* Complete orderbook level output definition
*/
export const KALSHI_ORDERBOOK_LEVEL_OUTPUT: OutputProperty = {
type: 'object',
description: 'Orderbook price level',
properties: KALSHI_ORDERBOOK_LEVEL_OUTPUT_PROPERTIES,
}
/**
* Output definition for series objects.
* @see https://trading-api.readme.io/reference/getseries
*/
export const KALSHI_SERIES_OUTPUT_PROPERTIES = {
ticker: { type: 'string', description: 'Unique series ticker' },
title: { type: 'string', description: 'Series title' },
frequency: { type: 'string', description: 'Event frequency (daily, weekly, etc.)' },
category: { type: 'string', description: 'Series category' },
tags: {
type: 'array',
description: 'Series tags',
items: { type: 'string', description: 'Tag name' },
optional: true,
},
contract_url: { type: 'string', description: 'Contract rules URL', optional: true },
} as const satisfies Record<string, OutputProperty>
/**
* Complete series output definition
*/
export const KALSHI_SERIES_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi series object',
properties: KALSHI_SERIES_OUTPUT_PROPERTIES,
}
/**
* Output definition for balance objects.
* @see https://trading-api.readme.io/reference/getbalance
*/
export const KALSHI_BALANCE_OUTPUT_PROPERTIES = {
balance: { type: 'number', description: 'Available balance in cents' },
portfolio_value: { type: 'number', description: 'Total portfolio value in cents' },
} as const satisfies Record<string, OutputProperty>
/**
* Complete balance output definition
*/
export const KALSHI_BALANCE_OUTPUT: OutputProperty = {
type: 'object',
description: 'Kalshi account balance',
properties: KALSHI_BALANCE_OUTPUT_PROPERTIES,
}
/**
* Output definition for settlement objects.
* @see https://docs.kalshi.com/api-reference/portfolio/get-settlements
*/
export const KALSHI_SETTLEMENT_OUTPUT_PROPERTIES = {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
market_result: { type: 'string', description: 'Settlement outcome (yes, no, scalar)' },
yes_count_fp: { type: 'string', description: 'Yes contracts owned (fixed-point)' },
yes_total_cost_dollars: { type: 'string', description: 'Yes cost basis in dollars' },
no_count_fp: { type: 'string', description: 'No contracts owned (fixed-point)' },
no_total_cost_dollars: { type: 'string', description: 'No cost basis in dollars' },
revenue: { type: 'number', description: 'Payout in cents' },
settled_time: { type: 'string', description: 'Settlement timestamp (ISO 8601)' },
fee_cost: { type: 'string', description: 'Fees in fixed-point dollars' },
value: { type: 'number', description: 'Single yes contract payout in cents', optional: true },
} as const satisfies Record<string, OutputProperty>
/**
* Output definition for exchange announcement objects.
* @see https://docs.kalshi.com/api-reference/exchange/get-exchange-announcements
*/
export const KALSHI_ANNOUNCEMENT_OUTPUT_PROPERTIES = {
type: { type: 'string', description: 'Announcement severity (info, warning, error)' },
message: { type: 'string', description: 'Announcement message' },
delivery_time: { type: 'string', description: 'Delivery time (ISO 8601)' },
status: { type: 'string', description: 'Announcement status (active, inactive)' },
} as const satisfies Record<string, OutputProperty>
/**
* Pagination output properties
*/
export const KALSHI_PAGING_OUTPUT_PROPERTIES = {
cursor: { type: 'string', description: 'Cursor for fetching next page', optional: true },
} as const satisfies Record<string, OutputProperty>
/**
* Complete paging output definition
*/
export const KALSHI_PAGING_OUTPUT: OutputProperty = {
type: 'object',
description: 'Pagination information',
properties: KALSHI_PAGING_OUTPUT_PROPERTIES,
}
// Base params for authenticated endpoints
export interface KalshiAuthParams {
keyId: string // API Key ID
privateKey: string // RSA Private Key (PEM format)
}
// Pagination params
export interface KalshiPaginationParams {
limit?: string // 1-1000, default 100
cursor?: string // Pagination cursor
}
// Pagination info in response
export interface KalshiPagingInfo {
cursor?: string | null
}
// Generic response type
interface KalshiResponse<T> {
success: boolean
output: T & {
paging?: KalshiPagingInfo
metadata: {
operation: string
[key: string]: any
}
success: boolean
}
}
// Market type
export interface KalshiMarket {
ticker: string
event_ticker: string
market_type: string
title: string
subtitle?: string
yes_sub_title?: string
no_sub_title?: string
open_time: string
close_time: string
expiration_time: string
status: string
yes_bid: number
yes_ask: number
no_bid: number
no_ask: number
last_price: number
previous_yes_bid?: number
previous_yes_ask?: number
previous_price?: number
volume: number
volume_24h: number
liquidity?: number
open_interest?: number
result?: string
cap_strike?: number
floor_strike?: number
}
// Event type
export interface KalshiEvent {
event_ticker: string
series_ticker: string
sub_title?: string
title: string
mutually_exclusive: boolean
category: string
markets?: KalshiMarket[]
strike_date?: string
status?: string
}
// Balance type
interface KalshiBalance {
balance: number // In cents
portfolio_value: number // In cents
}
// Position type
export interface KalshiPosition {
ticker: string
event_ticker: string
event_title?: string
market_title?: string
position: number
market_exposure?: number
realized_pnl?: number
total_traded?: number
resting_orders_count?: number
}
// Order type
export interface KalshiOrder {
order_id: string
ticker: string
event_ticker: string
status: string
side: string
type: string
yes_price?: number
no_price?: number
action: string
count: number
remaining_count: number
created_time: string
expiration_time?: string
place_count?: number
decrease_count?: number
maker_fill_count?: number
taker_fill_count?: number
taker_fees?: number
}
// Orderbook type
interface KalshiOrderbookLevel {
price: number
quantity: number
}
export interface KalshiOrderbook {
yes: KalshiOrderbookLevel[]
no: KalshiOrderbookLevel[]
}
// Trade type
export interface KalshiTrade {
ticker: string
yes_price: number
no_price: number
count: number
created_time: string
taker_side: string
}
// Candlestick type
export interface KalshiCandlestick {
open_time: string
close_time: string
open: number
high: number
low: number
close: number
volume: number
}
// Fill type
export interface KalshiFill {
created_time: string
ticker: string
is_taker: boolean
side: string
yes_price: number
no_price: number
count: number
order_id: string
trade_id: string
}
// Settlement source type
interface KalshiSettlementSource {
name: string
url: string
}
// Series type
export interface KalshiSeries {
ticker: string
title: string
frequency: string
category: string
tags?: string[]
settlement_sources?: KalshiSettlementSource[]
contract_url?: string
contract_terms_url?: string
fee_type?: string // 'quadratic' | 'quadratic_with_maker_fees' | 'flat'
fee_multiplier?: number
additional_prohibitions?: string[]
product_metadata?: Record<string, unknown>
}
// Exchange status type
export interface KalshiExchangeStatus {
trading_active: boolean
exchange_active: boolean
}
// Helper function to build Kalshi API URLs
export function buildKalshiUrl(path: string): string {
return `${KALSHI_BASE_URL}${path}`
}
// Helper to normalize PEM key format
// Handles: literal \n strings, missing line breaks, various PEM formats
function normalizePemKey(privateKey: string): string {
let key = privateKey.trim()
// Convert literal \n strings to actual newlines
key = key.replace(/\\n/g, '\n')
// Extract the key type and base64 content
const beginMatch = key.match(/-----BEGIN ([A-Z\s]+)-----/)
const endMatch = key.match(/-----END ([A-Z\s]+)-----/)
if (beginMatch && endMatch) {
// Extract the key type (e.g., "RSA PRIVATE KEY" or "PRIVATE KEY")
const keyType = beginMatch[1]
// Extract base64 content between headers
const startIdx = key.indexOf('-----', key.indexOf('-----') + 5) + 5
const endIdx = key.lastIndexOf('-----END')
let base64Content = key.substring(startIdx, endIdx)
// Remove all whitespace from base64 content
base64Content = base64Content.replace(/\s/g, '')
// Reconstruct PEM with proper 64-character line breaks
const lines: string[] = []
for (let i = 0; i < base64Content.length; i += 64) {
lines.push(base64Content.substring(i, i + 64))
}
return `-----BEGIN ${keyType}-----\n${lines.join('\n')}\n-----END ${keyType}-----`
}
// No PEM headers found - assume raw base64, wrap in PKCS#8 format
const cleanKey = key.replace(/\s/g, '')
const lines: string[] = []
for (let i = 0; i < cleanKey.length; i += 64) {
lines.push(cleanKey.substring(i, i + 64))
}
return `-----BEGIN PRIVATE KEY-----\n${lines.join('\n')}\n-----END PRIVATE KEY-----`
}
// RSA-PSS signature generation for authenticated requests
// Kalshi requires RSA-PSS with SHA256, not plain PKCS#1 v1.5
export function generateKalshiSignature(
privateKey: string,
timestamp: string,
method: string,
path: string
): string {
// Sign: timestamp + method + path (without query params)
// Strip query params from path for signing
const pathWithoutQuery = path.split('?')[0]
const message = timestamp + method.toUpperCase() + pathWithoutQuery
// Normalize PEM key format (handles literal \n, missing line breaks, etc.)
const pemKey = normalizePemKey(privateKey)
// Use RSA-PSS padding with SHA256 (required by Kalshi API)
const signature = crypto.sign('sha256', Buffer.from(message, 'utf-8'), {
key: pemKey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST,
})
return signature.toString('base64')
}
// Build auth headers for authenticated requests
export function buildKalshiAuthHeaders(
keyId: string,
privateKey: string,
method: string,
path: string
): Record<string, string> {
const timestamp = Date.now().toString()
const signature = generateKalshiSignature(privateKey, timestamp, method, path)
return {
'KALSHI-ACCESS-KEY': keyId,
'KALSHI-ACCESS-TIMESTAMP': timestamp,
'KALSHI-ACCESS-SIGNATURE': signature,
'Content-Type': 'application/json',
}
}
// Helper function for consistent error handling
export function handleKalshiError(data: any, status: number, operation: string): never {
const errorMessage =
data.error?.message || data.error || data.message || data.detail || 'Unknown error'
throw new Error(`Kalshi ${operation} failed: ${errorMessage}`)
}