use polars::prelude::*;
pub fn calculate_bop(df: &DataFrame) -> PolarsResult<Series> {
if !df.schema().contains("open")
|| !df.schema().contains("high")
|| !df.schema().contains("low")
|| !df.schema().contains("close")
{
return Err(PolarsError::ShapeMismatch(
"Missing required columns: open, high, low, close for BOP calculation".into(),
));
}
let open = df.column("open")?.f64()?;
let high = df.column("high")?.f64()?;
let low = df.column("low")?.f64()?;
let close = df.column("close")?.f64()?;
let mut bop_values = Vec::with_capacity(df.height());
for i in 0..df.height() {
let open_val = open.get(i).unwrap_or(f64::NAN);
let high_val = high.get(i).unwrap_or(f64::NAN);
let low_val = low.get(i).unwrap_or(f64::NAN);
let close_val = close.get(i).unwrap_or(f64::NAN);
if !open_val.is_nan() && !high_val.is_nan() && !low_val.is_nan() && !close_val.is_nan() {
let range = high_val - low_val;
if range.abs() > 1e-10 {
let bop = (close_val - open_val) / range;
bop_values.push(bop);
} else {
bop_values.push(0.0);
}
} else {
bop_values.push(f64::NAN);
}
}
Ok(Series::new("bop".into(), bop_values))
}