ta_lib_in_rust/indicators/momentum/
bop.rs1use polars::prelude::*;
2
3pub fn calculate_bop(df: &DataFrame) -> PolarsResult<Series> {
17 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 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 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 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}