use crate::{KandError, TAFloat};
pub const fn lookback() -> Result<usize, KandError> {
Ok(1)
}
#[allow(clippy::similar_names)]
pub fn ecl(
input_high: &[TAFloat],
input_low: &[TAFloat],
input_close: &[TAFloat],
output_h5: &mut [TAFloat],
output_h4: &mut [TAFloat],
output_h3: &mut [TAFloat],
output_h2: &mut [TAFloat],
output_h1: &mut [TAFloat],
output_l1: &mut [TAFloat],
output_l2: &mut [TAFloat],
output_l3: &mut [TAFloat],
output_l4: &mut [TAFloat],
output_l5: &mut [TAFloat],
) -> Result<(), KandError> {
let len = input_high.len();
let lookback = lookback()?;
#[cfg(feature = "check")]
{
if len < 2 {
return Err(KandError::InvalidData);
}
if len != input_low.len()
|| len != input_close.len()
|| len != output_h5.len()
|| len != output_h4.len()
|| len != output_h3.len()
|| len != output_h2.len()
|| len != output_h1.len()
|| len != output_l1.len()
|| len != output_l2.len()
|| len != output_l3.len()
|| len != output_l4.len()
|| len != output_l5.len()
{
return Err(KandError::LengthMismatch);
}
if len <= lookback + 1 {
return Err(KandError::InsufficientData);
}
}
#[cfg(feature = "deep-check")]
{
for i in 0..len {
if input_high[i].is_nan() || input_low[i].is_nan() || input_close[i].is_nan() {
return Err(KandError::NaNDetected);
}
}
}
let param_factor = 1.1;
for i in lookback..len {
let range = input_high[i - 1] - input_low[i - 1];
let h5_val = (input_high[i - 1] / input_low[i - 1]) * input_close[i - 1];
output_h5[i] = h5_val;
output_h4[i] = input_close[i - 1] + range * param_factor / 2.0;
output_h3[i] = input_close[i - 1] + range * param_factor / 4.0;
output_h2[i] = input_close[i - 1] + range * param_factor / 6.0;
output_h1[i] = input_close[i - 1] + range * param_factor / 12.0;
output_l1[i] = input_close[i - 1] - range * param_factor / 12.0;
output_l2[i] = input_close[i - 1] - range * param_factor / 6.0;
output_l3[i] = input_close[i - 1] - range * param_factor / 4.0;
output_l4[i] = input_close[i - 1] - range * param_factor / 2.0;
output_l5[i] = input_close[i - 1] - (h5_val - input_close[i - 1]);
}
for i in 0..lookback {
output_h5[i] = TAFloat::NAN;
output_h4[i] = TAFloat::NAN;
output_h3[i] = TAFloat::NAN;
output_h2[i] = TAFloat::NAN;
output_h1[i] = TAFloat::NAN;
output_l1[i] = TAFloat::NAN;
output_l2[i] = TAFloat::NAN;
output_l3[i] = TAFloat::NAN;
output_l4[i] = TAFloat::NAN;
output_l5[i] = TAFloat::NAN;
}
Ok(())
}
#[allow(clippy::similar_names)]
pub fn ecl_inc(
prev_high: TAFloat,
prev_low: TAFloat,
prev_close: TAFloat,
) -> Result<
(
TAFloat,
TAFloat,
TAFloat,
TAFloat,
TAFloat,
TAFloat,
TAFloat,
TAFloat,
TAFloat,
TAFloat,
),
KandError,
> {
#[cfg(feature = "deep-check")]
{
if prev_high.is_nan() || prev_low.is_nan() || prev_close.is_nan() {
return Err(KandError::NaNDetected);
}
}
let param_factor = 1.1;
let range = prev_high - prev_low;
let h5_val = (prev_high / prev_low) * prev_close;
let h4 = prev_close + range * param_factor / 2.0;
let h3 = prev_close + range * param_factor / 4.0;
let h2 = prev_close + range * param_factor / 6.0;
let h1 = prev_close + range * param_factor / 12.0;
let l1 = prev_close - range * param_factor / 12.0;
let l2 = prev_close - range * param_factor / 6.0;
let l3 = prev_close - range * param_factor / 4.0;
let l4 = prev_close - range * param_factor / 2.0;
let l5 = prev_close - (h5_val - prev_close);
Ok((h5_val, h4, h3, h2, h1, l1, l2, l3, l4, l5))
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
#[test]
#[allow(clippy::similar_names)]
fn test_ecl_calculation() {
let input_high = vec![24.20, 24.07, 24.04, 23.87, 23.67];
let input_low = vec![23.85, 23.72, 23.64, 23.37, 23.46];
let input_close = vec![23.89, 23.95, 23.67, 23.78, 23.50];
let mut output_h5 = vec![0.0; 5];
let mut output_h4 = vec![0.0; 5];
let mut output_h3 = vec![0.0; 5];
let mut output_h2 = vec![0.0; 5];
let mut output_h1 = vec![0.0; 5];
let mut output_l1 = vec![0.0; 5];
let mut output_l2 = vec![0.0; 5];
let mut output_l3 = vec![0.0; 5];
let mut output_l4 = vec![0.0; 5];
let mut output_l5 = vec![0.0; 5];
ecl(
&input_high,
&input_low,
&input_close,
&mut output_h5,
&mut output_h4,
&mut output_h3,
&mut output_h2,
&mut output_h1,
&mut output_l1,
&mut output_l2,
&mut output_l3,
&mut output_l4,
&mut output_l5,
)
.unwrap();
let outputs = [
&output_h5, &output_h4, &output_h3, &output_h2, &output_h1, &output_l1, &output_l2,
&output_l3, &output_l4, &output_l5,
];
for output in outputs {
assert!(output[0].is_nan());
}
let outputs = [
&output_h5, &output_h4, &output_h3, &output_h2, &output_h1, &output_l1, &output_l2,
&output_l3, &output_l4, &output_l5,
];
for i in 1..5 {
for output in &outputs {
assert!(output[i].is_finite());
}
}
let i = input_high.len() - 1;
let (h5_inc, h4_inc, h3_inc, h2_inc, h1_inc, l1_inc, l2_inc, l3_inc, l4_inc, l5_inc) =
ecl_inc(input_high[i - 1], input_low[i - 1], input_close[i - 1]).unwrap();
assert_relative_eq!(h5_inc, output_h5[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(h4_inc, output_h4[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(h3_inc, output_h3[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(h2_inc, output_h2[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(h1_inc, output_h1[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(l1_inc, output_l1[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(l2_inc, output_l2[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(l3_inc, output_l3[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(l4_inc, output_l4[i], epsilon = TAFloat::EPSILON);
assert_relative_eq!(l5_inc, output_l5[i], epsilon = TAFloat::EPSILON);
}
}