ta_lib_in_rust/indicators/volatility/
trange.rs1use polars::prelude::*;
2
3pub fn calculate_trange(df: &DataFrame) -> PolarsResult<Series> {
17 if !df.schema().contains("high")
19 || !df.schema().contains("low")
20 || !df.schema().contains("close")
21 {
22 return Err(PolarsError::ShapeMismatch(
23 "DataFrame must contain 'high', 'low', and 'close' columns for TRANGE calculation"
24 .into(),
25 ));
26 }
27
28 let high = df.column("high")?.f64()?;
29 let low = df.column("low")?.f64()?;
30 let close = df.column("close")?.f64()?;
31 let prev_close = close.shift(1);
32
33 let mut tr_values = Vec::with_capacity(df.height());
34
35 let first_tr = {
38 let h = high.get(0).unwrap_or(f64::NAN);
39 let l = low.get(0).unwrap_or(f64::NAN);
40
41 if h.is_nan() || l.is_nan() {
42 f64::NAN
43 } else {
44 h - l
45 }
46 };
47 tr_values.push(first_tr);
48
49 for i in 1..df.height() {
51 let h = high.get(i).unwrap_or(f64::NAN);
52 let l = low.get(i).unwrap_or(f64::NAN);
53 let pc = prev_close.get(i).unwrap_or(f64::NAN);
54
55 if h.is_nan() || l.is_nan() || pc.is_nan() {
56 tr_values.push(f64::NAN);
57 } else {
58 let range1 = h - l;
59 let range2 = (h - pc).abs();
60 let range3 = (l - pc).abs();
61
62 let tr = range1.max(range2).max(range3);
63 tr_values.push(tr);
64 }
65 }
66
67 Ok(Series::new("trange".into(), tr_values))
68}