Skip to main content

vector_ta/indicators/moving_averages/
ma.rs

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::ema_deviation_corrected_t3::{
38    ema_deviation_corrected_t3, EmaDeviationCorrectedT3Data, EmaDeviationCorrectedT3Input,
39    EmaDeviationCorrectedT3Params,
40};
41use crate::indicators::moving_averages::elastic_volume_weighted_moving_average::{
42    elastic_volume_weighted_moving_average, elastic_volume_weighted_moving_average_with_kernel,
43    ElasticVolumeWeightedMovingAverageData, ElasticVolumeWeightedMovingAverageInput,
44    ElasticVolumeWeightedMovingAverageParams,
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::volatility_adjusted_ma::{
54    vama, VamaData, VamaInput, VamaParams,
55};
56use crate::indicators::moving_averages::wave_smoother::{
57    wave_smoother, WaveSmootherData, WaveSmootherInput, WaveSmootherParams,
58};
59use crate::indicators::mwdx::{mwdx, MwdxData, MwdxInput, MwdxParams};
60use crate::indicators::nma::{nma, NmaData, NmaInput, NmaParams};
61use crate::indicators::pwma::{pwma, PwmaData, PwmaInput, PwmaParams};
62use crate::indicators::reflex::{reflex, ReflexData, ReflexInput, ReflexParams};
63use crate::indicators::moving_averages::sgf::{sgf, SgfData, SgfInput, SgfParams};
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::volatility_adjusted_ma::vama_with_kernel;
120use crate::indicators::moving_averages::wave_smoother::wave_smoother_with_kernel;
121use crate::indicators::mwdx::mwdx_with_kernel;
122use crate::indicators::nma::nma_with_kernel;
123use crate::indicators::pwma::pwma_with_kernel;
124use crate::indicators::reflex::reflex_with_kernel;
125use crate::indicators::moving_averages::sgf::sgf_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}