1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#[cfg(feature = "simd_assets")]
pub use crate::indicators::simd_indicators::by_asset::zlema::indicator_by_assets;
#[cfg(feature = "simd_options")]
pub use crate::indicators::simd_indicators::by_option::zlema::indicator_by_options;
use crate::indicators::zlema::State;
use std::simd::{Simd, StdFloat};
/// SIMD-parallel state for the Zero-Lag Exponential Moving Average (ZLEMA) indicator, holding `N` lanes of per-asset state.
pub struct SimdState<const N: usize> {
pub zlema: Simd<f64, N>,
pub per: Simd<f64, N>,
pub multiplier: Simd<f64, N>,
}
impl<const N: usize> SimdState<N> {
/// Constructs a `SimdState` by gathering scalar per-asset states into SIMD vectors.
pub fn new(states: &[&mut State]) -> Self {
let mut zlema = [0.0; N];
let mut per = [0.0; N];
let mut multiplier = [0.0; N];
for i in 0..N {
zlema[i] = states[i].zlema;
per[i] = states[i].per;
multiplier[i] = states[i].multiplier;
}
Self {
zlema: Simd::from_array(zlema),
per: Simd::from_array(per),
multiplier: Simd::from_array(multiplier),
}
}
/// Converts the SIMD state into an array of `N` scalar [`State`] values.
pub fn to_states(&self) -> [State; N] {
let zlema = self.zlema.to_array();
let per = self.per.to_array();
let multiplier = self.multiplier.to_array();
let states: [State; N] = std::array::from_fn(|i| State {
zlema: zlema[i],
per: per[i],
multiplier: multiplier[i],
});
states
}
/// Writes the current SIMD lane values back into the provided scalar per-asset states.
pub fn write_states(&self, states: &mut [&mut State]) {
let zlema = self.zlema.to_array();
for i in 0..N {
states[i].zlema = zlema[i];
}
}
/// Computes one bar of the Zero-Lag EMA (ZLEMA) for `N` assets simultaneously
/// using SIMD parallelism.
///
/// Adjusts the current price by `current + (current - lagged)` to remove lag,
/// then applies EMA smoothing: `zlema = zlema * per + adjusted * multiplier`.
///
/// # Arguments
///
/// * `current` - Current prices for this bar.
/// * `lagged` - Prices from `(period - 1) / 2` bars ago.
///
/// # Returns
///
/// Updated ZLEMA values for all `N` lanes.
#[inline(always)]
pub fn calc_simd(&mut self, current: Simd<f64, N>, lagged: Simd<f64, N>) -> Simd<f64, N> {
let adjusted = current + (current - lagged);
self.zlema = self.zlema.mul_add(self.per, adjusted * self.multiplier);
//self.zlema = self.zlema * self.per + adjusted * self.multiplier;
self.zlema
}
}
/// Computes one bar of the Zero-Lag EMA (ZLEMA) for `N` assets simultaneously
/// using SIMD parallelism.
///
/// Thin wrapper delegating to [`SimdState::calc_simd`].
///
/// # Arguments
///
/// * `state` - Mutable SIMD state holding current ZLEMA values and smoothing coefficients.
/// * `current` - Current prices for this bar.
/// * `lagged` - Prices from `(period - 1) / 2` bars ago.
///
/// # Returns
///
/// Updated ZLEMA values for all `N` lanes.
pub fn calc_simd<const N: usize>(
state: &mut SimdState<N>,
current: Simd<f64, N>,
lagged: Simd<f64, N>,
) -> Simd<f64, N> {
state.calc_simd(current, lagged)
}