value-metrics 0.1.0

Equity valuation ratios: P/E, P/B, earnings yield, EV/EBIT, dividend yield.
Documentation
//! # value-metrics
//!
//! Core equity valuation ratios used to screen for deep-value stocks: price-to-earnings,
//! price-to-book, earnings yield, EV/EBIT, and dividend yield. Pure Rust, no deps. Same
//! signals behind the [DeepValueRadar](https://deepvalueradar.com/) value screener.
//!
//! ```
//! use value_metrics::*;
//! assert!((price_to_earnings(100.0, 5.0) - 20.0).abs() < 1e-9);
//! assert!((earnings_yield(5.0, 100.0) - 0.05).abs() < 1e-9);
//! ```

/// Price-to-earnings ratio = price / earnings per share.
pub fn price_to_earnings(price: f64, eps: f64) -> f64 {
    if eps <= 0.0 { f64::INFINITY } else { price / eps }
}

/// Price-to-book ratio = price / book value per share.
pub fn price_to_book(price: f64, book_value_per_share: f64) -> f64 {
    if book_value_per_share <= 0.0 { f64::INFINITY } else { price / book_value_per_share }
}

/// Earnings yield = EPS / price (the reciprocal of P/E).
pub fn earnings_yield(eps: f64, price: f64) -> f64 {
    if price <= 0.0 { 0.0 } else { eps / price }
}

/// EV/EBIT = enterprise value / EBIT.
pub fn ev_to_ebit(enterprise_value: f64, ebit: f64) -> f64 {
    if ebit <= 0.0 { f64::INFINITY } else { enterprise_value / ebit }
}

/// Dividend yield = annual dividend per share / price.
pub fn dividend_yield(dividend_per_share: f64, price: f64) -> f64 {
    if price <= 0.0 { 0.0 } else { dividend_per_share / price }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn pe_basic() { assert!((price_to_earnings(100.0, 5.0) - 20.0).abs() < 1e-9); }
    #[test]
    fn pe_zero_eps_is_inf() { assert!(price_to_earnings(100.0, 0.0).is_infinite()); }
    #[test]
    fn earnings_yield_reciprocal() {
        let pe = price_to_earnings(100.0, 5.0);
        let ey = earnings_yield(5.0, 100.0);
        assert!((pe * ey - 1.0).abs() < 1e-9);
    }
    #[test]
    fn pb_basic() { assert!((price_to_book(50.0, 25.0) - 2.0).abs() < 1e-9); }
    #[test]
    fn div_yield_pct() { assert!((dividend_yield(4.0, 100.0) - 0.04).abs() < 1e-9); }
    #[test]
    fn ev_ebit() { assert!((ev_to_ebit(1_000_000.0, 100_000.0) - 10.0).abs() < 1e-9); }
}