yfinance-rs 0.9.1

Ergonomic Rust client for Yahoo Finance, supporting historical prices, real-time streaming, options, fundamentals, and more.
Documentation
use crate::core::{YfError, conversions::decimal_from_f64};
use paft::money::{Currency, Money, Price, PriceAmount};
use rust_decimal::Decimal;
use std::str::FromStr;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResolvedCurrencyUnit {
    currency: Currency,
    scale: Decimal,
}

impl ResolvedCurrencyUnit {
    pub const fn from_currency(currency: Currency) -> Self {
        Self {
            currency,
            scale: Decimal::ONE,
        }
    }

    pub fn from_code(code: &str) -> Option<Self> {
        let trimmed = code.trim();
        if trimmed.is_empty() {
            return None;
        }

        let (code, scale) = match trimmed {
            "GBp" | "GBX" => ("GBP", Decimal::new(1, 2)),
            "ZAc" => ("ZAR", Decimal::new(1, 2)),
            "ILA" => ("ILS", Decimal::new(1, 2)),
            _ => (trimmed, Decimal::ONE),
        };

        Currency::from_str(code)
            .ok()
            .map(|currency| Self { currency, scale })
    }

    pub fn major_from_code(code: &str) -> Option<Self> {
        Self::from_code(code).map(|unit| unit.major_unit())
    }

    pub fn major_unit(&self) -> Self {
        Self::from_currency(self.currency.clone())
    }

    pub const fn currency(&self) -> &Currency {
        &self.currency
    }

    pub fn price_amount_from_f64(&self, value: f64) -> Option<PriceAmount> {
        self.scaled_decimal_from_f64(value).map(PriceAmount::new)
    }

    pub(crate) fn price_amount_rounded_at_provider_precision(
        &self,
        value: &PriceAmount,
        precision: u32,
    ) -> Option<PriceAmount> {
        let provider_value = value.as_decimal().checked_div(self.scale)?;
        provider_value
            .round_dp(precision)
            .checked_mul(self.scale)
            .map(PriceAmount::new)
    }

    #[cfg(feature = "stream")]
    pub fn price_amount_from_decimal(&self, value: Decimal) -> Option<PriceAmount> {
        value.checked_mul(self.scale).map(PriceAmount::new)
    }

    pub fn price_from_f64(&self, value: f64) -> Option<Price> {
        self.scaled_decimal_from_f64(value)
            .map(|decimal| Price::new(decimal, self.currency.clone()))
    }

    #[cfg_attr(not(any(test, feature = "test-mode")), allow(dead_code))]
    pub fn money_from_f64(&self, value: f64) -> Option<Money> {
        decimal_from_f64(value).and_then(|decimal| Money::new(decimal, self.currency.clone()).ok())
    }

    pub fn money_from_i64(&self, value: i64) -> Result<Money, YfError> {
        let decimal = Decimal::from_i128_with_scale(i128::from(value), 0);
        self.money_from_decimal(decimal)
    }

    pub fn money_from_u64(&self, value: u64) -> Result<Money, YfError> {
        let decimal = Decimal::from_i128_with_scale(i128::from(value), 0);
        self.money_from_decimal(decimal)
    }

    pub fn money_from_decimal(&self, decimal: Decimal) -> Result<Money, YfError> {
        Ok(Money::new(decimal, self.currency.clone())?)
    }

    fn scaled_decimal_from_f64(&self, value: f64) -> Option<Decimal> {
        decimal_from_f64(value).and_then(|decimal| decimal.checked_mul(self.scale))
    }
}