use polars::prelude::*;
use crate::indicators::moving_averages::calculate_ema;
pub fn calculate_rapid_macd(
df: &DataFrame,
fast_period: Option<usize>,
slow_period: Option<usize>,
signal_period: Option<usize>,
price_col: Option<&str>,
) -> PolarsResult<(Series, Series, Series)> {
let fast = fast_period.unwrap_or(5);
let slow = slow_period.unwrap_or(13);
let signal = signal_period.unwrap_or(4);
let price_column = price_col.unwrap_or("close");
if fast >= slow {
return Err(PolarsError::ComputeError(
"Fast period must be smaller than slow period".into(),
));
}
let fast_ema = calculate_ema(df, price_column, fast)?;
let slow_ema = calculate_ema(df, price_column, slow)?;
let fast_vals = fast_ema.f64()?;
let slow_vals = slow_ema.f64()?;
let mut macd_line = Vec::with_capacity(df.height());
for i in 0..df.height() {
let fast_val = fast_vals.get(i).unwrap_or(f64::NAN);
let slow_val = slow_vals.get(i).unwrap_or(f64::NAN);
if !fast_val.is_nan() && !slow_val.is_nan() {
macd_line.push(fast_val - slow_val);
} else {
macd_line.push(f64::NAN);
}
}
let mut temp_df = DataFrame::new(vec![Series::new("macd_line", &macd_line)])?;
let signal_ema = calculate_ema(&temp_df, "macd_line", signal)?;
let signal_vals = signal_ema.f64()?;
let mut histogram = Vec::with_capacity(df.height());
for i in 0..df.height() {
let macd_val = macd_line[i];
let signal_val = signal_vals.get(i).unwrap_or(f64::NAN);
if !macd_val.is_nan() && !signal_val.is_nan() {
histogram.push(macd_val - signal_val);
} else {
histogram.push(f64::NAN);
}
}
Ok((
Series::new("rapid_macd", macd_line),
Series::new("rapid_macd_signal", signal_vals.into_iter().collect::<Vec<_>>()),
Series::new("rapid_macd_histogram", histogram),
))
}
pub fn add_rapid_macd(
df: &mut DataFrame,
fast_period: Option<usize>,
slow_period: Option<usize>,
signal_period: Option<usize>,
) -> PolarsResult<()> {
let (macd, signal, histogram) = calculate_rapid_macd(
df,
fast_period,
slow_period,
signal_period,
None
)?;
df.with_column(macd)?;
df.with_column(signal)?;
df.with_column(histogram)?;
Ok(())
}
pub fn calculate_rapid_macd_signals(df: &DataFrame) -> PolarsResult<Series> {
if !df.schema().contains("rapid_macd") {
return Err(PolarsError::ComputeError(
"rapid_macd column not found. Calculate Rapid MACD first.".into(),
));
}
let macd = df.column("rapid_macd")?.f64()?;
let mut signals = Vec::with_capacity(df.height());
signals.push(0);
for i in 1..df.height() {
let current = macd.get(i).unwrap_or(f64::NAN);
let previous = macd.get(i-1).unwrap_or(f64::NAN);
if current.is_nan() || previous.is_nan() {
signals.push(0);
} else if previous < 0.0 && current >= 0.0 {
signals.push(1);
} else if previous > 0.0 && current <= 0.0 {
signals.push(-1);
} else {
signals.push(0);
}
}
Ok(Series::new("rapid_macd_signal", signals))
}