use crate::common::validate_inputs;
pub use crate::indicator_types::TIndicatorState;
use crate::indicators::homodynediscriminator;
use crate::types::{DisplayGroup, DisplayType, IndicatorError, IndicatorType, Info};
use serde::{Deserialize, Serialize};
#[cfg(feature = "simd_assets")]
pub use crate::indicators::simd_indicators::instantaneoustrendline_simd::indicator_by_assets;
#[cfg(feature = "simd_assets")]
pub mod by_assets {
pub use crate::indicators::simd_indicators::instantaneoustrendline_simd::indicator_by_assets as indicator;
}
pub const INPUTS_WIDTH: usize = 1;
pub const OPTIONS_WIDTH: usize = 0;
pub const INFO: Info = Info {
name: "instantaneoustrendline",
indicator_type: IndicatorType::Cycle,
full_name: "Ehlers Instantaneous Trendline",
inputs: &["real"],
options: &[],
outputs: &["trendline"],
optional_outputs: &["trigger", "dc_period", "alpha"],
display_groups: &[
DisplayGroup {
offset: None,
id: "instantaneoustrendline",
label: "Ehlers Instantaneous Trendline",
display_type: DisplayType::Overlay,
outputs: &["trendline", "trigger"],
},
DisplayGroup {
offset: None,
id: "instantaneoustrendline_dc_period",
label: "IT Dominant Cycle Period",
display_type: DisplayType::Indicator,
outputs: &["dc_period"],
},
DisplayGroup {
offset: None,
id: "instantaneoustrendline_alpha",
label: "IT Adaptive Alpha",
display_type: DisplayType::Indicator,
outputs: &["alpha"],
},
],
};
#[derive(Serialize, Deserialize)]
pub struct State {
pub hd: homodynediscriminator::State,
pub it_prev: f64,
pub it_prev2: f64,
pub alpha: f64,
}
impl State {
pub fn new() -> Self {
Self {
hd: homodynediscriminator::State::new(),
it_prev: 0.0,
it_prev2: 0.0,
alpha: 0.0,
}
}
pub fn init_state(
real: &[f64],
trendline_line: &mut [f64],
trigger_line: &mut [f64],
dc_period_line: &mut [f64],
alpha_line: &mut [f64],
) -> Self {
let mut state = Self::new();
let mut i = 0;
while !state.hd.all_buffers_full() {
state.hd.calc(real[i]);
i += 1;
}
state.it_prev2 =
(state.hd.price_buf[1] + 2.0 * state.hd.price_buf[2] + state.hd.price_buf[3]) / 4.0;
state.it_prev =
(state.hd.price_buf[0] + 2.0 * state.hd.price_buf[1] + state.hd.price_buf[2]) / 4.0;
let it = unsafe { state.calc_unchecked(real[i]) };
trendline_line[0] = it;
let (_, want_trigger, want_dc, want_alpha) =
crate::calc_want_flags!(trigger_line, dc_period_line, alpha_line);
crate::store_optional_outputs!(0,
want_trigger, trigger_line => 2.0 * state.it_prev - state.it_prev2,
want_dc, dc_period_line => state.hd.smooth_period,
want_alpha, alpha_line => state.alpha
);
state
}
#[inline(always)]
pub fn calc(&mut self, price: f64) -> f64 {
self.hd.calc(price);
if !self.hd.all_buffers_full() {
return 0.0;
}
let dc = self.hd.smooth_period;
let alpha = 2.0 / (dc + 1.0);
self.alpha = alpha;
let a2 = alpha * alpha;
let beta = 1.0 - alpha;
let it = (2.0 * beta).mul_add(
self.it_prev,
(-(beta * beta)).mul_add(
self.it_prev2,
(alpha - a2 * 0.25).mul_add(
self.hd.price_buf[0],
(a2 * 0.5).mul_add(
self.hd.price_buf[1],
-(alpha - a2 * 0.75) * self.hd.price_buf[2],
),
),
),
);
self.it_prev2 = self.it_prev;
self.it_prev = it;
it
}
#[inline(always)]
pub unsafe fn calc_unchecked(&mut self, price: f64) -> f64 {
let dc = self.hd.calc_unchecked(price);
let alpha = 2.0 / (dc + 1.0);
self.alpha = alpha;
let a2 = alpha * alpha;
let beta = 1.0 - alpha;
let it = (2.0 * beta).mul_add(
self.it_prev,
(-(beta * beta)).mul_add(
self.it_prev2,
(alpha - a2 * 0.25).mul_add(
self.hd.price_buf[0],
(a2 * 0.5).mul_add(
self.hd.price_buf[1],
-(alpha - a2 * 0.75) * self.hd.price_buf[2], ),
),
),
);
self.it_prev2 = self.it_prev;
self.it_prev = it;
it
}
}
impl Default for State {
fn default() -> Self {
Self::new()
}
}
#[derive(Serialize, Deserialize)]
pub struct IndicatorState {
state: State,
}
impl IndicatorState {
pub fn new(state: State) -> Self {
Self { state }
}
}
impl TIndicatorState<INPUTS_WIDTH> for IndicatorState {
fn batch_indicator(
&mut self,
inputs: &[&[f64]; INPUTS_WIDTH],
optional_outputs: Option<&[bool]>,
) -> Result<Vec<Vec<f64>>, IndicatorError> {
validate_inputs(inputs, 1)?;
let len = inputs[0].len();
let mut trendline_line = crate::uninit_vec!(f64, len);
let (mut trigger_line, mut dc_period_line, mut alpha_line) = crate::init_optional_outputs!(
optional_outputs, &[false, false, false],
trigger_line: len,
dc_period_line: len,
alpha_line: len
);
cycle(
inputs[0],
&mut self.state,
&mut trendline_line,
&mut trigger_line,
&mut dc_period_line,
&mut alpha_line,
);
Ok(vec![
trendline_line,
trigger_line,
dc_period_line,
alpha_line,
])
}
}
pub fn min_data(_options: &[f64]) -> usize {
23
}
pub fn output_length(data_len: usize, options: &[f64]) -> usize {
data_len.saturating_sub(min_data(options) - 1)
}
pub fn indicator(
inputs: &[&[f64]; INPUTS_WIDTH],
options: &[f64; OPTIONS_WIDTH],
optional_outputs: Option<&[bool]>,
) -> Result<(Vec<Vec<f64>>, IndicatorState), IndicatorError> {
validate_inputs(inputs, min_data(options))?;
let real = inputs[0];
let capacity = output_length(real.len(), options);
let mut trendline_line = crate::uninit_vec!(f64, capacity);
let (mut trigger_line, mut dc_period_line, mut alpha_line) = crate::init_optional_outputs!(
optional_outputs, &[false, false, false],
trigger_line: capacity,
dc_period_line: capacity,
alpha_line: capacity
);
let mut state = State::init_state(
real,
&mut trendline_line,
&mut trigger_line,
&mut dc_period_line,
&mut alpha_line,
);
let real_tail = &real[min_data(options)..];
let (_, want_trigger, want_dc, want_alpha) =
crate::calc_want_flags!(trigger_line, dc_period_line, alpha_line);
let trigger_tail = if want_trigger {
&mut trigger_line[1..]
} else {
&mut trigger_line[..]
};
let dc_tail = if want_dc {
&mut dc_period_line[1..]
} else {
&mut dc_period_line[..]
};
let alpha_tail = if want_alpha {
&mut alpha_line[1..]
} else {
&mut alpha_line[..]
};
cycle(
real_tail,
&mut state,
&mut trendline_line[1..],
trigger_tail,
dc_tail,
alpha_tail,
);
Ok((
vec![trendline_line, trigger_line, dc_period_line, alpha_line],
IndicatorState::new(state),
))
}
fn cycle(
real: &[f64],
state: &mut State,
trendline_line: &mut [f64],
trigger_line: &mut [f64],
dc_period_line: &mut [f64],
alpha_line: &mut [f64],
) {
let (has_optional, want_trigger, want_dc, want_alpha) =
crate::calc_want_flags!(trigger_line, dc_period_line, alpha_line);
for i in 0..real.len() {
let it = unsafe { state.calc_unchecked(*real.get_unchecked(i)) };
unsafe {
*trendline_line.get_unchecked_mut(i) = it;
}
if has_optional {
crate::store_optional_outputs!(i,
want_trigger, trigger_line => 2.0 * state.it_prev - state.it_prev2,
want_dc, dc_period_line => state.hd.smooth_period,
want_alpha, alpha_line => state.alpha
);
}
}
}