Skip to content

Commit e69c69a

Browse files
committed
feat: add batch 6 API updates - tighten IncentiveProgram fields, market_id on WS types, send_initial_snapshot
- Make 8 IncentiveProgram fields non-optional to match official OpenAPI spec - Add market_id to OrderbookSnapshotData, OrderbookDeltaData, TickerData - Add yes_ask_dollars field to TickerData - Add send_initial_snapshot subscribe option - Fix period_reward doc comment (cents, not centi-cents)
1 parent 74881ab commit e69c69a

File tree

13 files changed

+142
-100
lines changed

13 files changed

+142
-100
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8989
- `subaccount` filter on `ListRfqsParams` for per-subaccount RFQ queries.
9090
- `subaccount` filter on `GetOrderGroupsParams` for per-subaccount order group
9191
queries. New `get_order_group_for_subaccount()` method on `KalshiClient`.
92-
- `market_id` and `target_size_fp` fields on `IncentiveProgram`.
92+
- `market_id` (required) and `target_size_fp` (optional) fields on `IncentiveProgram`.
93+
- Tightened `IncentiveProgram` struct: `id`, `market_id`, `market_ticker`,
94+
`incentive_type`, `start_date`, `end_date`, `period_reward`, and `paid_out`
95+
are now non-optional to match the official Kalshi OpenAPI spec.
9396

9497
### Fixed
9598

examples/batch_orders.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
1212
use kalshi_trade_rs::{
1313
Action, BatchCancelOrderItem, BatchCancelOrdersRequest, BatchCreateOrdersRequest,
14-
CreateOrderRequest, GetMarketsParams, KalshiClient, KalshiConfig, MarketFilterStatus,
15-
Side, TimeInForce, cents_to_dollars,
14+
CreateOrderRequest, GetMarketsParams, KalshiClient, KalshiConfig, MarketFilterStatus, Side,
15+
TimeInForce, cents_to_dollars,
1616
};
1717
use std::time::{SystemTime, UNIX_EPOCH};
1818

examples/exchange_status.rs

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -215,30 +215,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
215215
println!();
216216

217217
for program in programs.incentive_programs.iter().take(10) {
218-
let incentive_type = program.incentive_type.as_deref().unwrap_or("unknown");
219-
let market = program.market_ticker.as_deref().unwrap_or("(no market)");
220-
let paid = program.paid_out.unwrap_or(false);
221-
222218
println!(
223219
" {} [{}] {}",
224-
market,
225-
incentive_type,
226-
if paid { "(paid)" } else { "" }
220+
program.market_ticker,
221+
program.incentive_type,
222+
if program.paid_out { "(paid)" } else { "" }
227223
);
228224

229-
if let Some(reward) = program.period_reward {
230-
// Convert cents to dollars
231-
println!(" Reward: ${:.2}", reward as f64 / 100.0);
232-
}
233-
234-
if let Some(start) = &program.start_date {
235-
print!(" Period: {}", start);
236-
if let Some(end) = &program.end_date {
237-
println!(" to {}", end);
238-
} else {
239-
println!(" (ongoing)");
240-
}
241-
}
225+
// Convert cents to dollars
226+
println!(" Reward: ${:.2}", program.period_reward as f64 / 100.0);
227+
println!(" Period: {} to {}", program.start_date, program.end_date);
242228

243229
if let Some(discount) = program.discount_factor_bps {
244230
println!(" Discount: {} bps", discount);
@@ -265,12 +251,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
265251
volume_programs.incentive_programs.len()
266252
);
267253
for program in volume_programs.incentive_programs.iter().take(5) {
268-
let market = program.market_ticker.as_deref().unwrap_or("(unknown)");
269-
let reward = program
270-
.period_reward
271-
.map(|r| format!("${:.2}", r as f64 / 100.0))
272-
.unwrap_or_default();
273-
println!(" - {} {}", market, reward);
254+
let reward = format!("${:.2}", program.period_reward as f64 / 100.0);
255+
println!(" - {} {}", program.market_ticker, reward);
274256
}
275257
}
276258
println!();

src/client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,7 +2061,7 @@ impl KalshiClient {
20612061
/// ```ignore
20622062
/// let programs = client.get_incentive_programs().await?;
20632063
/// for program in programs.incentive_programs {
2064-
/// println!("{}: {:?}", program.name.unwrap_or_default(), program.status);
2064+
/// println!("{} [{}]: reward={}", program.market_ticker, program.incentive_type, program.period_reward);
20652065
/// }
20662066
/// ```
20672067
pub async fn get_incentive_programs(&self) -> Result<IncentiveProgramsResponse> {
@@ -2083,7 +2083,7 @@ impl KalshiClient {
20832083
/// .limit(50);
20842084
/// let programs = client.get_incentive_programs_with_params(params).await?;
20852085
/// for program in programs.incentive_programs {
2086-
/// println!("{}: {:?}", program.name.unwrap_or_default(), program.status);
2086+
/// println!("{} [{}]: reward={}", program.market_ticker, program.incentive_type, program.period_reward);
20872087
/// }
20882088
/// ```
20892089
pub async fn get_incentive_programs_with_params(

src/models/common.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ impl fmt::Display for Action {
4242
#[serde(rename_all = "lowercase")]
4343
pub enum OrderType {
4444
Limit,
45-
#[deprecated(note = "Market orders are no longer supported by the Kalshi API. Use limit orders instead.")]
45+
#[deprecated(
46+
note = "Market orders are no longer supported by the Kalshi API. Use limit orders instead."
47+
)]
4648
Market,
4749
}
4850

src/models/incentive_program.rs

Lines changed: 14 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,30 @@ use super::query::QueryBuilder;
1010
#[derive(Debug, Clone, Serialize, Deserialize)]
1111
pub struct IncentiveProgram {
1212
/// The unique identifier for the incentive program.
13-
#[serde(default)]
14-
pub id: Option<String>,
15-
/// The incentive type (e.g., "volume", "liquidity").
16-
#[serde(default)]
17-
pub incentive_type: Option<String>,
13+
pub id: String,
14+
/// The unique identifier of the market associated with this incentive program.
15+
pub market_id: String,
1816
/// The associated market ticker.
19-
#[serde(default)]
20-
pub market_ticker: Option<String>,
17+
pub market_ticker: String,
18+
/// The incentive type (e.g., "volume", "liquidity").
19+
pub incentive_type: String,
2120
/// The start date of the incentive program (RFC3339 timestamp).
22-
#[serde(default)]
23-
pub start_date: Option<String>,
21+
pub start_date: String,
2422
/// The end date of the incentive program (RFC3339 timestamp).
25-
#[serde(default)]
26-
pub end_date: Option<String>,
23+
pub end_date: String,
2724
/// The reward amount for the period (in cents).
28-
#[serde(default)]
29-
pub period_reward: Option<i64>,
25+
pub period_reward: i64,
26+
/// Whether the program has been paid out.
27+
pub paid_out: bool,
3028
/// Discount factor in basis points.
31-
#[serde(default)]
29+
#[serde(default, skip_serializing_if = "Option::is_none")]
3230
pub discount_factor_bps: Option<i64>,
33-
/// Whether the program has been paid out.
34-
#[serde(default)]
35-
pub paid_out: Option<bool>,
3631
/// Target size for the program.
37-
#[serde(default)]
32+
#[serde(default, skip_serializing_if = "Option::is_none")]
3833
pub target_size: Option<i64>,
3934
/// Target size (fixed-point decimal string).
40-
#[serde(default)]
35+
#[serde(default, skip_serializing_if = "Option::is_none")]
4136
pub target_size_fp: Option<String>,
42-
/// The market ID associated with this incentive program.
43-
#[serde(default)]
44-
pub market_id: Option<String>,
4537
}
4638

4739
/// Response from GET /incentive_programs.
@@ -122,33 +114,3 @@ impl GetIncentiveProgramsParams {
122114
qb.build()
123115
}
124116
}
125-
126-
#[cfg(test)]
127-
mod tests {
128-
use super::*;
129-
130-
#[test]
131-
fn test_deserialize_empty_response() {
132-
let json = r#"{"incentive_programs": []}"#;
133-
let response: IncentiveProgramsResponse = serde_json::from_str(json).unwrap();
134-
assert!(response.incentive_programs.is_empty());
135-
}
136-
137-
#[test]
138-
fn test_deserialize_program() {
139-
let json = r#"{
140-
"incentive_programs": [{
141-
"id": "prog_123",
142-
"incentive_type": "volume",
143-
"market_ticker": "KXTEST",
144-
"period_reward": 1000000
145-
}]
146-
}"#;
147-
let response: IncentiveProgramsResponse = serde_json::from_str(json).unwrap();
148-
assert_eq!(response.incentive_programs.len(), 1);
149-
assert_eq!(
150-
response.incentive_programs[0].id,
151-
Some("prog_123".to_string())
152-
);
153-
}
154-
}

src/orderbook/aggregator.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ mod tests {
437437

438438
let snapshot = OrderbookSnapshotData {
439439
market_ticker: "TEST".to_string(),
440+
market_id: None,
440441
yes: Some(vec![[45, 100], [44, 200]]),
441442
yes_dollars: None,
442443
no: Some(vec![[55, 150]]),
@@ -457,6 +458,7 @@ mod tests {
457458
// First add a snapshot
458459
let snapshot = OrderbookSnapshotData {
459460
market_ticker: "TEST".to_string(),
461+
market_id: None,
460462
yes: Some(vec![[45, 100]]),
461463
yes_dollars: None,
462464
no: Some(vec![[55, 150]]),
@@ -467,6 +469,7 @@ mod tests {
467469
// Then apply a delta
468470
let delta = OrderbookDeltaData {
469471
market_ticker: "TEST".to_string(),
472+
market_id: None,
470473
price: 46,
471474
delta: 50,
472475
side: Side::Yes,
@@ -486,6 +489,7 @@ mod tests {
486489

487490
let snapshot = OrderbookSnapshotData {
488491
market_ticker: "TEST".to_string(),
492+
market_id: None,
489493
yes: Some(vec![[45, 100]]),
490494
yes_dollars: None,
491495
no: None,
@@ -506,6 +510,7 @@ mod tests {
506510

507511
let snapshot1 = OrderbookSnapshotData {
508512
market_ticker: "TEST1".to_string(),
513+
market_id: None,
509514
yes: Some(vec![[45, 100]]),
510515
yes_dollars: None,
511516
no: None,
@@ -515,6 +520,7 @@ mod tests {
515520

516521
let snapshot2 = OrderbookSnapshotData {
517522
market_ticker: "TEST2".to_string(),
523+
market_id: None,
518524
yes: Some(vec![[50, 200]]),
519525
yes_dollars: None,
520526
no: None,
@@ -537,6 +543,7 @@ mod tests {
537543

538544
let snapshot = OrderbookSnapshotData {
539545
market_ticker: "TEST".to_string(),
546+
market_id: None,
540547
yes: Some(vec![[45, 100], [44, 200]]),
541548
yes_dollars: None,
542549
no: Some(vec![[53, 150]]), // YES ask at 47
@@ -573,6 +580,7 @@ mod tests {
573580

574581
let snapshot = OrderbookSnapshotData {
575582
market_ticker: "TEST".to_string(),
583+
market_id: None,
576584
yes: Some(vec![[45, 100], [44, 200]]),
577585
yes_dollars: None,
578586
no: Some(vec![[55, 150]]),
@@ -596,6 +604,7 @@ mod tests {
596604
// Snapshot creates initial book
597605
let snapshot = OrderbookSnapshotData {
598606
market_ticker: "TEST".to_string(),
607+
market_id: None,
599608
yes: Some(vec![[45, 100], [44, 200]]),
600609
yes_dollars: None,
601610
no: Some(vec![[55, 150]]),
@@ -607,6 +616,7 @@ mod tests {
607616
agg.handle_delta(
608617
&OrderbookDeltaData {
609618
market_ticker: "TEST".to_string(),
619+
market_id: None,
610620
price: 46,
611621
delta: 75,
612622
side: Side::Yes,
@@ -619,6 +629,7 @@ mod tests {
619629
agg.handle_delta(
620630
&OrderbookDeltaData {
621631
market_ticker: "TEST".to_string(),
632+
market_id: None,
622633
price: 44,
623634
delta: -200,
624635
side: Side::Yes,
@@ -647,6 +658,7 @@ mod tests {
647658
agg.handle_delta(
648659
&OrderbookDeltaData {
649660
market_ticker: "TEST".to_string(),
661+
market_id: None,
650662
price: 45,
651663
delta: 100,
652664
side: Side::Yes,
@@ -669,6 +681,7 @@ mod tests {
669681

670682
let snapshot = OrderbookSnapshotData {
671683
market_ticker: "TEST".to_string(),
684+
market_id: None,
672685
yes: Some(vec![[45, 100]]),
673686
yes_dollars: None,
674687
no: None,

0 commit comments

Comments
 (0)