use clap::ValueEnum;
use crate::error::AppError;
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum SessionChoice {
#[value(alias = "regular")]
Normal,
#[value(alias = "pre")]
Am,
#[value(alias = "post")]
Pm,
#[value(alias = "extended")]
Seamless,
}
impl From<SessionChoice> for schwab::Session {
fn from(choice: SessionChoice) -> Self {
match choice {
SessionChoice::Normal => schwab::Session::Normal,
SessionChoice::Am => schwab::Session::Am,
SessionChoice::Pm => schwab::Session::Pm,
SessionChoice::Seamless => schwab::Session::Seamless,
}
}
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum DurationChoice {
#[value(alias = "DAY")]
Day,
#[value(alias = "gtc", alias = "GTC")]
GoodTillCancel,
#[value(alias = "fok", alias = "FOK")]
FillOrKill,
#[value(alias = "ioc", alias = "IOC")]
ImmediateOrCancel,
}
impl From<DurationChoice> for schwab::Duration {
fn from(choice: DurationChoice) -> Self {
match choice {
DurationChoice::Day => schwab::Duration::Day,
DurationChoice::GoodTillCancel => schwab::Duration::GoodTillCancel,
DurationChoice::FillOrKill => schwab::Duration::FillOrKill,
DurationChoice::ImmediateOrCancel => schwab::Duration::ImmediateOrCancel,
}
}
}
#[cfg(not(feature = "decimal"))]
pub fn to_number(v: f64) -> Result<schwab::Number, AppError> {
Ok(v)
}
#[cfg(feature = "decimal")]
pub fn to_number(v: f64) -> Result<schwab::Number, AppError> {
use core::str::FromStr;
let s = format!("{v}");
schwab::Number::from_str(&s)
.map_err(|_| AppError::OrderValidation(format!("cannot convert {v} to decimal")))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn session_choice_maps_to_schwab_session() {
assert!(matches!(
schwab::Session::from(SessionChoice::Normal),
schwab::Session::Normal
));
assert!(matches!(
schwab::Session::from(SessionChoice::Am),
schwab::Session::Am
));
assert!(matches!(
schwab::Session::from(SessionChoice::Pm),
schwab::Session::Pm
));
assert!(matches!(
schwab::Session::from(SessionChoice::Seamless),
schwab::Session::Seamless
));
}
#[test]
fn duration_choice_maps_to_schwab_duration() {
assert!(matches!(
schwab::Duration::from(DurationChoice::Day),
schwab::Duration::Day
));
assert!(matches!(
schwab::Duration::from(DurationChoice::GoodTillCancel),
schwab::Duration::GoodTillCancel
));
assert!(matches!(
schwab::Duration::from(DurationChoice::FillOrKill),
schwab::Duration::FillOrKill
));
assert!(matches!(
schwab::Duration::from(DurationChoice::ImmediateOrCancel),
schwab::Duration::ImmediateOrCancel
));
}
#[test]
fn to_number_accepts_finite_value() {
let value = to_number(42.5).expect("finite value should convert");
assert_eq!(value.to_string(), "42.5");
}
}