alpaca-data 0.26.1

Rust client for the Alpaca Market Data HTTP API
Documentation
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Bar {
    pub t: Option<String>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
    )]
    pub o: Option<Decimal>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
    )]
    pub h: Option<Decimal>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
    )]
    pub l: Option<Decimal>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
    )]
    pub c: Option<Decimal>,
    pub v: Option<u64>,
    pub n: Option<u64>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
    )]
    pub vw: Option<Decimal>,
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Quote {
    pub t: Option<String>,
    pub bx: Option<String>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
    )]
    pub bp: Option<Decimal>,
    pub bs: Option<u64>,
    pub ax: Option<String>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
    )]
    pub ap: Option<Decimal>,
    #[serde(rename = "as")]
    pub r#as: Option<u64>,
    pub c: Option<String>,
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Trade {
    pub t: Option<String>,
    pub x: Option<String>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::decimal::deserialize_option_decimal_from_string_or_number"
    )]
    pub p: Option<Decimal>,
    pub s: Option<u64>,
    pub c: Option<String>,
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Greeks {
    #[serde(
        default,
        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
    )]
    pub delta: Option<f64>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
    )]
    pub gamma: Option<f64>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
    )]
    pub rho: Option<f64>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
    )]
    pub theta: Option<f64>,
    #[serde(
        default,
        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
    )]
    pub vega: Option<f64>,
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Snapshot {
    #[serde(rename = "latestTrade")]
    pub latest_trade: Option<Trade>,
    #[serde(rename = "latestQuote")]
    pub latest_quote: Option<Quote>,
    #[serde(rename = "minuteBar")]
    pub minute_bar: Option<Bar>,
    #[serde(rename = "dailyBar")]
    pub daily_bar: Option<Bar>,
    #[serde(rename = "prevDailyBar")]
    pub prev_daily_bar: Option<Bar>,
    pub greeks: Option<Greeks>,
    #[serde(
        rename = "impliedVolatility",
        default,
        deserialize_with = "alpaca_core::float::deserialize_option_f64_from_string_or_number"
    )]
    pub implied_volatility: Option<f64>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    fn assert_close(actual: Option<f64>, expected: f64) {
        let actual = actual.expect("value should deserialize");
        assert!(
            (actual - expected).abs() <= 1e-12,
            "actual={actual}, expected={expected}"
        );
    }

    #[test]
    fn option_snapshot_greeks_and_iv_deserialize_as_floats() {
        let snapshot: Snapshot = serde_json::from_value(json!({
            "greeks": {
                "delta": "0.123456789123",
                "gamma": 0.012345678912,
                "rho": "0.034567891234",
                "theta": -0.045678912345,
                "vega": "0.156789123456"
            },
            "impliedVolatility": "0.267891234567"
        }))
        .expect("snapshot should deserialize");
        let greeks = snapshot.greeks.expect("greeks should deserialize");

        assert_close(greeks.delta, 0.123456789123);
        assert_close(greeks.gamma, 0.012345678912);
        assert_close(greeks.rho, 0.034567891234);
        assert_close(greeks.theta, -0.045678912345);
        assert_close(greeks.vega, 0.156789123456);
        assert_close(snapshot.implied_volatility, 0.267891234567);
    }
}