use crate::common::{validate_inputs, validate_options};
pub use crate::indicator_types::TIndicatorState;
pub use crate::indicators::max::{min_data, output_length};
use crate::indicators::{
max::State as MaxState,
medprice::calc as calc_medprice,
min::State as MinState,
};
use crate::types::{DisplayGroup, DisplayType, IndicatorError, IndicatorType, Info};
use serde::{Deserialize, Serialize};
pub const INPUTS_WIDTH: usize = 2;
pub const OPTIONS_WIDTH: usize = 1;
#[cfg(feature = "simd_assets")]
pub use crate::indicators::simd_indicators::donchianchannel_simd::indicator_by_assets;
#[cfg(feature = "simd_options")]
pub use crate::indicators::simd_indicators::donchianchannel_simd::indicator_by_options;
#[cfg(feature = "simd_assets")]
pub mod by_assets {
pub use crate::indicators::simd_indicators::donchianchannel_simd::indicator_by_assets as indicator;
}
#[cfg(feature = "simd_options")]
pub mod by_options {
pub use crate::indicators::simd_indicators::donchianchannel_simd::indicator_by_options as indicator;
}
#[derive(Serialize, Deserialize)]
pub struct IndicatorState {
high: Vec<f64>,
low: Vec<f64>,
state: State,
periods: (usize, usize),
}
impl IndicatorState {
pub fn new(state: State, high: &[f64], low: &[f64], periods: (usize, usize)) -> Self {
Self {
high: high[high.len() - periods.1..].to_vec(),
low: low[low.len() - periods.1..].to_vec(),
state,
periods,
}
}
}
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)?;
self.high.extend_from_slice(inputs[0]);
self.low.extend_from_slice(inputs[1]);
let (mut lower_line, mut middle_line, mut upper_line) = {
let capacity = inputs[0].len();
(
crate::uninit_vec!(f64, capacity),
crate::uninit_vec!(f64, capacity),
crate::uninit_vec!(f64, capacity),
)
};
match self.periods.0 {
1..=14 => {
cycle::<1>(
(&self.high, &self.low),
self.periods,
(&mut lower_line, &mut middle_line, &mut upper_line),
&mut self.state,
);
}
15..=50 => {
cycle::<4>(
(&self.high, &self.low),
self.periods,
(&mut lower_line, &mut middle_line, &mut upper_line),
&mut self.state,
);
}
_ => {
cycle::<8>(
(&self.high, &self.low),
self.periods,
(&mut lower_line, &mut middle_line, &mut upper_line),
&mut self.state,
);
}
}
self.high.drain(..self.high.len() - self.periods.1);
self.low.drain(..self.low.len() - self.periods.1);
Ok(vec![lower_line, middle_line, upper_line])
}
}
#[derive(Serialize, Deserialize)]
pub struct State {
pub min_state: MinState,
pub max_state: MaxState,
}
impl State {
pub fn new(high: &[f64], low: &[f64], periods: (usize, usize)) -> Self {
let min_state = MinState::new(low[0], periods.1);
let max_state = MaxState::new(high[0], periods.1);
State {
min_state,
max_state,
}
}
#[inline(always)]
pub fn calc(
&mut self,
inputs: (&[f64], &[f64]),
i: usize,
periods: (usize, usize),
) -> (f64, f64, f64) {
let (high, low) = inputs;
let (min, _) = self.min_state.calc(low, i, periods);
let (max, _) = self.max_state.calc(high, i, periods);
let middle = calc_medprice(max, min);
(min, middle, max)
}
#[inline(always)]
pub(crate) unsafe fn calc_unchecked<const N: usize>(
&mut self,
inputs: (&[f64], &[f64]),
i: usize,
periods: (usize, usize),
) -> (f64, f64, f64) {
let (high, low) = inputs;
let (min, _) = self.min_state.calc_unchecked::<N>(low, i, periods);
let (max, _) = self.max_state.calc_unchecked::<N>(high, i, periods);
let middle = calc_medprice(max, min);
(min, middle, max)
}
}
pub const INFO: Info = Info {
name: "donchianchannel",
full_name: "Donchian Channel",
indicator_type: IndicatorType::Trend,
inputs: &["high", "low"],
options: &["period"],
outputs: &["lower", "middle", "upper"],
optional_outputs: &[],
display_groups: &[DisplayGroup {
offset: None,
id: "donchianchannel",
label: "Donchian Channel",
display_type: DisplayType::Overlay,
outputs: &["lower", "middle", "upper"],
}],
};
pub fn indicator(
inputs: &[&[f64]; INPUTS_WIDTH],
options: &[f64; OPTIONS_WIDTH],
_optional_outputs: Option<&[bool]>,
) -> Result<(Vec<Vec<f64>>, IndicatorState), IndicatorError> {
validate_options(options)?;
validate_inputs(inputs, min_data(options))?;
let periods = (options[0] as usize, options[0] as usize - 1);
let [high, low] = inputs;
let (mut lower_line, mut middle_line, mut upper_line) = {
let capacity = output_length(high.len(), options);
(
crate::uninit_vec!(f64, capacity),
crate::uninit_vec!(f64, capacity),
crate::uninit_vec!(f64, capacity),
)
};
let mut state = State::new(high, low, periods);
match periods.0 {
1..=14 => {
cycle::<1>(
(high, low),
periods,
(&mut lower_line, &mut middle_line, &mut upper_line),
&mut state,
);
}
15..=50 => {
cycle::<4>(
(high, low),
periods,
(&mut lower_line, &mut middle_line, &mut upper_line),
&mut state,
);
}
_ => {
cycle::<8>(
(high, low),
periods,
(&mut lower_line, &mut middle_line, &mut upper_line),
&mut state,
);
}
}
Ok((
vec![lower_line, middle_line, upper_line],
IndicatorState::new(state, high, low, periods),
))
}
fn cycle<const N: usize>(
inputs: (&[f64], &[f64]),
periods: (usize, usize),
output_lines: (&mut [f64], &mut [f64], &mut [f64]),
state: &mut State,
) {
let (lower_line, middle_line, upper_line) = output_lines;
for (j, i) in (periods.1..inputs.0.len()).enumerate() {
unsafe {
let (lower, middle, upper) = state.calc_unchecked::<N>(inputs, i, periods);
*lower_line.get_unchecked_mut(j) = lower;
*middle_line.get_unchecked_mut(j) = middle;
*upper_line.get_unchecked_mut(j) = upper;
}
}
}
#[inline(always)]
pub fn calc(
state: &mut State,
inputs: (&[f64], &[f64]),
i: usize,
periods: (usize, usize),
) -> (f64, f64, f64) {
state.calc(inputs, i, periods)
}
#[inline(always)]
pub unsafe fn calc_unchecked<const N: usize>(
state: &mut State,
inputs: (&[f64], &[f64]),
i: usize,
periods: (usize, usize),
) -> (f64, f64, f64) {
state.calc_unchecked::<N>(inputs, i, periods)
}