use crate::common::{validate_inputs, validate_options};
pub use crate::indicator_types::TIndicatorState;
use crate::types::{DisplayType, IndicatorError, IndicatorType, Info};
use serde::{Deserialize, Serialize};
pub const INPUTS_WIDTH: usize = 3;
pub const OPTIONS_WIDTH: usize = 1;
pub fn info() -> Info<'static> {
Info {
name: "pivotpoint",
full_name: "Pivot Point",
indicator_type: IndicatorType::Trend,
display_type: DisplayType::Overlay,
inputs: &["high", "low", "close"],
options: &["period"],
outputs: &["s3", "s2", "s1", "pp", "r1", "r2", "r3"],
optional_outputs: &[],
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct IndicatorState {
high: Vec<f64>,
low: Vec<f64>,
close: Vec<f64>,
period: usize,
}
impl TIndicatorState<3> for IndicatorState {
fn batch_indicator(
&mut self,
inputs: &[&[f64]; INPUTS_WIDTH],
_optional_outputs: Option<&[bool]>,
) -> Result<Vec<Vec<f64>>, IndicatorError> {
validate_inputs(inputs, 1)?;
self.high.extend_from_slice(inputs[0]);
self.low.extend_from_slice(inputs[1]);
self.close.extend_from_slice(inputs[2]);
let outputs = process(&self.high, &self.low, &self.close, self.period);
self.high.drain(..self.high.len() - self.period + 1);
self.low.drain(..self.low.len() - self.period + 1);
self.close.drain(..self.close.len() - self.period + 1);
Ok(outputs)
}
}
pub fn min_data(options: &[f64]) -> usize {
options[0] as usize
}
pub fn min_data_accuracy(options: &[f64], _decimal_places: usize) -> usize {
min_data(options)
}
pub fn output_length(_data_len: usize, _options: &[f64]) -> usize {
1
}
pub fn indicator(
inputs: &[&[f64]; INPUTS_WIDTH],
options: &[f64; OPTIONS_WIDTH],
_optional_outputs: Option<&[bool]>,
) -> Result<(Vec<Vec<f64>>, IndicatorState), IndicatorError> {
validate_options(options)?;
validate_inputs(inputs, min_data(options))?;
let period = options[0] as usize;
let high = inputs[0];
let low = inputs[1];
let close = inputs[2];
let outputs = process(high, low, close, period);
Ok((
outputs,
IndicatorState {
period,
high: high[high.len() - period + 1..].to_vec(),
low: low[low.len() - period + 1..].to_vec(),
close: close[close.len() - period + 1..].to_vec(),
},
))
}
fn process(high: &[f64], low: &[f64], close: &[f64], period: usize) -> Vec<Vec<f64>> {
let start_index = high.len() - period;
let high = &high[start_index..];
let low = &low[start_index..];
let close = &close[start_index..];
let (s3, s2, s1, pp, r1, r2, r3) = calc(high, low, close);
vec![vec![s3, s2, s1, pp, r1, r2, r3]]
}
#[inline(always)]
pub fn calc(high: &[f64], low: &[f64], close: &[f64]) -> (f64, f64, f64, f64, f64, f64, f64) {
let close_value = close[close.len() - 1];
let (low_value, high_value) = low
.iter()
.copied()
.zip(high.iter().copied())
.fold((f64::INFINITY, f64::NEG_INFINITY), |(min, max), (l, h)| {
(min.min(l), max.max(h))
});
let pivot_point = (high_value + low_value + close_value) / 3.0;
let s1 = (pivot_point * 2.0) - high_value;
let s2 = pivot_point - (high_value - low_value);
let s3 = s1 - (high_value - low_value);
let r1 = (pivot_point * 2.0) - low_value;
let r2 = pivot_point + (high_value - low_value);
let r3 = r1 + (high_value - low_value);
(s3, s2, s1, pivot_point, r1, r2, r3)
}