kalshi_rust/events/mod.rs
1use super::Kalshi;
2use crate::kalshi_error::*;
3use crate::market::Event; // Import from market module
4use serde::{Deserialize, Serialize};
5
6impl Kalshi {
7 /// Retrieves a list of events from the Kalshi exchange based on specified criteria.
8 ///
9 /// This method fetches multiple events, allowing for filtering by status, series ticker,
10 /// and pagination. The events represent prediction markets that users can trade on.
11 ///
12 /// # Arguments
13 ///
14 /// * `limit` - An optional integer to limit the number of events returned.
15 /// * `cursor` - An optional string for pagination cursor.
16 /// * `status` - An optional string to filter events by their status (e.g., "open", "closed", "settled").
17 /// * `series_ticker` - An optional string to filter events by series ticker.
18 /// * `with_nested_markets` - An optional boolean to include nested markets in the response.
19 /// * `with_milestones` - An optional boolean to include related milestones.
20 /// * `min_close_ts` - An optional Unix timestamp to filter events with at least one market closing after this time.
21 ///
22 /// # Returns
23 ///
24 /// - `Ok((Option<String>, Vec<Event>))`: A tuple containing an optional pagination cursor
25 /// and a vector of `Event` objects on successful retrieval.
26 /// - `Err(KalshiError)`: An error if there is an issue with the request.
27 ///
28 /// # Example
29 ///
30 /// ```
31 /// // Assuming `kalshi_instance` is an instance of `Kalshi`
32 /// let (cursor, events) = kalshi_instance.get_events(
33 /// Some(10), None, Some("open".to_string()), None, Some(true), Some(false), None
34 /// ).await.unwrap();
35 /// ```
36 ///
37 #[allow(clippy::too_many_arguments)]
38 pub async fn get_events(
39 &self,
40 limit: Option<i64>,
41 cursor: Option<String>,
42 status: Option<String>,
43 series_ticker: Option<String>,
44 with_nested_markets: Option<bool>,
45 with_milestones: Option<bool>,
46 min_close_ts: Option<i64>,
47 ) -> Result<(Option<String>, Vec<Event>), KalshiError> {
48 let path = "/events";
49 let mut params = vec![];
50 add_param!(params, "limit", limit);
51 add_param!(params, "cursor", cursor);
52 add_param!(params, "status", status);
53 add_param!(params, "series_ticker", series_ticker);
54 add_param!(params, "with_nested_markets", with_nested_markets);
55 add_param!(params, "with_milestones", with_milestones);
56 add_param!(params, "min_close_ts", min_close_ts);
57
58 let url = format!("{}{}", self.base_url, path);
59 let final_url = reqwest::Url::parse_with_params(&url, ¶ms)?;
60 let res: EventListResponse = self.client.get(final_url).send().await?.json().await?;
61 Ok((res.cursor, res.events))
62 }
63
64 /// Retrieves detailed information about a specific event from the Kalshi exchange.
65 ///
66 /// This method fetches data for a single event identified by its event ticker.
67 /// The event represents a prediction market with associated markets that users can trade on.
68 ///
69 /// # Arguments
70 ///
71 /// * `event_ticker` - A string slice referencing the event's unique ticker identifier.
72 ///
73 /// # Returns
74 ///
75 /// - `Ok(Event)`: Detailed information about the specified event on successful retrieval.
76 /// - `Err(KalshiError)`: An error if there is an issue with the request.
77 ///
78 /// # Example
79 ///
80 /// ```
81 /// // Assuming `kalshi_instance` is an instance of `Kalshi`
82 /// let event_ticker = "SOME-EVENT-2024";
83 /// let event = kalshi_instance.get_event(event_ticker).await.unwrap();
84 /// ```
85 ///
86 pub async fn get_event(&self, event_ticker: &str) -> Result<Event, KalshiError> {
87 let path = format!("/events/{}", event_ticker);
88 self.signed_get(&path).await
89 }
90
91 /// Retrieves candlestick data aggregated across all markets in an event.
92 ///
93 /// This method provides event-level candlestick data by aggregating trading data
94 /// from all markets within the specified event.
95 ///
96 /// # Arguments
97 ///
98 /// * `series_ticker` - The series ticker containing the event.
99 /// * `event_ticker` - The event ticker to get candlestick data for.
100 /// * `start_ts` - Optional start timestamp (Unix timestamp).
101 /// * `end_ts` - Optional end timestamp (Unix timestamp).
102 /// * `period_interval` - Optional candlestick interval (e.g., "1m", "5m", "1h", "1d").
103 ///
104 /// # Returns
105 ///
106 /// - `Ok(Vec<Candlestick>)`: A vector of candlestick data on successful retrieval.
107 /// - `Err(KalshiError)`: An error if there is an issue with the request.
108 ///
109 /// # Example
110 ///
111 /// ```
112 /// // Assuming `kalshi_instance` is an instance of `Kalshi`
113 /// let candlesticks = kalshi_instance.get_event_candlesticks(
114 /// "SERIES", "EVENT-TICKER", None, None, Some("1h".to_string())
115 /// ).await.unwrap();
116 /// ```
117 ///
118 pub async fn get_event_candlesticks(
119 &self,
120 series_ticker: &str,
121 event_ticker: &str,
122 start_ts: Option<i64>,
123 end_ts: Option<i64>,
124 period_interval: Option<String>,
125 ) -> Result<Vec<Candlestick>, KalshiError> {
126 let path = format!(
127 "/series/{}/events/{}/candlesticks",
128 series_ticker, event_ticker
129 );
130 let mut params = vec![];
131 add_param!(params, "start_ts", start_ts);
132 add_param!(params, "end_ts", end_ts);
133 add_param!(params, "period_interval", period_interval);
134
135 let url = format!("{}{}", self.base_url, path);
136 let final_url = reqwest::Url::parse_with_params(&url, ¶ms)?;
137 let res: CandlestickResponse = self.client.get(final_url).send().await?.json().await?;
138 Ok(res.candlesticks)
139 }
140
141 /// Retrieves metadata for a specific event.
142 ///
143 /// This method provides additional metadata and information about an event
144 /// that may not be included in the standard event details.
145 ///
146 /// # Arguments
147 ///
148 /// * `event_ticker` - The event ticker to get metadata for.
149 ///
150 /// # Returns
151 ///
152 /// - `Ok(EventMetadata)`: The event metadata on successful retrieval.
153 /// - `Err(KalshiError)`: An error if there is an issue with the request.
154 ///
155 /// # Example
156 ///
157 /// ```
158 /// // Assuming `kalshi_instance` is an instance of `Kalshi`
159 /// let metadata = kalshi_instance.get_event_metadata("EVENT-TICKER").await.unwrap();
160 /// ```
161 ///
162 pub async fn get_event_metadata(
163 &self,
164 event_ticker: &str,
165 ) -> Result<EventMetadata, KalshiError> {
166 let path = format!("/events/{}/metadata", event_ticker);
167 self.signed_get(&path).await
168 }
169
170 /// Retrieves forecast percentile history for a specific event.
171 ///
172 /// This method provides historical percentile data for event forecasts,
173 /// useful for analyzing prediction accuracy and trends over time.
174 ///
175 /// # Arguments
176 ///
177 /// * `event_ticker` - The event ticker to get forecast history for.
178 ///
179 /// # Returns
180 ///
181 /// - `Ok(ForecastPercentileHistory)`: The forecast percentile history data on successful retrieval.
182 /// - `Err(KalshiError)`: An error if there is an issue with the request.
183 ///
184 /// # Example
185 ///
186 /// ```
187 /// // Assuming `kalshi_instance` is an instance of `Kalshi`
188 /// let history = kalshi_instance.get_event_forecast_percentile_history("EVENT-TICKER").await.unwrap();
189 /// ```
190 ///
191 pub async fn get_event_forecast_percentile_history(
192 &self,
193 event_ticker: &str,
194 ) -> Result<ForecastPercentileHistory, KalshiError> {
195 let path = format!("/events/{}/forecast_percentile_history", event_ticker);
196 self.signed_get(&path).await
197 }
198
199 /// Retrieves multivariate events (combo markets) with optional filtering.
200 ///
201 /// Multivariate events are special event types that allow trading on
202 /// combinations of outcomes across multiple markets.
203 ///
204 /// # Arguments
205 ///
206 /// * `limit` - Number of results per page (default 100, max 200)
207 /// * `cursor` - Pagination cursor for fetching subsequent pages
208 /// * `series_ticker` - Filter by series ticker (cannot use with collection_ticker)
209 /// * `collection_ticker` - Filter by collection ticker (cannot use with series_ticker)
210 /// * `with_nested_markets` - Include nested markets in response (default false)
211 ///
212 /// # Returns
213 ///
214 /// - `Ok((Option<String>, Vec<Event>))`: A tuple containing an optional pagination cursor
215 /// and a vector of `Event` objects on successful retrieval.
216 /// - `Err(KalshiError)`: An error if there is an issue with the request.
217 ///
218 /// # Errors
219 ///
220 /// Returns `KalshiError::UserInputError` if both `series_ticker` and `collection_ticker`
221 /// are provided, as these filters are mutually exclusive.
222 ///
223 /// # Example
224 ///
225 /// ```
226 /// // Assuming `kalshi_instance` is an instance of `Kalshi`
227 /// let (cursor, events) = kalshi_instance.get_multivariate_events(
228 /// Some(10), None, None, None, Some(true)
229 /// ).await.unwrap();
230 /// ```
231 ///
232 pub async fn get_multivariate_events(
233 &self,
234 limit: Option<i32>,
235 cursor: Option<String>,
236 series_ticker: Option<String>,
237 collection_ticker: Option<String>,
238 with_nested_markets: Option<bool>,
239 ) -> Result<(Option<String>, Vec<Event>), KalshiError> {
240 // Validate: cannot use both series_ticker and collection_ticker
241 if series_ticker.is_some() && collection_ticker.is_some() {
242 return Err(KalshiError::UserInputError(
243 "Cannot use both series_ticker and collection_ticker - these filters are mutually exclusive".to_string()
244 ));
245 }
246
247 let mut params: Vec<(&str, String)> = Vec::with_capacity(5);
248 add_param!(params, "limit", limit);
249 add_param!(params, "cursor", cursor);
250 add_param!(params, "series_ticker", series_ticker);
251 add_param!(params, "collection_ticker", collection_ticker);
252 add_param!(params, "with_nested_markets", with_nested_markets);
253
254 let url = format!("{}/events/multivariate", self.base_url);
255 let final_url = reqwest::Url::parse_with_params(&url, ¶ms)?;
256 let res: EventListResponse = self.client.get(final_url).send().await?.json().await?;
257 Ok((res.cursor, res.events))
258 }
259}
260
261// -------- Response wrappers --------
262
263#[derive(Debug, Deserialize)]
264struct EventListResponse {
265 cursor: Option<String>,
266 events: Vec<Event>,
267}
268
269#[derive(Debug, Deserialize)]
270#[allow(dead_code)] // Used by serde for deserialization
271struct SingleEventResponse {
272 event: Event,
273}
274
275#[derive(Debug, Deserialize)]
276struct CandlestickResponse {
277 candlesticks: Vec<Candlestick>,
278}
279
280// -------- Public models --------
281
282/// Represents candlestick data for event-level aggregated trading.
283#[derive(Debug, Deserialize, Serialize)]
284pub struct Candlestick {
285 /// The timestamp for this candlestick period.
286 pub ts: String,
287 /// Opening price for the period.
288 pub open: Option<i64>,
289 /// Highest price during the period.
290 pub high: Option<i64>,
291 /// Lowest price during the period.
292 pub low: Option<i64>,
293 /// Closing price for the period.
294 pub close: Option<i64>,
295 /// Trading volume during the period.
296 pub volume: Option<i64>,
297}
298
299/// Represents additional metadata for an event.
300#[derive(Debug, Deserialize, Serialize)]
301pub struct EventMetadata {
302 /// Metadata fields as key-value pairs.
303 #[serde(flatten)]
304 pub fields: std::collections::HashMap<String, serde_json::Value>,
305}
306
307/// Represents forecast percentile history for an event.
308#[derive(Debug, Deserialize, Serialize)]
309pub struct ForecastPercentileHistory {
310 /// Historical forecast data points.
311 pub history: Vec<ForecastDataPoint>,
312}
313
314/// Represents a single forecast data point.
315#[derive(Debug, Deserialize, Serialize)]
316pub struct ForecastDataPoint {
317 /// The timestamp for this forecast.
318 pub ts: String,
319 /// Forecast percentile values.
320 pub percentiles: std::collections::HashMap<String, f64>,
321}