stock-trek 0.7.1

Stock Trek time-series analysis
Documentation
use rust_decimal::RoundingStrategy;
use serde::de::Error;
use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use strum::Display;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Preferences {
    pub rounding: Rounding,
    pub multi_leg: MultiLeg,
}

#[derive(Debug, Clone)]
pub struct Rounding {
    pub price: RoundingStrategy,
    pub quantity: RoundingStrategy,
    pub callback_rate: RoundingStrategy,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MultiLeg {
    pub if_different_symbol_unsupported: OnDifferent,
    pub if_different_price_unsupported: OnDifferent,
}

#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum OnDifferent {
    UseDataFromPrimary,
    SkipThisOrder,
    CancelEntireIteration,
}

impl Serialize for Rounding {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let Rounding {
            price,
            quantity,
            callback_rate,
        } = self;
        let mut state = serializer.serialize_struct("Rounding", 3)?;
        state.serialize_field("price", &serialize_rounding(price))?;
        state.serialize_field("quantity", &serialize_rounding(quantity))?;
        state.serialize_field("callback_rate", &serialize_rounding(callback_rate))?;
        state.end()
    }
}

impl<'de> Deserialize<'de> for Rounding {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct RoundingHelper {
            price: String,
            quantity: String,
            callback_rate: String,
        }

        let helper = RoundingHelper::deserialize(deserializer)?;

        Ok(Rounding {
            price: deserialize_rounding(&helper.price)
                .map_err(|e| D::Error::custom(format!("Invalid price rounding: {}", e)))?,
            quantity: deserialize_rounding(&helper.quantity)
                .map_err(|e| D::Error::custom(format!("Invalid quantity rounding: {}", e)))?,
            callback_rate: deserialize_rounding(&helper.callback_rate)
                .map_err(|e| D::Error::custom(format!("Invalid callback_rate rounding: {}", e)))?,
        })
    }
}

#[allow(deprecated)]
fn deserialize_rounding(text: &str) -> Result<RoundingStrategy, String> {
    match text {
        "AwayFromZero" => Ok(RoundingStrategy::AwayFromZero),
        "MidpointAwayFromZero" => Ok(RoundingStrategy::MidpointAwayFromZero),
        "MidpointNearestEven" => Ok(RoundingStrategy::MidpointNearestEven),
        "MidpointTowardZero" => Ok(RoundingStrategy::MidpointTowardZero),
        "ToNegativeInfinity" => Ok(RoundingStrategy::ToNegativeInfinity),
        "ToPositiveInfinity" => Ok(RoundingStrategy::ToPositiveInfinity),
        "ToZero" => Ok(RoundingStrategy::ToZero),
        "BankersRounding" => Ok(RoundingStrategy::BankersRounding),
        "RoundDown" => Ok(RoundingStrategy::RoundDown),
        "RoundHalfDown" => Ok(RoundingStrategy::RoundHalfDown),
        "RoundHalfUp" => Ok(RoundingStrategy::RoundHalfUp),
        "RoundUp" => Ok(RoundingStrategy::RoundUp),
        _ => Err(text.to_string()),
    }
}

#[allow(deprecated)]
fn serialize_rounding(strategy: &RoundingStrategy) -> &str {
    match *strategy {
        RoundingStrategy::AwayFromZero => "AwayFromZero",
        RoundingStrategy::MidpointAwayFromZero => "MidpointAwayFromZero",
        RoundingStrategy::MidpointNearestEven => "MidpointNearestEven",
        RoundingStrategy::MidpointTowardZero => "MidpointTowardZero",
        RoundingStrategy::ToNegativeInfinity => "ToNegativeInfinity",
        RoundingStrategy::ToPositiveInfinity => "ToPositiveInfinity",
        RoundingStrategy::ToZero => "ToZero",
        RoundingStrategy::BankersRounding => "BankersRounding",
        RoundingStrategy::RoundDown => "RoundDown",
        RoundingStrategy::RoundHalfDown => "RoundHalfDown",
        RoundingStrategy::RoundHalfUp => "RoundHalfUp",
        RoundingStrategy::RoundUp => "RoundUp",
    }
}