yata/methods/
highest_lowest.rs

1use crate::core::Method;
2use crate::core::{Error, PeriodType, ValueType, Window};
3use crate::helpers::Peekable;
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8/// Calculates absolute difference between highest and lowest values over the last `length` values for timeseries of type [`ValueType`]
9///
10/// # Parameters
11///
12/// Has a single parameter `length`: [`PeriodType`]
13///
14/// `length` should be > `0`
15///
16/// # Input type
17///
18/// Input type is [`ValueType`]
19///
20/// # Output type
21///
22/// Output type is [`ValueType`]
23///
24/// Output value is always >= `0.0`
25///
26/// # Examples
27///
28/// ```
29/// use yata::prelude::*;
30/// use yata::methods::HighestLowestDelta;
31///
32///
33/// let values = [1.0, 2.0, 3.0, 2.0, 1.0, 0.5, 2.0, 3.0];
34/// let r      = [0.0, 1.0, 2.0, 1.0, 2.0, 1.5, 1.5, 2.5];
35/// let mut hld = HighestLowestDelta::new(3, &values[0]).unwrap();
36///
37/// (0..values.len()).for_each(|i| {
38///     let v = hld.next(&values[i]);
39///     assert_eq!(v, r[i]);
40/// });
41/// ```
42///
43/// # Performance
44///
45/// O(`length`)
46///
47/// This method is relatively very slow compare to the other methods.
48///
49/// # See also
50///
51/// [`Highest`], [`Lowest`], [`HighestIndex`], [`LowestIndex`]
52///
53/// [`ValueType`]: crate::core::ValueType
54/// [`PeriodType`]: crate::core::PeriodType
55/// [`HighestIndex`]: crate::methods::HighestIndex
56/// [`LowestIndex`]: crate::methods::LowestIndex
57#[derive(Debug, Clone)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59pub struct HighestLowestDelta {
60	// highest: Highest,
61	// lowest: Lowest,
62	highest: ValueType,
63	lowest: ValueType,
64	window: Window<ValueType>,
65}
66
67impl Method for HighestLowestDelta {
68	type Params = PeriodType;
69	type Input = ValueType;
70	type Output = Self::Input;
71
72	fn new(length: Self::Params, &value: &Self::Input) -> Result<Self, Error>
73	where
74		Self: Sized,
75	{
76		if !value.is_finite() {
77			return Err(Error::InvalidCandles);
78		}
79
80		match length {
81			0 => Err(Error::WrongMethodParameters),
82			length => Ok(Self {
83				window: Window::new(length, value),
84				highest: value,
85				lowest: value,
86			}),
87		}
88	}
89
90	#[inline]
91	fn next(&mut self, &value: &Self::Input) -> ValueType {
92		let left_value = self.window.push(value);
93
94		let mut search = false;
95		if value >= self.highest {
96			self.highest = value;
97		// It's not a mistake. We really need a bit-to-bit comparison of float values here
98		} else if left_value.to_bits() == self.highest.to_bits() {
99			search = true;
100		}
101
102		if value <= self.lowest {
103			self.lowest = value;
104		// It's not a mistake. We really need a bit-to-bit comparison of float values here
105		} else if left_value.to_bits() == self.lowest.to_bits() {
106			search = true;
107		}
108
109		if search {
110			let (min, max) = self
111				.window
112				.iter()
113				.fold((value, value), |(min, max), &v| (min.min(v), max.max(v)));
114			self.highest = max;
115			self.lowest = min;
116		}
117
118		self.highest - self.lowest
119	}
120}
121
122impl Peekable<<Self as Method>::Output> for HighestLowestDelta {
123	fn peek(&self) -> <Self as Method>::Output {
124		self.highest - self.lowest
125	}
126}
127
128/// Returns highest value over the last `length` values for timeseries of type [`ValueType`]
129///
130/// # Parameters
131///
132/// Has a single parameter `length`: [`PeriodType`]
133///
134/// `length` should be > 0
135///
136/// # Input type
137///
138/// Input type is [`ValueType`]
139///
140/// # Output type
141///
142/// Output type is [`ValueType`]
143///
144/// # Examples
145///
146/// ```
147/// use yata::core::Method;
148/// use yata::methods::Highest;
149///
150/// let values = [1.0, 2.0, 3.0, 2.0, 1.0, 0.5, 2.0, 3.0];
151/// let r      = [1.0, 2.0, 3.0, 3.0, 3.0, 2.0, 2.0, 3.0];
152///
153/// let mut highest = Highest::new(3, &values[0]).unwrap();
154///
155/// (0..values.len()).for_each(|i| {
156///     let v = highest.next(&values[i]);
157///     assert_eq!(v, r[i]);
158/// });
159/// ```
160///
161/// # Performance
162///
163/// O(`length`)
164///
165/// This method is relatively slow compare to the other methods.
166///
167/// # See also
168///
169/// [`HighestLowestDelta`], [`Lowest`], [`HighestIndex`], [`LowestIndex`]
170///
171/// [`ValueType`]: crate::core::ValueType
172/// [`PeriodType`]: crate::core::PeriodType
173/// [`HighestIndex`]: crate::methods::HighestIndex
174/// [`LowestIndex`]: crate::methods::LowestIndex
175#[derive(Debug, Clone)]
176#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
177pub struct Highest {
178	value: ValueType,
179	window: Window<ValueType>,
180}
181
182impl Method for Highest {
183	type Params = PeriodType;
184	type Input = ValueType;
185	type Output = Self::Input;
186
187	fn new(length: Self::Params, &value: &Self::Input) -> Result<Self, Error> {
188		if !value.is_finite() {
189			return Err(Error::InvalidCandles);
190		}
191
192		match length {
193			0 => Err(Error::WrongMethodParameters),
194			length => Ok(Self {
195				window: Window::new(length, value),
196				value,
197			}),
198		}
199	}
200
201	#[inline]
202	fn next(&mut self, &value: &Self::Input) -> ValueType {
203		assert!(
204			value.is_finite(),
205			"Highest method cannot operate with NAN values"
206		);
207
208		let left_value = self.window.push(value);
209
210		if value >= self.value {
211			self.value = value;
212		// It's not a mistake. We really need a bit-to-bit comparison of float values here
213		} else if left_value.to_bits() == self.value.to_bits() {
214			self.value = self.window.iter().fold(value, |a, &b| a.max(b));
215		}
216
217		self.value
218	}
219}
220
221impl Peekable<<Self as Method>::Output> for Highest {
222	fn peek(&self) -> <Self as Method>::Output {
223		self.value
224	}
225}
226
227/// Returns lowest value over the last `length` values for timeseries of type [`ValueType`]
228///
229/// # Parameters
230///
231/// Has a single parameter `length`: [`PeriodType`]
232///
233/// `length` should be > 0
234///
235/// # Input type
236///
237/// Input type is [`ValueType`]
238///
239/// # Output type
240///
241/// Output type is [`ValueType`]
242///
243/// # Examples
244///
245/// ```
246/// use yata::core::Method;
247/// use yata::methods::Lowest;
248///
249/// let values = [1.0, 2.0, 3.0, 2.0, 1.0, 0.5, 2.0, 3.0];
250/// let r      = [1.0, 1.0, 1.0, 2.0, 1.0, 0.5, 0.5, 0.5];
251///
252/// let mut lowest = Lowest::new(3, &values[0]).unwrap();
253///
254/// (0..values.len()).for_each(|i| {
255///     let v = lowest.next(&values[i]);
256///     assert_eq!(v, r[i]);
257/// });
258/// ```
259///
260/// # Performance
261///
262/// O(`length`)
263///
264/// This method is relatively slow compare to the other methods.
265///
266/// # See also
267///
268/// [`HighestLowestDelta`], [`Highest`], [`HighestIndex`], [`LowestIndex`]
269///
270/// [`ValueType`]: crate::core::ValueType
271/// [`PeriodType`]: crate::core::PeriodType
272/// [`HighestIndex`]: crate::methods::HighestIndex
273/// [`LowestIndex`]: crate::methods::LowestIndex
274#[derive(Debug, Clone)]
275#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
276pub struct Lowest {
277	value: ValueType,
278	window: Window<ValueType>,
279}
280
281impl Method for Lowest {
282	type Params = PeriodType;
283	type Input = ValueType;
284	type Output = Self::Input;
285
286	fn new(length: Self::Params, &value: &Self::Input) -> Result<Self, Error> {
287		if !value.is_finite() {
288			return Err(Error::InvalidCandles);
289		}
290
291		match length {
292			0 => Err(Error::WrongMethodParameters),
293			length => Ok(Self {
294				window: Window::new(length, value),
295				value,
296			}),
297		}
298	}
299
300	#[inline]
301	fn next(&mut self, &value: &Self::Input) -> ValueType {
302		assert!(
303			value.is_finite(),
304			"Lowest method cannot operate with NAN values"
305		);
306
307		let left_value = self.window.push(value);
308
309		if value <= self.value {
310			self.value = value;
311		// It's not a mistake. We really need a bit-to-bit comparison of float values here
312		} else if left_value.to_bits() == self.value.to_bits() {
313			self.value = self.window.iter().fold(value, |a, &b| a.min(b));
314		}
315
316		self.value
317	}
318}
319
320impl Peekable<<Self as Method>::Output> for Lowest {
321	fn peek(&self) -> <Self as Method>::Output {
322		self.value
323	}
324}
325
326#[cfg(test)]
327mod tests {
328	use super::{Highest, HighestLowestDelta, Lowest};
329	use crate::core::{Method, ValueType};
330	use crate::helpers::{assert_eq_float, RandomCandles};
331	use crate::methods::tests::test_const;
332
333	#[test]
334	fn test_highest_const() {
335		for i in 1..255 {
336			let input = (i as ValueType + 56.0) / 16.3251;
337			let mut method = Highest::new(i, &input).unwrap();
338
339			let output = method.next(&input);
340			test_const(&mut method, &input, &output);
341		}
342	}
343
344	#[test]
345	fn test_highest1() {
346		use super::Highest as TestingMethod;
347
348		let mut candles = RandomCandles::default();
349
350		let mut ma = TestingMethod::new(1, &candles.first().close).unwrap();
351
352		candles.take(100).for_each(|x| {
353			assert_eq_float(x.close, ma.next(&x.close));
354		});
355	}
356
357	#[test]
358	fn test_highest() {
359		use super::Highest as TestingMethod;
360
361		let candles = RandomCandles::default();
362
363		let src: Vec<ValueType> = candles.take(300).map(|x| x.close).collect();
364
365		(2..255).for_each(|length| {
366			let mut ma = TestingMethod::new(length, &src[0]).unwrap();
367			let length = length as usize;
368
369			src.iter().enumerate().for_each(|(i, x)| {
370				let value1 = ma.next(x);
371				let value2 = (0..length).fold(src[i], |m, j| m.max(src[i.saturating_sub(j)]));
372				assert_eq_float(value2, value1);
373			});
374		});
375	}
376
377	#[test]
378	fn test_lowest_const() {
379		for i in 1..255 {
380			let input = (i as ValueType + 56.0) / 16.3251;
381			let mut method = Lowest::new(i, &input).unwrap();
382
383			let output = method.next(&input);
384			test_const(&mut method, &input, &output);
385		}
386	}
387
388	#[test]
389	fn test_lowest1() {
390		use super::Lowest as TestingMethod;
391		let mut candles = RandomCandles::default();
392
393		let mut ma = TestingMethod::new(1, &candles.first().close).unwrap();
394
395		candles.take(100).for_each(|x| {
396			assert_eq_float(x.close, ma.next(&x.close));
397		});
398	}
399
400	#[test]
401	fn test_lowest() {
402		use super::Lowest as TestingMethod;
403		let candles = RandomCandles::default();
404
405		let src: Vec<ValueType> = candles.take(300).map(|x| x.close).collect();
406
407		(2..255).for_each(|length| {
408			let mut ma = TestingMethod::new(length, &src[0]).unwrap();
409			let length = length as usize;
410
411			src.iter().enumerate().for_each(|(i, x)| {
412				let value1 = ma.next(x);
413				let value2 = (0..length).fold(src[i], |m, j| m.min(src[i.saturating_sub(j)]));
414				assert_eq_float(value2, value1);
415			});
416		});
417	}
418
419	#[test]
420	fn test_highest_lowest_delta_const() {
421		for i in 1..255 {
422			let input = (i as ValueType + 56.0) / 16.3251;
423			let mut method = HighestLowestDelta::new(i, &input).unwrap();
424
425			let output = method.next(&input);
426			test_const(&mut method, &input, &output);
427		}
428	}
429
430	#[test]
431	fn test_highest_lowest_delta1() {
432		use super::HighestLowestDelta as TestingMethod;
433		let mut candles = RandomCandles::default();
434
435		let mut ma = TestingMethod::new(1, &candles.first().close).unwrap();
436
437		candles.take(100).for_each(|x| {
438			assert_eq_float(0.0, ma.next(&x.close));
439		});
440	}
441
442	#[test]
443	fn test_highest_lowest_delta() {
444		use super::HighestLowestDelta as TestingMethod;
445		let candles = RandomCandles::default();
446
447		let src: Vec<ValueType> = candles.take(300).map(|x| x.close).collect();
448
449		(2..255).for_each(|length| {
450			let mut ma = TestingMethod::new(length, &src[0]).unwrap();
451			let length = length as usize;
452
453			src.iter().enumerate().for_each(|(i, x)| {
454				let value1 = ma.next(x);
455				let min = (0..length).fold(src[i], |m, j| m.min(src[i.saturating_sub(j)]));
456				let max = (0..length).fold(src[i], |m, j| m.max(src[i.saturating_sub(j)]));
457				assert_eq_float(max - min, value1);
458			});
459		});
460	}
461}