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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//use crate::common::validate_inputs;
use crate::common_simd::assets::validate_inputs;
use crate::indicators::simd_indicators::road_train::{Asset, Driver, PrimeMover};
use crate::indicators::simd_indicators::vwap_simd::SimdState;
use crate::indicators::vwap::{
min_data, output_length, IndicatorState as State, INPUTS_WIDTH, OPTIONS_WIDTH,
};
use crate::types::IndicatorError;
use std::simd::Simd;
/// SIMD driver that advances the VWAP indicator across `N` asset lanes per scheduling epoch.
struct VwapDriver {
want_optional_outputs: bool,
}
impl Driver<State> for VwapDriver {
/// Processes one epoch of bars for `N` assets simultaneously using SIMD.
fn next_run<const N: usize>(
&mut self,
inputs: Vec<Vec<&[f64]>>,
mut outputs: Vec<Vec<&mut [f64]>>,
mut states: Vec<&mut State>,
_options: Vec<Option<&()>>,
) {
let len = inputs[0][0].len();
let mut state = SimdState::new(&states);
// Optimization 2: Pre-compute all input and output pointers
let (high_ptrs, low_ptrs, close_ptrs, volume_ptrs) =
crate::extract_input_ptrs!(inputs, N, high_ptrs, low_ptrs, close_ptrs, volume_ptrs);
let (vwap_line_ptr, typprice_line_ptr) = crate::extract_output_ptrs!(outputs, N, vwap, tp);
let want_tp = self.want_optional_outputs;
// Optimization 3: Simplified main loop with pre-computed offsets
for i in 0..len {
let (high, low, close, volume) = crate::extract_simd_inputs_at_index!(i, N,
high @ high_ptrs,
low @ low_ptrs,
close @ close_ptrs,
volume @ volume_ptrs
);
let (vwap, tp) = state.calc_simd(high, low, close, volume);
// Store results using pre-computed pointers
crate::write_simd_at_indices!(N, i,
vwap_line_ptr => vwap
);
crate::store_simd_optional_outputs!(i, N,
want_tp, typprice_line_ptr => tp
);
}
state.write_states(&mut states);
}
}
/// Calculates the Volume Weighted Average Price (VWAP) for `N` assets simultaneously
/// using SIMD parallelism.
///
/// VWAP takes no configurable options and produces one optional output (`typprice`).
/// Uses the [`PrimeMover`] scheduler to batch assets into SIMD-width groups.
///
/// # Arguments
/// * `inputs` - An array of `N` asset input sets; `inputs[i]` is `[&[f64]; INPUTS_WIDTH]`
/// containing `[high, low, close, volume]` for asset `i`.
/// * `_options` - Unused; VWAP has no configurable options.
/// * `optional_outputs` - Pass `Some(&[true])` to include `typprice` as `outputs[i][1]`.
///
/// # Returns
/// `Ok((outputs, states))` where `outputs[i][0]` is the VWAP line for asset `i`,
/// `outputs[i][1]` is `typprice` (empty unless requested), and
/// `states[i]` is the final [`State`] for asset `i`.
/// Returns `Err(IndicatorError)` if any input slice is too short.
pub fn indicator_by_assets<const N: usize>(
inputs: &[&[&[f64]; INPUTS_WIDTH]; N], //stock[ fields [ field [f64] ] ]
_options: &[f64; OPTIONS_WIDTH],
optional_outputs: Option<&[bool]>,
) -> Result<(Vec<Vec<Vec<f64>>>, Vec<State>), IndicatorError> {
validate_inputs::<INPUTS_WIDTH>(inputs, min_data(&[]))?;
let mut road_train = PrimeMover::<N, State>::new();
let mut want_optional_outputs = false;
let mut output_buffers = Vec::with_capacity(N);
for i in 0..N {
let [high, low, close, volume] = *inputs[i];
let asset_inputs = vec![high, low, close, volume];
let (vwap_line, typprice_line) = {
let capacity = output_length(high.len(), &[]);
(
crate::uninit_vec!(f64, capacity),
crate::init_optional_outputs_eff!(
optional_outputs, &[false],
tp: capacity
),
)
};
let state = State::new();
if i == 0 {
(_, want_optional_outputs) = crate::calc_want_flags!(typprice_line);
}
let mut output_buffer = vec![vwap_line, typprice_line];
//let adosc_len = output_buffer[0].len();
let mut asset_outputs = Vec::with_capacity(output_buffer.len());
for j in 0..output_buffer.len() {
unsafe {
//let slice_len = output_buffer.len() - starts[j];
// Get a mutable reference to the output buffer for this asset
let output_buffer = &mut output_buffer[j];
asset_outputs.push(std::slice::from_raw_parts_mut(
output_buffer.as_mut_ptr(), //slice from
output_buffer.len(), // slice to
));
}
}
road_train.add_asset(Asset::new(
asset_inputs,
asset_outputs,
i,
0,
0,
state,
None,
));
output_buffers.push(output_buffer);
}
let mut driver = VwapDriver {
want_optional_outputs,
};
let states_vec = road_train.drive(&mut driver);
Ok((output_buffers, states_vec))
}