use super::Kalshi;
use crate::kalshi_error::*;
use serde::{Deserialize, Serialize};
impl Kalshi {
pub async fn get_exchange_status(&self) -> Result<ExchangeStatus, KalshiError> {
let url = format!("{}/exchange/status", self.base_url);
Ok(self.client.get(&url).send().await?.json().await?)
}
pub async fn get_exchange_schedule(&self) -> Result<ExchangeSchedule, KalshiError> {
let url = format!("{}/exchange/schedule", self.base_url);
let res: ExchangeScheduleResponse = self.client.get(&url).send().await?.json().await?;
Ok(res.schedule)
}
pub async fn get_exchange_announcements(
&self,
limit: Option<i64>,
cursor: Option<String>,
) -> Result<(Option<String>, Vec<ExchangeAnnouncement>), KalshiError> {
let url = format!("{}/exchange/announcements", self.base_url);
let mut params = vec![];
add_param!(params, "limit", limit);
add_param!(params, "cursor", cursor);
let final_url = reqwest::Url::parse_with_params(&url, ¶ms)?;
let res: ExchangeAnnouncementsResponse =
self.client.get(final_url).send().await?.json().await?;
Ok((res.cursor, res.announcements))
}
pub async fn get_user_data_timestamp(&self) -> Result<UserDataTimestamp, KalshiError> {
let url = format!("{}/exchange/user_data_timestamp", self.base_url);
Ok(self.client.get(&url).send().await?.json().await?)
}
pub async fn check_exchange_active_with_backoff(
&self,
max_attempts: u32,
base_delay_secs: f64,
max_delay_secs: f64,
) -> Result<(), KalshiError> {
use std::process;
use tokio::time::{sleep, Duration};
for attempt in 1..=max_attempts {
match self.get_exchange_status().await {
Ok(status) => {
if status.trading_active && status.exchange_active {
println!("Exchange is active (attempt {}/{}", attempt, max_attempts);
return Ok(());
}
if attempt < max_attempts {
let delay_secs = (base_delay_secs * (2.0_f64.powi((attempt - 1) as i32)))
.min(max_delay_secs);
println!(
"Exchange not active (attempt {}/{}). Waiting {:.1} seconds before retry...",
attempt, max_attempts, delay_secs
);
sleep(Duration::from_secs_f64(delay_secs)).await;
} else {
println!("Exchange not active after {} attempts", max_attempts);
}
}
Err(e) => {
if attempt < max_attempts {
let delay_secs = (base_delay_secs * (2.0_f64.powi((attempt - 1) as i32)))
.min(max_delay_secs);
println!(
"Error checking exchange status (attempt {}/{}): {}. Waiting {:.1} seconds before retry...",
attempt, max_attempts, e, delay_secs
);
sleep(Duration::from_secs_f64(delay_secs)).await;
} else {
println!(
"Failed to check exchange status after {} attempts: {}",
max_attempts, e
);
}
}
}
}
println!("Exiting as exchange is not active after all retry attempts");
process::exit(1);
}
pub async fn check_exchange_active(&self) -> Result<(), KalshiError> {
self.check_exchange_active_with_backoff(5, 30.0, 300.0)
.await
}
pub async fn get_series_fee_changes(
&self,
series_ticker: Option<String>,
) -> Result<Vec<SeriesFeeChange>, KalshiError> {
let path = "/series/fee_changes";
let mut params = vec![];
add_param!(params, "series_ticker", series_ticker);
let url = format!("{}{}", self.base_url, path);
let final_url = reqwest::Url::parse_with_params(&url, ¶ms)?;
let res: SeriesFeeChangesResponse = self.client.get(final_url).send().await?.json().await?;
Ok(res.fee_changes)
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ExchangeStatus {
pub trading_active: bool,
pub exchange_active: bool,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ExchangeSchedule {
pub standard_hours: Vec<StandardHours>,
pub maintenance_windows: Vec<MaintenanceWindow>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ExchangeAnnouncement {
pub message: String,
pub ts: String,
pub status: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct UserDataTimestamp {
pub last_validated_ts: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct MaintenanceWindow {
pub start_datetime: String,
pub end_datetime: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct DaySchedule {
pub open_time: String,
pub close_time: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct StandardHours {
pub start_time: String,
pub end_time: String,
#[serde(default)]
pub monday: Vec<DaySchedule>,
#[serde(default)]
pub tuesday: Vec<DaySchedule>,
#[serde(default)]
pub wednesday: Vec<DaySchedule>,
#[serde(default)]
pub thursday: Vec<DaySchedule>,
#[serde(default)]
pub friday: Vec<DaySchedule>,
#[serde(default)]
pub saturday: Vec<DaySchedule>,
#[serde(default)]
pub sunday: Vec<DaySchedule>,
}
#[derive(Debug, Deserialize)]
struct ExchangeScheduleResponse {
schedule: ExchangeSchedule,
}
#[derive(Debug, Deserialize)]
struct ExchangeAnnouncementsResponse {
cursor: Option<String>,
announcements: Vec<ExchangeAnnouncement>,
}
#[derive(Debug, Deserialize)]
struct SeriesFeeChangesResponse {
fee_changes: Vec<SeriesFeeChange>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct SeriesFeeChange {
pub series_ticker: String,
pub old_fee: Option<f64>,
pub new_fee: f64,
pub effective_date: String,
}