use crate::common::validate_inputs;
pub use crate::indicator_types::TIndicatorState;
use crate::indicators::medprice::calc as calc_medprice;
use crate::types::{DisplayType, IndicatorError, IndicatorType, Info};
use serde::{Deserialize, Serialize};
pub const INPUTS_WIDTH: usize = 3;
pub const OPTIONS_WIDTH: usize = 0;
#[cfg(feature = "simd_assets")]
pub use crate::indicators::simd_indicators::emv_simd::indicator_by_assets;
#[cfg(feature = "simd_assets")]
pub mod by_assets {
pub use crate::indicators::simd_indicators::emv_simd::indicator_by_assets as indicator;
}
#[derive(Serialize, Deserialize)]
pub struct IndicatorState {
prev_medprice: f64,
}
impl IndicatorState {
pub fn new(prev_medprice: f64) -> Self {
Self { prev_medprice }
}
}
impl TIndicatorState<3> 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 (mut emv_line, mut medprice_line) = {
let capacity = inputs[0].len();
(
crate::uninit_vec!(f64, capacity),
crate::init_optional_outputs_eff!(
optional_outputs, &[false],
medprice_line: capacity
),
)
};
let [high, low, volume] = inputs;
cycle_emv(
high,
low,
volume,
&mut self.prev_medprice,
&mut emv_line,
&mut medprice_line,
);
Ok(vec![emv_line, medprice_line])
}
}
pub fn info() -> Info<'static> {
Info {
name: "emv",
display_type: DisplayType::Indicator,
indicator_type: IndicatorType::Momentum,
full_name: "Ease of Movement",
inputs: &["high", "low", "volume"],
options: &[],
outputs: &["emv"],
optional_outputs: &["medprice"],
}
}
pub fn min_data_accuracy(options: &[f64], _decimals: usize) -> usize {
min_data(options)
}
pub fn min_data(_options: &[f64]) -> usize {
2 }
pub fn output_length(data_len: usize, options: &[f64]) -> usize {
data_len - 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 [high, low, volume] = inputs;
let mut prev_medprice = calc_medprice(high[0], low[0]);
let (mut emv_line, mut medprice_line);
{
let capacity = output_length(high.len(), _options);
let medprice_capacity = high.len();
emv_line = crate::uninit_vec!(f64, capacity);
medprice_line = crate::init_optional_outputs_eff!(
optional_outputs, &[false],
medprice_line: medprice_capacity
);
crate::init_store_optional_outputs!(0, medprice_capacity,
medprice_line => prev_medprice
);
}
let medprice = {
let offset = crate::slice_outputs_start!(emv_line.len(), medprice_line);
&mut medprice_line[offset..]
};
let (high, low, volume) = (&high[1..], &low[1..], &volume[1..]);
cycle_emv(
high,
low,
volume,
&mut prev_medprice,
&mut emv_line,
medprice,
);
Ok((
vec![emv_line, medprice_line],
IndicatorState { prev_medprice },
))
}
fn cycle_emv(
high: &[f64],
low: &[f64],
volume: &[f64],
prev_medprice: &mut f64,
emv_line: &mut [f64],
medprice_line: &mut [f64],
) {
let (_, want_medprice) = crate::calc_want_flags!(medprice_line);
for i in 0..high.len() {
unsafe {
*emv_line.get_unchecked_mut(i) = calc(
*high.get_unchecked(i),
*low.get_unchecked(i),
*volume.get_unchecked(i),
prev_medprice,
);
}
crate::store_optional_outputs!(i,
want_medprice, medprice_line => *prev_medprice);
}
}
#[inline(always)]
pub fn calc(high: f64, low: f64, volume: f64, prev_medprice: &mut f64) -> f64 {
let medprice = calc_medprice(high, low);
let distance_moved = medprice - *prev_medprice;
let hl_diff = (high - low).max(f64::EPSILON);
let volume_safe = volume.max(f64::EPSILON);
*prev_medprice = medprice;
distance_moved * 10000.0 * hl_diff / volume_safe
}