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::mama_simd::indicator_by_assets;
#[cfg(feature = "simd_options")]
pub use crate::indicators::simd_indicators::mama_simd::indicator_by_options;
#[cfg(feature = "simd_assets")]
pub mod by_assets {
pub use crate::indicators::simd_indicators::mama_simd::indicator_by_assets as indicator;
}
#[cfg(feature = "simd_options")]
pub mod by_options {
pub use crate::indicators::simd_indicators::mama_simd::indicator_by_options as indicator;
}
pub const INPUTS_WIDTH: usize = 1;
pub const OPTIONS_WIDTH: usize = 2;
pub const INFO: Info = Info {
name: "mama",
indicator_type: IndicatorType::Trend,
full_name: "MESA Adaptive Moving Average",
inputs: &["real"],
options: &["fast_limit", "slow_limit"],
outputs: &["mama", "fama"],
optional_outputs: &["dc_period", "alpha"],
display_groups: &[
DisplayGroup {
offset: None,
id: "mama",
label: "MAMA / FAMA",
display_type: DisplayType::Overlay,
outputs: &["mama", "fama"],
},
DisplayGroup {
offset: None,
id: "mama_dc_period",
label: "MAMA Dominant Cycle Period",
display_type: DisplayType::Indicator,
outputs: &["dc_period"],
},
DisplayGroup {
offset: None,
id: "mama_alpha",
label: "MAMA Alpha",
display_type: DisplayType::Indicator,
outputs: &["alpha"],
},
],
};
#[derive(Serialize, Deserialize)]
pub struct State {
pub hd: homodynediscriminator::State,
pub prev_phase: f64,
pub mama: f64,
pub fama: f64,
pub alpha: f64,
}
impl State {
pub fn new() -> Self {
Self {
hd: homodynediscriminator::State::new(),
prev_phase: 0.0,
mama: 0.0,
fama: 0.0,
alpha: 0.0,
}
}
pub fn init_state(
real: &[f64],
fast_limit: f64,
slow_limit: f64,
mama_line: &mut [f64],
fama_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;
}
let seed = real[i];
state.mama = seed;
state.fama = seed;
let (mama, fama) = unsafe { state.calc_unchecked(real[i], fast_limit, slow_limit) };
mama_line[0] = mama;
fama_line[0] = fama;
let (_, want_dc, want_alpha) = crate::calc_want_flags!(dc_period_line, alpha_line);
crate::store_optional_outputs!(0,
want_dc, dc_period_line => state.hd.smooth_period,
want_alpha, alpha_line => state.alpha
);
state
}
#[inline(always)]
pub fn calc(&mut self, real: f64, fast_limit: f64, slow_limit: f64) -> (f64, f64) {
let (_, i1, q1) = self.hd.calc_with_iq(real);
if !self.hd.all_buffers_full() {
return (0.0, 0.0);
}
self.apply_mama(real, i1, q1, fast_limit, slow_limit);
(self.mama, self.fama)
}
#[inline(always)]
pub unsafe fn calc_unchecked(
&mut self,
real: f64,
fast_limit: f64,
slow_limit: f64,
) -> (f64, f64) {
let (_, i1, q1) = self.hd.calc_unchecked_with_iq(real);
self.apply_mama(real, i1, q1, fast_limit, slow_limit);
(self.mama, self.fama)
}
#[inline(always)]
fn apply_mama(&mut self, real: f64, i1: f64, q1: f64, fast_limit: f64, slow_limit: f64) {
const RAD_TO_DEG: f64 = 180.0 / std::f64::consts::PI;
let phase = if i1 != 0.0 {
(q1 / i1).atan() * RAD_TO_DEG
} else {
0.0
};
let delta_phase = (self.prev_phase - phase).max(1.0);
self.prev_phase = phase;
self.alpha = (fast_limit / delta_phase).clamp(slow_limit, fast_limit);
self.mama = self.alpha.mul_add(real, (1.0 - self.alpha) * self.mama);
let half_alpha = 0.5 * self.alpha;
self.fama = half_alpha.mul_add(self.mama, (1.0 - half_alpha) * self.fama);
}
}
impl Default for State {
fn default() -> Self {
Self::new()
}
}
#[derive(Serialize, Deserialize)]
pub struct IndicatorState {
state: State,
fast_limit: f64,
slow_limit: f64,
}
impl IndicatorState {
pub fn new(state: State, fast_limit: f64, slow_limit: f64) -> Self {
Self {
state,
fast_limit,
slow_limit,
}
}
}
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 mama_line, mut fama_line, (mut dc_period_line, mut alpha_line)) = (
crate::uninit_vec!(f64, len),
crate::uninit_vec!(f64, len),
crate::init_optional_outputs!(
optional_outputs, &[false, false],
dc_period_line: len,
alpha_line: len
),
);
cycle(
inputs[0],
&mut self.state,
self.fast_limit,
self.slow_limit,
&mut mama_line,
&mut fama_line,
(&mut dc_period_line, &mut alpha_line),
);
Ok(vec![mama_line, fama_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 - min_data(options) + 1
}
pub(crate) fn validate_options(options: &[f64; OPTIONS_WIDTH]) -> Result<(), IndicatorError> {
let fast = options[0];
let slow = options[1];
if fast <= 0.0 || fast > 1.0 || slow <= 0.0 || slow >= fast {
return Err(IndicatorError::InvalidOptions);
}
Ok(())
}
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 fast_limit = options[0];
let slow_limit = options[1];
validate_inputs(inputs, min_data(options))?;
let real = inputs[0];
let capacity = output_length(real.len(), options);
let (mut mama_line, mut fama_line, (mut dc_period_line, mut alpha_line)) = (
crate::uninit_vec!(f64, capacity),
crate::uninit_vec!(f64, capacity),
crate::init_optional_outputs!(
optional_outputs, &[false, false],
dc_period_line: capacity,
alpha_line: capacity
),
);
let mut state = State::init_state(
real,
fast_limit,
slow_limit,
&mut mama_line,
&mut fama_line,
&mut dc_period_line,
&mut alpha_line,
);
let real_tail = &real[min_data(options)..];
let (_, want_dc, want_alpha) = crate::calc_want_flags!(dc_period_line, alpha_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,
fast_limit,
slow_limit,
&mut mama_line[1..],
&mut fama_line[1..],
(dc_tail, alpha_tail),
);
Ok((
vec![mama_line, fama_line, dc_period_line, alpha_line],
IndicatorState::new(state, fast_limit, slow_limit),
))
}
fn cycle(
real: &[f64],
state: &mut State,
fast_limit: f64,
slow_limit: f64,
mama_line: &mut [f64],
fama_line: &mut [f64],
optional_outputs: (&mut [f64], &mut [f64]),
) {
let (dc_period_line, alpha_line) = optional_outputs;
let (has_optional, want_dc, want_alpha) = crate::calc_want_flags!(dc_period_line, alpha_line);
for i in 0..real.len() {
let (mama, fama) =
unsafe { state.calc_unchecked(*real.get_unchecked(i), fast_limit, slow_limit) };
unsafe {
*mama_line.get_unchecked_mut(i) = mama;
*fama_line.get_unchecked_mut(i) = fama;
}
if has_optional {
crate::store_optional_outputs!(i,
want_dc, dc_period_line => state.hd.smooth_period,
want_alpha, alpha_line => state.alpha
);
}
}
}