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