ta_lib_in_rust/indicators/momentum/
bop.rs

1use polars::prelude::*;
2
3/// Calculates Balance of Power (BOP)
4/// Formula: (Close - Open) / (High - Low)
5///
6/// This oscillator measures the strength of buyers vs. sellers in the market.
7/// Values range typically between -1 and +1.
8///
9/// # Arguments
10///
11/// * `df` - DataFrame containing OHLC data ("open", "high", "low", "close" columns)
12///
13/// # Returns
14///
15/// Returns a PolarsResult containing the BOP Series
16pub fn calculate_bop(df: &DataFrame) -> PolarsResult<Series> {
17    // Check that required columns exist
18    if !df.schema().contains("open")
19        || !df.schema().contains("high")
20        || !df.schema().contains("low")
21        || !df.schema().contains("close")
22    {
23        return Err(PolarsError::ShapeMismatch(
24            "Missing required columns: open, high, low, close for BOP calculation".into(),
25        ));
26    }
27
28    // Extract the required columns
29    let open = df.column("open")?.f64()?;
30    let high = df.column("high")?.f64()?;
31    let low = df.column("low")?.f64()?;
32    let close = df.column("close")?.f64()?;
33
34    // Calculate BOP: (Close - Open) / (High - Low)
35    let mut bop_values = Vec::with_capacity(df.height());
36
37    for i in 0..df.height() {
38        let open_val = open.get(i).unwrap_or(f64::NAN);
39        let high_val = high.get(i).unwrap_or(f64::NAN);
40        let low_val = low.get(i).unwrap_or(f64::NAN);
41        let close_val = close.get(i).unwrap_or(f64::NAN);
42
43        if !open_val.is_nan() && !high_val.is_nan() && !low_val.is_nan() && !close_val.is_nan() {
44            let range = high_val - low_val;
45
46            if range.abs() > 1e-10 {
47                let bop = (close_val - open_val) / range;
48                bop_values.push(bop);
49            } else {
50                // When the range is too small, we can consider it as zero, meaning no significant price movement
51                bop_values.push(0.0);
52            }
53        } else {
54            bop_values.push(f64::NAN);
55        }
56    }
57
58    Ok(Series::new("bop".into(), bop_values))
59}