nautilus_indicators/volatility/
vr.rs1use std::fmt::{Debug, Display};
17
18use nautilus_model::data::Bar;
19
20use crate::{average::MovingAverageType, indicator::Indicator, volatility::atr::AverageTrueRange};
21
22#[repr(C)]
23#[derive(Debug)]
24#[cfg_attr(
25 feature = "python",
26 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.indicators", unsendable)
27)]
28#[cfg_attr(
29 feature = "python",
30 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.indicators")
31)]
32pub struct VolatilityRatio {
33 pub fast_period: usize,
34 pub slow_period: usize,
35 pub ma_type: MovingAverageType,
36 pub use_previous: bool,
37 pub value_floor: f64,
38 pub value: f64,
39 pub initialized: bool,
40 has_inputs: bool,
41 atr_fast: AverageTrueRange,
42 atr_slow: AverageTrueRange,
43}
44
45impl Display for VolatilityRatio {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 write!(
48 f,
49 "{}({},{},{})",
50 self.name(),
51 self.fast_period,
52 self.slow_period,
53 self.ma_type,
54 )
55 }
56}
57
58impl Indicator for VolatilityRatio {
59 fn name(&self) -> String {
60 stringify!(VolatilityRatio).to_string()
61 }
62
63 fn has_inputs(&self) -> bool {
64 self.has_inputs
65 }
66
67 fn initialized(&self) -> bool {
68 self.initialized
69 }
70
71 fn handle_bar(&mut self, bar: &Bar) {
72 self.update_raw((&bar.high).into(), (&bar.low).into(), (&bar.close).into());
73 }
74
75 fn reset(&mut self) {
76 self.atr_fast.reset();
77 self.atr_slow.reset();
78 self.value = 0.0;
79 self.initialized = false;
80 self.has_inputs = false;
81 }
82}
83
84impl VolatilityRatio {
85 #[must_use]
87 pub fn new(
88 fast_period: usize,
89 slow_period: usize,
90 ma_type: Option<MovingAverageType>,
91 use_previous: Option<bool>,
92 value_floor: Option<f64>,
93 ) -> Self {
94 Self {
95 fast_period,
96 slow_period,
97 ma_type: ma_type.unwrap_or(MovingAverageType::Simple),
98 use_previous: use_previous.unwrap_or(false),
99 value_floor: value_floor.unwrap_or(0.0),
100 value: 0.0,
101 has_inputs: false,
102 initialized: false,
103 atr_fast: AverageTrueRange::new(fast_period, ma_type, use_previous, value_floor),
104 atr_slow: AverageTrueRange::new(slow_period, ma_type, use_previous, value_floor),
105 }
106 }
107
108 pub fn update_raw(&mut self, high: f64, low: f64, close: f64) {
109 self.atr_fast.update_raw(high, low, close);
110 self.atr_slow.update_raw(high, low, close);
111
112 if self.atr_fast.value > 0.0 {
113 self.value = self.atr_slow.value / self.atr_fast.value;
114 }
115
116 if !self.initialized {
117 self.has_inputs = true;
118
119 if self.atr_fast.initialized && self.atr_slow.initialized {
120 self.initialized = true;
121 }
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use rstest::rstest;
129
130 use super::*;
131 use crate::stubs::vr_10;
132
133 #[rstest]
134 fn test_name_returns_expected_string(vr_10: VolatilityRatio) {
135 assert_eq!(vr_10.name(), "VolatilityRatio");
136 }
137
138 #[rstest]
139 fn test_str_repr_returns_expected_string(vr_10: VolatilityRatio) {
140 assert_eq!(format!("{vr_10}"), "VolatilityRatio(10,10,SIMPLE)");
141 }
142
143 #[rstest]
144 fn test_period_returns_expected_value(vr_10: VolatilityRatio) {
145 assert_eq!(vr_10.fast_period, 10);
146 assert_eq!(vr_10.slow_period, 10);
147 assert!(!vr_10.use_previous);
148 assert_eq!(vr_10.value_floor, 10.0);
149 }
150
151 #[rstest]
152 fn test_initialized_without_inputs_returns_false(vr_10: VolatilityRatio) {
153 assert!(!vr_10.initialized());
154 }
155
156 #[rstest]
157 fn test_value_with_all_higher_inputs_returns_expected_value(mut vr_10: VolatilityRatio) {
158 let high_values = [
159 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0,
160 ];
161 let low_values = [
162 0.9, 1.9, 2.9, 3.9, 4.9, 5.9, 6.9, 7.9, 8.9, 9.9, 10.1, 10.2, 10.3, 11.1, 11.4,
163 ];
164 let close_values = [
165 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0,
166 ];
167
168 for i in 0..15 {
169 vr_10.update_raw(high_values[i], low_values[i], close_values[i]);
170 }
171
172 assert!(vr_10.initialized());
173 assert_eq!(vr_10.value, 1.0);
174 }
175
176 #[rstest]
177 fn test_reset_successfully_returns_indicator_to_fresh_state(mut vr_10: VolatilityRatio) {
178 vr_10.update_raw(1.00020, 1.00050, 1.00030);
179 vr_10.update_raw(1.00030, 1.00060, 1.00030);
180 vr_10.update_raw(1.00070, 1.00080, 1.00030);
181
182 vr_10.reset();
183
184 assert!(!vr_10.initialized());
185 assert_eq!(vr_10.value, 0.0);
186 assert!(!vr_10.initialized);
187 assert!(!vr_10.has_inputs);
188 assert_eq!(vr_10.atr_fast.value, 0.0);
189 assert_eq!(vr_10.atr_slow.value, 0.0);
190 }
191}