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, &params)?;
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, &params)?;
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, &params)?;
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}