use crate::{KandError, TAFloat};
pub const fn lookback(
_param_acceleration: TAFloat,
_param_maximum: TAFloat,
) -> Result<usize, KandError> {
Ok(1)
}
pub fn sar(
input_high: &[TAFloat],
input_low: &[TAFloat],
param_acceleration: TAFloat,
param_maximum: TAFloat,
output_sar: &mut [TAFloat],
output_is_long: &mut [bool],
output_af: &mut [TAFloat],
output_ep: &mut [TAFloat],
) -> Result<(), KandError> {
let len = input_high.len();
let lookback = lookback(param_acceleration, param_maximum)?;
#[cfg(feature = "check")]
{
if len == 0 {
return Err(KandError::InvalidData);
}
if len != input_low.len()
|| len != output_sar.len()
|| len != output_is_long.len()
|| len != output_af.len()
|| len != output_ep.len()
{
return Err(KandError::LengthMismatch);
}
if param_acceleration <= 0.0 || param_maximum <= param_acceleration {
return Err(KandError::InvalidParameter);
}
if len <= lookback {
return Err(KandError::InsufficientData);
}
}
#[cfg(feature = "deep-check")]
{
for i in 0..len {
if input_high[i].is_nan() || input_low[i].is_nan() {
return Err(KandError::NaNDetected);
}
}
}
let plus_dm = input_high[1] - input_high[0];
let minus_dm = input_low[0] - input_low[1];
let initial_trend = plus_dm >= minus_dm;
let mut af = param_acceleration;
let mut ep = if initial_trend {
input_high[1]
} else {
input_low[1]
};
output_sar[0] = TAFloat::NAN;
output_is_long[0] = initial_trend;
output_af[0] = 0.0;
output_ep[0] = TAFloat::NAN;
output_sar[1] = if initial_trend {
input_low[0]
} else {
input_high[0]
};
output_is_long[1] = initial_trend;
output_af[1] = af;
output_ep[1] = ep;
let mut is_long = initial_trend;
let mut trend_start = 1;
for i in 2..len {
let prev_sar = output_sar[i - 1];
let high = input_high[i];
let low = input_low[i];
let mut sar_val = af.mul_add(ep - prev_sar, prev_sar);
if is_long {
if i - trend_start < 2 {
sar_val = sar_val.min(input_low[i - 1]);
} else {
sar_val = sar_val.min(input_low[i - 1]).min(input_low[i - 2]);
}
if high > ep {
ep = high;
af = (af + param_acceleration).min(param_maximum);
}
if low < sar_val {
is_long = false;
sar_val = ep;
ep = low;
af = param_acceleration;
trend_start = i - 1;
}
} else {
if i - trend_start < 2 {
sar_val = sar_val.max(input_high[i - 1]);
} else {
sar_val = sar_val.max(input_high[i - 1]).max(input_high[i - 2]);
}
if low < ep {
ep = low;
af = (af + param_acceleration).min(param_maximum);
}
if high > sar_val {
is_long = true;
sar_val = ep;
ep = high;
af = param_acceleration;
trend_start = i - 1;
}
}
output_sar[i] = sar_val;
output_is_long[i] = is_long;
output_af[i] = af;
output_ep[i] = ep;
}
Ok(())
}
pub fn sar_inc(
input_high: TAFloat,
input_low: TAFloat,
prev_high: TAFloat,
prev_low: TAFloat,
prev_sar: TAFloat,
input_is_long: bool,
input_af: TAFloat,
input_ep: TAFloat,
param_acceleration: TAFloat,
param_maximum: TAFloat,
) -> Result<(TAFloat, bool, TAFloat, TAFloat), KandError> {
#[cfg(feature = "check")]
{
if param_acceleration <= 0.0 || param_maximum <= param_acceleration {
return Err(KandError::InvalidParameter);
}
}
#[cfg(feature = "deep-check")]
{
if input_high.is_nan()
|| input_low.is_nan()
|| prev_high.is_nan()
|| prev_low.is_nan()
|| prev_sar.is_nan()
{
return Err(KandError::NaNDetected);
}
}
let high = input_high;
let low = input_low;
let mut is_long = input_is_long;
let mut af = input_af;
let mut ep = input_ep;
let mut sar = af.mul_add(ep - prev_sar, prev_sar);
if is_long {
sar = sar.min(prev_low);
if high > ep {
ep = high;
af = (af + param_acceleration).min(param_maximum);
}
if low < sar {
is_long = false;
sar = ep;
ep = low;
af = param_acceleration;
}
} else {
sar = sar.max(prev_high);
if low < ep {
ep = low;
af = (af + param_acceleration).min(param_maximum);
}
if high > sar {
is_long = true;
sar = ep;
ep = high;
af = param_acceleration;
}
}
Ok((sar, is_long, af, ep))
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
#[test]
fn test_sar_calculation() {
let input_high = vec![
35266.0, 35247.5, 35235.7, 35190.8, 35182.0, 35258.0, 35262.9, 35281.5, 35256.0,
35210.0, 35185.4, 35230.0, 35241.0, 35218.1, 35212.6, 35128.9, 35047.7, 35019.5,
35078.8, 35085.0,
];
let input_low = vec![
35216.1, 35206.5, 35180.0, 35130.7, 35153.6, 35174.7, 35202.6, 35203.5, 35175.0,
35166.0, 35170.9, 35154.1, 35186.0, 35143.9, 35080.1, 35021.1, 34950.1, 34966.0,
35012.3, 35022.2,
];
let param_acceleration = 0.02;
let param_maximum = 0.2;
let mut output_sar = vec![0.0; input_high.len()];
let mut output_is_long = vec![false; input_high.len()];
let mut output_af = vec![0.0; input_high.len()];
let mut output_ep = vec![0.0; input_high.len()];
sar(
&input_high,
&input_low,
param_acceleration,
param_maximum,
&mut output_sar,
&mut output_is_long,
&mut output_af,
&mut output_ep,
)
.unwrap();
assert!(output_sar[0].is_nan());
let expected_values = [
35266.0,
35264.81,
35261.4176,
35_253.574_544,
35130.7,
35133.246,
35138.43216,
35_147.016_230_4,
35_155.085_256_576,
35_162.670_141_181_44,
35281.5,
35278.952,
35_276.454_959_999_995,
35_271.152_761_599_995,
35_259.689_595_904,
35_240.602_428_231_68,
35_211.552_185_408_51,
35_185.406_966_867_66,
35_161.876_270_180_896,
];
for i in 1..expected_values.len() {
assert_relative_eq!(output_sar[i], expected_values[i - 1], epsilon = 0.0001);
}
let mut prev_sar = output_sar[1];
let mut is_long = output_is_long[1];
let mut af = param_acceleration;
let mut ep = output_ep[1];
for i in 2..6 {
let (sar_value, new_is_long, new_af, new_ep) = sar_inc(
input_high[i],
input_low[i],
input_high[i - 1],
input_low[i - 1],
prev_sar,
is_long,
af,
ep,
param_acceleration,
param_maximum,
)
.unwrap();
assert_relative_eq!(sar_value, output_sar[i], epsilon = 0.0001);
prev_sar = sar_value;
is_long = new_is_long;
af = new_af;
ep = new_ep;
}
}
}