Skip to content

Commit 45a1a82

Browse files
committed
docs: verify remaining API endpoints and fix examples
- Add get_event_candlesticks and get_event_forecast_percentile_history verification to examples/events.rs - Add get_settlements verification to examples/portfolio.rs - Add decrease_order verification to examples/trading.rs - Remove non-existent update_order_group from README (75 endpoints total) - Update README status markers for newly verified endpoints - Add error handling for forecast_percentile_history (not all events support it) - Add delay in trading.rs for demo environment consistency - Fix portfolio.rs Settlement field access
1 parent ff40bbf commit 45a1a82

File tree

7 files changed

+444
-1030
lines changed

7 files changed

+444
-1030
lines changed

examples/events.rs

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@
66
//!
77
//! Run with: cargo run --example events
88
9+
use std::time::{SystemTime, UNIX_EPOCH};
10+
911
use kalshi_trade_rs::{
1012
EventStatus, GetEventParams, GetEventsParams, GetSeriesParams, KalshiClient, KalshiConfig,
13+
models::{
14+
CandlestickPeriod, ForecastPeriod, GetEventCandlesticksParams,
15+
GetEventForecastPercentileHistoryParams,
16+
},
1117
};
1218

1319
#[tokio::main]
@@ -22,15 +28,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
2228

2329
// 1. Get Events (default parameters)
2430
println!("=== Get Events (default) ===");
31+
2532
let events = client.get_events().await?;
33+
2634
println!("Total events returned: {}", events.events.len());
35+
2736
if let Some(cursor) = &events.cursor {
2837
println!("Next cursor: {}...", &cursor[..cursor.len().min(20)]);
2938
}
39+
3040
println!();
3141

3242
// 2. Get Events with filters
3343
println!("=== Get Open Events (limit 5) ===");
44+
3445
let params = GetEventsParams::new().status(EventStatus::Open).limit(5);
3546
let open_events = client.get_events_with_params(params).await?;
3647

@@ -48,6 +59,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4859

4960
// 3. Get Events with nested markets
5061
println!("=== Get Events with Nested Markets ===");
62+
5163
let params = GetEventsParams::new()
5264
.status(EventStatus::Open)
5365
.with_nested_markets(true)
@@ -245,11 +257,117 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
245257
}
246258
println!();
247259

260+
// ==========================================================================
261+
// Event Candlesticks & Forecast History
262+
// ==========================================================================
263+
264+
// 10. Get event candlesticks (aggregated across all markets in an event)
265+
if let Some(first_event) = open_events.events.first() {
266+
let event_ticker = &first_event.event_ticker;
267+
let series_ticker = &first_event.series_ticker;
268+
269+
println!("=== Event Candlesticks: {} ===", event_ticker);
270+
271+
let now = SystemTime::now()
272+
.duration_since(UNIX_EPOCH)
273+
.unwrap()
274+
.as_secs() as i64;
275+
let one_day_ago = now - 86400;
276+
277+
let params = GetEventCandlesticksParams::new(one_day_ago, now, CandlestickPeriod::OneHour);
278+
let candles = client
279+
.get_event_candlesticks(series_ticker, event_ticker, params)
280+
.await?;
281+
282+
println!("Markets in event: {}", candles.market_tickers.len());
283+
for ticker in candles.market_tickers.iter().take(5) {
284+
println!(" - {}", ticker);
285+
}
286+
if candles.market_tickers.len() > 5 {
287+
println!(" ... and {} more", candles.market_tickers.len() - 5);
288+
}
289+
290+
println!(
291+
"Market candlestick arrays: {}",
292+
candles.market_candlesticks.len()
293+
);
294+
for (i, market_candles) in candles.market_candlesticks.iter().enumerate().take(3) {
295+
println!(
296+
" Market {} has {} candles",
297+
candles.market_tickers.get(i).unwrap_or(&"?".to_string()),
298+
market_candles.len()
299+
);
300+
}
301+
if let Some(adjusted) = candles.adjusted_end_ts {
302+
println!("Adjusted end timestamp: {}", adjusted);
303+
}
304+
println!();
305+
306+
// 11. Get event forecast percentile history
307+
// Note: This endpoint only works for events with numeric/forecast data.
308+
// Many events (like yes/no binary events) don't support this endpoint.
309+
println!(
310+
"=== Event Forecast Percentile History: {} ===",
311+
event_ticker
312+
);
313+
314+
// Request median (50th percentile = 5000) and quartiles (25th = 2500, 75th = 7500)
315+
let percentiles = vec![2500, 5000, 7500];
316+
let params = GetEventForecastPercentileHistoryParams::new(
317+
percentiles.clone(),
318+
one_day_ago,
319+
now,
320+
ForecastPeriod::OneHour,
321+
);
322+
323+
match client
324+
.get_event_forecast_percentile_history(series_ticker, event_ticker, params)
325+
.await
326+
{
327+
Ok(forecast) => {
328+
println!(
329+
"Forecast history points: {}",
330+
forecast.forecast_history.len()
331+
);
332+
333+
for point in forecast.forecast_history.iter().take(3) {
334+
println!(
335+
" Event: {} | ts: {} | period: {}min",
336+
point.event_ticker, point.end_period_ts, point.period_interval
337+
);
338+
for pp in &point.percentile_points {
339+
let formatted = pp.formatted_forecast.as_deref().unwrap_or("-");
340+
let numerical = pp.numerical_forecast.map(|v| format!("{:.2}", v));
341+
println!(
342+
" {}th percentile: {} ({})",
343+
pp.percentile / 100,
344+
formatted,
345+
numerical.as_deref().unwrap_or("-")
346+
);
347+
}
348+
}
349+
if forecast.forecast_history.len() > 3 {
350+
println!(
351+
" ... and {} more points",
352+
forecast.forecast_history.len() - 3
353+
);
354+
}
355+
}
356+
Err(e) => {
357+
println!(
358+
" (Forecast history not available for this event type: {})",
359+
e
360+
);
361+
}
362+
}
363+
println!();
364+
}
365+
248366
// ==========================================================================
249367
// Series API
250368
// ==========================================================================
251369

252-
// 10. Get list of series
370+
// 12. Get list of series
253371
println!("=== Get Series List ===");
254372
let series_list = client.get_series_list().await?;
255373
println!("Total series returned: {}", series_list.series.len());
@@ -265,7 +383,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
265383
}
266384
println!();
267385

268-
// 11. Get series list with params
386+
// 13. Get series list with params
269387
println!("=== Get Series List with Params ===");
270388
let params = GetSeriesParams::new().include_volume(true);
271389
let series_with_volume = client.get_series_list_with_params(params).await?;
@@ -282,7 +400,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
282400
}
283401
println!();
284402

285-
// 12. Get a specific series
403+
// 14. Get a specific series
286404
if let Some(first_series) = series_list.series.first() {
287405
let series_ticker = &first_series.ticker;
288406

examples/portfolio.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//! Portfolio API example
22
//!
3-
//! Demonstrates all Portfolio API methods including balance, positions, fills, and orders.
3+
//! Demonstrates all Portfolio API methods including balance, positions, fills, orders,
4+
//! and settlements.
45
//!
56
//! Run with: cargo run --example portfolio
67
78
use kalshi_trade_rs::{
8-
GetFillsParams, GetOrdersParams, GetPositionsParams, KalshiClient, KalshiConfig, OrderStatus,
9-
cents_to_dollars,
9+
GetFillsParams, GetOrdersParams, GetPositionsParams, GetSettlementsParams, KalshiClient,
10+
KalshiConfig, OrderStatus, cents_to_dollars,
1011
};
1112

1213
#[tokio::main]
@@ -15,23 +16,30 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
1516

1617
// Initialize client
1718
let config = KalshiConfig::from_env()?;
19+
1820
println!("Connected to {:?} environment\n", config.environment);
1921

2022
let client = KalshiClient::new(config)?;
2123

2224
// 1. Get Balance
2325
println!("=== Balance ===");
26+
2427
let balance = client.get_balance().await?;
28+
2529
println!("Available: ${:.2}", cents_to_dollars(balance.balance));
30+
2631
println!(
2732
"Portfolio Value: ${:.2}",
2833
cents_to_dollars(balance.portfolio_value)
2934
);
35+
3036
println!();
3137

3238
// 2. Get Positions
3339
println!("=== Positions ===");
40+
3441
let positions = client.get_positions().await?;
42+
3543
println!(
3644
"Market positions: {}, Event positions: {}",
3745
positions.market_positions.len(),
@@ -40,6 +48,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4048

4149
for pos in positions.market_positions.iter().take(5) {
4250
let side = if pos.position > 0 { "YES" } else { "NO" };
51+
4352
println!(
4453
" {} {} {} | exposure: ${:.2} | realized P&L: ${:.2}",
4554
pos.ticker,
@@ -49,25 +58,32 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4958
cents_to_dollars(pos.realized_pnl)
5059
);
5160
}
61+
5262
if positions.market_positions.len() > 5 {
5363
println!(" ... and {} more", positions.market_positions.len() - 5);
5464
}
65+
5566
println!();
5667

5768
// 3. Get Positions with params (filter example)
5869
println!("=== Positions (with limit) ===");
70+
5971
let params = GetPositionsParams::new().limit(3);
6072
let limited_positions = client.get_positions_with_params(params).await?;
73+
6174
println!(
6275
"Fetched {} positions (limited to 3)",
6376
limited_positions.market_positions.len()
6477
);
78+
6579
println!();
6680

6781
// 4. Get Fills
6882
println!("=== Recent Fills ===");
83+
6984
let params = GetFillsParams::new().limit(5);
7085
let fills = client.get_fills_with_params(params).await?;
86+
7187
println!("Recent fills: {}", fills.fills.len());
7288

7389
for fill in &fills.fills {
@@ -85,7 +101,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
85101

86102
// 5. Get Orders
87103
println!("=== Orders ===");
104+
88105
let orders = client.get_orders().await?;
106+
89107
println!("Total orders: {}", orders.orders.len());
90108

91109
// Count by status
@@ -104,6 +122,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
104122
.iter()
105123
.filter(|o| o.status == OrderStatus::Canceled)
106124
.count();
125+
107126
println!(
108127
" Resting: {}, Executed: {}, Canceled: {}",
109128
resting, executed, canceled
@@ -112,6 +131,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
112131
// Show resting orders
113132
if resting > 0 {
114133
println!("\nResting orders:");
134+
115135
let params = GetOrdersParams::new().status(OrderStatus::Resting).limit(5);
116136
let resting_orders = client.get_orders_with_params(params).await?;
117137

@@ -129,6 +149,38 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
129149
}
130150
}
131151

152+
println!();
153+
154+
// 6. Get Settlements
155+
println!("=== Settlements ===");
156+
157+
let params = GetSettlementsParams::new().limit(10);
158+
let settlements = client.get_settlements_with_params(params).await?;
159+
160+
println!("Recent settlements: {}", settlements.settlements.len());
161+
162+
for settlement in settlements.settlements.iter().take(5) {
163+
let ticker = &settlement.ticker;
164+
let pnl = settlement.revenue;
165+
let result = format!("{:?}", settlement.market_result);
166+
167+
println!(
168+
" {} | result: {} | P&L: ${:.2}",
169+
ticker,
170+
result,
171+
cents_to_dollars(pnl)
172+
);
173+
}
174+
175+
if settlements.settlements.len() > 5 {
176+
println!(" ... and {} more", settlements.settlements.len() - 5);
177+
}
178+
179+
if settlements.settlements.is_empty() {
180+
println!(" (No settlements yet - markets must settle first)");
181+
}
182+
132183
println!("\n=== Done ===");
184+
133185
Ok(())
134186
}

0 commit comments

Comments
 (0)