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
101
102
103
104
105
106
107
#[cfg(feature = "simd_assets")]
pub use crate::indicators::simd_indicators::by_asset::trima::indicator_by_assets;
#[cfg(feature = "simd_options")]
pub use crate::indicators::simd_indicators::by_option::trima::indicator_by_options;
use crate::indicators::trima::State;
use std::simd::Simd;
/// SIMD-parallel state for computing the Triangular Moving Average (TRIMA) across `N` assets simultaneously.
/// Each field is a SIMD vector where lane `i` corresponds to asset `i`.
pub struct SimdState<const N: usize> {
/// Weighted running sum (numerator of the current TRIMA) for each lane.
pub weight_sum: Simd<f64, N>,
/// Running sum of values entering the leading half of the triangular window for each lane.
pub lead_sum: Simd<f64, N>,
/// Running sum of values in the trailing half of the triangular window for each lane.
pub trail_sum: Simd<f64, N>,
}
impl<const N: usize> SimdState<N> {
/// Gathers `N` scalar [`State`] references into a single `SimdState`, packing each field into a SIMD lane.
pub fn new(states: &[&mut State]) -> Self {
let mut weight_sum = [0.0; N];
let mut lead_sum = [0.0; N];
let mut trail_sum = [0.0; N];
for i in 0..N {
weight_sum[i] = states[i].weight_sum;
lead_sum[i] = states[i].lead_sum;
trail_sum[i] = states[i].trail_sum;
}
Self {
weight_sum: Simd::from_array(weight_sum),
lead_sum: Simd::from_array(lead_sum),
trail_sum: Simd::from_array(trail_sum),
}
}
/// Scatters the SIMD state back into an array of `N` scalar [`State`] values.
pub fn to_states(&self) -> [State; N] {
let weight_sum = self.weight_sum.to_array();
let lead_sum = self.lead_sum.to_array();
let trail_sum = self.trail_sum.to_array();
let states: [State; N] =
std::array::from_fn(|i| State::new(weight_sum[i], lead_sum[i], trail_sum[i]));
states
}
/// Writes the SIMD state back into `N` existing mutable scalar [`State`] references in place.
pub fn write_states(&self, states: &mut [&mut State]) {
let weight_sum = self.weight_sum.to_array();
let lead_sum = self.lead_sum.to_array();
let trail_sum = self.trail_sum.to_array();
for i in 0..N {
states[i].weight_sum = weight_sum[i];
states[i].lead_sum = lead_sum[i];
states[i].trail_sum = trail_sum[i];
}
}
/// Advances one bar of the TRIMA computation for `N` lanes simultaneously.
///
/// The TRIMA is a double-smoothed SMA. On each step the weighted sum is updated by adding
/// the incoming value and the lead sum, then removing the trail sum. The result is multiplied
/// by `1 / triangular_weight` to produce the average.
///
/// # Returns
///
/// The TRIMA value for the current bar across all `N` lanes.
pub fn calc_simd(
&mut self,
real: Simd<f64, N>,
lsi: Simd<f64, N>,
tsi1: Simd<f64, N>,
tsi2: Simd<f64, N>,
multiplier: Simd<f64, N>,
) -> Simd<f64, N> {
//calc_simd(self, real, lsi, tsi1, tsi2, multiplier)
self.weight_sum += real;
let trima = self.weight_sum * multiplier;
self.lead_sum += real;
self.weight_sum += self.lead_sum - self.trail_sum;
self.lead_sum -= lsi;
self.trail_sum += tsi1 - tsi2;
trima
}
}
/// Advances one bar of the TRIMA computation for `N` lanes simultaneously.
///
/// Delegates to [`SimdState::calc_simd`]. Returns the TRIMA value for the current bar.
#[inline(always)]
pub fn calc_simd<const N: usize>(
state: &mut SimdState<N>,
real: Simd<f64, N>,
lsi: Simd<f64, N>,
tsi1: Simd<f64, N>,
tsi2: Simd<f64, N>,
multiplier: Simd<f64, N>,
) -> Simd<f64, N> {
state.calc_simd(real, lsi, tsi1, tsi2, multiplier)
}