use super::{ema, roc};
use crate::{KandError, TAFloat};
pub fn lookback(param_period: usize) -> Result<usize, KandError> {
Ok(3 * ema::lookback(param_period)? + roc::lookback(1)?)
}
pub fn trix(
input: &[TAFloat],
param_period: usize,
output: &mut [TAFloat],
ema1_output: &mut [TAFloat],
ema2_output: &mut [TAFloat],
ema3_output: &mut [TAFloat],
) -> Result<(), KandError> {
let len = input.len();
let lookback = lookback(param_period)?;
#[cfg(feature = "check")]
{
if len == 0 {
return Err(KandError::InvalidData);
}
if len <= lookback {
return Err(KandError::InsufficientData);
}
if len != output.len()
|| len != ema1_output.len()
|| len != ema2_output.len()
|| len != ema3_output.len()
{
return Err(KandError::LengthMismatch);
}
}
#[cfg(feature = "deep-check")]
{
for value in input {
if value.is_nan() {
return Err(KandError::NaNDetected);
}
}
}
ema::ema(input, param_period, None, ema1_output)?;
ema::ema(
&ema1_output[param_period - 1..],
param_period,
None,
&mut ema2_output[param_period - 1..],
)?;
ema::ema(
&ema2_output[2 * (param_period - 1)..],
param_period,
None,
&mut ema3_output[2 * (param_period - 1)..],
)?;
roc::roc(
&ema3_output[3 * (param_period - 1)..],
1,
&mut output[3 * (param_period - 1)..],
)?;
for i in 0..lookback {
output[i] = TAFloat::NAN;
ema1_output[i] = TAFloat::NAN;
ema2_output[i] = TAFloat::NAN;
ema3_output[i] = TAFloat::NAN;
}
Ok(())
}
pub fn trix_inc(
input: TAFloat,
prev_ema1: TAFloat,
prev_ema2: TAFloat,
prev_ema3: TAFloat,
param_period: usize,
) -> Result<(TAFloat, TAFloat, TAFloat, TAFloat), KandError> {
#[cfg(feature = "check")]
{
if param_period < 2 {
return Err(KandError::InvalidParameter);
}
}
#[cfg(feature = "deep-check")]
{
if input.is_nan() || prev_ema1.is_nan() || prev_ema2.is_nan() || prev_ema3.is_nan() {
return Err(KandError::NaNDetected);
}
}
let new_ema1 = ema::ema_inc(input, prev_ema1, param_period, None)?;
let new_ema2 = ema::ema_inc(new_ema1, prev_ema2, param_period, None)?;
let new_ema3 = ema::ema_inc(new_ema2, prev_ema3, param_period, None)?;
let trix = roc::roc_inc(new_ema3, prev_ema3)?;
Ok((trix, new_ema1, new_ema2, new_ema3))
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
#[test]
fn test_trix_calculation() {
let input = vec![
35216.1, 35221.4, 35190.7, 35170.0, 35181.5, 35254.6, 35202.8, 35251.9, 35197.6,
35184.7, 35175.1, 35229.9, 35212.5, 35160.7, 35090.3, 35041.2, 34999.3, 35013.4,
35069.0, 35024.6,
];
let param_period = 3;
let mut output = vec![0.0; input.len()];
let mut ema1 = vec![0.0; input.len()];
let mut ema2 = vec![0.0; input.len()];
let mut ema3 = vec![0.0; input.len()];
trix(
&input,
param_period,
&mut output,
&mut ema1,
&mut ema2,
&mut ema3,
)
.unwrap();
for value in output.iter().take(7) {
assert!(value.is_nan());
}
let expected_values = [
0.023_600_565_750_747_65,
0.007_581_993_206_917_659,
-0.008_943_588_952_370_352,
-0.019_561_690_627_645_234,
-0.002_233_206_002_377_752_2,
0.004_028_034_632_819_2,
-0.013_120_959_352_708_184,
-0.047_983_857_499_556_14,
-0.079_103_810_365_388_5,
-0.099_256_202_780_007_02,
-0.090_591_666_739_903_15,
-0.051_535_144_257_075_505,
-0.037_564_038_843_540_54,
];
for (i, expected) in expected_values.iter().enumerate() {
assert_relative_eq!(output[i + 7], *expected, epsilon = 0.0001);
}
let mut prev_ema1 = ema1[10];
let mut prev_ema2 = ema2[10];
let mut prev_ema3 = ema3[10];
for i in 11..15 {
let (trix_val, new_ema1, new_ema2, new_ema3) =
trix_inc(input[i], prev_ema1, prev_ema2, prev_ema3, param_period).unwrap();
assert_relative_eq!(trix_val, output[i], epsilon = 0.0001);
prev_ema1 = new_ema1;
prev_ema2 = new_ema2;
prev_ema3 = new_ema3;
}
}
}