use crate::{KandError, TAFloat};
pub const fn lookback() -> Result<usize, KandError> {
Ok(1)
}
pub fn ha(
input_open: &[TAFloat],
input_high: &[TAFloat],
input_low: &[TAFloat],
input_close: &[TAFloat],
output_open: &mut [TAFloat],
output_high: &mut [TAFloat],
output_low: &mut [TAFloat],
output_close: &mut [TAFloat],
) -> Result<(), KandError> {
let len = input_open.len();
let lookback = lookback()?;
#[cfg(feature = "check")]
{
if len == 0 {
return Err(KandError::InvalidData);
}
if len != input_high.len()
|| len != input_low.len()
|| len != input_close.len()
|| len != output_open.len()
|| len != output_high.len()
|| len != output_low.len()
|| len != output_close.len()
{
return Err(KandError::LengthMismatch);
}
}
#[cfg(feature = "deep-check")]
{
for i in 0..len {
if input_open[i].is_nan()
|| input_high[i].is_nan()
|| input_low[i].is_nan()
|| input_close[i].is_nan()
{
return Err(KandError::NaNDetected);
}
}
}
output_close[0] = (input_open[0] + input_high[0] + input_low[0] + input_close[0]) / 4.0;
output_open[0] = (input_open[0] + input_close[0]) / 2.0;
output_high[0] = input_high[0];
output_low[0] = input_low[0];
for i in lookback..len {
let (o, h, l, c) = ha_inc(
input_open[i],
input_high[i],
input_low[i],
input_close[i],
output_open[i - 1],
output_close[i - 1],
)?;
output_open[i] = o;
output_high[i] = h;
output_low[i] = l;
output_close[i] = c;
}
Ok(())
}
pub fn ha_inc(
curr_open: TAFloat,
curr_high: TAFloat,
curr_low: TAFloat,
curr_close: TAFloat,
prev_ha_open: TAFloat,
prev_ha_close: TAFloat,
) -> Result<(TAFloat, TAFloat, TAFloat, TAFloat), KandError> {
#[cfg(feature = "deep-check")]
{
if curr_open.is_nan()
|| curr_high.is_nan()
|| curr_low.is_nan()
|| curr_close.is_nan()
|| prev_ha_open.is_nan()
|| prev_ha_close.is_nan()
{
return Err(KandError::NaNDetected);
}
}
let ha_close = (curr_open + curr_high + curr_low + curr_close) / 4.0;
let ha_open = (prev_ha_open + prev_ha_close) / 2.0;
let ha_high = curr_high.max(ha_open).max(ha_close);
let ha_low = curr_low.min(ha_open).min(ha_close);
Ok((ha_open, ha_high, ha_low, ha_close))
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
#[test]
fn test_ha_calculation() {
let input_open = vec![10.0, 10.5, 11.2, 10.8, 11.5];
let input_high = vec![11.0, 11.5, 11.8, 11.3, 12.0];
let input_low = vec![9.5, 10.2, 10.8, 10.5, 11.3];
let input_close = vec![10.8, 11.3, 11.5, 11.0, 11.8];
let mut output_open = vec![0.0; 5];
let mut output_high = vec![0.0; 5];
let mut output_low = vec![0.0; 5];
let mut output_close = vec![0.0; 5];
ha(
&input_open,
&input_high,
&input_low,
&input_close,
&mut output_open,
&mut output_high,
&mut output_low,
&mut output_close,
)
.unwrap();
assert_relative_eq!(output_open[0], 10.4, epsilon = 0.0001);
assert_relative_eq!(output_high[0], 11.0, epsilon = 0.0001);
assert_relative_eq!(output_low[0], 9.5, epsilon = 0.0001);
assert_relative_eq!(output_close[0], 10.325, epsilon = 0.0001);
assert_relative_eq!(output_open[1], 10.3625, epsilon = 0.0001);
assert_relative_eq!(output_close[1], 10.875, epsilon = 0.0001);
assert_relative_eq!(output_high[1], 11.5, epsilon = 0.0001);
assert_relative_eq!(output_low[1], 10.2, epsilon = 0.0001);
for i in 1..5 {
let (ha_open, ha_high, ha_low, ha_close) = ha_inc(
input_open[i],
input_high[i],
input_low[i],
input_close[i],
output_open[i - 1],
output_close[i - 1],
)
.unwrap();
assert_relative_eq!(ha_open, output_open[i], epsilon = 0.0001);
assert_relative_eq!(ha_high, output_high[i], epsilon = 0.0001);
assert_relative_eq!(ha_low, output_low[i], epsilon = 0.0001);
assert_relative_eq!(ha_close, output_close[i], epsilon = 0.0001);
}
}
}