use crate::common::{validate_inputs, validate_options};
pub use crate::indicator_types::TIndicatorState;
use crate::ring_buffer::single_buffer::generic_buffer::{RingBuffer, SimdBuffer};
use crate::types::{DisplayGroup, DisplayType, IndicatorError, IndicatorType, Info};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::simd::Simd;
pub const INPUTS_WIDTH: usize = 4;
pub const OPTIONS_WIDTH: usize = 1;
#[cfg(feature = "simd_assets")]
pub use crate::indicators::simd_indicators::chaikinmf_simd::indicator_by_assets;
#[cfg(feature = "simd_options")]
pub use crate::indicators::simd_indicators::chaikinmf_simd::indicator_by_options;
#[cfg(feature = "simd_assets")]
pub mod by_assets {
pub use crate::indicators::simd_indicators::chaikinmf_simd::indicator_by_assets as indicator;
}
#[cfg(feature = "simd_options")]
pub mod by_options {
pub use crate::indicators::simd_indicators::chaikinmf_simd::indicator_by_options as indicator;
}
pub const INFO: Info = Info {
name: "chaikinmf",
indicator_type: IndicatorType::Volume,
full_name: "Chaikin Money Flow",
inputs: &["high", "low", "close", "volume"],
options: &["period"],
outputs: &["cmf"],
optional_outputs: &[],
display_groups: &[DisplayGroup {
id: "cmf",
label: "Chaikin Money Flow",
display_type: DisplayType::Indicator,
outputs: &["cmf"],
}],
};
impl TIndicatorState<4> 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 [high, low, close, volume] = inputs;
let mut cmf_line = {
let capacity = inputs[0].len();
crate::uninit_vec!(f64, capacity)
};
cycle_mfi((high, low, close, volume), self, &mut cmf_line);
Ok(vec![cmf_line])
}
}
#[derive(Serialize, Deserialize)]
pub struct IndicatorState {
pub buffer: SimdBuffer<2>,
#[serde(
serialize_with = "serialize_f64x2",
deserialize_with = "deserialize_f64x2"
)]
pub sums: Simd<f64, 2>,
}
fn serialize_f64x2<S>(data: &Simd<f64, 2>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
data.to_array().serialize(serializer)
}
fn deserialize_f64x2<'de, D>(deserializer: D) -> Result<Simd<f64, 2>, D::Error>
where
D: Deserializer<'de>,
{
let array = <[f64; 2]>::deserialize(deserializer)?;
Ok(Simd::from_array(array))
}
impl IndicatorState {
pub fn new(period: usize) -> Self {
Self {
buffer: SimdBuffer::new(period),
sums: Simd::splat(0.0),
}
}
pub fn init_state(inputs: (&[f64], &[f64], &[f64], &[f64]), period: usize) -> Self {
let (high, low, close, volume) = inputs;
let mut state = Self::new(period);
let mut i = 0;
while !state.buffer.is_full() {
state.calc(high[i], low[i], close[i], volume[i]);
i += 1;
}
state
}
#[inline(always)]
pub fn calc(&mut self, high: f64, low: f64, close: f64, volume: f64) -> f64 {
let mfv = ((close - low) - (high - close)) / (high - low) * volume;
let values = Simd::from_array([mfv, volume]);
if let Some(old_vals) = self.buffer.push_with_info(values) {
self.sums += values - old_vals;
} else {
self.sums += values;
}
self.sums[0] / self.sums[1]
}
#[inline(always)]
pub unsafe fn calc_unchecked(&mut self, high: f64, low: f64, close: f64, volume: f64) -> f64 {
let mfv = ((close - low) - (high - close)) / (high - low).max(f64::EPSILON) * volume;
let values = Simd::from_array([mfv, volume]);
let old_vals = self.buffer.push_with_info_unchecked(values);
self.sums += values - old_vals;
self.sums[0] / self.sums[1]
}
}
pub fn min_data_accuracy(options: &[f64], _decimals: usize) -> usize {
min_data(options)
}
pub fn min_data(options: &[f64]) -> usize {
options[0] as usize + 1
}
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_options(options)?;
let period = options[0] as usize;
let [high, low, close, volume] = *inputs;
validate_inputs(inputs, min_data(options))?;
let mut cmf_line = {
let len = inputs[0].len();
let capacity = output_length(len, options);
crate::uninit_vec!(f64, capacity)
};
let mut state = IndicatorState::init_state((high, low, close, volume), period);
cycle_mfi(
(
&high[period..],
&low[period..],
&close[period..],
&volume[period..],
),
&mut state,
&mut cmf_line,
);
Ok((vec![cmf_line], state))
}
fn cycle_mfi(
inputs: (&[f64], &[f64], &[f64], &[f64]),
state: &mut IndicatorState,
cmf_line: &mut [f64],
) {
let (high, low, close, volume) = inputs;
for i in 0..volume.len() {
unsafe {
*cmf_line.get_unchecked_mut(i) = state.calc_unchecked(
*high.get_unchecked(i),
*low.get_unchecked(i),
*close.get_unchecked(i),
*volume.get_unchecked(i),
);
}
}
}