use crate::core::Method;
use crate::core::{Error, PeriodType, ValueType, Window};
use crate::helpers::Peekable;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct HighestIndex {
index: PeriodType,
value: ValueType,
window: Window<ValueType>,
}
impl Method for HighestIndex {
type Params = PeriodType;
type Input = ValueType;
type Output = PeriodType;
fn new(length: Self::Params, &value: &Self::Input) -> Result<Self, Error> {
if !value.is_finite() {
return Err(Error::InvalidCandles);
}
match length {
0 => Err(Error::WrongMethodParameters),
length => Ok(Self {
window: Window::new(length, value),
index: 0,
value,
}),
}
}
#[inline]
fn next(&mut self, &value: &Self::Input) -> Self::Output {
assert!(
value.is_finite(),
"HighestIndex method cannot operate with NAN values"
);
self.window.push(value);
self.index += 1;
#[allow(clippy::cast_possible_truncation)]
if value >= self.value {
self.value = value;
self.index = 0;
} else if self.index == self.window.len() {
let (index, value) = self
.window
.iter()
.copied()
.enumerate()
.fold((0, value), |a, b| if b.1 > a.1 { b } else { a });
self.index = index as PeriodType; self.value = value;
}
self.index
}
}
impl Peekable<<Self as Method>::Output> for HighestIndex {
fn peek(&self) -> <Self as Method>::Output {
self.index
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LowestIndex {
index: PeriodType,
value: ValueType,
window: Window<ValueType>,
}
impl Method for LowestIndex {
type Params = PeriodType;
type Input = ValueType;
type Output = PeriodType;
fn new(length: Self::Params, &value: &Self::Input) -> Result<Self, Error> {
if !value.is_finite() {
return Err(Error::InvalidCandles);
}
match length {
0 => Err(Error::WrongMethodParameters),
length => Ok(Self {
window: Window::new(length, value),
index: 0,
value,
}),
}
}
#[inline]
fn next(&mut self, &value: &Self::Input) -> Self::Output {
assert!(
value.is_finite(),
"LowestIndex method cannot operate with NAN values"
);
self.window.push(value);
self.index += 1;
#[allow(clippy::cast_possible_truncation)]
if value <= self.value {
self.value = value;
self.index = 0;
} else if self.index == self.window.len() {
let (index, value) = self
.window
.iter()
.copied()
.enumerate()
.fold((0, value), |a, b| if b.1 < a.1 { b } else { a });
self.index = index as PeriodType; self.value = value;
}
self.index
}
}
impl Peekable<<Self as Method>::Output> for LowestIndex {
fn peek(&self) -> <Self as Method>::Output {
self.index
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::Method;
use crate::core::ValueType;
use crate::helpers::RandomCandles;
use crate::methods::tests::test_const;
#[test]
fn test_highest_index_const() {
for i in 1..255 {
let input = (i as ValueType + 56.0) / 16.3251;
let mut method = HighestIndex::new(i, &input).unwrap();
let output = method.next(&input);
test_const(&mut method, &input, &output);
}
}
#[test]
fn test_highest_index1() {
use super::HighestIndex as TestingMethod;
let mut candles = RandomCandles::default();
let mut ma = TestingMethod::new(1, &candles.first().close).unwrap();
candles.take(100).for_each(|x| {
assert_eq!(0, ma.next(&x.close));
});
}
#[test]
fn test_highest_index() {
use super::HighestIndex as TestingMethod;
let candles = RandomCandles::default();
let src: Vec<ValueType> = candles.take(300).map(|x| x.close).collect();
(1..255).for_each(|length| {
let mut ma = TestingMethod::new(length, &src[0]).unwrap();
let length = length as usize;
src.iter().enumerate().for_each(|(i, &x)| {
let mut max_value = x;
let mut max_index = 0;
for j in 0..length {
let v = src[i.saturating_sub(j)];
if v > max_value {
max_value = v;
max_index = j;
}
}
assert_eq!(
max_index,
ma.next(&x) as usize,
"{}, {:?}, {:?}",
length,
&src[i.saturating_sub(length)..=i],
ma
);
});
});
}
#[test]
fn test_lowest_index_const() {
for i in 1..255 {
let input = (i as ValueType + 56.0) / 16.3251;
let mut method = LowestIndex::new(i, &input).unwrap();
let output = method.next(&input);
test_const(&mut method, &input, &output);
}
}
#[test]
fn test_lowest_index1() {
use super::LowestIndex as TestingMethod;
let mut candles = RandomCandles::default();
let mut ma = TestingMethod::new(1, &candles.first().close).unwrap();
candles.take(100).for_each(|x| {
assert_eq!(0, ma.next(&x.close));
});
}
#[test]
fn test_lowest_index() {
use super::LowestIndex as TestingMethod;
let candles = RandomCandles::default();
let src: Vec<ValueType> = candles.take(300).map(|x| x.close).collect();
(1..255).for_each(|length| {
let mut ma = TestingMethod::new(length, &src[0]).unwrap();
let length = length as usize;
src.iter().enumerate().for_each(|(i, &x)| {
let mut max_value = x;
let mut max_index = 0;
for j in 0..length {
let v = src[i.saturating_sub(j)];
if v < max_value {
max_value = v;
max_index = j;
}
}
assert_eq!(
max_index,
ma.next(&x) as usize,
"{}, {:?}, {:?}",
length,
&src[i.saturating_sub(length)..=i],
ma
);
});
});
}
}