use super::ema;
use crate::{KandError, TAFloat};
pub fn lookback(
param_fast_period: usize,
param_slow_period: usize,
param_signal_period: usize,
) -> Result<usize, KandError> {
#[cfg(feature = "check")]
{
if param_fast_period < 2 || param_slow_period < 2 || param_signal_period < 2 {
return Err(KandError::InvalidParameter);
}
if param_fast_period >= param_slow_period {
return Err(KandError::InvalidParameter);
}
}
let slow_lookback = ema::lookback(param_slow_period)?;
let signal_lookback = ema::lookback(param_signal_period)?;
Ok(slow_lookback + signal_lookback)
}
pub fn macd(
input_price: &[TAFloat],
param_fast_period: usize,
param_slow_period: usize,
param_signal_period: usize,
output_macd_line: &mut [TAFloat],
output_signal_line: &mut [TAFloat],
output_histogram: &mut [TAFloat],
output_fast_ema: &mut [TAFloat],
output_slow_ema: &mut [TAFloat],
) -> Result<(), KandError> {
let len = input_price.len();
let lookback = lookback(param_fast_period, param_slow_period, param_signal_period)?;
#[cfg(feature = "check")]
{
if len == 0 {
return Err(KandError::InvalidData);
}
if len <= lookback {
return Err(KandError::InsufficientData);
}
if len != output_macd_line.len()
|| len != output_signal_line.len()
|| len != output_histogram.len()
|| len != output_fast_ema.len()
|| len != output_slow_ema.len()
{
return Err(KandError::LengthMismatch);
}
if len.saturating_sub(param_slow_period) < param_signal_period {
return Err(KandError::InsufficientData);
}
}
#[cfg(feature = "deep-check")]
{
for price in input_price {
if price.is_nan() {
return Err(KandError::NaNDetected);
}
}
}
ema::ema(input_price, param_fast_period, None, output_fast_ema)?;
ema::ema(input_price, param_slow_period, None, output_slow_ema)?;
for i in 0..len {
output_macd_line[i] = output_fast_ema[i] - output_slow_ema[i];
}
ema::ema(
&output_macd_line[param_slow_period - 1..],
param_signal_period,
None,
&mut output_signal_line[param_slow_period - 1..],
)?;
for i in lookback..len {
output_histogram[i] = output_macd_line[i] - output_signal_line[i];
}
for i in 0..lookback {
output_macd_line[i] = TAFloat::NAN;
output_signal_line[i] = TAFloat::NAN;
output_histogram[i] = TAFloat::NAN;
output_fast_ema[i] = TAFloat::NAN;
output_slow_ema[i] = TAFloat::NAN;
}
Ok(())
}
pub fn macd_inc(
input_price: TAFloat,
prev_fast_ema: TAFloat,
prev_slow_ema: TAFloat,
prev_signal: TAFloat,
param_fast_period: usize,
param_slow_period: usize,
param_signal_period: usize,
) -> Result<(TAFloat, TAFloat, TAFloat), KandError> {
#[cfg(feature = "check")]
{
if param_fast_period < 2 || param_slow_period < 2 || param_signal_period < 2 {
return Err(KandError::InvalidParameter);
}
if param_fast_period >= param_slow_period {
return Err(KandError::InvalidParameter);
}
}
#[cfg(feature = "deep-check")]
{
if input_price.is_nan()
|| prev_fast_ema.is_nan()
|| prev_slow_ema.is_nan()
|| prev_signal.is_nan()
{
return Err(KandError::NaNDetected);
}
}
let fast_ema = ema::ema_inc(input_price, prev_fast_ema, param_fast_period, None)?;
let slow_ema = ema::ema_inc(input_price, prev_slow_ema, param_slow_period, None)?;
let macd = fast_ema - slow_ema;
let signal = ema::ema_inc(macd, prev_signal, param_signal_period, None)?;
let histogram = macd - signal;
Ok((macd, signal, histogram))
}