Skip to main content

finance_query/indicators/
bull_bear_power.rs

1//! Bull Bear Power indicator.
2
3use super::{IndicatorError, Result, ema::ema};
4use serde::{Deserialize, Serialize};
5
6/// Result of Bull Bear Power calculation
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub struct BullBearPowerResult {
9    /// Bull Power
10    pub bull_power: Vec<Option<f64>>,
11    /// Bear Power
12    pub bear_power: Vec<Option<f64>>,
13}
14
15/// Calculate Bull Bear Power.
16///
17/// Bull Power = High - EMA(13)
18/// Bear Power = Low - EMA(13)
19///
20/// # Arguments
21///
22/// * `highs` - High prices
23/// * `lows` - Low prices
24/// * `closes` - Close prices
25///
26/// # Example
27///
28/// ```
29/// use finance_query::indicators::bull_bear_power;
30///
31/// let highs = vec![10.0; 20];
32/// let lows = vec![8.0; 20];
33/// let closes = vec![9.0; 20];
34/// let result = bull_bear_power(&highs, &lows, &closes).unwrap();
35/// ```
36pub fn bull_bear_power(highs: &[f64], lows: &[f64], closes: &[f64]) -> Result<BullBearPowerResult> {
37    let len = highs.len();
38    if lows.len() != len || closes.len() != len {
39        return Err(IndicatorError::InvalidPeriod(
40            "Data lengths must match".to_string(),
41        ));
42    }
43    if len < 13 {
44        return Err(IndicatorError::InsufficientData { need: 13, got: len });
45    }
46
47    let ema13 = ema(closes, 13);
48
49    let mut bull_power = vec![None; len];
50    let mut bear_power = vec![None; len];
51
52    for i in 0..len {
53        if let Some(ema_val) = ema13[i] {
54            bull_power[i] = Some(highs[i] - ema_val);
55            bear_power[i] = Some(lows[i] - ema_val);
56        }
57    }
58
59    Ok(BullBearPowerResult {
60        bull_power,
61        bear_power,
62    })
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_bull_bear_power() {
71        let highs = vec![10.0; 20];
72        let lows = vec![8.0; 20];
73        let closes = vec![9.0; 20];
74        let result = bull_bear_power(&highs, &lows, &closes).unwrap();
75
76        assert_eq!(result.bull_power.len(), 20);
77        assert!(result.bull_power[11].is_none());
78        assert!(result.bull_power[12].is_some());
79    }
80}