1use crate::indicators::alma::{alma, AlmaData, AlmaInput, AlmaParams};
2use crate::indicators::cora_wave::{cora_wave, CoraWaveData, CoraWaveInput, CoraWaveParams};
3use crate::indicators::cwma::{cwma, CwmaData, CwmaInput, CwmaParams};
4use crate::indicators::dema::{dema, DemaData, DemaInput, DemaParams};
5use crate::indicators::edcf::{edcf, EdcfData, EdcfInput, EdcfParams};
6use crate::indicators::ehlers_itrend::{
7 ehlers_itrend, EhlersITrendData, EhlersITrendInput, EhlersITrendParams,
8};
9use crate::indicators::ema::{ema, EmaData, EmaInput, EmaParams};
10use crate::indicators::epma::{epma, EpmaData, EpmaInput, EpmaParams};
11use crate::indicators::fwma::{fwma, FwmaData, FwmaInput, FwmaParams};
12use crate::indicators::gaussian::{gaussian, GaussianData, GaussianInput, GaussianParams};
13use crate::indicators::highpass::{highpass, HighPassData, HighPassInput, HighPassParams};
14use crate::indicators::highpass_2_pole::{
15 highpass_2_pole, HighPass2Data, HighPass2Input, HighPass2Params,
16};
17use crate::indicators::hma::{hma, HmaData, HmaInput, HmaParams};
18use crate::indicators::hwma::{hwma, HwmaData, HwmaInput, HwmaParams};
19use crate::indicators::jma::{jma, JmaData, JmaInput, JmaParams};
20use crate::indicators::jsa::{jsa, JsaData, JsaInput, JsaParams};
21use crate::indicators::kama::{kama, KamaData, KamaInput, KamaParams};
22use crate::indicators::linreg::{linreg, LinRegData, LinRegInput, LinRegParams};
23use crate::indicators::maaq::{maaq, MaaqData, MaaqInput, MaaqParams};
24use crate::indicators::mama::{mama, MamaData, MamaInput, MamaParams};
25use crate::indicators::moving_averages::corrected_moving_average::{
26 corrected_moving_average, corrected_moving_average_with_kernel, CorrectedMovingAverageData,
27 CorrectedMovingAverageInput, CorrectedMovingAverageParams,
28};
29use crate::indicators::moving_averages::dma::{dma, DmaData, DmaInput, DmaParams};
30use crate::indicators::moving_averages::ehlers_ecema::{
31 ehlers_ecema, EhlersEcemaData, EhlersEcemaInput, EhlersEcemaParams,
32};
33use crate::indicators::moving_averages::ehlers_kama::{
34 ehlers_kama, EhlersKamaData, EhlersKamaInput, EhlersKamaParams,
35};
36use crate::indicators::moving_averages::ehma::{ehma, EhmaData, EhmaInput, EhmaParams};
37use crate::indicators::moving_averages::elastic_volume_weighted_moving_average::{
38 elastic_volume_weighted_moving_average, elastic_volume_weighted_moving_average_with_kernel,
39 ElasticVolumeWeightedMovingAverageData, ElasticVolumeWeightedMovingAverageInput,
40 ElasticVolumeWeightedMovingAverageParams,
41};
42use crate::indicators::moving_averages::ema_deviation_corrected_t3::{
43 ema_deviation_corrected_t3, EmaDeviationCorrectedT3Data, EmaDeviationCorrectedT3Input,
44 EmaDeviationCorrectedT3Params,
45};
46use crate::indicators::moving_averages::frama::{frama, FramaInput, FramaParams};
47use crate::indicators::moving_averages::n_order_ema::{
48 n_order_ema, n_order_ema_with_kernel, NOrderEmaData, NOrderEmaIirStyle, NOrderEmaInput,
49 NOrderEmaParams, NOrderEmaStyle,
50};
51use crate::indicators::moving_averages::nama::{nama, NamaData, NamaInput, NamaParams};
52use crate::indicators::moving_averages::sama::{sama, SamaData, SamaInput, SamaParams};
53use crate::indicators::moving_averages::sgf::{sgf, SgfData, SgfInput, SgfParams};
54use crate::indicators::moving_averages::volatility_adjusted_ma::{
55 vama, VamaData, VamaInput, VamaParams,
56};
57use crate::indicators::moving_averages::wave_smoother::{
58 wave_smoother, WaveSmootherData, WaveSmootherInput, WaveSmootherParams,
59};
60use crate::indicators::mwdx::{mwdx, MwdxData, MwdxInput, MwdxParams};
61use crate::indicators::nma::{nma, NmaData, NmaInput, NmaParams};
62use crate::indicators::pwma::{pwma, PwmaData, PwmaInput, PwmaParams};
63use crate::indicators::reflex::{reflex, ReflexData, ReflexInput, ReflexParams};
64use crate::indicators::sinwma::{sinwma, SinWmaData, SinWmaInput, SinWmaParams};
65use crate::indicators::sma::{sma, SmaData, SmaInput, SmaParams};
66use crate::indicators::smma::{smma, SmmaData, SmmaInput, SmmaParams};
67use crate::indicators::sqwma::{sqwma, SqwmaData, SqwmaInput, SqwmaParams};
68use crate::indicators::srwma::{srwma, SrwmaData, SrwmaInput, SrwmaParams};
69use crate::indicators::supersmoother::{
70 supersmoother, SuperSmootherData, SuperSmootherInput, SuperSmootherParams,
71};
72use crate::indicators::supersmoother_3_pole::{
73 supersmoother_3_pole, SuperSmoother3PoleData, SuperSmoother3PoleInput, SuperSmoother3PoleParams,
74};
75use crate::indicators::swma::{swma, SwmaData, SwmaInput, SwmaParams};
76use crate::indicators::tema::{tema, TemaData, TemaInput, TemaParams};
77use crate::indicators::tilson::{tilson, TilsonData, TilsonInput, TilsonParams};
78use crate::indicators::trendflex::{trendflex, TrendFlexData, TrendFlexInput, TrendFlexParams};
79use crate::indicators::trima::{trima, TrimaData, TrimaInput, TrimaParams};
80use crate::indicators::vpwma::{vpwma, VpwmaData, VpwmaInput, VpwmaParams};
81use crate::indicators::vwap::{vwap, VwapData, VwapInput, VwapParams};
82use crate::indicators::vwma::{vwma, VwmaData, VwmaInput, VwmaParams};
83use crate::indicators::wilders::{wilders, WildersData, WildersInput, WildersParams};
84use crate::indicators::wma::{wma, WmaData, WmaInput, WmaParams};
85use crate::indicators::zlema::{zlema, ZlemaData, ZlemaInput, ZlemaParams};
86use crate::utilities::data_loader::Candles;
87use crate::utilities::enums::Kernel;
88use std::error::Error;
89use thiserror::Error;
90
91use crate::indicators::alma::alma_with_kernel;
92use crate::indicators::cora_wave::cora_wave_with_kernel;
93use crate::indicators::cwma::cwma_with_kernel;
94use crate::indicators::dema::dema_with_kernel;
95use crate::indicators::edcf::edcf_with_kernel;
96use crate::indicators::ehlers_itrend::ehlers_itrend_with_kernel;
97use crate::indicators::ema::ema_with_kernel;
98use crate::indicators::epma::epma_with_kernel;
99use crate::indicators::fwma::fwma_with_kernel;
100use crate::indicators::gaussian::gaussian_with_kernel;
101use crate::indicators::highpass::highpass_with_kernel;
102use crate::indicators::highpass_2_pole::highpass_2_pole_with_kernel;
103use crate::indicators::hma::hma_with_kernel;
104use crate::indicators::hwma::hwma_with_kernel;
105use crate::indicators::jma::jma_with_kernel;
106use crate::indicators::jsa::jsa_with_kernel;
107use crate::indicators::kama::kama_with_kernel;
108use crate::indicators::linreg::linreg_with_kernel;
109use crate::indicators::maaq::maaq_with_kernel;
110use crate::indicators::mama::mama_with_kernel;
111use crate::indicators::moving_averages::dma::dma_with_kernel;
112use crate::indicators::moving_averages::ehlers_ecema::ehlers_ecema_with_kernel;
113use crate::indicators::moving_averages::ehlers_kama::ehlers_kama_with_kernel;
114use crate::indicators::moving_averages::ehma::ehma_with_kernel;
115use crate::indicators::moving_averages::ema_deviation_corrected_t3::ema_deviation_corrected_t3_with_kernel;
116use crate::indicators::moving_averages::frama::frama_with_kernel;
117use crate::indicators::moving_averages::nama::nama_with_kernel;
118use crate::indicators::moving_averages::sama::sama_with_kernel;
119use crate::indicators::moving_averages::sgf::sgf_with_kernel;
120use crate::indicators::moving_averages::volatility_adjusted_ma::vama_with_kernel;
121use crate::indicators::moving_averages::wave_smoother::wave_smoother_with_kernel;
122use crate::indicators::mwdx::mwdx_with_kernel;
123use crate::indicators::nma::nma_with_kernel;
124use crate::indicators::pwma::pwma_with_kernel;
125use crate::indicators::reflex::reflex_with_kernel;
126use crate::indicators::sinwma::sinwma_with_kernel;
127use crate::indicators::sma::sma_with_kernel;
128use crate::indicators::smma::smma_with_kernel;
129use crate::indicators::sqwma::sqwma_with_kernel;
130use crate::indicators::srwma::srwma_with_kernel;
131use crate::indicators::supersmoother::supersmoother_with_kernel;
132use crate::indicators::supersmoother_3_pole::supersmoother_3_pole_with_kernel;
133use crate::indicators::swma::swma_with_kernel;
134use crate::indicators::tema::tema_with_kernel;
135use crate::indicators::tilson::tilson_with_kernel;
136use crate::indicators::trendflex::trendflex_with_kernel;
137use crate::indicators::trima::trima_with_kernel;
138use crate::indicators::vpwma::vpwma_with_kernel;
139use crate::indicators::vwap::vwap_with_kernel;
140use crate::indicators::vwma::vwma_with_kernel;
141use crate::indicators::wilders::wilders_with_kernel;
142use crate::indicators::wma::wma_with_kernel;
143use crate::indicators::zlema::zlema_with_kernel;
144
145#[cfg(feature = "python")]
146use crate::utilities::kernel_validation::validate_kernel;
147#[cfg(feature = "python")]
148use numpy::{PyArray1, PyReadonlyArray1};
149#[cfg(feature = "python")]
150use pyo3::exceptions::PyValueError;
151#[cfg(feature = "python")]
152use pyo3::prelude::*;
153
154#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
155use serde::{Deserialize, Serialize};
156#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
157use wasm_bindgen::prelude::*;
158
159#[derive(Debug, Clone)]
160pub enum MaData<'a> {
161 Candles {
162 candles: &'a Candles,
163 source: &'a str,
164 },
165 Slice(&'a [f64]),
166}
167
168#[derive(Debug, Error)]
169pub enum MaError {
170 #[error("Unknown moving average type: {ma_type}")]
171 UnknownType { ma_type: String },
172 #[error("{indicator} requires high/low data, use the indicator directly")]
173 RequiresHighLow { indicator: &'static str },
174 #[error("{indicator} requires volume data, use the indicator directly")]
175 RequiresVolume { indicator: &'static str },
176 #[error("{indicator} returns dual outputs, use the indicator directly")]
177 DualOutputNotSupported { indicator: &'static str },
178
179 #[error("input data is empty")]
180 EmptyInputData,
181 #[error("all input values are NaN")]
182 AllValuesNaN,
183 #[error("invalid period {period} for data length {data_len}")]
184 InvalidPeriod { period: usize, data_len: usize },
185 #[error("not enough valid data: needed {needed}, found {valid}")]
186 NotEnoughValidData { needed: usize, valid: usize },
187 #[error("output length mismatch: expected {expected}, got {got}")]
188 OutputLengthMismatch { expected: usize, got: usize },
189 #[error("invalid sigma value: {sigma}")]
190 InvalidSigma { sigma: f64 },
191 #[error("invalid offset value: {offset}")]
192 InvalidOffset { offset: f64 },
193 #[error("invalid range: start={start}, end={end}, step={step}")]
194 InvalidRange { start: f64, end: f64, step: f64 },
195 #[error("invalid kernel for batch path: {0:?}")]
196 InvalidKernelForBatch(Kernel),
197}
198
199#[inline]
200pub fn ma<'a>(ma_type: &str, data: MaData<'a>, period: usize) -> Result<Vec<f64>, Box<dyn Error>> {
201 match ma_type.to_lowercase().as_str() {
202 "sma" => {
203 let input = match data {
204 MaData::Candles { candles, source } => SmaInput {
205 data: SmaData::Candles { candles, source },
206 params: SmaParams {
207 period: Some(period),
208 },
209 },
210 MaData::Slice(slice) => SmaInput {
211 data: SmaData::Slice(slice),
212 params: SmaParams {
213 period: Some(period),
214 },
215 },
216 };
217 let output = sma(&input)?;
218 Ok(output.values)
219 }
220
221 "alma" => {
222 let input = match data {
223 MaData::Candles { candles, source } => AlmaInput {
224 data: AlmaData::Candles { candles, source },
225 params: AlmaParams {
226 period: Some(period),
227 offset: None,
228 sigma: None,
229 },
230 },
231 MaData::Slice(slice) => AlmaInput {
232 data: AlmaData::Slice(slice),
233 params: AlmaParams {
234 period: Some(period),
235 offset: None,
236 sigma: None,
237 },
238 },
239 };
240 let output = alma(&input)?;
241 Ok(output.values)
242 }
243
244 "cwma" => {
245 let input = match data {
246 MaData::Candles { candles, source } => CwmaInput {
247 data: CwmaData::Candles { candles, source },
248 params: CwmaParams {
249 period: Some(period),
250 },
251 },
252 MaData::Slice(slice) => CwmaInput {
253 data: CwmaData::Slice(slice),
254 params: CwmaParams {
255 period: Some(period),
256 },
257 },
258 };
259 let output = cwma(&input)?;
260 Ok(output.values)
261 }
262
263 "corrected_moving_average" | "cma" => {
264 let input = match data {
265 MaData::Candles { candles, source } => CorrectedMovingAverageInput {
266 data: CorrectedMovingAverageData::Candles { candles, source },
267 params: CorrectedMovingAverageParams {
268 period: Some(period),
269 },
270 },
271 MaData::Slice(slice) => CorrectedMovingAverageInput {
272 data: CorrectedMovingAverageData::Slice(slice),
273 params: CorrectedMovingAverageParams {
274 period: Some(period),
275 },
276 },
277 };
278 let output = corrected_moving_average(&input)?;
279 Ok(output.values)
280 }
281
282 "cora_wave" => {
283 let input = match data {
284 MaData::Candles { candles, source } => CoraWaveInput {
285 data: CoraWaveData::Candles { candles, source },
286 params: CoraWaveParams {
287 period: Some(period),
288 r_multi: None,
289 smooth: None,
290 },
291 },
292 MaData::Slice(slice) => CoraWaveInput {
293 data: CoraWaveData::Slice(slice),
294 params: CoraWaveParams {
295 period: Some(period),
296 r_multi: None,
297 smooth: None,
298 },
299 },
300 };
301 let output = cora_wave(&input)?;
302 Ok(output.values)
303 }
304
305 "corrected_moving_average" => {
306 let input = match data {
307 MaData::Candles { candles, source } => CorrectedMovingAverageInput {
308 data: CorrectedMovingAverageData::Candles { candles, source },
309 params: CorrectedMovingAverageParams {
310 period: Some(period),
311 },
312 },
313 MaData::Slice(slice) => CorrectedMovingAverageInput {
314 data: CorrectedMovingAverageData::Slice(slice),
315 params: CorrectedMovingAverageParams {
316 period: Some(period),
317 },
318 },
319 };
320 let output = corrected_moving_average(&input)?;
321 Ok(output.values)
322 }
323
324 "dema" => {
325 let input = match data {
326 MaData::Candles { candles, source } => DemaInput {
327 data: DemaData::Candles { candles, source },
328 params: DemaParams {
329 period: Some(period),
330 },
331 },
332 MaData::Slice(slice) => DemaInput {
333 data: DemaData::Slice(slice),
334 params: DemaParams {
335 period: Some(period),
336 },
337 },
338 };
339 let output = dema(&input)?;
340 Ok(output.values)
341 }
342
343 "edcf" => {
344 let input = match data {
345 MaData::Candles { candles, source } => EdcfInput {
346 data: EdcfData::Candles { candles, source },
347 params: EdcfParams {
348 period: Some(period),
349 },
350 },
351 MaData::Slice(slice) => EdcfInput {
352 data: EdcfData::Slice(slice),
353 params: EdcfParams {
354 period: Some(period),
355 },
356 },
357 };
358 let output = edcf(&input)?;
359 Ok(output.values)
360 }
361
362 "ema_deviation_corrected_t3" => {
363 let input = match data {
364 MaData::Candles { candles, source } => EmaDeviationCorrectedT3Input {
365 data: EmaDeviationCorrectedT3Data::Candles { candles, source },
366 params: EmaDeviationCorrectedT3Params {
367 period: Some(period),
368 ..Default::default()
369 },
370 },
371 MaData::Slice(slice) => EmaDeviationCorrectedT3Input {
372 data: EmaDeviationCorrectedT3Data::Slice(slice),
373 params: EmaDeviationCorrectedT3Params {
374 period: Some(period),
375 ..Default::default()
376 },
377 },
378 };
379 let output = ema_deviation_corrected_t3(&input)?;
380 Ok(output.corrected)
381 }
382
383 "wave_smoother" => {
384 let input = match data {
385 MaData::Candles { candles, source } => WaveSmootherInput {
386 data: WaveSmootherData::Candles { candles, source },
387 params: WaveSmootherParams {
388 period: Some(period),
389 phase: None,
390 },
391 },
392 MaData::Slice(slice) => WaveSmootherInput {
393 data: WaveSmootherData::Slice(slice),
394 params: WaveSmootherParams {
395 period: Some(period),
396 phase: None,
397 },
398 },
399 };
400 let output = wave_smoother(&input)?;
401 Ok(output.values)
402 }
403
404 "ema" => {
405 let input = match data {
406 MaData::Candles { candles, source } => EmaInput {
407 data: EmaData::Candles { candles, source },
408 params: EmaParams {
409 period: Some(period),
410 },
411 },
412 MaData::Slice(slice) => EmaInput {
413 data: EmaData::Slice(slice),
414 params: EmaParams {
415 period: Some(period),
416 },
417 },
418 };
419 let output = ema(&input)?;
420 Ok(output.values)
421 }
422
423 "epma" => {
424 let input = match data {
425 MaData::Candles { candles, source } => EpmaInput {
426 data: EpmaData::Candles { candles, source },
427 params: EpmaParams {
428 period: Some(period),
429 offset: None,
430 },
431 },
432 MaData::Slice(slice) => EpmaInput {
433 data: EpmaData::Slice(slice),
434 params: EpmaParams {
435 period: Some(period),
436 offset: None,
437 },
438 },
439 };
440 let output = epma(&input)?;
441 Ok(output.values)
442 }
443
444 "fwma" => {
445 let input = match data {
446 MaData::Candles { candles, source } => FwmaInput {
447 data: FwmaData::Candles { candles, source },
448 params: FwmaParams {
449 period: Some(period),
450 },
451 },
452 MaData::Slice(slice) => FwmaInput {
453 data: FwmaData::Slice(slice),
454 params: FwmaParams {
455 period: Some(period),
456 },
457 },
458 };
459 let output = fwma(&input)?;
460 Ok(output.values)
461 }
462
463 "gaussian" => {
464 let input = match data {
465 MaData::Candles { candles, source } => GaussianInput {
466 data: GaussianData::Candles { candles, source },
467 params: GaussianParams {
468 period: Some(period),
469 poles: None,
470 },
471 },
472 MaData::Slice(slice) => GaussianInput {
473 data: GaussianData::Slice(slice),
474 params: GaussianParams {
475 period: Some(period),
476 poles: None,
477 },
478 },
479 };
480 let output = gaussian(&input)?;
481 Ok(output.values)
482 }
483
484 "highpass" => {
485 let input = match data {
486 MaData::Candles { candles, source } => HighPassInput {
487 data: HighPassData::Candles { candles, source },
488 params: HighPassParams {
489 period: Some(period),
490 },
491 },
492 MaData::Slice(slice) => HighPassInput {
493 data: HighPassData::Slice(slice),
494 params: HighPassParams {
495 period: Some(period),
496 },
497 },
498 };
499 let output = highpass(&input)?;
500 Ok(output.values)
501 }
502
503 "highpass2" | "highpass_2_pole" => {
504 let input = match data {
505 MaData::Candles { candles, source } => HighPass2Input {
506 data: HighPass2Data::Candles { candles, source },
507 params: HighPass2Params {
508 period: Some(period),
509 k: Some(0.707),
510 },
511 },
512 MaData::Slice(slice) => HighPass2Input {
513 data: HighPass2Data::Slice(slice),
514 params: HighPass2Params {
515 period: Some(period),
516 k: Some(0.707),
517 },
518 },
519 };
520 let output = highpass_2_pole(&input)?;
521 Ok(output.values)
522 }
523
524 "hma" => {
525 let input = match data {
526 MaData::Candles { candles, source } => HmaInput {
527 data: HmaData::Candles { candles, source },
528 params: HmaParams {
529 period: Some(period),
530 },
531 },
532 MaData::Slice(slice) => HmaInput {
533 data: HmaData::Slice(slice),
534 params: HmaParams {
535 period: Some(period),
536 },
537 },
538 };
539 let output = hma(&input)?;
540 Ok(output.values)
541 }
542
543 "ehlers_itrend" => {
544 let input = match data {
545 MaData::Candles { candles, source } => EhlersITrendInput {
546 data: EhlersITrendData::Candles { candles, source },
547 params: EhlersITrendParams {
548 warmup_bars: Some(20),
549 max_dc_period: Some(period),
550 },
551 },
552 MaData::Slice(slice) => EhlersITrendInput {
553 data: EhlersITrendData::Slice(slice),
554 params: EhlersITrendParams {
555 warmup_bars: Some(20),
556 max_dc_period: Some(period),
557 },
558 },
559 };
560 let output = ehlers_itrend(&input)?;
561 Ok(output.values)
562 }
563
564 "hwma" => {
565 let input = match data {
566 MaData::Candles { candles, source } => HwmaInput {
567 data: HwmaData::Candles { candles, source },
568 params: HwmaParams {
569 na: None,
570 nb: None,
571 nc: None,
572 },
573 },
574 MaData::Slice(slice) => HwmaInput {
575 data: HwmaData::Slice(slice),
576 params: HwmaParams {
577 na: None,
578 nb: None,
579 nc: None,
580 },
581 },
582 };
583 let output = hwma(&input)?;
584 Ok(output.values)
585 }
586
587 "jma" => {
588 let input = match data {
589 MaData::Candles { candles, source } => JmaInput {
590 data: JmaData::Candles { candles, source },
591 params: JmaParams {
592 period: Some(period),
593 phase: None,
594 power: None,
595 },
596 },
597 MaData::Slice(slice) => JmaInput {
598 data: JmaData::Slice(slice),
599 params: JmaParams {
600 period: Some(period),
601 phase: None,
602 power: None,
603 },
604 },
605 };
606 let output = jma(&input)?;
607 Ok(output.values)
608 }
609
610 "jsa" => {
611 let input = match data {
612 MaData::Candles { candles, source } => JsaInput {
613 data: JsaData::Candles { candles, source },
614 params: JsaParams {
615 period: Some(period),
616 },
617 },
618 MaData::Slice(slice) => JsaInput {
619 data: JsaData::Slice(slice),
620 params: JsaParams {
621 period: Some(period),
622 },
623 },
624 };
625 let output = jsa(&input)?;
626 Ok(output.values)
627 }
628
629 "kama" => {
630 let input = match data {
631 MaData::Candles { candles, source } => KamaInput {
632 data: KamaData::Candles { candles, source },
633 params: KamaParams {
634 period: Some(period),
635 },
636 },
637 MaData::Slice(slice) => KamaInput {
638 data: KamaData::Slice(slice),
639 params: KamaParams {
640 period: Some(period),
641 },
642 },
643 };
644 let output = kama(&input)?;
645 Ok(output.values)
646 }
647
648 "linreg" => {
649 let input = match data {
650 MaData::Candles { candles, source } => LinRegInput {
651 data: LinRegData::Candles { candles, source },
652 params: LinRegParams {
653 period: Some(period),
654 },
655 },
656 MaData::Slice(s) => LinRegInput {
657 data: LinRegData::Slice(s),
658 params: LinRegParams {
659 period: Some(period),
660 },
661 },
662 };
663 let output = linreg(&input)?;
664 Ok(output.values)
665 }
666
667 "maaq" => {
668 let slow = period.checked_mul(2).ok_or(MaError::InvalidPeriod {
669 period,
670 data_len: 0,
671 })?;
672 let input = match data {
673 MaData::Candles { candles, source } => MaaqInput {
674 data: MaaqData::Candles { candles, source },
675 params: MaaqParams {
676 period: Some(period),
677 fast_period: Some(period / 2),
678 slow_period: Some(slow),
679 },
680 },
681 MaData::Slice(s) => MaaqInput {
682 data: MaaqData::Slice(s),
683 params: MaaqParams {
684 period: Some(period),
685 fast_period: Some(period / 2),
686 slow_period: Some(slow),
687 },
688 },
689 };
690 let output = maaq(&input)?;
691 Ok(output.values)
692 }
693
694 "mama" => {
695 let input = match data {
696 MaData::Candles { candles, source } => {
697 MamaInput::from_candles(candles, source, MamaParams::default())
698 }
699 MaData::Slice(s) => MamaInput::from_slice(s, MamaParams::default()),
700 };
701 let output = mama(&input)?;
702 Ok(output.mama_values)
703 }
704
705 "mwdx" => {
706 let input = match data {
707 MaData::Candles { candles, source } => MwdxInput {
708 data: MwdxData::Candles { candles, source },
709 params: MwdxParams { factor: None },
710 },
711 MaData::Slice(s) => MwdxInput {
712 data: MwdxData::Slice(s),
713 params: MwdxParams { factor: None },
714 },
715 };
716 let output = mwdx(&input)?;
717 Ok(output.values)
718 }
719
720 "nma" => {
721 let input = match data {
722 MaData::Candles { candles, source } => NmaInput {
723 data: NmaData::Candles { candles, source },
724 params: NmaParams {
725 period: Some(period),
726 },
727 },
728 MaData::Slice(s) => NmaInput {
729 data: NmaData::Slice(s),
730 params: NmaParams {
731 period: Some(period),
732 },
733 },
734 };
735 let output = nma(&input)?;
736 Ok(output.values)
737 }
738
739 "pwma" => {
740 let input = match data {
741 MaData::Candles { candles, source } => PwmaInput {
742 data: PwmaData::Candles { candles, source },
743 params: PwmaParams {
744 period: Some(period),
745 },
746 },
747 MaData::Slice(s) => PwmaInput {
748 data: PwmaData::Slice(s),
749 params: PwmaParams {
750 period: Some(period),
751 },
752 },
753 };
754 let output = pwma(&input)?;
755 Ok(output.values)
756 }
757
758 "reflex" => {
759 let input = match data {
760 MaData::Candles { candles, source } => ReflexInput {
761 data: ReflexData::Candles { candles, source },
762 params: ReflexParams {
763 period: Some(period),
764 },
765 },
766 MaData::Slice(s) => ReflexInput {
767 data: ReflexData::Slice(s),
768 params: ReflexParams {
769 period: Some(period),
770 },
771 },
772 };
773 let output = reflex(&input)?;
774 Ok(output.values)
775 }
776
777 "sinwma" => {
778 let input = match data {
779 MaData::Candles { candles, source } => SinWmaInput {
780 data: SinWmaData::Candles { candles, source },
781 params: SinWmaParams {
782 period: Some(period),
783 },
784 },
785 MaData::Slice(s) => SinWmaInput {
786 data: SinWmaData::Slice(s),
787 params: SinWmaParams {
788 period: Some(period),
789 },
790 },
791 };
792 let output = sinwma(&input)?;
793 Ok(output.values)
794 }
795
796 "smma" => {
797 let input = match data {
798 MaData::Candles { candles, source } => SmmaInput {
799 data: SmmaData::Candles { candles, source },
800 params: SmmaParams {
801 period: Some(period),
802 },
803 },
804 MaData::Slice(s) => SmmaInput {
805 data: SmmaData::Slice(s),
806 params: SmmaParams {
807 period: Some(period),
808 },
809 },
810 };
811 let output = smma(&input)?;
812 Ok(output.values)
813 }
814
815 "sqwma" => {
816 let input = match data {
817 MaData::Candles { candles, source } => SqwmaInput {
818 data: SqwmaData::Candles { candles, source },
819 params: SqwmaParams {
820 period: Some(period),
821 },
822 },
823 MaData::Slice(s) => SqwmaInput {
824 data: SqwmaData::Slice(s),
825 params: SqwmaParams {
826 period: Some(period),
827 },
828 },
829 };
830 let output = sqwma(&input)?;
831 Ok(output.values)
832 }
833
834 "srwma" => {
835 let input = match data {
836 MaData::Candles { candles, source } => SrwmaInput {
837 data: SrwmaData::Candles { candles, source },
838 params: SrwmaParams {
839 period: Some(period),
840 },
841 },
842 MaData::Slice(s) => SrwmaInput {
843 data: SrwmaData::Slice(s),
844 params: SrwmaParams {
845 period: Some(period),
846 },
847 },
848 };
849 let output = srwma(&input)?;
850 Ok(output.values)
851 }
852
853 "supersmoother" => {
854 let input = match data {
855 MaData::Candles { candles, source } => SuperSmootherInput {
856 data: SuperSmootherData::Candles { candles, source },
857 params: SuperSmootherParams {
858 period: Some(period),
859 },
860 },
861 MaData::Slice(s) => SuperSmootherInput {
862 data: SuperSmootherData::Slice(s),
863 params: SuperSmootherParams {
864 period: Some(period),
865 },
866 },
867 };
868 let output = supersmoother(&input)?;
869 Ok(output.values)
870 }
871
872 "supersmoother_3_pole" => {
873 let input = match data {
874 MaData::Candles { candles, source } => SuperSmoother3PoleInput {
875 data: SuperSmoother3PoleData::Candles { candles, source },
876 params: SuperSmoother3PoleParams {
877 period: Some(period),
878 },
879 },
880 MaData::Slice(s) => SuperSmoother3PoleInput {
881 data: SuperSmoother3PoleData::Slice(s),
882 params: SuperSmoother3PoleParams {
883 period: Some(period),
884 },
885 },
886 };
887 let output = supersmoother_3_pole(&input)?;
888 Ok(output.values)
889 }
890
891 "sgf" => {
892 let input = match data {
893 MaData::Candles { candles, source } => SgfInput {
894 data: SgfData::Candles { candles, source },
895 params: SgfParams {
896 period: Some(period),
897 poly_order: Some(2),
898 },
899 },
900 MaData::Slice(s) => SgfInput {
901 data: SgfData::Slice(s),
902 params: SgfParams {
903 period: Some(period),
904 poly_order: Some(2),
905 },
906 },
907 };
908 let output = sgf(&input)?;
909 Ok(output.values)
910 }
911
912 "swma" => {
913 let input = match data {
914 MaData::Candles { candles, source } => SwmaInput {
915 data: SwmaData::Candles { candles, source },
916 params: SwmaParams {
917 period: Some(period),
918 },
919 },
920 MaData::Slice(s) => SwmaInput {
921 data: SwmaData::Slice(s),
922 params: SwmaParams {
923 period: Some(period),
924 },
925 },
926 };
927 let output = swma(&input)?;
928 Ok(output.values)
929 }
930
931 "tema" => {
932 let input = match data {
933 MaData::Candles { candles, source } => TemaInput {
934 data: TemaData::Candles { candles, source },
935 params: TemaParams {
936 period: Some(period),
937 },
938 },
939 MaData::Slice(s) => TemaInput {
940 data: TemaData::Slice(s),
941 params: TemaParams {
942 period: Some(period),
943 },
944 },
945 };
946 let output = tema(&input)?;
947 Ok(output.values)
948 }
949
950 "tilson" => {
951 let input = match data {
952 MaData::Candles { candles, source } => TilsonInput {
953 data: TilsonData::Candles { candles, source },
954 params: TilsonParams {
955 period: Some(period),
956 volume_factor: None,
957 },
958 },
959 MaData::Slice(s) => TilsonInput {
960 data: TilsonData::Slice(s),
961 params: TilsonParams {
962 period: Some(period),
963 volume_factor: None,
964 },
965 },
966 };
967 let output = tilson(&input)?;
968 Ok(output.values)
969 }
970
971 "trendflex" => {
972 let input = match data {
973 MaData::Candles { candles, source } => TrendFlexInput {
974 data: TrendFlexData::Candles { candles, source },
975 params: TrendFlexParams {
976 period: Some(period),
977 },
978 },
979 MaData::Slice(s) => TrendFlexInput {
980 data: TrendFlexData::Slice(s),
981 params: TrendFlexParams {
982 period: Some(period),
983 },
984 },
985 };
986 let output = trendflex(&input)?;
987 Ok(output.values)
988 }
989
990 "trima" => {
991 let input = match data {
992 MaData::Candles { candles, source } => TrimaInput {
993 data: TrimaData::Candles { candles, source },
994 params: TrimaParams {
995 period: Some(period),
996 },
997 },
998 MaData::Slice(s) => TrimaInput {
999 data: TrimaData::Slice(s),
1000 params: TrimaParams {
1001 period: Some(period),
1002 },
1003 },
1004 };
1005 let output = trima(&input)?;
1006 Ok(output.values)
1007 }
1008
1009 "vpwma" => {
1010 if let MaData::Candles { candles, source } = data {
1011 let input = VpwmaInput {
1012 data: VpwmaData::Candles { candles, source },
1013 params: VpwmaParams {
1014 period: Some(period),
1015 power: None,
1016 },
1017 };
1018 let output = vpwma(&input)?;
1019 Ok(output.values)
1020 } else {
1021 eprintln!("Unknown data type for 'vpwma'. Defaulting to 'sma'.");
1022
1023 let input = match data {
1024 MaData::Candles { candles, source } => SmaInput::from_candles(
1025 candles,
1026 source,
1027 SmaParams {
1028 period: Some(period),
1029 },
1030 ),
1031 MaData::Slice(slice) => SmaInput::from_slice(
1032 slice,
1033 SmaParams {
1034 period: Some(period),
1035 },
1036 ),
1037 };
1038 let output = sma(&input)?;
1039 Ok(output.values)
1040 }
1041 }
1042
1043 "vwap" => {
1044 if let MaData::Candles { candles, source } = data {
1045 let input = VwapInput {
1046 data: VwapData::Candles { candles, source },
1047 params: VwapParams { anchor: None },
1048 };
1049 let output = vwap(&input)?;
1050 Ok(output.values)
1051 } else {
1052 eprintln!("Unknown data type for 'vwap'. Defaulting to 'sma'.");
1053
1054 let input = match data {
1055 MaData::Candles { candles, source } => SmaInput::from_candles(
1056 candles,
1057 source,
1058 SmaParams {
1059 period: Some(period),
1060 },
1061 ),
1062 MaData::Slice(slice) => SmaInput::from_slice(
1063 slice,
1064 SmaParams {
1065 period: Some(period),
1066 },
1067 ),
1068 };
1069 let output = sma(&input)?;
1070 Ok(output.values)
1071 }
1072 }
1073 "vwma" => {
1074 if let MaData::Candles { candles, source } = data {
1075 let input = VwmaInput {
1076 data: VwmaData::Candles { candles, source },
1077 params: VwmaParams {
1078 period: Some(period),
1079 },
1080 };
1081 let output = vwma(&input)?;
1082 Ok(output.values)
1083 } else {
1084 eprintln!("Unknown data type for 'vwma'. Defaulting to 'sma'.");
1085
1086 let input = match data {
1087 MaData::Candles { candles, source } => SmaInput::from_candles(
1088 candles,
1089 source,
1090 SmaParams {
1091 period: Some(period),
1092 },
1093 ),
1094 MaData::Slice(slice) => SmaInput::from_slice(
1095 slice,
1096 SmaParams {
1097 period: Some(period),
1098 },
1099 ),
1100 };
1101 let output = sma(&input)?;
1102 Ok(output.values)
1103 }
1104 }
1105 "elastic_volume_weighted_moving_average" => {
1106 if let MaData::Candles { candles, source } = data {
1107 let input = ElasticVolumeWeightedMovingAverageInput {
1108 data: ElasticVolumeWeightedMovingAverageData::Candles { candles, source },
1109 params: ElasticVolumeWeightedMovingAverageParams {
1110 length: Some(period),
1111 absolute_volume_millions: None,
1112 use_volume_sum: Some(true),
1113 },
1114 };
1115 let output = elastic_volume_weighted_moving_average(&input)?;
1116 Ok(output.values)
1117 } else {
1118 eprintln!(
1119 "Unknown data type for 'elastic_volume_weighted_moving_average'. Defaulting to 'sma'."
1120 );
1121 let input = match data {
1122 MaData::Candles { candles, source } => SmaInput::from_candles(
1123 candles,
1124 source,
1125 SmaParams {
1126 period: Some(period),
1127 },
1128 ),
1129 MaData::Slice(slice) => SmaInput::from_slice(
1130 slice,
1131 SmaParams {
1132 period: Some(period),
1133 },
1134 ),
1135 };
1136 let output = sma(&input)?;
1137 Ok(output.values)
1138 }
1139 }
1140
1141 "wilders" => {
1142 let input = match data {
1143 MaData::Candles { candles, source } => WildersInput {
1144 data: WildersData::Candles { candles, source },
1145 params: WildersParams {
1146 period: Some(period),
1147 },
1148 },
1149 MaData::Slice(s) => WildersInput {
1150 data: WildersData::Slice(s),
1151 params: WildersParams {
1152 period: Some(period),
1153 },
1154 },
1155 };
1156 let output = wilders(&input)?;
1157 Ok(output.values)
1158 }
1159
1160 "wma" => {
1161 let input = match data {
1162 MaData::Candles { candles, source } => WmaInput {
1163 data: WmaData::Candles { candles, source },
1164 params: WmaParams {
1165 period: Some(period),
1166 },
1167 },
1168 MaData::Slice(s) => WmaInput {
1169 data: WmaData::Slice(s),
1170 params: WmaParams {
1171 period: Some(period),
1172 },
1173 },
1174 };
1175 let output = wma(&input)?;
1176 Ok(output.values)
1177 }
1178
1179 "zlema" => {
1180 let input = match data {
1181 MaData::Candles { candles, source } => ZlemaInput {
1182 data: ZlemaData::Candles { candles, source },
1183 params: ZlemaParams {
1184 period: Some(period),
1185 },
1186 },
1187 MaData::Slice(s) => ZlemaInput {
1188 data: ZlemaData::Slice(s),
1189 params: ZlemaParams {
1190 period: Some(period),
1191 },
1192 },
1193 };
1194 let output = zlema(&input)?;
1195 Ok(output.values)
1196 }
1197
1198 "buff_averages" => {
1199 return Err(MaError::RequiresVolume {
1200 indicator: "buff_averages",
1201 }
1202 .into());
1203 }
1204
1205 "dma" => {
1206 let input = match data {
1207 MaData::Candles { candles, source } => DmaInput {
1208 data: DmaData::Candles { candles, source },
1209 params: DmaParams {
1210 ema_length: Some(period),
1211 ..Default::default()
1212 },
1213 },
1214 MaData::Slice(s) => DmaInput {
1215 data: DmaData::Slice(s),
1216 params: DmaParams {
1217 ema_length: Some(period),
1218 ..Default::default()
1219 },
1220 },
1221 };
1222 let output = dma(&input)?;
1223 Ok(output.values)
1224 }
1225
1226 "ehlers_ecema" => {
1227 let input = match data {
1228 MaData::Candles { candles, source } => EhlersEcemaInput {
1229 data: EhlersEcemaData::Candles { candles, source },
1230 params: EhlersEcemaParams {
1231 length: Some(period),
1232 ..Default::default()
1233 },
1234 },
1235 MaData::Slice(s) => EhlersEcemaInput {
1236 data: EhlersEcemaData::Slice(s),
1237 params: EhlersEcemaParams {
1238 length: Some(period),
1239 ..Default::default()
1240 },
1241 },
1242 };
1243 let output = ehlers_ecema(&input)?;
1244 Ok(output.values)
1245 }
1246
1247 "ehlers_kama" => {
1248 let input = match data {
1249 MaData::Candles { candles, source } => EhlersKamaInput {
1250 data: EhlersKamaData::Candles { candles, source },
1251 params: EhlersKamaParams {
1252 period: Some(period),
1253 ..Default::default()
1254 },
1255 },
1256 MaData::Slice(s) => EhlersKamaInput {
1257 data: EhlersKamaData::Slice(s),
1258 params: EhlersKamaParams {
1259 period: Some(period),
1260 ..Default::default()
1261 },
1262 },
1263 };
1264 let output = ehlers_kama(&input)?;
1265 Ok(output.values)
1266 }
1267
1268 "ehlers_pma" => {
1269 return Err(MaError::DualOutputNotSupported {
1270 indicator: "ehlers_pma",
1271 }
1272 .into());
1273 }
1274
1275 "ehma" => {
1276 let input = match data {
1277 MaData::Candles { candles, source } => EhmaInput {
1278 data: EhmaData::Candles { candles, source },
1279 params: EhmaParams {
1280 period: Some(period),
1281 ..Default::default()
1282 },
1283 },
1284 MaData::Slice(s) => EhmaInput {
1285 data: EhmaData::Slice(s),
1286 params: EhmaParams {
1287 period: Some(period),
1288 ..Default::default()
1289 },
1290 },
1291 };
1292 let output = ehma(&input)?;
1293 Ok(output.values)
1294 }
1295
1296 "frama" => {
1297 let input = match data {
1298 MaData::Candles { candles, .. } => FramaInput::from_candles(
1299 candles,
1300 FramaParams {
1301 window: Some(period),
1302 ..Default::default()
1303 },
1304 ),
1305 MaData::Slice(slice) => FramaInput::from_slices(
1306 slice,
1307 slice,
1308 slice,
1309 FramaParams {
1310 window: Some(period),
1311 ..Default::default()
1312 },
1313 ),
1314 };
1315 let output = frama(&input)?;
1316 Ok(output.values)
1317 }
1318
1319 "nama" => {
1320 let input = match data {
1321 MaData::Candles { candles, source } => NamaInput {
1322 data: NamaData::Candles { candles, source },
1323 params: NamaParams {
1324 period: Some(period),
1325 ..Default::default()
1326 },
1327 },
1328 MaData::Slice(s) => NamaInput {
1329 data: NamaData::Slice(s),
1330 params: NamaParams {
1331 period: Some(period),
1332 ..Default::default()
1333 },
1334 },
1335 };
1336 let output = nama(&input)?;
1337 Ok(output.values)
1338 }
1339
1340 "n_order_ema" => {
1341 let input = match data {
1342 MaData::Candles { candles, source } => NOrderEmaInput {
1343 data: NOrderEmaData::Candles { candles, source },
1344 params: NOrderEmaParams {
1345 period: Some(period as f64),
1346 order: Some(1),
1347 ema_style: Some(NOrderEmaStyle::Ema.as_str().to_string()),
1348 iir_style: Some(NOrderEmaIirStyle::ImpulseMatched.as_str().to_string()),
1349 },
1350 },
1351 MaData::Slice(s) => NOrderEmaInput {
1352 data: NOrderEmaData::Slice(s),
1353 params: NOrderEmaParams {
1354 period: Some(period as f64),
1355 order: Some(1),
1356 ema_style: Some(NOrderEmaStyle::Ema.as_str().to_string()),
1357 iir_style: Some(NOrderEmaIirStyle::ImpulseMatched.as_str().to_string()),
1358 },
1359 },
1360 };
1361 let output = n_order_ema(&input)?;
1362 Ok(output.values)
1363 }
1364
1365 "sama" => {
1366 let input = match data {
1367 MaData::Candles { candles, source } => SamaInput {
1368 data: SamaData::Candles { candles, source },
1369 params: SamaParams {
1370 length: Some(period),
1371 ..Default::default()
1372 },
1373 },
1374 MaData::Slice(s) => SamaInput {
1375 data: SamaData::Slice(s),
1376 params: SamaParams {
1377 length: Some(period),
1378 ..Default::default()
1379 },
1380 },
1381 };
1382 let output = sama(&input)?;
1383 Ok(output.values)
1384 }
1385
1386 "tradjema" => {
1387 return Err(MaError::RequiresHighLow {
1388 indicator: "tradjema",
1389 }
1390 .into());
1391 }
1392
1393 "uma" => {
1394 return Err(MaError::RequiresVolume { indicator: "uma" }.into());
1395 }
1396
1397 "volatility_adjusted_ma" | "vama" => {
1398 let input = match data {
1399 MaData::Candles { candles, source } => VamaInput {
1400 data: VamaData::Candles { candles, source },
1401 params: VamaParams {
1402 base_period: Some(period),
1403 ..Default::default()
1404 },
1405 },
1406 MaData::Slice(s) => VamaInput {
1407 data: VamaData::Slice(s),
1408 params: VamaParams {
1409 base_period: Some(period),
1410 ..Default::default()
1411 },
1412 },
1413 };
1414 let output = vama(&input)?;
1415 Ok(output.values)
1416 }
1417
1418 "volume_adjusted_ma" => {
1419 return Err(MaError::RequiresVolume {
1420 indicator: "volume_adjusted_ma",
1421 }
1422 .into());
1423 }
1424
1425 _ => {
1426 return Err(MaError::UnknownType {
1427 ma_type: ma_type.to_string(),
1428 }
1429 .into());
1430 }
1431 }
1432}
1433
1434#[inline]
1435pub fn ma_with_kernel<'a>(
1436 ma_type: &str,
1437 data: MaData<'a>,
1438 period: usize,
1439 kernel: Kernel,
1440) -> Result<Vec<f64>, Box<dyn Error>> {
1441 match ma_type.to_lowercase().as_str() {
1442 "sma" => {
1443 let input = match data {
1444 MaData::Candles { candles, source } => SmaInput {
1445 data: SmaData::Candles { candles, source },
1446 params: SmaParams {
1447 period: Some(period),
1448 },
1449 },
1450 MaData::Slice(slice) => SmaInput {
1451 data: SmaData::Slice(slice),
1452 params: SmaParams {
1453 period: Some(period),
1454 },
1455 },
1456 };
1457 let output = sma_with_kernel(&input, kernel)?;
1458 Ok(output.values)
1459 }
1460
1461 "alma" => {
1462 let input = match data {
1463 MaData::Candles { candles, source } => AlmaInput {
1464 data: AlmaData::Candles { candles, source },
1465 params: AlmaParams {
1466 period: Some(period),
1467 offset: None,
1468 sigma: None,
1469 },
1470 },
1471 MaData::Slice(slice) => AlmaInput {
1472 data: AlmaData::Slice(slice),
1473 params: AlmaParams {
1474 period: Some(period),
1475 offset: None,
1476 sigma: None,
1477 },
1478 },
1479 };
1480 let output = alma_with_kernel(&input, kernel)?;
1481 Ok(output.values)
1482 }
1483
1484 "cwma" => {
1485 let input = match data {
1486 MaData::Candles { candles, source } => CwmaInput {
1487 data: CwmaData::Candles { candles, source },
1488 params: CwmaParams {
1489 period: Some(period),
1490 },
1491 },
1492 MaData::Slice(slice) => CwmaInput {
1493 data: CwmaData::Slice(slice),
1494 params: CwmaParams {
1495 period: Some(period),
1496 },
1497 },
1498 };
1499 let output = cwma_with_kernel(&input, kernel)?;
1500 Ok(output.values)
1501 }
1502
1503 "corrected_moving_average" | "cma" => {
1504 let input = match data {
1505 MaData::Candles { candles, source } => CorrectedMovingAverageInput {
1506 data: CorrectedMovingAverageData::Candles { candles, source },
1507 params: CorrectedMovingAverageParams {
1508 period: Some(period),
1509 },
1510 },
1511 MaData::Slice(slice) => CorrectedMovingAverageInput {
1512 data: CorrectedMovingAverageData::Slice(slice),
1513 params: CorrectedMovingAverageParams {
1514 period: Some(period),
1515 },
1516 },
1517 };
1518 let output = corrected_moving_average_with_kernel(&input, kernel)?;
1519 Ok(output.values)
1520 }
1521
1522 "cora_wave" => {
1523 let input = match data {
1524 MaData::Candles { candles, source } => CoraWaveInput {
1525 data: CoraWaveData::Candles { candles, source },
1526 params: CoraWaveParams {
1527 period: Some(period),
1528 r_multi: None,
1529 smooth: None,
1530 },
1531 },
1532 MaData::Slice(slice) => CoraWaveInput {
1533 data: CoraWaveData::Slice(slice),
1534 params: CoraWaveParams {
1535 period: Some(period),
1536 r_multi: None,
1537 smooth: None,
1538 },
1539 },
1540 };
1541 let output = cora_wave_with_kernel(&input, kernel)?;
1542 Ok(output.values)
1543 }
1544
1545 "dema" => {
1546 let input = match data {
1547 MaData::Candles { candles, source } => DemaInput {
1548 data: DemaData::Candles { candles, source },
1549 params: DemaParams {
1550 period: Some(period),
1551 },
1552 },
1553 MaData::Slice(slice) => DemaInput {
1554 data: DemaData::Slice(slice),
1555 params: DemaParams {
1556 period: Some(period),
1557 },
1558 },
1559 };
1560 let output = dema_with_kernel(&input, kernel)?;
1561 Ok(output.values)
1562 }
1563
1564 "edcf" => {
1565 let input = match data {
1566 MaData::Candles { candles, source } => EdcfInput {
1567 data: EdcfData::Candles { candles, source },
1568 params: EdcfParams {
1569 period: Some(period),
1570 },
1571 },
1572 MaData::Slice(slice) => EdcfInput {
1573 data: EdcfData::Slice(slice),
1574 params: EdcfParams {
1575 period: Some(period),
1576 },
1577 },
1578 };
1579 let output = edcf_with_kernel(&input, kernel)?;
1580 Ok(output.values)
1581 }
1582
1583 "ema_deviation_corrected_t3" => {
1584 let input = match data {
1585 MaData::Candles { candles, source } => EmaDeviationCorrectedT3Input {
1586 data: EmaDeviationCorrectedT3Data::Candles { candles, source },
1587 params: EmaDeviationCorrectedT3Params {
1588 period: Some(period),
1589 ..Default::default()
1590 },
1591 },
1592 MaData::Slice(slice) => EmaDeviationCorrectedT3Input {
1593 data: EmaDeviationCorrectedT3Data::Slice(slice),
1594 params: EmaDeviationCorrectedT3Params {
1595 period: Some(period),
1596 ..Default::default()
1597 },
1598 },
1599 };
1600 let output = ema_deviation_corrected_t3_with_kernel(&input, kernel)?;
1601 Ok(output.corrected)
1602 }
1603
1604 "wave_smoother" => {
1605 let input = match data {
1606 MaData::Candles { candles, source } => WaveSmootherInput {
1607 data: WaveSmootherData::Candles { candles, source },
1608 params: WaveSmootherParams {
1609 period: Some(period),
1610 phase: None,
1611 },
1612 },
1613 MaData::Slice(slice) => WaveSmootherInput {
1614 data: WaveSmootherData::Slice(slice),
1615 params: WaveSmootherParams {
1616 period: Some(period),
1617 phase: None,
1618 },
1619 },
1620 };
1621 let output = wave_smoother_with_kernel(&input, kernel)?;
1622 Ok(output.values)
1623 }
1624
1625 "ema" => {
1626 let input = match data {
1627 MaData::Candles { candles, source } => EmaInput {
1628 data: EmaData::Candles { candles, source },
1629 params: EmaParams {
1630 period: Some(period),
1631 },
1632 },
1633 MaData::Slice(slice) => EmaInput {
1634 data: EmaData::Slice(slice),
1635 params: EmaParams {
1636 period: Some(period),
1637 },
1638 },
1639 };
1640 let output = ema_with_kernel(&input, kernel)?;
1641 Ok(output.values)
1642 }
1643
1644 "epma" => {
1645 let input = match data {
1646 MaData::Candles { candles, source } => EpmaInput {
1647 data: EpmaData::Candles { candles, source },
1648 params: EpmaParams {
1649 period: Some(period),
1650 offset: None,
1651 },
1652 },
1653 MaData::Slice(slice) => EpmaInput {
1654 data: EpmaData::Slice(slice),
1655 params: EpmaParams {
1656 period: Some(period),
1657 offset: None,
1658 },
1659 },
1660 };
1661 let output = epma_with_kernel(&input, kernel)?;
1662 Ok(output.values)
1663 }
1664
1665 "fwma" => {
1666 let input = match data {
1667 MaData::Candles { candles, source } => FwmaInput {
1668 data: FwmaData::Candles { candles, source },
1669 params: FwmaParams {
1670 period: Some(period),
1671 },
1672 },
1673 MaData::Slice(slice) => FwmaInput {
1674 data: FwmaData::Slice(slice),
1675 params: FwmaParams {
1676 period: Some(period),
1677 },
1678 },
1679 };
1680 let output = fwma_with_kernel(&input, kernel)?;
1681 Ok(output.values)
1682 }
1683
1684 "gaussian" => {
1685 let input = match data {
1686 MaData::Candles { candles, source } => GaussianInput {
1687 data: GaussianData::Candles { candles, source },
1688 params: GaussianParams {
1689 period: Some(period),
1690 poles: None,
1691 },
1692 },
1693 MaData::Slice(slice) => GaussianInput {
1694 data: GaussianData::Slice(slice),
1695 params: GaussianParams {
1696 period: Some(period),
1697 poles: None,
1698 },
1699 },
1700 };
1701 let output = gaussian_with_kernel(&input, kernel)?;
1702 Ok(output.values)
1703 }
1704
1705 "highpass" => {
1706 let input = match data {
1707 MaData::Candles { candles, source } => HighPassInput {
1708 data: HighPassData::Candles { candles, source },
1709 params: HighPassParams {
1710 period: Some(period),
1711 },
1712 },
1713 MaData::Slice(slice) => HighPassInput {
1714 data: HighPassData::Slice(slice),
1715 params: HighPassParams {
1716 period: Some(period),
1717 },
1718 },
1719 };
1720 let output = highpass_with_kernel(&input, kernel)?;
1721 Ok(output.values)
1722 }
1723
1724 "highpass2" | "highpass_2_pole" => {
1725 let input = match data {
1726 MaData::Candles { candles, source } => HighPass2Input {
1727 data: HighPass2Data::Candles { candles, source },
1728 params: HighPass2Params {
1729 period: Some(period),
1730 k: Some(0.707),
1731 },
1732 },
1733 MaData::Slice(slice) => HighPass2Input {
1734 data: HighPass2Data::Slice(slice),
1735 params: HighPass2Params {
1736 period: Some(period),
1737 k: Some(0.707),
1738 },
1739 },
1740 };
1741 let output = highpass_2_pole_with_kernel(&input, kernel)?;
1742 Ok(output.values)
1743 }
1744
1745 "hma" => {
1746 let input = match data {
1747 MaData::Candles { candles, source } => HmaInput {
1748 data: HmaData::Candles { candles, source },
1749 params: HmaParams {
1750 period: Some(period),
1751 },
1752 },
1753 MaData::Slice(slice) => HmaInput {
1754 data: HmaData::Slice(slice),
1755 params: HmaParams {
1756 period: Some(period),
1757 },
1758 },
1759 };
1760 let output = hma_with_kernel(&input, kernel)?;
1761 Ok(output.values)
1762 }
1763
1764 "ehlers_itrend" => {
1765 let input = match data {
1766 MaData::Candles { candles, source } => EhlersITrendInput {
1767 data: EhlersITrendData::Candles { candles, source },
1768 params: EhlersITrendParams {
1769 warmup_bars: Some(12),
1770 max_dc_period: Some(50),
1771 },
1772 },
1773 MaData::Slice(slice) => EhlersITrendInput {
1774 data: EhlersITrendData::Slice(slice),
1775 params: EhlersITrendParams {
1776 warmup_bars: Some(12),
1777 max_dc_period: Some(50),
1778 },
1779 },
1780 };
1781 let output = ehlers_itrend_with_kernel(&input, kernel)?;
1782 Ok(output.values)
1783 }
1784
1785 "hwma" => {
1786 let input = match data {
1787 MaData::Candles { candles, source } => HwmaInput {
1788 data: HwmaData::Candles { candles, source },
1789 params: HwmaParams {
1790 na: None,
1791 nb: None,
1792 nc: None,
1793 },
1794 },
1795 MaData::Slice(slice) => HwmaInput {
1796 data: HwmaData::Slice(slice),
1797 params: HwmaParams {
1798 na: None,
1799 nb: None,
1800 nc: None,
1801 },
1802 },
1803 };
1804 let output = hwma_with_kernel(&input, kernel)?;
1805 Ok(output.values)
1806 }
1807
1808 "jma" => {
1809 let input = match data {
1810 MaData::Candles { candles, source } => JmaInput {
1811 data: JmaData::Candles { candles, source },
1812 params: JmaParams {
1813 period: Some(period),
1814 phase: None,
1815 power: None,
1816 },
1817 },
1818 MaData::Slice(slice) => JmaInput {
1819 data: JmaData::Slice(slice),
1820 params: JmaParams {
1821 period: Some(period),
1822 phase: None,
1823 power: None,
1824 },
1825 },
1826 };
1827 let output = jma_with_kernel(&input, kernel)?;
1828 Ok(output.values)
1829 }
1830
1831 "jsa" => {
1832 let input = match data {
1833 MaData::Candles { candles, source } => JsaInput {
1834 data: JsaData::Candles { candles, source },
1835 params: JsaParams {
1836 period: Some(period),
1837 },
1838 },
1839 MaData::Slice(slice) => JsaInput {
1840 data: JsaData::Slice(slice),
1841 params: JsaParams {
1842 period: Some(period),
1843 },
1844 },
1845 };
1846 let output = jsa_with_kernel(&input, kernel)?;
1847 Ok(output.values)
1848 }
1849
1850 "kama" => {
1851 let input = match data {
1852 MaData::Candles { candles, source } => KamaInput {
1853 data: KamaData::Candles { candles, source },
1854 params: KamaParams {
1855 period: Some(period),
1856 },
1857 },
1858 MaData::Slice(slice) => KamaInput {
1859 data: KamaData::Slice(slice),
1860 params: KamaParams {
1861 period: Some(period),
1862 },
1863 },
1864 };
1865 let output = kama_with_kernel(&input, kernel)?;
1866 Ok(output.values)
1867 }
1868
1869 "linreg" => {
1870 let input = match data {
1871 MaData::Candles { candles, source } => LinRegInput {
1872 data: LinRegData::Candles { candles, source },
1873 params: LinRegParams {
1874 period: Some(period),
1875 },
1876 },
1877 MaData::Slice(slice) => LinRegInput {
1878 data: LinRegData::Slice(slice),
1879 params: LinRegParams {
1880 period: Some(period),
1881 },
1882 },
1883 };
1884 let output = linreg_with_kernel(&input, kernel)?;
1885 Ok(output.values)
1886 }
1887
1888 "maaq" => {
1889 let input = match data {
1890 MaData::Candles { candles, source } => MaaqInput {
1891 data: MaaqData::Candles { candles, source },
1892 params: MaaqParams {
1893 period: Some(period),
1894 fast_period: None,
1895 slow_period: None,
1896 },
1897 },
1898 MaData::Slice(slice) => MaaqInput {
1899 data: MaaqData::Slice(slice),
1900 params: MaaqParams {
1901 period: Some(period),
1902 fast_period: None,
1903 slow_period: None,
1904 },
1905 },
1906 };
1907 let output = maaq_with_kernel(&input, kernel)?;
1908 Ok(output.values)
1909 }
1910
1911 "mama" => {
1912 let input = match data {
1913 MaData::Candles { candles, source } => {
1914 MamaInput::from_candles(candles, source, MamaParams::default())
1915 }
1916 MaData::Slice(slice) => MamaInput::from_slice(slice, MamaParams::default()),
1917 };
1918 let output = mama_with_kernel(&input, kernel)?;
1919 Ok(output.mama_values)
1920 }
1921
1922 "mwdx" => {
1923 let input = match data {
1924 MaData::Candles { candles, source } => {
1925 MwdxInput::from_candles(candles, source, MwdxParams { factor: Some(0.2) })
1926 }
1927 MaData::Slice(slice) => {
1928 MwdxInput::from_slice(slice, MwdxParams { factor: Some(0.2) })
1929 }
1930 };
1931 let output = mwdx_with_kernel(&input, kernel)?;
1932 Ok(output.values)
1933 }
1934
1935 "nma" => {
1936 let input = match data {
1937 MaData::Candles { candles, source } => NmaInput {
1938 data: NmaData::Candles { candles, source },
1939 params: NmaParams {
1940 period: Some(period),
1941 },
1942 },
1943 MaData::Slice(slice) => NmaInput {
1944 data: NmaData::Slice(slice),
1945 params: NmaParams {
1946 period: Some(period),
1947 },
1948 },
1949 };
1950 let output = nma_with_kernel(&input, kernel)?;
1951 Ok(output.values)
1952 }
1953
1954 "pwma" => {
1955 let input = match data {
1956 MaData::Candles { candles, source } => PwmaInput {
1957 data: PwmaData::Candles { candles, source },
1958 params: PwmaParams {
1959 period: Some(period),
1960 },
1961 },
1962 MaData::Slice(slice) => PwmaInput {
1963 data: PwmaData::Slice(slice),
1964 params: PwmaParams {
1965 period: Some(period),
1966 },
1967 },
1968 };
1969 let output = pwma_with_kernel(&input, kernel)?;
1970 Ok(output.values)
1971 }
1972
1973 "reflex" => {
1974 let input = match data {
1975 MaData::Candles { candles, source } => ReflexInput {
1976 data: ReflexData::Candles { candles, source },
1977 params: ReflexParams {
1978 period: Some(period),
1979 },
1980 },
1981 MaData::Slice(slice) => ReflexInput {
1982 data: ReflexData::Slice(slice),
1983 params: ReflexParams {
1984 period: Some(period),
1985 },
1986 },
1987 };
1988 let output = reflex_with_kernel(&input, kernel)?;
1989 Ok(output.values)
1990 }
1991
1992 "sinwma" => {
1993 let input = match data {
1994 MaData::Candles { candles, source } => SinWmaInput {
1995 data: SinWmaData::Candles { candles, source },
1996 params: SinWmaParams {
1997 period: Some(period),
1998 },
1999 },
2000 MaData::Slice(slice) => SinWmaInput {
2001 data: SinWmaData::Slice(slice),
2002 params: SinWmaParams {
2003 period: Some(period),
2004 },
2005 },
2006 };
2007 let output = sinwma_with_kernel(&input, kernel)?;
2008 Ok(output.values)
2009 }
2010
2011 "sqwma" => {
2012 let input = match data {
2013 MaData::Candles { candles, source } => SqwmaInput {
2014 data: SqwmaData::Candles { candles, source },
2015 params: SqwmaParams {
2016 period: Some(period),
2017 },
2018 },
2019 MaData::Slice(slice) => SqwmaInput {
2020 data: SqwmaData::Slice(slice),
2021 params: SqwmaParams {
2022 period: Some(period),
2023 },
2024 },
2025 };
2026 let output = sqwma_with_kernel(&input, kernel)?;
2027 Ok(output.values)
2028 }
2029
2030 "srwma" => {
2031 let input = match data {
2032 MaData::Candles { candles, source } => SrwmaInput {
2033 data: SrwmaData::Candles { candles, source },
2034 params: SrwmaParams {
2035 period: Some(period),
2036 },
2037 },
2038 MaData::Slice(slice) => SrwmaInput {
2039 data: SrwmaData::Slice(slice),
2040 params: SrwmaParams {
2041 period: Some(period),
2042 },
2043 },
2044 };
2045 let output = srwma_with_kernel(&input, kernel)?;
2046 Ok(output.values)
2047 }
2048
2049 "smma" => {
2050 let input = match data {
2051 MaData::Candles { candles, source } => SmmaInput {
2052 data: SmmaData::Candles { candles, source },
2053 params: SmmaParams {
2054 period: Some(period),
2055 },
2056 },
2057 MaData::Slice(slice) => SmmaInput {
2058 data: SmmaData::Slice(slice),
2059 params: SmmaParams {
2060 period: Some(period),
2061 },
2062 },
2063 };
2064 let output = smma_with_kernel(&input, kernel)?;
2065 Ok(output.values)
2066 }
2067
2068 "supersmoother" => {
2069 let input = match data {
2070 MaData::Candles { candles, source } => SuperSmootherInput {
2071 data: SuperSmootherData::Candles { candles, source },
2072 params: SuperSmootherParams {
2073 period: Some(period),
2074 },
2075 },
2076 MaData::Slice(slice) => SuperSmootherInput {
2077 data: SuperSmootherData::Slice(slice),
2078 params: SuperSmootherParams {
2079 period: Some(period),
2080 },
2081 },
2082 };
2083 let output = supersmoother_with_kernel(&input, kernel)?;
2084 Ok(output.values)
2085 }
2086
2087 "supersmoother_3_pole" => {
2088 let input = match data {
2089 MaData::Candles { candles, source } => SuperSmoother3PoleInput {
2090 data: SuperSmoother3PoleData::Candles { candles, source },
2091 params: SuperSmoother3PoleParams {
2092 period: Some(period),
2093 },
2094 },
2095 MaData::Slice(slice) => SuperSmoother3PoleInput {
2096 data: SuperSmoother3PoleData::Slice(slice),
2097 params: SuperSmoother3PoleParams {
2098 period: Some(period),
2099 },
2100 },
2101 };
2102 let output = supersmoother_3_pole_with_kernel(&input, kernel)?;
2103 Ok(output.values)
2104 }
2105
2106 "sgf" => {
2107 let input = match data {
2108 MaData::Candles { candles, source } => SgfInput {
2109 data: SgfData::Candles { candles, source },
2110 params: SgfParams {
2111 period: Some(period),
2112 poly_order: Some(2),
2113 },
2114 },
2115 MaData::Slice(slice) => SgfInput {
2116 data: SgfData::Slice(slice),
2117 params: SgfParams {
2118 period: Some(period),
2119 poly_order: Some(2),
2120 },
2121 },
2122 };
2123 let output = sgf_with_kernel(&input, kernel)?;
2124 Ok(output.values)
2125 }
2126
2127 "swma" => {
2128 let input = match data {
2129 MaData::Candles { candles, source } => SwmaInput {
2130 data: SwmaData::Candles { candles, source },
2131 params: SwmaParams {
2132 period: Some(period),
2133 },
2134 },
2135 MaData::Slice(slice) => SwmaInput {
2136 data: SwmaData::Slice(slice),
2137 params: SwmaParams {
2138 period: Some(period),
2139 },
2140 },
2141 };
2142 let output = swma_with_kernel(&input, kernel)?;
2143 Ok(output.values)
2144 }
2145
2146 "tema" => {
2147 let input = match data {
2148 MaData::Candles { candles, source } => TemaInput {
2149 data: TemaData::Candles { candles, source },
2150 params: TemaParams {
2151 period: Some(period),
2152 },
2153 },
2154 MaData::Slice(slice) => TemaInput {
2155 data: TemaData::Slice(slice),
2156 params: TemaParams {
2157 period: Some(period),
2158 },
2159 },
2160 };
2161 let output = tema_with_kernel(&input, kernel)?;
2162 Ok(output.values)
2163 }
2164
2165 "tilson" => {
2166 let input = match data {
2167 MaData::Candles { candles, source } => TilsonInput {
2168 data: TilsonData::Candles { candles, source },
2169 params: TilsonParams {
2170 period: Some(period),
2171 volume_factor: None,
2172 },
2173 },
2174 MaData::Slice(slice) => TilsonInput {
2175 data: TilsonData::Slice(slice),
2176 params: TilsonParams {
2177 period: Some(period),
2178 volume_factor: None,
2179 },
2180 },
2181 };
2182 let output = tilson_with_kernel(&input, kernel)?;
2183 Ok(output.values)
2184 }
2185
2186 "trendflex" => {
2187 let input = match data {
2188 MaData::Candles { candles, source } => TrendFlexInput {
2189 data: TrendFlexData::Candles { candles, source },
2190 params: TrendFlexParams {
2191 period: Some(period),
2192 },
2193 },
2194 MaData::Slice(slice) => TrendFlexInput {
2195 data: TrendFlexData::Slice(slice),
2196 params: TrendFlexParams {
2197 period: Some(period),
2198 },
2199 },
2200 };
2201 let output = trendflex_with_kernel(&input, kernel)?;
2202 Ok(output.values)
2203 }
2204
2205 "corrected_moving_average" => {
2206 let input = match data {
2207 MaData::Candles { candles, source } => CorrectedMovingAverageInput {
2208 data: CorrectedMovingAverageData::Candles { candles, source },
2209 params: CorrectedMovingAverageParams {
2210 period: Some(period),
2211 },
2212 },
2213 MaData::Slice(slice) => CorrectedMovingAverageInput {
2214 data: CorrectedMovingAverageData::Slice(slice),
2215 params: CorrectedMovingAverageParams {
2216 period: Some(period),
2217 },
2218 },
2219 };
2220 let output = corrected_moving_average_with_kernel(&input, kernel)?;
2221 Ok(output.values)
2222 }
2223
2224 "wave_smoother" => {
2225 let input = match data {
2226 MaData::Candles { candles, source } => WaveSmootherInput {
2227 data: WaveSmootherData::Candles { candles, source },
2228 params: WaveSmootherParams {
2229 period: Some(period),
2230 phase: None,
2231 },
2232 },
2233 MaData::Slice(slice) => WaveSmootherInput {
2234 data: WaveSmootherData::Slice(slice),
2235 params: WaveSmootherParams {
2236 period: Some(period),
2237 phase: None,
2238 },
2239 },
2240 };
2241 let output = wave_smoother_with_kernel(&input, kernel)?;
2242 Ok(output.values)
2243 }
2244
2245 "trima" => {
2246 let input = match data {
2247 MaData::Candles { candles, source } => TrimaInput {
2248 data: TrimaData::Candles { candles, source },
2249 params: TrimaParams {
2250 period: Some(period),
2251 },
2252 },
2253 MaData::Slice(slice) => TrimaInput {
2254 data: TrimaData::Slice(slice),
2255 params: TrimaParams {
2256 period: Some(period),
2257 },
2258 },
2259 };
2260 let output = trima_with_kernel(&input, kernel)?;
2261 Ok(output.values)
2262 }
2263
2264 "wilders" => {
2265 let input = match data {
2266 MaData::Candles { candles, source } => WildersInput {
2267 data: WildersData::Candles { candles, source },
2268 params: WildersParams {
2269 period: Some(period),
2270 },
2271 },
2272 MaData::Slice(slice) => WildersInput {
2273 data: WildersData::Slice(slice),
2274 params: WildersParams {
2275 period: Some(period),
2276 },
2277 },
2278 };
2279 let output = wilders_with_kernel(&input, kernel)?;
2280 Ok(output.values)
2281 }
2282
2283 "wma" => {
2284 let input = match data {
2285 MaData::Candles { candles, source } => WmaInput {
2286 data: WmaData::Candles { candles, source },
2287 params: WmaParams {
2288 period: Some(period),
2289 },
2290 },
2291 MaData::Slice(slice) => WmaInput {
2292 data: WmaData::Slice(slice),
2293 params: WmaParams {
2294 period: Some(period),
2295 },
2296 },
2297 };
2298 let output = wma_with_kernel(&input, kernel)?;
2299 Ok(output.values)
2300 }
2301
2302 "vpwma" => {
2303 let input = match data {
2304 MaData::Candles { candles, source } => VpwmaInput {
2305 data: VpwmaData::Candles { candles, source },
2306 params: VpwmaParams {
2307 period: Some(period),
2308 power: Some(0.382),
2309 },
2310 },
2311 MaData::Slice(_) => {
2312 return Err(MaError::RequiresVolume { indicator: "vpwma" }.into());
2313 }
2314 };
2315 let output = vpwma_with_kernel(&input, kernel)?;
2316 Ok(output.values)
2317 }
2318
2319 "vwap" => {
2320 let input = match data {
2321 MaData::Candles { candles, .. } => VwapInput {
2322 data: VwapData::Candles {
2323 candles,
2324 source: "hlc3",
2325 },
2326 params: VwapParams::default(),
2327 },
2328 MaData::Slice(_) => {
2329 return Err(MaError::RequiresVolume { indicator: "vwap" }.into());
2330 }
2331 };
2332 let output = vwap_with_kernel(&input, kernel)?;
2333 Ok(output.values)
2334 }
2335
2336 "vwma" => {
2337 let input = match data {
2338 MaData::Candles { candles, source } => VwmaInput {
2339 data: VwmaData::Candles { candles, source },
2340 params: VwmaParams {
2341 period: Some(period),
2342 },
2343 },
2344 MaData::Slice(_) => {
2345 return Err(MaError::RequiresVolume { indicator: "vwma" }.into());
2346 }
2347 };
2348 let output = vwma_with_kernel(&input, kernel)?;
2349 Ok(output.values)
2350 }
2351 "elastic_volume_weighted_moving_average" => {
2352 let input = match data {
2353 MaData::Candles { candles, source } => ElasticVolumeWeightedMovingAverageInput {
2354 data: ElasticVolumeWeightedMovingAverageData::Candles { candles, source },
2355 params: ElasticVolumeWeightedMovingAverageParams {
2356 length: Some(period),
2357 absolute_volume_millions: None,
2358 use_volume_sum: Some(true),
2359 },
2360 },
2361 MaData::Slice(_) => {
2362 return Err(MaError::RequiresVolume {
2363 indicator: "elastic_volume_weighted_moving_average",
2364 }
2365 .into());
2366 }
2367 };
2368 let output = elastic_volume_weighted_moving_average_with_kernel(&input, kernel)?;
2369 Ok(output.values)
2370 }
2371
2372 "zlema" => {
2373 let input = match data {
2374 MaData::Candles { candles, source } => ZlemaInput {
2375 data: ZlemaData::Candles { candles, source },
2376 params: ZlemaParams {
2377 period: Some(period),
2378 },
2379 },
2380 MaData::Slice(slice) => ZlemaInput {
2381 data: ZlemaData::Slice(slice),
2382 params: ZlemaParams {
2383 period: Some(period),
2384 },
2385 },
2386 };
2387 let output = zlema_with_kernel(&input, kernel)?;
2388 Ok(output.values)
2389 }
2390
2391 "buff_averages" => {
2392 return Err(MaError::RequiresVolume {
2393 indicator: "buff_averages",
2394 }
2395 .into());
2396 }
2397
2398 "dma" => {
2399 let input = match data {
2400 MaData::Candles { candles, source } => DmaInput {
2401 data: DmaData::Candles { candles, source },
2402 params: DmaParams {
2403 ema_length: Some(period),
2404 ..Default::default()
2405 },
2406 },
2407 MaData::Slice(slice) => DmaInput {
2408 data: DmaData::Slice(slice),
2409 params: DmaParams {
2410 ema_length: Some(period),
2411 ..Default::default()
2412 },
2413 },
2414 };
2415 let output = dma_with_kernel(&input, kernel)?;
2416 Ok(output.values)
2417 }
2418
2419 "ehlers_ecema" => {
2420 let input = match data {
2421 MaData::Candles { candles, source } => EhlersEcemaInput {
2422 data: EhlersEcemaData::Candles { candles, source },
2423 params: EhlersEcemaParams {
2424 length: Some(period),
2425 ..Default::default()
2426 },
2427 },
2428 MaData::Slice(slice) => EhlersEcemaInput {
2429 data: EhlersEcemaData::Slice(slice),
2430 params: EhlersEcemaParams {
2431 length: Some(period),
2432 ..Default::default()
2433 },
2434 },
2435 };
2436 let output = ehlers_ecema_with_kernel(&input, kernel)?;
2437 Ok(output.values)
2438 }
2439
2440 "ehlers_kama" => {
2441 let input = match data {
2442 MaData::Candles { candles, source } => EhlersKamaInput {
2443 data: EhlersKamaData::Candles { candles, source },
2444 params: EhlersKamaParams {
2445 period: Some(period),
2446 ..Default::default()
2447 },
2448 },
2449 MaData::Slice(slice) => EhlersKamaInput {
2450 data: EhlersKamaData::Slice(slice),
2451 params: EhlersKamaParams {
2452 period: Some(period),
2453 ..Default::default()
2454 },
2455 },
2456 };
2457 let output = ehlers_kama_with_kernel(&input, kernel)?;
2458 Ok(output.values)
2459 }
2460
2461 "ehlers_pma" => {
2462 return Err(MaError::DualOutputNotSupported {
2463 indicator: "ehlers_pma",
2464 }
2465 .into());
2466 }
2467
2468 "ehma" => {
2469 let input = match data {
2470 MaData::Candles { candles, source } => EhmaInput {
2471 data: EhmaData::Candles { candles, source },
2472 params: EhmaParams {
2473 period: Some(period),
2474 ..Default::default()
2475 },
2476 },
2477 MaData::Slice(slice) => EhmaInput {
2478 data: EhmaData::Slice(slice),
2479 params: EhmaParams {
2480 period: Some(period),
2481 ..Default::default()
2482 },
2483 },
2484 };
2485 let output = ehma_with_kernel(&input, kernel)?;
2486 Ok(output.values)
2487 }
2488
2489 "frama" => {
2490 let input = match data {
2491 MaData::Candles { candles, .. } => FramaInput::from_candles(
2492 candles,
2493 FramaParams {
2494 window: Some(period),
2495 ..Default::default()
2496 },
2497 ),
2498 MaData::Slice(slice) => FramaInput::from_slices(
2499 slice,
2500 slice,
2501 slice,
2502 FramaParams {
2503 window: Some(period),
2504 ..Default::default()
2505 },
2506 ),
2507 };
2508 let output = frama_with_kernel(&input, kernel)?;
2509 Ok(output.values)
2510 }
2511
2512 "nama" => {
2513 let input = match data {
2514 MaData::Candles { candles, source } => NamaInput {
2515 data: NamaData::Candles { candles, source },
2516 params: NamaParams {
2517 period: Some(period),
2518 ..Default::default()
2519 },
2520 },
2521 MaData::Slice(slice) => NamaInput {
2522 data: NamaData::Slice(slice),
2523 params: NamaParams {
2524 period: Some(period),
2525 ..Default::default()
2526 },
2527 },
2528 };
2529 let output = nama_with_kernel(&input, kernel)?;
2530 Ok(output.values)
2531 }
2532
2533 "n_order_ema" => {
2534 let input = match data {
2535 MaData::Candles { candles, source } => NOrderEmaInput {
2536 data: NOrderEmaData::Candles { candles, source },
2537 params: NOrderEmaParams {
2538 period: Some(period as f64),
2539 order: Some(1),
2540 ema_style: Some(NOrderEmaStyle::Ema.as_str().to_string()),
2541 iir_style: Some(NOrderEmaIirStyle::ImpulseMatched.as_str().to_string()),
2542 },
2543 },
2544 MaData::Slice(slice) => NOrderEmaInput {
2545 data: NOrderEmaData::Slice(slice),
2546 params: NOrderEmaParams {
2547 period: Some(period as f64),
2548 order: Some(1),
2549 ema_style: Some(NOrderEmaStyle::Ema.as_str().to_string()),
2550 iir_style: Some(NOrderEmaIirStyle::ImpulseMatched.as_str().to_string()),
2551 },
2552 },
2553 };
2554 let output = n_order_ema_with_kernel(&input, kernel)?;
2555 Ok(output.values)
2556 }
2557
2558 "sama" => {
2559 let input = match data {
2560 MaData::Candles { candles, source } => SamaInput {
2561 data: SamaData::Candles { candles, source },
2562 params: SamaParams {
2563 length: Some(period),
2564 ..Default::default()
2565 },
2566 },
2567 MaData::Slice(slice) => SamaInput {
2568 data: SamaData::Slice(slice),
2569 params: SamaParams {
2570 length: Some(period),
2571 ..Default::default()
2572 },
2573 },
2574 };
2575 let output = sama_with_kernel(&input, kernel)?;
2576 Ok(output.values)
2577 }
2578
2579 "tradjema" => {
2580 return Err(MaError::RequiresHighLow {
2581 indicator: "tradjema",
2582 }
2583 .into());
2584 }
2585
2586 "uma" => {
2587 return Err(MaError::RequiresVolume { indicator: "uma" }.into());
2588 }
2589
2590 "volatility_adjusted_ma" | "vama" => {
2591 let input = match data {
2592 MaData::Candles { candles, source } => VamaInput {
2593 data: VamaData::Candles { candles, source },
2594 params: VamaParams {
2595 base_period: Some(period),
2596 ..Default::default()
2597 },
2598 },
2599 MaData::Slice(slice) => VamaInput {
2600 data: VamaData::Slice(slice),
2601 params: VamaParams {
2602 base_period: Some(period),
2603 ..Default::default()
2604 },
2605 },
2606 };
2607 let output = vama_with_kernel(&input, kernel)?;
2608 Ok(output.values)
2609 }
2610
2611 "volume_adjusted_ma" => {
2612 return Err(MaError::RequiresVolume {
2613 indicator: "volume_adjusted_ma",
2614 }
2615 .into());
2616 }
2617
2618 _ => {
2619 return Err(MaError::UnknownType {
2620 ma_type: ma_type.to_string(),
2621 }
2622 .into());
2623 }
2624 }
2625}
2626
2627#[cfg(feature = "python")]
2628#[pyfunction(name = "ma")]
2629#[pyo3(signature = (data, ma_type, period, kernel=None))]
2630pub fn ma_py<'py>(
2631 py: Python<'py>,
2632 data: PyReadonlyArray1<'py, f64>,
2633 ma_type: &str,
2634 period: usize,
2635 kernel: Option<&str>,
2636) -> PyResult<Bound<'py, PyArray1<f64>>> {
2637 use numpy::{IntoPyArray, PyArrayMethods};
2638
2639 let slice_in = data.as_slice()?;
2640 let kern = validate_kernel(kernel, false)?;
2641
2642 let result_vec: Vec<f64> = py
2643 .allow_threads(|| -> Result<Vec<f64>, Box<dyn Error + Send + Sync>> {
2644 match ma_with_kernel(ma_type, MaData::Slice(slice_in), period, kern) {
2645 Ok(result) => Ok(result),
2646 Err(e) => {
2647 if e.to_string().contains("Unknown moving average type") {
2648 ma_with_kernel("sma", MaData::Slice(slice_in), period, kern).map_err(
2649 |e| -> Box<dyn Error + Send + Sync> {
2650 Box::new(std::io::Error::new(
2651 std::io::ErrorKind::Other,
2652 e.to_string(),
2653 ))
2654 },
2655 )
2656 } else {
2657 Err(Box::new(std::io::Error::new(
2658 std::io::ErrorKind::Other,
2659 e.to_string(),
2660 )) as Box<dyn Error + Send + Sync>)
2661 }
2662 }
2663 }
2664 })
2665 .map_err(|e| PyValueError::new_err(e.to_string()))?;
2666
2667 Ok(result_vec.into_pyarray(py))
2668}
2669
2670#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
2671#[wasm_bindgen(js_name = "ma")]
2672pub fn ma_js(data: &[f64], ma_type: &str, period: usize) -> Result<Vec<f64>, JsValue> {
2673 match ma(ma_type, MaData::Slice(data), period) {
2674 Ok(result) => Ok(result),
2675 Err(e) => {
2676 if e.to_string().contains("Unknown moving average type") {
2677 ma("sma", MaData::Slice(data), period)
2678 .map_err(|e| JsValue::from_str(&e.to_string()))
2679 } else {
2680 Err(JsValue::from_str(&e.to_string()))
2681 }
2682 }
2683 }
2684}
2685
2686#[cfg(test)]
2687mod tests {
2688 use super::*;
2689 use crate::utilities::data_loader::read_candles_from_csv;
2690
2691 #[test]
2692 fn test_all_ma_variants() {
2693 let ma_types = vec![
2694 "sma",
2695 "ema",
2696 "dema",
2697 "tema",
2698 "smma",
2699 "zlema",
2700 "alma",
2701 "cwma",
2702 "corrected_moving_average",
2703 "edcf",
2704 "fwma",
2705 "gaussian",
2706 "highpass",
2707 "highpass2",
2708 "hma",
2709 "hwma",
2710 "jma",
2711 "jsa",
2712 "frama",
2713 "linreg",
2714 "maaq",
2715 "mwdx",
2716 "nma",
2717 "pwma",
2718 "reflex",
2719 "sinwma",
2720 "sqwma",
2721 "srwma",
2722 "sgf",
2723 "supersmoother",
2724 "supersmoother_3_pole",
2725 "swma",
2726 "tilson",
2727 "trendflex",
2728 "corrected_moving_average",
2729 "ema_deviation_corrected_t3",
2730 "wave_smoother",
2731 "trima",
2732 "wilders",
2733 "wma",
2734 "vpwma",
2735 "vwap",
2736 "vwma",
2737 "elastic_volume_weighted_moving_average",
2738 "mama",
2739 ];
2740
2741 let file_path = "src/data/2018-09-01-2024-Bitfinex_Spot-4h.csv";
2742 let candles = read_candles_from_csv(file_path).expect("Failed to load test candles");
2743
2744 for &ma_type in &ma_types {
2745 let period = 80;
2746 let candles_result = ma(
2747 ma_type,
2748 MaData::Candles {
2749 candles: &candles,
2750 source: "close",
2751 },
2752 period,
2753 )
2754 .unwrap_or_else(|err| panic!("`ma({})` failed with error: {}", ma_type, err));
2755
2756 assert_eq!(
2757 candles_result.len(),
2758 candles.close.len(),
2759 "MA output length for '{}' mismatch",
2760 ma_type
2761 );
2762
2763 let skip_amount = if ma_type == "mama" { 10 } else { 960 };
2764 for (i, &value) in candles_result.iter().enumerate().skip(skip_amount) {
2765 assert!(
2766 !value.is_nan(),
2767 "MA result for '{}' at index {} is NaN",
2768 ma_type,
2769 i
2770 );
2771 }
2772
2773 if ma_type != "mama" && ma_type != "elastic_volume_weighted_moving_average" {
2774 let slice_result = ma(ma_type, MaData::Slice(&candles_result), 60)
2775 .unwrap_or_else(|err| panic!("`ma({})` failed with error: {}", ma_type, err));
2776
2777 assert_eq!(
2778 slice_result.len(),
2779 candles.close.len(),
2780 "MA output length for '{}' mismatch",
2781 ma_type
2782 );
2783
2784 for (i, &value) in slice_result.iter().enumerate().skip(960) {
2785 assert!(
2786 !value.is_nan(),
2787 "MA result for '{}' at index {} is NaN",
2788 ma_type,
2789 i
2790 );
2791 }
2792 }
2793 }
2794 }
2795}