Skip to main content

quantwave_plugins/
custom_0.rs

1use polars::prelude::*;
2use pyo3_polars::derive::polars_expr;
3use serde::Deserialize;
4use quantwave_core::*;
5use quantwave_core::traits::Next;
6
7#[polars_expr(output_type=Float64)]
8fn anchored_vwap(inputs: &[Series]) -> PolarsResult<Series> {
9    let price_s = &inputs[0];
10    let volume_s = &inputs[1];
11    let anchor_s = &inputs[2];
12
13    let price = price_s.f64()?;
14    let volume = volume_s.f64()?;
15    let anchor = anchor_s.bool()?;
16
17    let mut avwap = quantwave_core::AnchoredVWAP::new();
18    let mut values = Vec::with_capacity(price_s.len());
19
20    for i in 0..price_s.len() {
21        let p = price.get(i).unwrap_or(0.0);
22        let v = volume.get(i).unwrap_or(0.0);
23        let a = anchor.get(i).unwrap_or(false);
24        values.push(avwap.next((p, v, a)));
25    }
26
27    Ok(Series::new("anchored_vwap".into(), values))
28}
29
30#[derive(Deserialize)]
31pub struct KinematicKalmanKwargs {
32    pub q_pos: f64,
33    pub q_vel: f64,
34    pub r: f64,
35}
36
37#[polars_expr(output_type=Float64)]
38fn kinematic_kalman(inputs: &[Series], kwargs: KinematicKalmanKwargs) -> PolarsResult<Series> {
39    let s = inputs[0].f64()?;
40    let mut indicator = quantwave_core::indicators::kinematic_kalman::KinematicKalmanFilter::new(
41        kwargs.q_pos, kwargs.q_vel, kwargs.r
42    );
43    let mut values = Vec::with_capacity(s.len());
44
45    for i in 0..s.len() {
46        let val = s.get(i).unwrap_or(f64::NAN);
47        values.push(indicator.next(val));
48    }
49
50    Ok(Series::new("kinematic_kalman".into(), values))
51}
52
53#[derive(Deserialize)]
54pub struct VortexKwargs {
55    pub period: usize,
56}
57
58pub fn vortex_output(_: &[Field]) -> PolarsResult<Field> {
59    Ok(Field::new(
60        "vortex".into(),
61        DataType::Struct(vec![
62            Field::new("vi_plus".into(), DataType::Float64),
63            Field::new("vi_minus".into(), DataType::Float64),
64        ]),
65    ))
66}
67
68#[polars_expr(output_type_func=vortex_output)]
69fn vortex_indicator(inputs: &[Series], kwargs: VortexKwargs) -> PolarsResult<Series> {
70    let high = inputs[0].f64()?;
71    let low = inputs[1].f64()?;
72    let close = inputs[2].f64()?;
73
74    let mut vi = quantwave_core::VortexIndicator::new(kwargs.period);
75    let mut plus_vals = Vec::with_capacity(high.len());
76    let mut minus_vals = Vec::with_capacity(high.len());
77
78    for i in 0..high.len() {
79        let h = high.get(i).unwrap_or(0.0);
80        let l = low.get(i).unwrap_or(0.0);
81        let c = close.get(i).unwrap_or(0.0);
82        let (plus, minus) = vi.next((h, l, c));
83        plus_vals.push(plus);
84        minus_vals.push(minus);
85    }
86
87    let plus_series = Series::new("vi_plus".into(), plus_vals);
88    let minus_series = Series::new("vi_minus".into(), minus_vals);
89
90    let out = StructChunked::from_series(
91        "vortex_output".into(),
92        high.len(),
93        [plus_series, minus_series].iter(),
94    )?;
95
96    Ok(out.into_series())
97}
98
99#[derive(Deserialize)]
100pub struct SveKwargs {
101    pub bands_period: usize,
102    pub bands_deviation: f64,
103    pub low_band_adjust: f64,
104    pub mid_line_length: usize,
105}
106
107pub fn sve_bands_output(_: &[Field]) -> PolarsResult<Field> {
108    Ok(Field::new(
109        "sve_bands".into(),
110        DataType::Struct(vec![
111            Field::new("upper".into(), DataType::Float64),
112            Field::new("middle".into(), DataType::Float64),
113            Field::new("lower".into(), DataType::Float64),
114        ]),
115    ))
116}
117
118#[polars_expr(output_type_func=sve_bands_output)]
119fn sve_volatility_bands(inputs: &[Series], kwargs: SveKwargs) -> PolarsResult<Series> {
120    let high = inputs[0].f64()?;
121    let low = inputs[1].f64()?;
122    let close = inputs[2].f64()?;
123
124    let mut indicator = quantwave_core::SVEVolatilityBands::new(
125        kwargs.bands_period,
126        kwargs.bands_deviation,
127        kwargs.low_band_adjust,
128        kwargs.mid_line_length,
129    );
130    let mut upper_vals = Vec::with_capacity(high.len());
131    let mut mid_vals = Vec::with_capacity(high.len());
132    let mut lower_vals = Vec::with_capacity(high.len());
133
134    for i in 0..high.len() {
135        let h = high.get(i).unwrap_or(f64::NAN);
136        let l = low.get(i).unwrap_or(f64::NAN);
137        let c = close.get(i).unwrap_or(f64::NAN);
138        let (upper, mid, lower) = indicator.next((h, l, c));
139        upper_vals.push(upper);
140        mid_vals.push(mid);
141        lower_vals.push(lower);
142    }
143
144    let s_upper = Series::new("upper".into(), upper_vals);
145    let s_mid = Series::new("middle".into(), mid_vals);
146    let s_lower = Series::new("lower".into(), lower_vals);
147
148    let out = StructChunked::from_series(
149        "sve_bands_data".into(),
150        high.len(),
151        [s_upper, s_mid, s_lower].iter(),
152    )?;
153
154    Ok(out.into_series())
155}
156
157#[derive(Deserialize)]
158pub struct TtmKwargs {
159    pub period: usize,
160    pub multiplier_bb: f64,
161    pub multiplier_kc: f64,
162}
163
164pub fn ttm_squeeze_output(_: &[Field]) -> PolarsResult<Field> {
165    Ok(Field::new(
166        "ttm_squeeze".into(),
167        DataType::Struct(vec![
168            Field::new("histogram".into(), DataType::Float64),
169            Field::new("is_squeezed".into(), DataType::Boolean),
170        ]),
171    ))
172}
173
174#[polars_expr(output_type_func=ttm_squeeze_output)]
175fn ttm_squeeze(inputs: &[Series], kwargs: TtmKwargs) -> PolarsResult<Series> {
176    let high = inputs[0].f64()?;
177    let low = inputs[1].f64()?;
178    let close = inputs[2].f64()?;
179
180    let mut ttm = quantwave_core::TTMSqueeze::new(kwargs.period, kwargs.multiplier_bb, kwargs.multiplier_kc);
181    let mut histograms = Vec::with_capacity(high.len());
182    let mut squeezed = Vec::with_capacity(high.len());
183
184    for i in 0..high.len() {
185        let h = high.get(i).unwrap_or(0.0);
186        let l = low.get(i).unwrap_or(0.0);
187        let c = close.get(i).unwrap_or(0.0);
188        let (hist, is_sq) = ttm.next((h, l, c));
189        histograms.push(hist);
190        squeezed.push(is_sq);
191    }
192
193    let hist_series = Series::new("histogram".into(), histograms);
194    let squeezed_series = Series::new("is_squeezed".into(), squeezed);
195
196    let out = StructChunked::from_series(
197        "ttm_squeeze_output".into(),
198        high.len(),
199        [hist_series, squeezed_series].iter(),
200    )?;
201
202    Ok(out.into_series())
203}