1use polars::prelude::*;
2use quantwave_core::traits::Next;
3use quantwave_core::*;
4
5pub mod prelude {
6 pub use crate::{QuantWaveExt, QuantWaveNamespace};
7}
8
9pub trait QuantWaveExt {
10 fn ta(&self) -> QuantWaveNamespace<'_>;
11}
12
13pub struct QuantWaveNamespace<'a>(&'a LazyFrame);
14
15impl<'a> QuantWaveNamespace<'a> {
16 pub fn acos(self, name: &str) -> LazyFrame {
17 self.math_transform_1_in_1_out::<ACOS>(name, "acos")
18 }
19 pub fn asin(self, name: &str) -> LazyFrame {
20 self.math_transform_1_in_1_out::<ASIN>(name, "asin")
21 }
22 pub fn atan(self, name: &str) -> LazyFrame {
23 self.math_transform_1_in_1_out::<ATAN>(name, "atan")
24 }
25 pub fn ceil(self, name: &str) -> LazyFrame {
26 self.math_transform_1_in_1_out::<CEIL>(name, "ceil")
27 }
28 pub fn cos(self, name: &str) -> LazyFrame {
29 self.math_transform_1_in_1_out::<COS>(name, "cos")
30 }
31 pub fn cosh(self, name: &str) -> LazyFrame {
32 self.math_transform_1_in_1_out::<COSH>(name, "cosh")
33 }
34 pub fn exp(self, name: &str) -> LazyFrame {
35 self.math_transform_1_in_1_out::<EXP>(name, "exp")
36 }
37 pub fn floor(self, name: &str) -> LazyFrame {
38 self.math_transform_1_in_1_out::<FLOOR>(name, "floor")
39 }
40 pub fn ln(self, name: &str) -> LazyFrame {
41 self.math_transform_1_in_1_out::<LN>(name, "ln")
42 }
43 pub fn log10(self, name: &str) -> LazyFrame {
44 self.math_transform_1_in_1_out::<LOG10>(name, "log10")
45 }
46 pub fn sin(self, name: &str) -> LazyFrame {
47 self.math_transform_1_in_1_out::<SIN>(name, "sin")
48 }
49 pub fn sinh(self, name: &str) -> LazyFrame {
50 self.math_transform_1_in_1_out::<SINH>(name, "sinh")
51 }
52 pub fn sqrt(self, name: &str) -> LazyFrame {
53 self.math_transform_1_in_1_out::<SQRT>(name, "sqrt")
54 }
55 pub fn tan(self, name: &str) -> LazyFrame {
56 self.math_transform_1_in_1_out::<TAN>(name, "tan")
57 }
58 pub fn tanh(self, name: &str) -> LazyFrame {
59 self.math_transform_1_in_1_out::<TANH>(name, "tanh")
60 }
61
62 pub fn add(self, in1: &str, in2: &str) -> LazyFrame {
63 self.math_operator_2_in_1_out::<ADD>(in1, in2, "add")
64 }
65 pub fn sub(self, in1: &str, in2: &str) -> LazyFrame {
66 self.math_operator_2_in_1_out::<SUB>(in1, in2, "sub")
67 }
68 pub fn mult(self, in1: &str, in2: &str) -> LazyFrame {
69 self.math_operator_2_in_1_out::<MULT>(in1, in2, "mult")
70 }
71 pub fn div(self, in1: &str, in2: &str) -> LazyFrame {
72 self.math_operator_2_in_1_out::<DIV>(in1, in2, "div")
73 }
74
75 pub fn max(self, name: &str, period: usize) -> LazyFrame {
76 self.math_operator_1_in_1_out_period::<MAX>(name, period, "max")
77 }
78 pub fn maxindex(self, name: &str, period: usize) -> LazyFrame {
79 self.math_operator_1_in_1_out_period::<MAXINDEX>(name, period, "maxindex")
80 }
81 pub fn min(self, name: &str, period: usize) -> LazyFrame {
82 self.math_operator_1_in_1_out_period::<MIN>(name, period, "min")
83 }
84 pub fn minindex(self, name: &str, period: usize) -> LazyFrame {
85 self.math_operator_1_in_1_out_period::<MININDEX>(name, period, "minindex")
86 }
87 pub fn sum(self, name: &str, period: usize) -> LazyFrame {
88 self.math_operator_1_in_1_out_period::<SUM>(name, period, "sum")
89 }
90
91 pub fn sma(self, name: &str, period: usize) -> LazyFrame {
92 self.math_operator_1_in_1_out_period::<SMA>(name, period, "sma")
93 }
94 pub fn ema(self, name: &str, period: usize) -> LazyFrame {
95 self.math_operator_1_in_1_out_period::<EMA>(name, period, "ema")
96 }
97 pub fn wma(self, name: &str, period: usize) -> LazyFrame {
98 self.math_operator_1_in_1_out_period::<WMA>(name, period, "wma")
99 }
100 pub fn dema(self, name: &str, period: usize) -> LazyFrame {
101 self.math_operator_1_in_1_out_period::<DEMA>(name, period, "dema")
102 }
103 pub fn trima(self, name: &str, period: usize) -> LazyFrame {
104 self.math_operator_1_in_1_out_period::<TRIMA>(name, period, "trima")
105 }
106 pub fn kama(self, name: &str, period: usize) -> LazyFrame {
107 self.math_operator_1_in_1_out_period::<KAMA>(name, period, "kama")
108 }
109 pub fn midpoint(self, name: &str, period: usize) -> LazyFrame {
110 self.math_operator_1_in_1_out_period::<MIDPOINT>(name, period, "midpoint")
111 }
112 pub fn ht_trendline(self, name: &str) -> LazyFrame {
113 self.math_transform_1_in_1_out::<HT_TRENDLINE>(name, "ht_trendline")
114 }
115 pub fn midprice(self, high: &str, low: &str, period: usize) -> LazyFrame {
116 self.math_operator_2_in_1_out_period::<MIDPRICE>(high, low, period, "midprice")
117 }
118
119 pub fn rsi(self, name: &str, period: usize) -> LazyFrame {
120 self.math_operator_1_in_1_out_period::<RSI>(name, period, "rsi")
121 }
122 pub fn mom(self, name: &str, period: usize) -> LazyFrame {
123 self.math_operator_1_in_1_out_period::<MOM>(name, period, "mom")
124 }
125 pub fn roc(self, name: &str, period: usize) -> LazyFrame {
126 self.math_operator_1_in_1_out_period::<ROC>(name, period, "roc")
127 }
128 pub fn rocp(self, name: &str, period: usize) -> LazyFrame {
129 self.math_operator_1_in_1_out_period::<ROCP>(name, period, "rocp")
130 }
131 pub fn rocr(self, name: &str, period: usize) -> LazyFrame {
132 self.math_operator_1_in_1_out_period::<ROCR>(name, period, "rocr")
133 }
134 pub fn rocr100(self, name: &str, period: usize) -> LazyFrame {
135 self.math_operator_1_in_1_out_period::<ROCR100>(name, period, "rocr100")
136 }
137 pub fn trix(self, name: &str, period: usize) -> LazyFrame {
138 self.math_operator_1_in_1_out_period::<TRIX>(name, period, "trix")
139 }
140 pub fn cmo(self, name: &str, period: usize) -> LazyFrame {
141 self.math_operator_1_in_1_out_period::<CMO>(name, period, "cmo")
142 }
143
144 pub fn adx(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
145 self.ta_3_in_1_out_period::<ADX>(high, low, close, period, "adx")
146 }
147 pub fn adxr(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
148 self.ta_3_in_1_out_period::<ADXR>(high, low, close, period, "adxr")
149 }
150 pub fn cci(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
151 self.ta_3_in_1_out_period::<CCI>(high, low, close, period, "cci")
152 }
153 pub fn willr(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
154 self.ta_3_in_1_out_period::<WILLR>(high, low, close, period, "willr")
155 }
156 pub fn dx(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
157 self.ta_3_in_1_out_period::<DX>(high, low, close, period, "dx")
158 }
159 pub fn plus_di(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
160 self.ta_3_in_1_out_period::<PLUS_DI>(high, low, close, period, "plus_di")
161 }
162 pub fn minus_di(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
163 self.ta_3_in_1_out_period::<MINUS_DI>(high, low, close, period, "minus_di")
164 }
165
166 pub fn ta_atr(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
167 self.ta_3_in_1_out_period::<TaATR>(high, low, close, period, "ta_atr")
168 }
169 pub fn ta_natr(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
170 self.ta_3_in_1_out_period::<TaNATR>(high, low, close, period, "ta_natr")
171 }
172 pub fn ta_trange(self, high: &str, low: &str, close: &str) -> LazyFrame {
173 self.ta_3_in_1_out_default::<TaTRANGE>(high, low, close, "ta_trange")
174 }
175
176 pub fn obv(self, close: &str, volume: &str) -> LazyFrame {
177 self.math_operator_2_in_1_out::<OBV>(close, volume, "obv")
178 }
179 pub fn ad(self, high: &str, low: &str, close: &str, volume: &str) -> LazyFrame {
180 self.ta_4_in_1_out_default::<AD>(high, low, close, volume, "ad")
181 }
182 pub fn adosc(
183 self,
184 high: &str,
185 low: &str,
186 close: &str,
187 volume: &str,
188 fast: usize,
189 slow: usize,
190 ) -> LazyFrame {
191 let high_str = high.to_string();
192 let low_str = low.to_string();
193 let close_str = close.to_string();
194 let volume_str = volume.to_string();
195 self.0.clone().with_columns([as_struct(vec![
196 col(&high_str),
197 col(&low_str),
198 col(&close_str),
199 col(&volume_str),
200 ])
201 .map(
202 move |s| {
203 let ca = s.struct_()?;
204 let s_h = ca.field_by_name(&high_str)?;
205 let s_l = ca.field_by_name(&low_str)?;
206 let s_c = ca.field_by_name(&close_str)?;
207 let s_v = ca.field_by_name(&volume_str)?;
208
209 let high = s_h.f64()?;
210 let low = s_l.f64()?;
211 let close = s_c.f64()?;
212 let volume = s_v.f64()?;
213
214 let mut indicator = ADOSC::new(fast, slow);
215 let mut values = Vec::with_capacity(s.len());
216
217 for i in 0..s.len() {
218 let h = high.get(i).unwrap_or(f64::NAN);
219 let l = low.get(i).unwrap_or(f64::NAN);
220 let c = close.get(i).unwrap_or(f64::NAN);
221 let v = volume.get(i).unwrap_or(f64::NAN);
222 values.push(indicator.next((h, l, c, v)));
223 }
224
225 Ok(Some(Column::from(Series::new("adosc".into(), values))))
226 },
227 GetOutput::from_type(DataType::Float64),
228 )
229 .alias("adosc")])
230 }
231
232 pub fn aroon(self, high: &str, low: &str, period: usize) -> LazyFrame {
233 let high_str = high.to_string();
234 let low_str = low.to_string();
235 self.0
236 .clone()
237 .with_columns([as_struct(vec![col(&high_str), col(&low_str)])
238 .map(
239 move |s| {
240 let ca = s.struct_()?;
241 let s_h = ca.field_by_name(&high_str)?;
242 let s_l = ca.field_by_name(&low_str)?;
243 let high = s_h.f64()?;
244 let low = s_l.f64()?;
245
246 let mut indicator = AROON::new(period);
247 let mut up_vals = Vec::with_capacity(s.len());
248 let mut down_vals = Vec::with_capacity(s.len());
249
250 for i in 0..s.len() {
251 let h = high.get(i).unwrap_or(f64::NAN);
252 let l = low.get(i).unwrap_or(f64::NAN);
253 let (up, down) = indicator.next((h, l));
254 up_vals.push(up);
255 down_vals.push(down);
256 }
257
258 let s_up = Series::new("aroon_up".into(), up_vals);
259 let s_down = Series::new("aroon_down".into(), down_vals);
260 let struct_series = StructChunked::from_series(
261 "aroon_result".into(),
262 s.len(),
263 [s_up, s_down].iter(),
264 )?;
265 Ok(Some(Column::from(struct_series.into_series())))
266 },
267 GetOutput::from_type(DataType::Struct(vec![
268 Field::new("aroon_up".into(), DataType::Float64),
269 Field::new("aroon_down".into(), DataType::Float64),
270 ])),
271 )
272 .alias("aroon")])
273 }
274
275 #[allow(clippy::too_many_arguments)]
276 pub fn stoch(
277 self,
278 high: &str,
279 low: &str,
280 close: &str,
281 fastk: usize,
282 slowk: usize,
283 slowk_matype: talib::MaType,
284 slowd: usize,
285 slowd_matype: talib::MaType,
286 ) -> LazyFrame {
287 let high_str = high.to_string();
288 let low_str = low.to_string();
289 let close_str = close.to_string();
290 self.0.clone().with_columns([as_struct(vec![
291 col(&high_str),
292 col(&low_str),
293 col(&close_str),
294 ])
295 .map(
296 move |s| {
297 let ca = s.struct_()?;
298 let s_h = ca.field_by_name(&high_str)?;
299 let s_l = ca.field_by_name(&low_str)?;
300 let s_c = ca.field_by_name(&close_str)?;
301 let high = s_h.f64()?;
302 let low = s_l.f64()?;
303 let close = s_c.f64()?;
304
305 let mut indicator = STOCH::new(fastk, slowk, slowk_matype, slowd, slowd_matype);
306 let mut k_vals = Vec::with_capacity(s.len());
307 let mut d_vals = Vec::with_capacity(s.len());
308
309 for i in 0..s.len() {
310 let h = high.get(i).unwrap_or(f64::NAN);
311 let l = low.get(i).unwrap_or(f64::NAN);
312 let c = close.get(i).unwrap_or(f64::NAN);
313 let (k, d) = indicator.next((h, l, c));
314 k_vals.push(k);
315 d_vals.push(d);
316 }
317
318 let s_k = Series::new("slowk".into(), k_vals);
319 let s_d = Series::new("slowd".into(), d_vals);
320 let struct_series =
321 StructChunked::from_series("stoch_result".into(), s.len(), [s_k, s_d].iter())?;
322 Ok(Some(Column::from(struct_series.into_series())))
323 },
324 GetOutput::from_type(DataType::Struct(vec![
325 Field::new("slowk".into(), DataType::Float64),
326 Field::new("slowd".into(), DataType::Float64),
327 ])),
328 )
329 .alias("stoch")])
330 }
331
332 pub fn avgprice(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
333 self.ta_4_in_1_out_default::<AVGPRICE>(open, high, low, close, "avgprice")
334 }
335 pub fn medprice(self, high: &str, low: &str) -> LazyFrame {
336 self.math_operator_2_in_1_out::<MEDPRICE>(high, low, "medprice")
337 }
338 pub fn typprice(self, high: &str, low: &str, close: &str) -> LazyFrame {
339 self.ta_3_in_1_out_default::<TYPPRICE>(high, low, close, "typprice")
340 }
341 pub fn wclprice(self, high: &str, low: &str, close: &str) -> LazyFrame {
342 self.ta_3_in_1_out_default::<WCLPRICE>(high, low, close, "wclprice")
343 }
344
345 pub fn ht_dcperiod(self, name: &str) -> LazyFrame {
346 self.math_transform_1_in_1_out::<HT_DCPERIOD>(name, "ht_dcperiod")
347 }
348 pub fn ht_dcphase(self, name: &str) -> LazyFrame {
349 self.math_transform_1_in_1_out::<HT_DCPHASE>(name, "ht_dcphase")
350 }
351 pub fn ht_trendmode(self, name: &str) -> LazyFrame {
352 self.math_transform_1_in_1_out::<HT_TRENDMODE>(name, "ht_trendmode")
353 }
354
355 pub fn ta_stddev(self, name: &str, period: usize, nbdev: f64) -> LazyFrame {
356 let name_str = name.to_string();
357 self.0.clone().with_columns([col(&name_str)
358 .map(
359 move |s| {
360 let ca = s.f64()?;
361 let mut indicator = TaSTDDEV::new(period, nbdev);
362 let mut values = Vec::with_capacity(s.len());
363 for i in 0..s.len() {
364 let val = ca.get(i).unwrap_or(f64::NAN);
365 values.push(indicator.next(val));
366 }
367 Ok(Some(Column::from(Series::new("ta_stddev".into(), values))))
368 },
369 GetOutput::from_type(DataType::Float64),
370 )
371 .alias("ta_stddev")])
372 }
373 pub fn ta_var(self, name: &str, period: usize, nbdev: f64) -> LazyFrame {
374 let name_str = name.to_string();
375 self.0.clone().with_columns([col(&name_str)
376 .map(
377 move |s| {
378 let ca = s.f64()?;
379 let mut indicator = TaVAR::new(period, nbdev);
380 let mut values = Vec::with_capacity(s.len());
381 for i in 0..s.len() {
382 let val = ca.get(i).unwrap_or(f64::NAN);
383 values.push(indicator.next(val));
384 }
385 Ok(Some(Column::from(Series::new("ta_var".into(), values))))
386 },
387 GetOutput::from_type(DataType::Float64),
388 )
389 .alias("ta_var")])
390 }
391 pub fn ta_beta(self, in1: &str, in2: &str, period: usize) -> LazyFrame {
392 self.math_operator_2_in_1_out_period::<TaBETA>(in1, in2, period, "ta_beta")
393 }
394 pub fn ta_correl(self, in1: &str, in2: &str, period: usize) -> LazyFrame {
395 self.math_operator_2_in_1_out_period::<TaCORREL>(in1, in2, period, "ta_correl")
396 }
397 pub fn ta_linearreg(self, name: &str, period: usize) -> LazyFrame {
398 self.math_operator_1_in_1_out_period::<TaLINEARREG>(name, period, "ta_linearreg")
399 }
400 pub fn ta_linearreg_slope(self, name: &str, period: usize) -> LazyFrame {
401 self.math_operator_1_in_1_out_period::<TaLINEARREG_SLOPE>(
402 name,
403 period,
404 "ta_linearreg_slope",
405 )
406 }
407 pub fn ta_linearreg_intercept(self, name: &str, period: usize) -> LazyFrame {
408 self.math_operator_1_in_1_out_period::<TaLINEARREG_INTERCEPT>(
409 name,
410 period,
411 "ta_linearreg_intercept",
412 )
413 }
414 pub fn ta_linearreg_angle(self, name: &str, period: usize) -> LazyFrame {
415 self.math_operator_1_in_1_out_period::<TaLINEARREG_ANGLE>(
416 name,
417 period,
418 "ta_linearreg_angle",
419 )
420 }
421 pub fn ta_tsf(self, name: &str, period: usize) -> LazyFrame {
422 self.math_operator_1_in_1_out_period::<TaTSF>(name, period, "ta_tsf")
423 }
424
425 pub fn cdl_2crows(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
426 self.ta_4_in_1_out_default::<CDL2CROWS>(open, high, low, close, "cdl_2crows")
427 }
428 pub fn cdl_3blackcrows(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
429 self.ta_4_in_1_out_default::<CDL3BLACKCROWS>(open, high, low, close, "cdl_3blackcrows")
430 }
431 pub fn cdl_3inside(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
432 self.ta_4_in_1_out_default::<CDL3INSIDE>(open, high, low, close, "cdl_3inside")
433 }
434 pub fn cdl_3linestrike(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
435 self.ta_4_in_1_out_default::<CDL3LINESTRIKE>(open, high, low, close, "cdl_3linestrike")
436 }
437 pub fn cdl_3outside(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
438 self.ta_4_in_1_out_default::<CDL3OUTSIDE>(open, high, low, close, "cdl_3outside")
439 }
440 pub fn cdl_3starsinsouth(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
441 self.ta_4_in_1_out_default::<CDL3STARSINSOUTH>(open, high, low, close, "cdl_3starsinsouth")
442 }
443 pub fn cdl_3whitesoldiers(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
444 self.ta_4_in_1_out_default::<CDL3WHITESOLDIERS>(
445 open,
446 high,
447 low,
448 close,
449 "cdl_3whitesoldiers",
450 )
451 }
452 pub fn cdl_abandonedbaby(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
453 self.ta_4_in_1_out_default::<CDLABANDONEDBABY>(open, high, low, close, "cdl_abandonedbaby")
454 }
455 pub fn cdl_advanceblock(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
456 self.ta_4_in_1_out_default::<CDLADVANCEBLOCK>(open, high, low, close, "cdl_advanceblock")
457 }
458 pub fn cdl_belthold(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
459 self.ta_4_in_1_out_default::<CDLBELTHOLD>(open, high, low, close, "cdl_belthold")
460 }
461 pub fn cdl_breakaway(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
462 self.ta_4_in_1_out_default::<CDLBREAKAWAY>(open, high, low, close, "cdl_breakaway")
463 }
464 pub fn cdl_closingmarubozu(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
465 self.ta_4_in_1_out_default::<CDLCLOSINGMARUBOZU>(
466 open,
467 high,
468 low,
469 close,
470 "cdl_closingmarubozu",
471 )
472 }
473 pub fn cdl_concealbabyswall(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
474 self.ta_4_in_1_out_default::<CDLCONCEALBABYSWALL>(
475 open,
476 high,
477 low,
478 close,
479 "cdl_concealbabyswall",
480 )
481 }
482 pub fn cdl_counterattack(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
483 self.ta_4_in_1_out_default::<CDLCOUNTERATTACK>(open, high, low, close, "cdl_counterattack")
484 }
485 pub fn cdl_darkcloudcover(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
486 self.ta_4_in_1_out_default::<CDLDARKCLOUDCOVER>(
487 open,
488 high,
489 low,
490 close,
491 "cdl_darkcloudcover",
492 )
493 }
494 pub fn cdl_doji(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
495 self.ta_4_in_1_out_default::<CDLDOJI>(open, high, low, close, "cdl_doji")
496 }
497 pub fn cdl_dojistar(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
498 self.ta_4_in_1_out_default::<CDLDOJISTAR>(open, high, low, close, "cdl_dojistar")
499 }
500 pub fn cdl_dragonflydoji(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
501 self.ta_4_in_1_out_default::<CDLDRAGONFLYDOJI>(open, high, low, close, "cdl_dragonflydoji")
502 }
503 pub fn cdl_engulfing(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
504 self.ta_4_in_1_out_default::<CDLENGULFING>(open, high, low, close, "cdl_engulfing")
505 }
506 pub fn cdl_eveningdojistar(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
507 self.ta_4_in_1_out_default::<CDLEVENINGDOJISTAR>(
508 open,
509 high,
510 low,
511 close,
512 "cdl_eveningdojistar",
513 )
514 }
515 pub fn cdl_eveningstar(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
516 self.ta_4_in_1_out_default::<CDLEVENINGSTAR>(open, high, low, close, "cdl_eveningstar")
517 }
518 pub fn cdl_gapsidesidewhite(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
519 self.ta_4_in_1_out_default::<CDLGAPSIDESIDEWHITE>(
520 open,
521 high,
522 low,
523 close,
524 "cdl_gapsidesidewhite",
525 )
526 }
527 pub fn cdl_gravestonedoji(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
528 self.ta_4_in_1_out_default::<CDLGRAVESTONEDOJI>(
529 open,
530 high,
531 low,
532 close,
533 "cdl_gravestonedoji",
534 )
535 }
536 pub fn cdl_hammer(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
537 self.ta_4_in_1_out_default::<CDLHAMMER>(open, high, low, close, "cdl_hammer")
538 }
539 pub fn cdl_hangingman(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
540 self.ta_4_in_1_out_default::<CDLHANGINGMAN>(open, high, low, close, "cdl_hangingman")
541 }
542 pub fn cdl_harami(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
543 self.ta_4_in_1_out_default::<CDLHARAMI>(open, high, low, close, "cdl_harami")
544 }
545 pub fn cdl_haramicross(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
546 self.ta_4_in_1_out_default::<CDLHARAMICROSS>(open, high, low, close, "cdl_haramicross")
547 }
548 pub fn cdl_highwave(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
549 self.ta_4_in_1_out_default::<CDLHIGHWAVE>(open, high, low, close, "cdl_highwave")
550 }
551 pub fn cdl_hikkake(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
552 self.ta_4_in_1_out_default::<CDLHIKKAKE>(open, high, low, close, "cdl_hikkake")
553 }
554 pub fn cdl_hikkakemod(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
555 self.ta_4_in_1_out_default::<CDLHIKKAKEMOD>(open, high, low, close, "cdl_hikkakemod")
556 }
557 pub fn cdl_homingpigeon(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
558 self.ta_4_in_1_out_default::<CDLHOMINGPIGEON>(open, high, low, close, "cdl_homingpigeon")
559 }
560 pub fn cdl_identical3crows(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
561 self.ta_4_in_1_out_default::<CDLIDENTICAL3CROWS>(
562 open,
563 high,
564 low,
565 close,
566 "cdl_identical3crows",
567 )
568 }
569 pub fn cdl_inneck(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
570 self.ta_4_in_1_out_default::<CDLINNECK>(open, high, low, close, "cdl_inneck")
571 }
572 pub fn cdl_invertedhammer(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
573 self.ta_4_in_1_out_default::<CDLINVERTEDHAMMER>(
574 open,
575 high,
576 low,
577 close,
578 "cdl_invertedhammer",
579 )
580 }
581 pub fn cdl_kicking(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
582 self.ta_4_in_1_out_default::<CDLKICKING>(open, high, low, close, "cdl_kicking")
583 }
584 pub fn cdl_kickingbylength(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
585 self.ta_4_in_1_out_default::<CDLKICKINGBYLENGTH>(
586 open,
587 high,
588 low,
589 close,
590 "cdl_kickingbylength",
591 )
592 }
593 pub fn cdl_ladderbottom(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
594 self.ta_4_in_1_out_default::<CDLLADDERBOTTOM>(open, high, low, close, "cdl_ladderbottom")
595 }
596 pub fn cdl_longleggeddoji(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
597 self.ta_4_in_1_out_default::<CDLLONGLEGGEDDOJI>(
598 open,
599 high,
600 low,
601 close,
602 "cdl_longleggeddoji",
603 )
604 }
605 pub fn cdl_longline(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
606 self.ta_4_in_1_out_default::<CDLLONGLINE>(open, high, low, close, "cdl_longline")
607 }
608 pub fn cdl_marubozu(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
609 self.ta_4_in_1_out_default::<CDLMARUBOZU>(open, high, low, close, "cdl_marubozu")
610 }
611 pub fn cdl_matchinglow(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
612 self.ta_4_in_1_out_default::<CDLMATCHINGLOW>(open, high, low, close, "cdl_matchinglow")
613 }
614 pub fn cdl_mathold(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
615 self.ta_4_in_1_out_default::<CDLMATHOLD>(open, high, low, close, "cdl_mathold")
616 }
617 pub fn cdl_morningdojistar(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
618 self.ta_4_in_1_out_default::<CDLMORNINGDOJISTAR>(
619 open,
620 high,
621 low,
622 close,
623 "cdl_morningdojistar",
624 )
625 }
626 pub fn cdl_morningstar(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
627 self.ta_4_in_1_out_default::<CDLMORNINGSTAR>(open, high, low, close, "cdl_morningstar")
628 }
629 pub fn cdl_onneck(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
630 self.ta_4_in_1_out_default::<CDLONNECK>(open, high, low, close, "cdl_onneck")
631 }
632 pub fn cdl_piercing(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
633 self.ta_4_in_1_out_default::<CDLPIERCING>(open, high, low, close, "cdl_piercing")
634 }
635 pub fn cdl_rickshawman(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
636 self.ta_4_in_1_out_default::<CDLRICKSHAWMAN>(open, high, low, close, "cdl_rickshawman")
637 }
638 pub fn cdl_risefall3methods(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
639 self.ta_4_in_1_out_default::<CDLRISEFALL3METHODS>(
640 open,
641 high,
642 low,
643 close,
644 "cdl_risefall3methods",
645 )
646 }
647 pub fn cdl_separatinglines(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
648 self.ta_4_in_1_out_default::<CDLSEPARATINGLINES>(
649 open,
650 high,
651 low,
652 close,
653 "cdl_separatinglines",
654 )
655 }
656 pub fn cdl_shootingstar(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
657 self.ta_4_in_1_out_default::<CDLSHOOTINGSTAR>(open, high, low, close, "cdl_shootingstar")
658 }
659 pub fn cdl_shortline(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
660 self.ta_4_in_1_out_default::<CDLSHORTLINE>(open, high, low, close, "cdl_shortline")
661 }
662 pub fn cdl_spinningtop(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
663 self.ta_4_in_1_out_default::<CDLSPINNINGTOP>(open, high, low, close, "cdl_spinningtop")
664 }
665 pub fn cdl_stalledpattern(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
666 self.ta_4_in_1_out_default::<CDLSTALLEDPATTERN>(
667 open,
668 high,
669 low,
670 close,
671 "cdl_stalledpattern",
672 )
673 }
674 pub fn cdl_sticksandwich(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
675 self.ta_4_in_1_out_default::<CDLSTICKSANDWICH>(open, high, low, close, "cdl_sticksandwich")
676 }
677 pub fn cdl_takuri(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
678 self.ta_4_in_1_out_default::<CDLTAKURI>(open, high, low, close, "cdl_takuri")
679 }
680 pub fn cdl_tasukigap(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
681 self.ta_4_in_1_out_default::<CDLTASUKIGAP>(open, high, low, close, "cdl_tasukigap")
682 }
683 pub fn cdl_thrusting(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
684 self.ta_4_in_1_out_default::<CDLTHRUSTING>(open, high, low, close, "cdl_thrusting")
685 }
686 pub fn cdl_tristar(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
687 self.ta_4_in_1_out_default::<CDLTRISTAR>(open, high, low, close, "cdl_tristar")
688 }
689 pub fn cdl_unique3river(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
690 self.ta_4_in_1_out_default::<CDLUNIQUE3RIVER>(open, high, low, close, "cdl_unique3river")
691 }
692 pub fn cdl_upsidegap2crows(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
693 self.ta_4_in_1_out_default::<CDLUPSIDEGAP2CROWS>(
694 open,
695 high,
696 low,
697 close,
698 "cdl_upsidegap2crows",
699 )
700 }
701 pub fn cdl_xsidegap3methods(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
702 self.ta_4_in_1_out_default::<CDLXSIDEGAP3METHODS>(
703 open,
704 high,
705 low,
706 close,
707 "cdl_xsidegap3methods",
708 )
709 }
710
711 pub fn macd(self, name: &str, fast: usize, slow: usize, signal: usize) -> LazyFrame {
712 let name_str = name.to_string();
713 self.0.clone().with_columns([col(&name_str)
714 .map(
715 move |s| {
716 let ca = s.f64()?;
717 let mut indicator = MACD::new(fast, slow, signal);
718 let mut macd_vals = Vec::with_capacity(s.len());
719 let mut signal_vals = Vec::with_capacity(s.len());
720 let mut hist_vals = Vec::with_capacity(s.len());
721
722 for i in 0..s.len() {
723 let val = ca.get(i).unwrap_or(f64::NAN);
724 let (m, s_val, h) = indicator.next(val);
725 macd_vals.push(m);
726 signal_vals.push(s_val);
727 hist_vals.push(h);
728 }
729
730 let s_macd = Series::new("macd".into(), macd_vals);
731 let s_signal = Series::new("macd_signal".into(), signal_vals);
732 let s_hist = Series::new("macd_hist".into(), hist_vals);
733
734 let struct_series = StructChunked::from_series(
735 "macd_result".into(),
736 s.len(),
737 [s_macd, s_signal, s_hist].iter(),
738 )?;
739 Ok(Some(Column::from(struct_series.into_series())))
740 },
741 GetOutput::from_type(DataType::Struct(vec![
742 Field::new("macd".into(), DataType::Float64),
743 Field::new("macd_signal".into(), DataType::Float64),
744 Field::new("macd_hist".into(), DataType::Float64),
745 ])),
746 )
747 .alias("macd")])
748 }
749
750 pub fn bbands(
751 self,
752 name: &str,
753 period: usize,
754 nbdevup: f64,
755 nbdevdn: f64,
756 matype: talib::MaType,
757 ) -> LazyFrame {
758 let name_str = name.to_string();
759 self.0.clone().with_columns([col(&name_str)
760 .map(
761 move |s| {
762 let ca = s.f64()?;
763 let mut indicator = BBANDS::new(period, nbdevup, nbdevdn, matype);
764 let mut upper_vals = Vec::with_capacity(s.len());
765 let mut middle_vals = Vec::with_capacity(s.len());
766 let mut lower_vals = Vec::with_capacity(s.len());
767
768 for i in 0..s.len() {
769 let val = ca.get(i).unwrap_or(f64::NAN);
770 let (u, m, l) = indicator.next(val);
771 upper_vals.push(u);
772 middle_vals.push(m);
773 lower_vals.push(l);
774 }
775
776 let s_upper = Series::new("upper".into(), upper_vals);
777 let s_middle = Series::new("middle".into(), middle_vals);
778 let s_lower = Series::new("lower".into(), lower_vals);
779
780 let struct_series = StructChunked::from_series(
781 "bbands_result".into(),
782 s.len(),
783 [s_upper, s_middle, s_lower].iter(),
784 )?;
785 Ok(Some(Column::from(struct_series.into_series())))
786 },
787 GetOutput::from_type(DataType::Struct(vec![
788 Field::new("upper".into(), DataType::Float64),
789 Field::new("middle".into(), DataType::Float64),
790 Field::new("lower".into(), DataType::Float64),
791 ])),
792 )
793 .alias("bbands")])
794 }
795
796 fn ta_3_in_1_out_period<I>(
797 self,
798 in1: &str,
799 in2: &str,
800 in3: &str,
801 period: usize,
802 output_name: &str,
803 ) -> LazyFrame
804 where
805 I: Next<(f64, f64, f64), Output = f64> + Send + Sync + 'static,
806 I: From<usize>,
807 {
808 let in1_str = in1.to_string();
809 let in2_str = in2.to_string();
810 let in3_str = in3.to_string();
811 let output_name_str = output_name.to_string();
812 let output_name_for_closure = output_name_str.clone();
813 self.0.clone().with_columns(
814 [as_struct(vec![col(&in1_str), col(&in2_str), col(&in3_str)])
815 .map(
816 move |s| {
817 let ca = s.struct_()?;
818 let s1 = ca.field_by_name(&in1_str)?;
819 let s2 = ca.field_by_name(&in2_str)?;
820 let s3 = ca.field_by_name(&in3_str)?;
821
822 let ca1 = s1.f64()?;
823 let ca2 = s2.f64()?;
824 let ca3 = s3.f64()?;
825
826 let mut indicator = I::from(period);
827 let mut values = Vec::with_capacity(s.len());
828
829 for i in 0..s.len() {
830 let v1 = ca1.get(i).unwrap_or(f64::NAN);
831 let v2 = ca2.get(i).unwrap_or(f64::NAN);
832 let v3 = ca3.get(i).unwrap_or(f64::NAN);
833 values.push(indicator.next((v1, v2, v3)));
834 }
835
836 Ok(Some(Column::from(Series::new(
837 output_name_for_closure.clone().into(),
838 values,
839 ))))
840 },
841 GetOutput::from_type(DataType::Float64),
842 )
843 .alias(&output_name_str)],
844 )
845 }
846
847 fn ta_3_in_1_out_default<I>(
848 self,
849 in1: &str,
850 in2: &str,
851 in3: &str,
852 output_name: &str,
853 ) -> LazyFrame
854 where
855 I: Next<(f64, f64, f64), Output = f64> + Default + Send + Sync + 'static,
856 {
857 let in1_str = in1.to_string();
858 let in2_str = in2.to_string();
859 let in3_str = in3.to_string();
860 let output_name_str = output_name.to_string();
861 let output_name_for_closure = output_name_str.clone();
862 self.0.clone().with_columns(
863 [as_struct(vec![col(&in1_str), col(&in2_str), col(&in3_str)])
864 .map(
865 move |s| {
866 let ca = s.struct_()?;
867 let s1 = ca.field_by_name(&in1_str)?;
868 let s2 = ca.field_by_name(&in2_str)?;
869 let s3 = ca.field_by_name(&in3_str)?;
870
871 let ca1 = s1.f64()?;
872 let ca2 = s2.f64()?;
873 let ca3 = s3.f64()?;
874
875 let mut indicator = I::default();
876 let mut values = Vec::with_capacity(s.len());
877
878 for i in 0..s.len() {
879 let v1 = ca1.get(i).unwrap_or(f64::NAN);
880 let v2 = ca2.get(i).unwrap_or(f64::NAN);
881 let v3 = ca3.get(i).unwrap_or(f64::NAN);
882 values.push(indicator.next((v1, v2, v3)));
883 }
884
885 Ok(Some(Column::from(Series::new(
886 output_name_for_closure.clone().into(),
887 values,
888 ))))
889 },
890 GetOutput::from_type(DataType::Float64),
891 )
892 .alias(&output_name_str)],
893 )
894 }
895
896 fn ta_4_in_1_out_default<I>(
897 self,
898 in1: &str,
899 in2: &str,
900 in3: &str,
901 in4: &str,
902 output_name: &str,
903 ) -> LazyFrame
904 where
905 I: Next<(f64, f64, f64, f64), Output = f64> + Default + Send + Sync + 'static,
906 {
907 let in1_str = in1.to_string();
908 let in2_str = in2.to_string();
909 let in3_str = in3.to_string();
910 let in4_str = in4.to_string();
911 let output_name_str = output_name.to_string();
912 let output_name_for_closure = output_name_str.clone();
913 self.0.clone().with_columns([as_struct(vec![
914 col(&in1_str),
915 col(&in2_str),
916 col(&in3_str),
917 col(&in4_str),
918 ])
919 .map(
920 move |s| {
921 let ca = s.struct_()?;
922 let s1 = ca.field_by_name(&in1_str)?;
923 let s2 = ca.field_by_name(&in2_str)?;
924 let s3 = ca.field_by_name(&in3_str)?;
925 let s4 = ca.field_by_name(&in4_str)?;
926
927 let ca1 = s1.f64()?;
928 let ca2 = s2.f64()?;
929 let ca3 = s3.f64()?;
930 let ca4 = s4.f64()?;
931
932 let mut indicator = I::default();
933 let mut values = Vec::with_capacity(s.len());
934
935 for i in 0..s.len() {
936 let v1 = ca1.get(i).unwrap_or(f64::NAN);
937 let v2 = ca2.get(i).unwrap_or(f64::NAN);
938 let v3 = ca3.get(i).unwrap_or(f64::NAN);
939 let v4 = ca4.get(i).unwrap_or(f64::NAN);
940 values.push(indicator.next((v1, v2, v3, v4)));
941 }
942
943 Ok(Some(Column::from(Series::new(
944 output_name_for_closure.clone().into(),
945 values,
946 ))))
947 },
948 GetOutput::from_type(DataType::Float64),
949 )
950 .alias(&output_name_str)])
951 }
952
953 fn math_transform_1_in_1_out<I>(self, name: &str, output_name: &str) -> LazyFrame
954 where
955 I: Next<f64, Output = f64> + Default + Send + Sync + 'static,
956 {
957 let name = name.to_string();
958 let output_name_str = output_name.to_string();
959 let output_name_for_closure = output_name_str.clone();
960 self.0.clone().with_columns([col(&name)
961 .map(
962 move |s| {
963 let ca = s.f64()?;
964 let mut indicator = I::default();
965 let mut values = Vec::with_capacity(s.len());
966
967 for i in 0..s.len() {
968 let val = ca.get(i).unwrap_or(f64::NAN);
969 values.push(indicator.next(val));
970 }
971
972 Ok(Some(Column::from(Series::new(
973 output_name_for_closure.clone().into(),
974 values,
975 ))))
976 },
977 GetOutput::from_type(DataType::Float64),
978 )
979 .alias(&output_name_str)])
980 }
981
982 fn math_operator_2_in_1_out<I>(self, in1: &str, in2: &str, output_name: &str) -> LazyFrame
983 where
984 I: Next<(f64, f64), Output = f64> + Default + Send + Sync + 'static,
985 {
986 let in1_str = in1.to_string();
987 let in2_str = in2.to_string();
988 let output_name_str = output_name.to_string();
989 let output_name_for_closure = output_name_str.clone();
990 self.0
991 .clone()
992 .with_columns([as_struct(vec![col(&in1_str), col(&in2_str)])
993 .map(
994 move |s| {
995 let ca = s.struct_()?;
996 let s1 = ca.field_by_name(&in1_str)?;
997 let s2 = ca.field_by_name(&in2_str)?;
998
999 let ca1 = s1.f64()?;
1000 let ca2 = s2.f64()?;
1001
1002 let mut indicator = I::default();
1003 let mut values = Vec::with_capacity(s.len());
1004
1005 for i in 0..s.len() {
1006 let v1 = ca1.get(i).unwrap_or(f64::NAN);
1007 let v2 = ca2.get(i).unwrap_or(f64::NAN);
1008 values.push(indicator.next((v1, v2)));
1009 }
1010
1011 Ok(Some(Column::from(Series::new(
1012 output_name_for_closure.clone().into(),
1013 values,
1014 ))))
1015 },
1016 GetOutput::from_type(DataType::Float64),
1017 )
1018 .alias(&output_name_str)])
1019 }
1020
1021 fn math_operator_1_in_1_out_period<I>(
1022 self,
1023 name: &str,
1024 period: usize,
1025 output_name: &str,
1026 ) -> LazyFrame
1027 where
1028 I: Next<f64, Output = f64> + Send + Sync + 'static,
1029 I: From<usize>,
1030 {
1031 let name = name.to_string();
1032 let output_name_str = output_name.to_string();
1033 let output_name_for_closure = output_name_str.clone();
1034 self.0.clone().with_columns([col(&name)
1035 .map(
1036 move |s| {
1037 let ca = s.f64()?;
1038 let mut indicator = I::from(period);
1039 let mut values = Vec::with_capacity(s.len());
1040
1041 for i in 0..s.len() {
1042 let val = ca.get(i).unwrap_or(f64::NAN);
1043 values.push(indicator.next(val));
1044 }
1045
1046 Ok(Some(Column::from(Series::new(
1047 output_name_for_closure.clone().into(),
1048 values,
1049 ))))
1050 },
1051 GetOutput::from_type(DataType::Float64),
1052 )
1053 .alias(&output_name_str)])
1054 }
1055
1056 fn math_operator_2_in_1_out_period<I>(
1057 self,
1058 in1: &str,
1059 in2: &str,
1060 period: usize,
1061 output_name: &str,
1062 ) -> LazyFrame
1063 where
1064 I: Next<(f64, f64), Output = f64> + Send + Sync + 'static,
1065 I: From<usize>,
1066 {
1067 let in1_str = in1.to_string();
1068 let in2_str = in2.to_string();
1069 let output_name_str = output_name.to_string();
1070 let output_name_for_closure = output_name_str.clone();
1071 self.0
1072 .clone()
1073 .with_columns([as_struct(vec![col(&in1_str), col(&in2_str)])
1074 .map(
1075 move |s| {
1076 let ca = s.struct_()?;
1077 let s1 = ca.field_by_name(&in1_str)?;
1078 let s2 = ca.field_by_name(&in2_str)?;
1079
1080 let ca1 = s1.f64()?;
1081 let ca2 = s2.f64()?;
1082
1083 let mut indicator = I::from(period);
1084 let mut values = Vec::with_capacity(s.len());
1085
1086 for i in 0..s.len() {
1087 let v1 = ca1.get(i).unwrap_or(f64::NAN);
1088 let v2 = ca2.get(i).unwrap_or(f64::NAN);
1089 values.push(indicator.next((v1, v2)));
1090 }
1091
1092 Ok(Some(Column::from(Series::new(
1093 output_name_for_closure.clone().into(),
1094 values,
1095 ))))
1096 },
1097 GetOutput::from_type(DataType::Float64),
1098 )
1099 .alias(&output_name_str)])
1100 }
1101
1102 pub fn supertrend(self, period: usize, multiplier: f64) -> LazyFrame {
1103 self.0
1104 .clone()
1105 .with_columns([as_struct(vec![col("high"), col("low"), col("close")])
1106 .map(
1107 move |s| {
1108 let ca = s.struct_()?;
1109 let s_high = ca.field_by_name("high")?;
1110 let s_low = ca.field_by_name("low")?;
1111 let s_close = ca.field_by_name("close")?;
1112
1113 let high = s_high.f64()?;
1114 let low = s_low.f64()?;
1115 let close = s_close.f64()?;
1116
1117 let mut st = SuperTrend::new(period, multiplier);
1118 let mut values = Vec::with_capacity(s.len());
1119 let mut directions = Vec::with_capacity(s.len());
1120
1121 for i in 0..s.len() {
1122 let h = high.get(i).unwrap_or(0.0);
1123 let l = low.get(i).unwrap_or(0.0);
1124 let c = close.get(i).unwrap_or(0.0);
1125 let (val, dir) = st.next((h, l, c));
1126 values.push(val);
1127 directions.push(dir as f64);
1128 }
1129
1130 let st_series = Series::new("supertrend".into(), values);
1131 let dir_series = Series::new("supertrend_direction".into(), directions);
1132
1133 let out = StructChunked::from_series(
1134 "supertrend_output".into(),
1135 s.len(),
1136 [st_series, dir_series].iter(),
1137 )?;
1138 Ok(Some(Column::from(out.into_series())))
1139 },
1140 GetOutput::from_type(DataType::Struct(vec![
1141 Field::new("supertrend".into(), DataType::Float64),
1142 Field::new("supertrend_direction".into(), DataType::Float64),
1143 ])),
1144 )
1145 .alias("supertrend_data")])
1146 }
1147
1148 pub fn anchored_vwap(self, price: &str, volume: &str, anchor: &str) -> LazyFrame {
1149 let price = price.to_string();
1150 let volume = volume.to_string();
1151 let anchor = anchor.to_string();
1152
1153 self.0
1154 .clone()
1155 .with_columns([as_struct(vec![col(&price), col(&volume), col(&anchor)])
1156 .map(
1157 move |s| {
1158 let ca = s.struct_()?;
1159 let s_price = ca.field_by_name(&price)?;
1160 let s_volume = ca.field_by_name(&volume)?;
1161 let s_anchor = ca.field_by_name(&anchor)?;
1162
1163 let price = s_price.f64()?;
1164 let volume = s_volume.f64()?;
1165 let anchor = s_anchor.bool()?;
1166
1167 let mut avwap = quantwave_core::AnchoredVWAP::new();
1168 let mut values = Vec::with_capacity(s.len());
1169
1170 for i in 0..s.len() {
1171 let p = price.get(i).unwrap_or(0.0);
1172 let v = volume.get(i).unwrap_or(0.0);
1173 let a = anchor.get(i).unwrap_or(false);
1174 values.push(avwap.next((p, v, a)));
1175 }
1176
1177 Ok(Some(Column::from(Series::new(
1178 "anchored_vwap".into(),
1179 values,
1180 ))))
1181 },
1182 GetOutput::from_type(DataType::Float64),
1183 )
1184 .alias("avwap")])
1185 }
1186
1187 pub fn hma(self, name: &str, period: usize) -> LazyFrame {
1188 let name = name.to_string();
1189 self.0.clone().with_columns([col(&name)
1190 .map(
1191 move |s| {
1192 let ca = s.f64()?;
1193 let mut hma = quantwave_core::HMA::new(period);
1194 let mut values = Vec::with_capacity(s.len());
1195
1196 for i in 0..s.len() {
1197 let val = ca.get(i).unwrap_or(0.0);
1198 values.push(hma.next(val));
1199 }
1200
1201 Ok(Some(Column::from(Series::new("hma".into(), values))))
1202 },
1203 GetOutput::from_type(DataType::Float64),
1204 )
1205 .alias("hma")])
1206 }
1207
1208 pub fn keltner_channels(
1209 self,
1210 high: &str,
1211 low: &str,
1212 close: &str,
1213 ema_period: usize,
1214 atr_period: usize,
1215 multiplier: f64,
1216 ) -> LazyFrame {
1217 let high = high.to_string();
1218 let low = low.to_string();
1219 let close = close.to_string();
1220
1221 self.0
1222 .clone()
1223 .with_columns([as_struct(vec![col(&high), col(&low), col(&close)])
1224 .map(
1225 move |s| {
1226 let ca = s.struct_()?;
1227 let s_high = ca.field_by_name(&high)?;
1228 let s_low = ca.field_by_name(&low)?;
1229 let s_close = ca.field_by_name(&close)?;
1230
1231 let high = s_high.f64()?;
1232 let low = s_low.f64()?;
1233 let close = s_close.f64()?;
1234
1235 let mut kc = quantwave_core::KeltnerChannels::new(
1236 ema_period, atr_period, multiplier,
1237 );
1238 let mut uppers = Vec::with_capacity(s.len());
1239 let mut middles = Vec::with_capacity(s.len());
1240 let mut lowers = Vec::with_capacity(s.len());
1241
1242 for i in 0..s.len() {
1243 let h = high.get(i).unwrap_or(0.0);
1244 let l = low.get(i).unwrap_or(0.0);
1245 let c = close.get(i).unwrap_or(0.0);
1246 let (upper, middle, lower) = kc.next((h, l, c));
1247 uppers.push(upper);
1248 middles.push(middle);
1249 lowers.push(lower);
1250 }
1251
1252 let upper_series = Series::new("upper".into(), uppers);
1253 let middle_series = Series::new("middle".into(), middles);
1254 let lower_series = Series::new("lower".into(), lowers);
1255
1256 let out = StructChunked::from_series(
1257 "keltner_output".into(),
1258 s.len(),
1259 [upper_series, middle_series, lower_series].iter(),
1260 )?;
1261 Ok(Some(Column::from(out.into_series())))
1262 },
1263 GetOutput::from_type(DataType::Struct(vec![
1264 Field::new("upper".into(), DataType::Float64),
1265 Field::new("middle".into(), DataType::Float64),
1266 Field::new("lower".into(), DataType::Float64),
1267 ])),
1268 )
1269 .alias("keltner_data")])
1270 }
1271
1272 pub fn alma(self, name: &str, period: usize, offset: f64, sigma: f64) -> LazyFrame {
1273 let name = name.to_string();
1274 self.0.clone().with_columns([col(&name)
1275 .map(
1276 move |s| {
1277 let ca = s.f64()?;
1278 let mut alma = quantwave_core::ALMA::new(period, offset, sigma);
1279 let mut values = Vec::with_capacity(s.len());
1280
1281 for i in 0..s.len() {
1282 let val = ca.get(i).unwrap_or(0.0);
1283 values.push(alma.next(val));
1284 }
1285
1286 Ok(Some(Column::from(Series::new("alma".into(), values))))
1287 },
1288 GetOutput::from_type(DataType::Float64),
1289 )
1290 .alias("alma")])
1291 }
1292
1293 pub fn donchian_channels(self, high: &str, low: &str, period: usize) -> LazyFrame {
1294 let high = high.to_string();
1295 let low = low.to_string();
1296
1297 self.0
1298 .clone()
1299 .with_columns([as_struct(vec![col(&high), col(&low)])
1300 .map(
1301 move |s| {
1302 let ca = s.struct_()?;
1303 let s_high = ca.field_by_name(&high)?;
1304 let s_low = ca.field_by_name(&low)?;
1305
1306 let high = s_high.f64()?;
1307 let low = s_low.f64()?;
1308
1309 let mut dc = quantwave_core::DonchianChannels::new(period);
1310 let mut uppers = Vec::with_capacity(s.len());
1311 let mut middles = Vec::with_capacity(s.len());
1312 let mut lowers = Vec::with_capacity(s.len());
1313
1314 for i in 0..s.len() {
1315 let h = high.get(i).unwrap_or(0.0);
1316 let l = low.get(i).unwrap_or(0.0);
1317 let (upper, middle, lower) = dc.next((h, l));
1318 uppers.push(upper);
1319 middles.push(middle);
1320 lowers.push(lower);
1321 }
1322
1323 let upper_series = Series::new("upper".into(), uppers);
1324 let middle_series = Series::new("middle".into(), middles);
1325 let lower_series = Series::new("lower".into(), lowers);
1326
1327 let out = StructChunked::from_series(
1328 "donchian_output".into(),
1329 s.len(),
1330 [upper_series, middle_series, lower_series].iter(),
1331 )?;
1332 Ok(Some(Column::from(out.into_series())))
1333 },
1334 GetOutput::from_type(DataType::Struct(vec![
1335 Field::new("upper".into(), DataType::Float64),
1336 Field::new("middle".into(), DataType::Float64),
1337 Field::new("lower".into(), DataType::Float64),
1338 ])),
1339 )
1340 .alias("donchian_data")])
1341 }
1342
1343 pub fn ttm_squeeze(
1344 self,
1345 high: &str,
1346 low: &str,
1347 close: &str,
1348 period: usize,
1349 multiplier_bb: f64,
1350 multiplier_kc: f64,
1351 ) -> LazyFrame {
1352 let high = high.to_string();
1353 let low = low.to_string();
1354 let close = close.to_string();
1355
1356 self.0
1357 .clone()
1358 .with_columns([as_struct(vec![col(&high), col(&low), col(&close)])
1359 .map(
1360 move |s| {
1361 let ca = s.struct_()?;
1362 let s_high = ca.field_by_name(&high)?;
1363 let s_low = ca.field_by_name(&low)?;
1364 let s_close = ca.field_by_name(&close)?;
1365
1366 let high = s_high.f64()?;
1367 let low = s_low.f64()?;
1368 let close = s_close.f64()?;
1369
1370 let mut ttm =
1371 quantwave_core::TTMSqueeze::new(period, multiplier_bb, multiplier_kc);
1372 let mut histograms = Vec::with_capacity(s.len());
1373 let mut squeezed = Vec::with_capacity(s.len());
1374
1375 for i in 0..s.len() {
1376 let h = high.get(i).unwrap_or(0.0);
1377 let l = low.get(i).unwrap_or(0.0);
1378 let c = close.get(i).unwrap_or(0.0);
1379 let (hist, is_sq) = ttm.next((h, l, c));
1380 histograms.push(hist);
1381 squeezed.push(is_sq);
1382 }
1383
1384 let hist_series = Series::new("histogram".into(), histograms);
1385 let squeezed_series = Series::new("is_squeezed".into(), squeezed);
1386
1387 let out = StructChunked::from_series(
1388 "ttm_squeeze_output".into(),
1389 s.len(),
1390 [hist_series, squeezed_series].iter(),
1391 )?;
1392 Ok(Some(Column::from(out.into_series())))
1393 },
1394 GetOutput::from_type(DataType::Struct(vec![
1395 Field::new("histogram".into(), DataType::Float64),
1396 Field::new("is_squeezed".into(), DataType::Boolean),
1397 ])),
1398 )
1399 .alias("ttm_squeeze_data")])
1400 }
1401
1402 pub fn vortex_indicator(self, high: &str, low: &str, close: &str, period: usize) -> LazyFrame {
1403 let high = high.to_string();
1404 let low = low.to_string();
1405 let close = close.to_string();
1406
1407 self.0
1408 .clone()
1409 .with_columns([as_struct(vec![col(&high), col(&low), col(&close)])
1410 .map(
1411 move |s| {
1412 let ca = s.struct_()?;
1413 let s_high = ca.field_by_name(&high)?;
1414 let s_low = ca.field_by_name(&low)?;
1415 let s_close = ca.field_by_name(&close)?;
1416
1417 let high = s_high.f64()?;
1418 let low = s_low.f64()?;
1419 let close = s_close.f64()?;
1420
1421 let mut vi = quantwave_core::VortexIndicator::new(period);
1422 let mut plus_vals = Vec::with_capacity(s.len());
1423 let mut minus_vals = Vec::with_capacity(s.len());
1424
1425 for i in 0..s.len() {
1426 let h = high.get(i).unwrap_or(0.0);
1427 let l = low.get(i).unwrap_or(0.0);
1428 let c = close.get(i).unwrap_or(0.0);
1429 let (plus, minus) = vi.next((h, l, c));
1430 plus_vals.push(plus);
1431 minus_vals.push(minus);
1432 }
1433
1434 let plus_series = Series::new("vi_plus".into(), plus_vals);
1435 let minus_series = Series::new("vi_minus".into(), minus_vals);
1436
1437 let out = StructChunked::from_series(
1438 "vortex_output".into(),
1439 s.len(),
1440 [plus_series, minus_series].iter(),
1441 )?;
1442 Ok(Some(Column::from(out.into_series())))
1443 },
1444 GetOutput::from_type(DataType::Struct(vec![
1445 Field::new("vi_plus".into(), DataType::Float64),
1446 Field::new("vi_minus".into(), DataType::Float64),
1447 ])),
1448 )
1449 .alias("vortex_data")])
1450 }
1451
1452 pub fn heikin_ashi(self, open: &str, high: &str, low: &str, close: &str) -> LazyFrame {
1453 let open = open.to_string();
1454 let high = high.to_string();
1455 let low = low.to_string();
1456 let close = close.to_string();
1457
1458 self.0.clone().with_columns([as_struct(vec![
1459 col(&open),
1460 col(&high),
1461 col(&low),
1462 col(&close),
1463 ])
1464 .map(
1465 move |s| {
1466 let ca = s.struct_()?;
1467 let s_open = ca.field_by_name(&open)?;
1468 let s_high = ca.field_by_name(&high)?;
1469 let s_low = ca.field_by_name(&low)?;
1470 let s_close = ca.field_by_name(&close)?;
1471
1472 let open = s_open.f64()?;
1473 let high = s_high.f64()?;
1474 let low = s_low.f64()?;
1475 let close = s_close.f64()?;
1476
1477 let mut ha = quantwave_core::HeikinAshi::new();
1478 let mut ha_opens = Vec::with_capacity(s.len());
1479 let mut ha_highs = Vec::with_capacity(s.len());
1480 let mut ha_lows = Vec::with_capacity(s.len());
1481 let mut ha_closes = Vec::with_capacity(s.len());
1482
1483 for i in 0..s.len() {
1484 let o = open.get(i).unwrap_or(0.0);
1485 let h = high.get(i).unwrap_or(0.0);
1486 let l = low.get(i).unwrap_or(0.0);
1487 let c = close.get(i).unwrap_or(0.0);
1488 let (ha_o, ha_h, ha_l, ha_c) = ha.next((o, h, l, c));
1489 ha_opens.push(ha_o);
1490 ha_highs.push(ha_h);
1491 ha_lows.push(ha_l);
1492 ha_closes.push(ha_c);
1493 }
1494
1495 let o_series = Series::new("ha_open".into(), ha_opens);
1496 let h_series = Series::new("ha_high".into(), ha_highs);
1497 let l_series = Series::new("ha_low".into(), ha_lows);
1498 let c_series = Series::new("ha_close".into(), ha_closes);
1499
1500 let out = StructChunked::from_series(
1501 "heikin_ashi_output".into(),
1502 s.len(),
1503 [o_series, h_series, l_series, c_series].iter(),
1504 )?;
1505 Ok(Some(Column::from(out.into_series())))
1506 },
1507 GetOutput::from_type(DataType::Struct(vec![
1508 Field::new("ha_open".into(), DataType::Float64),
1509 Field::new("ha_high".into(), DataType::Float64),
1510 Field::new("ha_low".into(), DataType::Float64),
1511 Field::new("ha_close".into(), DataType::Float64),
1512 ])),
1513 )
1514 .alias("heikin_ashi_data")])
1515 }
1516
1517 pub fn wavetrend(
1518 self,
1519 high: &str,
1520 low: &str,
1521 close: &str,
1522 n1: usize,
1523 n2: usize,
1524 n3: usize,
1525 ) -> LazyFrame {
1526 let high = high.to_string();
1527 let low = low.to_string();
1528 let close = close.to_string();
1529
1530 self.0
1531 .clone()
1532 .with_columns([as_struct(vec![col(&high), col(&low), col(&close)])
1533 .map(
1534 move |s| {
1535 let ca = s.struct_()?;
1536 let s_high = ca.field_by_name(&high)?;
1537 let s_low = ca.field_by_name(&low)?;
1538 let s_close = ca.field_by_name(&close)?;
1539
1540 let high = s_high.f64()?;
1541 let low = s_low.f64()?;
1542 let close = s_close.f64()?;
1543
1544 let mut wt = quantwave_core::WaveTrend::new(n1, n2, n3);
1545 let mut wt1_vals = Vec::with_capacity(s.len());
1546 let mut wt2_vals = Vec::with_capacity(s.len());
1547
1548 for i in 0..s.len() {
1549 let h = high.get(i).unwrap_or(0.0);
1550 let l = low.get(i).unwrap_or(0.0);
1551 let c = close.get(i).unwrap_or(0.0);
1552 let (wt1, wt2) = wt.next((h, l, c));
1553 wt1_vals.push(wt1);
1554 wt2_vals.push(wt2);
1555 }
1556
1557 let wt1_series = Series::new("wt1".into(), wt1_vals);
1558 let wt2_series = Series::new("wt2".into(), wt2_vals);
1559
1560 let out = StructChunked::from_series(
1561 "wavetrend_output".into(),
1562 s.len(),
1563 [wt1_series, wt2_series].iter(),
1564 )?;
1565 Ok(Some(Column::from(out.into_series())))
1566 },
1567 GetOutput::from_type(DataType::Struct(vec![
1568 Field::new("wt1".into(), DataType::Float64),
1569 Field::new("wt2".into(), DataType::Float64),
1570 ])),
1571 )
1572 .alias("wavetrend_data")])
1573 }
1574
1575 pub fn tema(self, name: &str, period: usize) -> LazyFrame {
1576 let name = name.to_string();
1577 self.0.clone().with_columns([col(&name)
1578 .map(
1579 move |s| {
1580 let ca = s.f64()?;
1581 let mut tema = quantwave_core::TEMA::new(period);
1582 let mut values = Vec::with_capacity(s.len());
1583
1584 for i in 0..s.len() {
1585 let val = ca.get(i).unwrap_or(0.0);
1586 values.push(tema.next(val));
1587 }
1588
1589 Ok(Some(Column::from(Series::new("tema".into(), values))))
1590 },
1591 GetOutput::from_type(DataType::Float64),
1592 )
1593 .alias("tema")])
1594 }
1595
1596 pub fn zlema(self, name: &str, period: usize) -> LazyFrame {
1597 let name = name.to_string();
1598 self.0.clone().with_columns([col(&name)
1599 .map(
1600 move |s| {
1601 let ca = s.f64()?;
1602 let mut zlema = quantwave_core::ZLEMA::new(period);
1603 let mut values = Vec::with_capacity(s.len());
1604
1605 for i in 0..s.len() {
1606 let val = ca.get(i).unwrap_or(0.0);
1607 values.push(zlema.next(val));
1608 }
1609
1610 Ok(Some(Column::from(Series::new("zlema".into(), values))))
1611 },
1612 GetOutput::from_type(DataType::Float64),
1613 )
1614 .alias("zlema")])
1615 }
1616
1617 pub fn atr_trailing_stop(
1618 self,
1619 high: &str,
1620 low: &str,
1621 close: &str,
1622 period: usize,
1623 multiplier: f64,
1624 ) -> LazyFrame {
1625 let high = high.to_string();
1626 let low = low.to_string();
1627 let close = close.to_string();
1628
1629 self.0
1630 .clone()
1631 .with_columns([as_struct(vec![col(&high), col(&low), col(&close)])
1632 .map(
1633 move |s| {
1634 let ca = s.struct_()?;
1635 let s_high = ca.field_by_name(&high)?;
1636 let s_low = ca.field_by_name(&low)?;
1637 let s_close = ca.field_by_name(&close)?;
1638
1639 let high = s_high.f64()?;
1640 let low = s_low.f64()?;
1641 let close = s_close.f64()?;
1642
1643 let mut atr_ts = quantwave_core::ATRTrailingStop::new(period, multiplier);
1644 let mut stops = Vec::with_capacity(s.len());
1645 let mut directions = Vec::with_capacity(s.len());
1646
1647 for i in 0..s.len() {
1648 let h = high.get(i).unwrap_or(0.0);
1649 let l = low.get(i).unwrap_or(0.0);
1650 let c = close.get(i).unwrap_or(0.0);
1651 let (stop, dir) = atr_ts.next((h, l, c));
1652 stops.push(stop);
1653 directions.push(dir as f64);
1654 }
1655
1656 let stop_series = Series::new("stop".into(), stops);
1657 let dir_series = Series::new("direction".into(), directions);
1658
1659 let out = StructChunked::from_series(
1660 "atr_ts_output".into(),
1661 s.len(),
1662 [stop_series, dir_series].iter(),
1663 )?;
1664 Ok(Some(Column::from(out.into_series())))
1665 },
1666 GetOutput::from_type(DataType::Struct(vec![
1667 Field::new("stop".into(), DataType::Float64),
1668 Field::new("direction".into(), DataType::Float64),
1669 ])),
1670 )
1671 .alias("atr_ts_data")])
1672 }
1673
1674 pub fn pivot_points(self, high: &str, low: &str, close: &str) -> LazyFrame {
1675 let high = high.to_string();
1676 let low = low.to_string();
1677 let close = close.to_string();
1678
1679 self.0
1680 .clone()
1681 .with_columns([as_struct(vec![col(&high), col(&low), col(&close)])
1682 .map(
1683 move |s| {
1684 let ca = s.struct_()?;
1685 let s_high = ca.field_by_name(&high)?;
1686 let s_low = ca.field_by_name(&low)?;
1687 let s_close = ca.field_by_name(&close)?;
1688
1689 let high = s_high.f64()?;
1690 let low = s_low.f64()?;
1691 let close = s_close.f64()?;
1692
1693 let mut pivot = quantwave_core::PivotPoints::new();
1694 let mut p_vals = Vec::with_capacity(s.len());
1695 let mut r1_vals = Vec::with_capacity(s.len());
1696 let mut s1_vals = Vec::with_capacity(s.len());
1697 let mut r2_vals = Vec::with_capacity(s.len());
1698 let mut s2_vals = Vec::with_capacity(s.len());
1699
1700 for i in 0..s.len() {
1701 let h = high.get(i).unwrap_or(0.0);
1702 let l = low.get(i).unwrap_or(0.0);
1703 let c = close.get(i).unwrap_or(0.0);
1704 let (p, r1, s1, r2, s2) = pivot.next((h, l, c));
1705 p_vals.push(p);
1706 r1_vals.push(r1);
1707 s1_vals.push(s1);
1708 r2_vals.push(r2);
1709 s2_vals.push(s2);
1710 }
1711
1712 let p_series = Series::new("p".into(), p_vals);
1713 let r1_series = Series::new("r1".into(), r1_vals);
1714 let s1_series = Series::new("s1".into(), s1_vals);
1715 let r2_series = Series::new("r2".into(), r2_vals);
1716 let s2_series = Series::new("s2".into(), s2_vals);
1717
1718 let out = StructChunked::from_series(
1719 "pivot_output".into(),
1720 s.len(),
1721 [p_series, r1_series, s1_series, r2_series, s2_series].iter(),
1722 )?;
1723 Ok(Some(Column::from(out.into_series())))
1724 },
1725 GetOutput::from_type(DataType::Struct(vec![
1726 Field::new("p".into(), DataType::Float64),
1727 Field::new("r1".into(), DataType::Float64),
1728 Field::new("s1".into(), DataType::Float64),
1729 Field::new("r2".into(), DataType::Float64),
1730 Field::new("s2".into(), DataType::Float64),
1731 ])),
1732 )
1733 .alias("pivot_points_data")])
1734 }
1735
1736 pub fn bill_williams_fractals(self, high: &str, low: &str) -> LazyFrame {
1737 let high = high.to_string();
1738 let low = low.to_string();
1739
1740 self.0
1741 .clone()
1742 .with_columns([as_struct(vec![col(&high), col(&low)])
1743 .map(
1744 move |s| {
1745 let ca = s.struct_()?;
1746 let s_high = ca.field_by_name(&high)?;
1747 let s_low = ca.field_by_name(&low)?;
1748
1749 let high = s_high.f64()?;
1750 let low = s_low.f64()?;
1751
1752 let mut fractals = quantwave_core::BillWilliamsFractals::new();
1753 let mut bearish_vals = Vec::with_capacity(s.len());
1754 let mut bullish_vals = Vec::with_capacity(s.len());
1755
1756 for i in 0..s.len() {
1757 let h = high.get(i).unwrap_or(0.0);
1758 let l = low.get(i).unwrap_or(0.0);
1759 let (bear, bull) = fractals.next((h, l));
1760 bearish_vals.push(bear);
1761 bullish_vals.push(bull);
1762 }
1763
1764 let bearish_series = Series::new("bearish".into(), bearish_vals);
1765 let bullish_series = Series::new("bullish".into(), bullish_vals);
1766
1767 let out = StructChunked::from_series(
1768 "fractals_output".into(),
1769 s.len(),
1770 [bearish_series, bullish_series].iter(),
1771 )?;
1772 Ok(Some(Column::from(out.into_series())))
1773 },
1774 GetOutput::from_type(DataType::Struct(vec![
1775 Field::new("bearish".into(), DataType::Boolean),
1776 Field::new("bullish".into(), DataType::Boolean),
1777 ])),
1778 )
1779 .alias("fractals_data")])
1780 }
1781
1782 pub fn ichimoku_cloud(
1783 self,
1784 high: &str,
1785 low: &str,
1786 p1: usize,
1787 p2: usize,
1788 p3: usize,
1789 ) -> LazyFrame {
1790 let high = high.to_string();
1791 let low = low.to_string();
1792
1793 self.0
1794 .clone()
1795 .with_columns([as_struct(vec![col(&high), col(&low)])
1796 .map(
1797 move |s| {
1798 let ca = s.struct_()?;
1799 let s_high = ca.field_by_name(&high)?;
1800 let s_low = ca.field_by_name(&low)?;
1801
1802 let high = s_high.f64()?;
1803 let low = s_low.f64()?;
1804
1805 let mut ic = quantwave_core::IchimokuCloud::new(p1, p2, p3);
1806 let mut t_vals = Vec::with_capacity(s.len());
1807 let mut k_vals = Vec::with_capacity(s.len());
1808 let mut sa_vals = Vec::with_capacity(s.len());
1809 let mut sb_vals = Vec::with_capacity(s.len());
1810
1811 for i in 0..s.len() {
1812 let h = high.get(i).unwrap_or(0.0);
1813 let l = low.get(i).unwrap_or(0.0);
1814 let (t, k, sa, sb) = ic.next((h, l));
1815 t_vals.push(t);
1816 k_vals.push(k);
1817 sa_vals.push(sa);
1818 sb_vals.push(sb);
1819 }
1820
1821 let t_series = Series::new("tenkan".into(), t_vals);
1822 let k_series = Series::new("kijun".into(), k_vals);
1823 let sa_series = Series::new("senkou_a".into(), sa_vals);
1824 let sb_series = Series::new("senkou_b".into(), sb_vals);
1825
1826 let out = StructChunked::from_series(
1827 "ichimoku_output".into(),
1828 s.len(),
1829 [t_series, k_series, sa_series, sb_series].iter(),
1830 )?;
1831 Ok(Some(Column::from(out.into_series())))
1832 },
1833 GetOutput::from_type(DataType::Struct(vec![
1834 Field::new("tenkan".into(), DataType::Float64),
1835 Field::new("kijun".into(), DataType::Float64),
1836 Field::new("senkou_a".into(), DataType::Float64),
1837 Field::new("senkou_b".into(), DataType::Float64),
1838 ])),
1839 )
1840 .alias("ichimoku_data")])
1841 }
1842}
1843
1844#[cfg(test)]
1845mod tests {
1846 use super::*;
1847
1848 #[test]
1849 fn test_polars_heikin_ashi() -> PolarsResult<()> {
1850 let df = df![
1851 "open" => [10.0, 11.0],
1852 "high" => [12.0, 13.0],
1853 "low" => [8.0, 10.0],
1854 "close" => [11.0, 12.0]
1855 ]?;
1856
1857 let out = df
1858 .lazy()
1859 .ta()
1860 .heikin_ashi("open", "high", "low", "close")
1861 .collect()?;
1862
1863 let ha = out.column("heikin_ashi_data")?.struct_()?;
1864 assert_eq!(
1865 ha.field_by_name("ha_open".into())?.f64()?.get(0),
1866 Some(10.5)
1867 );
1868 assert_eq!(
1869 ha.field_by_name("ha_close".into())?.f64()?.get(0),
1870 Some(10.25)
1871 );
1872
1873 Ok(())
1874 }
1875
1876 #[test]
1877 fn test_polars_tema_zlema() -> PolarsResult<()> {
1878 let df = df![
1879 "price" => [1.0, 2.0, 3.0, 4.0, 5.0]
1880 ]?;
1881
1882 let out = df.clone().lazy().ta().tema("price", 3).collect()?;
1883
1884 let tema = out.column("tema")?.f64()?;
1885 assert!(tema.get(4).is_some());
1886
1887 let out2 = df.lazy().ta().zlema("price", 3).collect()?;
1888
1889 let zlema = out2.column("zlema")?.f64()?;
1890 assert!(zlema.get(4).is_some());
1891
1892 Ok(())
1893 }
1894
1895 #[test]
1896 fn test_polars_atr_ts() -> PolarsResult<()> {
1897 let df = df![
1898 "high" => [10.0, 12.0, 11.0],
1899 "low" => [8.0, 10.0, 9.0],
1900 "close" => [9.0, 11.0, 10.0]
1901 ]?;
1902
1903 let out = df
1904 .lazy()
1905 .ta()
1906 .atr_trailing_stop("high", "low", "close", 14, 2.5)
1907 .collect()?;
1908
1909 let atr_ts = out.column("atr_ts_data")?.struct_()?;
1910 assert!(atr_ts.field_by_name("stop".into())?.f64()?.get(0).is_some());
1911 assert!(
1912 atr_ts
1913 .field_by_name("direction".into())?
1914 .f64()?
1915 .get(0)
1916 .is_some()
1917 );
1918
1919 Ok(())
1920 }
1921
1922 #[test]
1923 fn test_polars_pivot_points() -> PolarsResult<()> {
1924 let df = df![
1925 "high" => [10.0, 12.0, 11.0],
1926 "low" => [8.0, 10.0, 9.0],
1927 "close" => [9.0, 11.0, 10.0]
1928 ]?;
1929
1930 let out = df
1931 .lazy()
1932 .ta()
1933 .pivot_points("high", "low", "close")
1934 .collect()?;
1935
1936 let pivot = out.column("pivot_points_data")?.struct_()?;
1937 assert!(pivot.field_by_name("p".into())?.f64()?.get(0).is_some());
1938 assert!(pivot.field_by_name("r1".into())?.f64()?.get(0).is_some());
1939
1940 Ok(())
1941 }
1942
1943 #[test]
1944 fn test_polars_fractals() -> PolarsResult<()> {
1945 let df = df![
1946 "high" => [10.0, 11.0, 15.0, 12.0, 10.0],
1947 "low" => [5.0, 6.0, 2.0, 6.0, 7.0]
1948 ]?;
1949
1950 let out = df
1951 .lazy()
1952 .ta()
1953 .bill_williams_fractals("high", "low")
1954 .collect()?;
1955
1956 let fractals = out.column("fractals_data")?.struct_()?;
1957 assert!(
1958 fractals
1959 .field_by_name("bearish".into())?
1960 .bool()?
1961 .get(4)
1962 .unwrap()
1963 );
1964 assert!(
1965 fractals
1966 .field_by_name("bullish".into())?
1967 .bool()?
1968 .get(4)
1969 .unwrap()
1970 );
1971
1972 Ok(())
1973 }
1974
1975 #[test]
1976 fn test_polars_ichimoku() -> PolarsResult<()> {
1977 let df = df![
1978 "high" => [10.0, 11.0, 15.0, 12.0, 10.0],
1979 "low" => [5.0, 6.0, 2.0, 6.0, 7.0]
1980 ]?;
1981
1982 let out = df
1983 .lazy()
1984 .ta()
1985 .ichimoku_cloud("high", "low", 9, 26, 52)
1986 .collect()?;
1987
1988 let ichimoku = out.column("ichimoku_data")?.struct_()?;
1989 assert!(
1990 ichimoku
1991 .field_by_name("tenkan".into())?
1992 .f64()?
1993 .get(4)
1994 .is_some()
1995 );
1996 assert!(
1997 ichimoku
1998 .field_by_name("kijun".into())?
1999 .f64()?
2000 .get(4)
2001 .is_some()
2002 );
2003
2004 Ok(())
2005 }
2006
2007 #[test]
2008 fn test_polars_wavetrend() -> PolarsResult<()> {
2009 let df = df![
2010 "high" => [10.0, 12.0, 11.0],
2011 "low" => [8.0, 10.0, 9.0],
2012 "close" => [9.0, 11.0, 10.0]
2013 ]?;
2014
2015 let out = df
2016 .lazy()
2017 .ta()
2018 .wavetrend("high", "low", "close", 10, 21, 4)
2019 .collect()?;
2020
2021 let wt = out.column("wavetrend_data")?.struct_()?;
2022 assert!(wt.field_by_name("wt1".into())?.f64()?.get(0).is_some());
2023 assert!(wt.field_by_name("wt2".into())?.f64()?.get(0).is_some());
2024
2025 Ok(())
2026 }
2027
2028 #[test]
2029 fn test_polars_vortex() -> PolarsResult<()> {
2030 let df = df![
2031 "high" => [10.0, 12.0, 11.0],
2032 "low" => [8.0, 10.0, 9.0],
2033 "close" => [9.0, 11.0, 10.0]
2034 ]?;
2035
2036 let out = df
2037 .lazy()
2038 .ta()
2039 .vortex_indicator("high", "low", "close", 14)
2040 .collect()?;
2041
2042 let vortex = out.column("vortex_data")?.struct_()?;
2043 assert!(
2044 vortex
2045 .field_by_name("vi_plus".into())?
2046 .f64()?
2047 .get(0)
2048 .is_some()
2049 );
2050 assert!(
2051 vortex
2052 .field_by_name("vi_minus".into())?
2053 .f64()?
2054 .get(0)
2055 .is_some()
2056 );
2057
2058 Ok(())
2059 }
2060
2061 #[test]
2062 fn test_polars_ttm_squeeze() -> PolarsResult<()> {
2063 let df = df![
2064 "high" => [11.0, 12.0, 13.0, 14.0],
2065 "low" => [9.0, 10.0, 11.0, 12.0],
2066 "close" => [10.0, 11.0, 12.0, 13.0]
2067 ]?;
2068
2069 let out = df
2070 .lazy()
2071 .ta()
2072 .ttm_squeeze("high", "low", "close", 20, 2.0, 1.5)
2073 .collect()?;
2074
2075 let ttm = out.column("ttm_squeeze_data")?.struct_()?;
2076 assert!(
2077 ttm.field_by_name("histogram".into())?
2078 .f64()?
2079 .get(0)
2080 .is_some()
2081 );
2082 assert!(
2083 ttm.field_by_name("is_squeezed".into())?
2084 .bool()?
2085 .get(0)
2086 .is_some()
2087 );
2088
2089 Ok(())
2090 }
2091
2092 #[test]
2093 fn test_polars_donchian() -> PolarsResult<()> {
2094 let df = df![
2095 "high" => [10.0, 12.0, 11.0, 13.0, 15.0],
2096 "low" => [8.0, 7.0, 9.0, 10.0, 12.0]
2097 ]?;
2098
2099 let out = df
2100 .lazy()
2101 .ta()
2102 .donchian_channels("high", "low", 3)
2103 .collect()?;
2104
2105 let donchian = out.column("donchian_data")?.struct_()?;
2106 assert_eq!(
2108 donchian.field_by_name("upper".into())?.f64()?.get(3),
2109 Some(13.0)
2110 );
2111 assert_eq!(
2112 donchian.field_by_name("middle".into())?.f64()?.get(3),
2113 Some(10.0)
2114 );
2115 assert_eq!(
2116 donchian.field_by_name("lower".into())?.f64()?.get(3),
2117 Some(7.0)
2118 );
2119
2120 Ok(())
2121 }
2122
2123 #[test]
2124 fn test_polars_alma() -> PolarsResult<()> {
2125 let df = df![
2126 "price" => [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
2127 ]?;
2128
2129 let out = df.lazy().ta().alma("price", 9, 0.85, 6.0).collect()?;
2130
2131 let alma = out.column("alma")?.f64()?;
2132 assert!(alma.get(9).is_some());
2133
2134 Ok(())
2135 }
2136
2137 #[test]
2138 fn test_polars_keltner() -> PolarsResult<()> {
2139 let df = df![
2140 "high" => [12.0],
2141 "low" => [8.0],
2142 "close" => [10.0]
2143 ]?;
2144
2145 let out = df
2146 .lazy()
2147 .ta()
2148 .keltner_channels("high", "low", "close", 3, 3, 2.0)
2149 .collect()?;
2150
2151 let keltner = out.column("keltner_data")?.struct_()?;
2152 assert_eq!(
2153 keltner.field_by_name("middle".into())?.f64()?.get(0),
2154 Some(10.0)
2155 );
2156 assert_eq!(
2157 keltner.field_by_name("upper".into())?.f64()?.get(0),
2158 Some(18.0)
2159 );
2160 assert_eq!(
2161 keltner.field_by_name("lower".into())?.f64()?.get(0),
2162 Some(2.0)
2163 );
2164
2165 Ok(())
2166 }
2167
2168 #[test]
2169 fn test_polars_hma() -> PolarsResult<()> {
2170 let df = df![
2171 "price" => [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
2172 ]?;
2173
2174 let out = df.lazy().ta().hma("price", 4).collect()?;
2175
2176 let hma = out.column("hma")?.f64()?;
2177 assert!(hma.get(9).is_some());
2178
2179 Ok(())
2180 }
2181
2182 #[test]
2183 fn test_polars_anchored_vwap() -> PolarsResult<()> {
2184 let df = df![
2185 "price" => [10.0, 12.0, 15.0, 16.0],
2186 "volume" => [100.0, 200.0, 100.0, 100.0],
2187 "anchor" => [false, false, true, false]
2188 ]?;
2189
2190 let out = df
2191 .lazy()
2192 .ta()
2193 .anchored_vwap("price", "volume", "anchor")
2194 .collect()?;
2195
2196 let avwap = out.column("avwap")?.f64()?;
2197 assert_eq!(avwap.get(0), Some(10.0));
2198 assert_eq!(avwap.get(1), Some(11.333333333333334));
2199 assert_eq!(avwap.get(2), Some(15.0));
2200 assert_eq!(avwap.get(3), Some(15.5));
2201
2202 Ok(())
2203 }
2204
2205 #[test]
2206 fn test_polars_math_transforms() -> PolarsResult<()> {
2207 let df = df![
2208 "val" => [0.0, 1.5707963267948966] ]?;
2210
2211 let out = df.lazy().ta().sin("val").collect()?;
2212
2213 let sin = out.column("sin")?.f64()?;
2214 assert!((sin.get(0).unwrap() - 0.0).abs() < 1e-10);
2215 assert!((sin.get(1).unwrap() - 1.0).abs() < 1e-10);
2216
2217 Ok(())
2218 }
2219
2220 #[test]
2221 fn test_polars_math_operators() -> PolarsResult<()> {
2222 let df = df![
2223 "v1" => [10.0, 20.0],
2224 "v2" => [5.0, 30.0]
2225 ]?;
2226
2227 let out = df.lazy().ta().add("v1", "v2").ta().max("v1", 2).collect()?;
2228
2229 let add = out.column("add")?.f64()?;
2230 assert_eq!(add.get(0), Some(15.0));
2231 assert_eq!(add.get(1), Some(50.0));
2232
2233 let max = out.column("max")?.f64()?;
2234 assert_eq!(max.get(1), Some(20.0));
2235
2236 Ok(())
2237 }
2238}
2239
2240impl QuantWaveExt for LazyFrame {
2241 fn ta(&self) -> QuantWaveNamespace<'_> {
2242 QuantWaveNamespace(self)
2243 }
2244}