pub mod single {
use crate::basic_indicators::single::{median, mode};
use crate::moving_average::single::moving_average;
use crate::validation::{assert_non_empty, assert_same_len, unsupported_type};
use crate::{ConstantModelType, MovingAverageType};
#[inline]
pub fn return_on_investment(start_price: f64, end_price: f64, investment: f64) -> (f64, f64) {
let initial_investment = investment / start_price;
let final_investment_value = end_price * initial_investment;
let percent_return = ((final_investment_value - investment) / investment) * 100.0;
(final_investment_value, percent_return)
}
#[inline]
pub fn true_range(close: f64, high: f64, low: f64) -> f64 {
let h_l_tr = high - low;
let h_c_tr = high - close;
let c_l_tr = close - low;
if h_l_tr >= h_c_tr && h_l_tr >= c_l_tr {
h_l_tr
} else if h_c_tr >= c_l_tr {
h_c_tr
} else {
c_l_tr
}
}
#[inline]
pub fn average_true_range(
close: &[f64],
highs: &[f64],
lows: &[f64],
constant_model_type: ConstantModelType,
) -> crate::Result<f64> {
assert_same_len(&[("close", close), ("highs", highs), ("lows", lows)])?;
assert_non_empty("close", close)?;
let trs: Vec<f64> = close
.iter()
.zip(highs.iter())
.zip(lows.iter())
.map(|((c, h), l)| true_range(*c, *h, *l))
.collect();
match constant_model_type {
ConstantModelType::SimpleMovingAverage => {
moving_average(&trs, MovingAverageType::Simple)
}
ConstantModelType::SmoothedMovingAverage => {
moving_average(&trs, MovingAverageType::Smoothed)
}
ConstantModelType::ExponentialMovingAverage => {
moving_average(&trs, MovingAverageType::Exponential)
}
ConstantModelType::PersonalisedMovingAverage {
alpha_num,
alpha_den,
} => moving_average(
&trs,
MovingAverageType::Personalised {
alpha_num,
alpha_den,
},
),
ConstantModelType::SimpleMovingMedian => median(&trs),
ConstantModelType::SimpleMovingMode => mode(&trs),
_ => Err(unsupported_type("ConstantModelType")),
}
}
#[inline]
pub fn internal_bar_strength(high: f64, low: f64, close: f64) -> f64 {
(close - low) / (high - low)
}
}
pub mod bulk {
use crate::basic_indicators::bulk::{median, mode};
use crate::moving_average::bulk::moving_average;
use crate::other_indicators::single;
use crate::validation::{assert_non_empty, assert_period, assert_same_len, unsupported_type};
use crate::{ConstantModelType, MovingAverageType};
#[inline]
pub fn return_on_investment(prices: &[f64], investment: f64) -> crate::Result<Vec<(f64, f64)>> {
assert_non_empty("prices", prices)?;
let mut rois = Vec::with_capacity(prices.len() - 1);
let mut roi = single::return_on_investment(prices[0], prices[1], investment);
rois.push(roi);
for i in 2..prices.len() {
roi = single::return_on_investment(prices[i - 1], prices[i], rois[i - 2].0);
rois.push(roi);
}
Ok(rois)
}
#[inline]
pub fn true_range(close: &[f64], highs: &[f64], lows: &[f64]) -> crate::Result<Vec<f64>> {
let length = close.len();
assert_same_len(&[("close", close), ("highs", highs), ("lows", lows)])?;
assert_non_empty("close", close)?;
Ok((0..length)
.map(|i| single::true_range(close[i], highs[i], lows[i]))
.collect())
}
#[inline]
pub fn average_true_range(
close: &[f64],
highs: &[f64],
lows: &[f64],
constant_model_type: ConstantModelType,
period: usize,
) -> crate::Result<Vec<f64>> {
let length = close.len();
assert_period(period, length)?;
assert_same_len(&[("close", close), ("lows", lows), ("highs", highs)])?;
assert_non_empty("close", close)?;
(0..=length - period)
.map(|i| {
single::average_true_range(
&close[i..i + period],
&highs[i..i + period],
&lows[i..i + period],
constant_model_type,
)
})
.collect()
}
#[inline]
pub fn internal_bar_strength(
highs: &[f64],
lows: &[f64],
close: &[f64],
) -> crate::Result<Vec<f64>> {
let length = highs.len();
assert_same_len(&[("highs", highs), ("lows", lows), ("close", close)])?;
assert_non_empty("highs", highs)?;
Ok((0..length)
.map(|i| single::internal_bar_strength(highs[i], lows[i], close[i]))
.collect())
}
pub fn positivity_indicator(
open: &[f64],
previous_close: &[f64],
signal_period: usize,
constant_model_type: ConstantModelType,
) -> crate::Result<Vec<(f64, f64)>> {
let length = open.len();
assert_same_len(&[("open", open), ("previous_close", previous_close)])?;
assert_non_empty("open", open)?;
assert_period(signal_period, length)?;
let pis: Vec<f64> = (0..length)
.map(|i| ((open[i] - previous_close[i]) / previous_close[i]) * 100.0)
.collect();
let signal_line = match constant_model_type {
ConstantModelType::SimpleMovingAverage => {
moving_average(&pis, MovingAverageType::Simple, signal_period)?
}
ConstantModelType::SmoothedMovingAverage => {
moving_average(&pis, MovingAverageType::Smoothed, signal_period)?
}
ConstantModelType::ExponentialMovingAverage => {
moving_average(&pis, MovingAverageType::Exponential, signal_period)?
}
ConstantModelType::PersonalisedMovingAverage {
alpha_num,
alpha_den,
} => moving_average(
&pis,
MovingAverageType::Personalised {
alpha_num,
alpha_den,
},
signal_period,
)?,
ConstantModelType::SimpleMovingMedian => median(&pis, signal_period)?,
ConstantModelType::SimpleMovingMode => mode(&pis, signal_period)?,
_ => return Err(unsupported_type("ConstantModelType")),
};
Ok(signal_line
.iter()
.enumerate()
.map(|(i, &sig)| (pis[i + signal_period - 1], sig))
.collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single_return_on_investment() {
let start_price = 100.46;
let end_price = 100.53;
let investment = 1000.0;
assert_eq!(
(1000.6967947441768, 0.06967947441768274),
single::return_on_investment(start_price, end_price, investment)
);
}
#[test]
fn bulk_return_on_investment() {
let prices = vec![100.46, 100.53, 100.38, 100.19, 100.21];
let investment = 1000.0;
assert_eq!(
vec![
(1000.6967947441768, 0.06967947441768274),
(999.2036631495122, -0.14920919128619353),
(997.3123631296038, -0.18928073321378402),
(997.5114473422257, 0.01996207206307317)
],
bulk::return_on_investment(&prices, investment).unwrap()
);
}
#[test]
fn bulk_return_on_investment_error() {
let prices = Vec::new();
let investment = 1000.0;
assert!(bulk::return_on_investment(&prices, investment).is_err());
}
#[test]
fn single_true_range_high_low() {
assert_eq!(
0.8299999999999983,
single::true_range(100.46, 101.12, 100.29)
);
}
#[test]
fn single_true_range_high_close() {
assert_eq!(0.769999999999996, single::true_range(100.53, 101.3, 100.87));
}
#[test]
fn single_true_range_close_low() {
assert_eq!(
0.4399999999999977,
single::true_range(100.38, 100.11, 99.94)
);
}
#[test]
fn bulk_true_range() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert_eq!(
vec![0.8299999999999983, 0.769999999999996, 0.4399999999999977],
bulk::true_range(&close, &high, &low).unwrap()
);
}
#[test]
fn bulk_true_range_close_length_error() {
let close = vec![100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert!(bulk::true_range(&close, &high, &low).is_err());
}
#[test]
fn bulk_true_range_high_length_error() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert!(bulk::true_range(&close, &high, &low).is_err());
}
#[test]
fn bulk_true_range_low_length_error() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 99.94];
assert!(bulk::true_range(&close, &high, &low).is_err());
}
#[test]
fn bulk_true_range_empty_error() {
let close = Vec::new();
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert!(bulk::true_range(&close, &high, &low).is_err());
}
#[test]
fn single_average_true_range_simple() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert_eq!(
0.6799999999999974,
single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingAverage
)
.unwrap()
);
}
#[test]
fn single_average_true_range_smoothed() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert_eq!(
0.6263157894736815,
single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SmoothedMovingAverage
)
.unwrap()
);
}
#[test]
fn single_average_true_range_exponential() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert_eq!(
0.5899999999999973,
single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::ExponentialMovingAverage
)
.unwrap()
);
}
#[test]
fn single_average_true_range_personalised() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert_eq!(
0.5322388059701466,
single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::PersonalisedMovingAverage {
alpha_num: 5.0,
alpha_den: 4.0
}
)
.unwrap()
);
}
#[test]
fn single_average_true_range_median() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert_eq!(
0.769999999999996,
single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingMedian
)
.unwrap()
);
}
#[test]
fn single_average_true_range_mode() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert_eq!(
1.0,
single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingMode
)
.unwrap()
);
}
#[test]
fn single_average_true_range_close_length_error() {
let close = vec![100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert!(single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingMode,
)
.is_err());
}
#[test]
fn single_average_true_range_high_length_error() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert!(single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingMode,
)
.is_err());
}
#[test]
fn single_average_true_range_low_length_error() {
let close = vec![100.46, 100.53, 100.38];
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 99.94];
assert!(single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingMode,
)
.is_err());
}
#[test]
fn single_average_true_range_empty_error() {
let close = Vec::new();
let high = vec![101.12, 101.3, 100.11];
let low = vec![100.29, 100.87, 99.94];
assert!(single::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingMode,
)
.is_err());
}
#[test]
fn bulk_average_true_range() {
let close = vec![100.46, 100.53, 100.38, 100.19, 100.21];
let high = vec![101.12, 101.3, 100.11, 100.55, 100.43];
let low = vec![100.29, 100.87, 99.94, 99.86, 99.91];
let period: usize = 3;
assert_eq!(
vec![0.6799999999999974, 0.6333333333333305, 0.5500000000000019],
bulk::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingAverage,
period
)
.unwrap()
);
}
#[test]
fn bulk_average_true_range_error_period() {
let close = vec![100.46, 100.53, 100.38, 100.19, 100.21];
let high = vec![101.12, 101.3, 100.11, 100.55, 100.43];
let low = vec![100.29, 100.87, 99.94, 99.86, 99.91];
let period: usize = 30;
assert!(bulk::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingAverage,
period,
)
.is_err());
}
#[test]
fn bulk_average_true_range_error_close_length() {
let close = vec![100.46, 100.53, 100.19, 100.21];
let high = vec![101.12, 101.3, 100.11, 100.55, 100.43];
let low = vec![100.29, 100.87, 99.94, 99.86, 99.91];
let period: usize = 3;
assert!(bulk::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingAverage,
period,
)
.is_err());
}
#[test]
fn bulk_average_true_range_error_high_length() {
let close = vec![100.46, 100.53, 100.38, 100.19, 100.21];
let high = vec![101.12, 101.3, 100.11, 100.43];
let low = vec![100.29, 100.87, 99.94, 99.86, 99.91];
let period: usize = 3;
assert!(bulk::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingAverage,
period,
)
.is_err());
}
#[test]
fn bulk_average_true_range_error_low_length() {
let close = vec![100.46, 100.53, 100.38, 100.19, 100.21];
let high = vec![101.12, 101.3, 100.11, 100.55, 100.43];
let low = vec![100.29, 99.94, 99.86, 99.91];
let period: usize = 3;
assert!(bulk::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingAverage,
period,
)
.is_err());
}
#[test]
fn bulk_average_true_range_error_empty() {
let close = Vec::new();
let high = vec![101.12, 101.3, 100.11, 100.55, 100.43];
let low = vec![100.29, 100.87, 99.94, 99.86, 99.91];
let period: usize = 3;
assert!(bulk::average_true_range(
&close,
&high,
&low,
crate::ConstantModelType::SimpleMovingAverage,
period,
)
.is_err());
}
#[test]
fn single_internal_bar_strengh() {
let close = 100.55;
let high = 102.32;
let low = 100.14;
assert_eq!(
0.1880733944954119,
single::internal_bar_strength(high, low, close)
);
}
#[test]
fn bulk_internal_bar_strength() {
let close = vec![100.55, 99.01, 100.43, 101.0, 101.76];
let high = vec![102.32, 100.69, 100.83, 101.73, 102.01];
let low = vec![100.14, 98.98, 99.07, 100.1, 99.96];
assert_eq!(
vec![
0.1880733944954119,
0.017543859649123535,
0.7727272727272783,
0.5521472392638039,
0.8780487804878055
],
bulk::internal_bar_strength(&high, &low, &close).unwrap()
);
}
#[test]
fn bulk_internal_bar_strength_error_close_length() {
let close = vec![100.55, 99.01, 100.43, 101.0];
let high = vec![102.32, 100.69, 100.83, 101.73, 102.01];
let low = vec![100.14, 98.98, 99.07, 100.1, 99.96];
assert!(bulk::internal_bar_strength(&high, &low, &close).is_err());
}
#[test]
fn bulk_internal_bar_strength_error_high_length() {
let close = vec![100.55, 99.01, 100.43, 101.0, 101.76];
let high = vec![102.32, 100.69, 100.83, 101.73];
let low = vec![100.14, 98.98, 99.07, 100.1, 99.96];
assert!(bulk::internal_bar_strength(&high, &low, &close).is_err());
}
#[test]
fn bulk_internal_bar_strength_error_low_length() {
let close = vec![100.55, 99.01, 100.43, 101.0, 101.76];
let high = vec![102.32, 100.69, 100.83, 101.73, 102.01];
let low = vec![100.14, 98.98, 99.07, 100.1];
assert!(bulk::internal_bar_strength(&high, &low, &close).is_err());
}
#[test]
fn bulk_internal_bar_strength_error_empty() {
let close = Vec::new();
let high = Vec::new();
let low = Vec::new();
assert!(bulk::internal_bar_strength(&high, &low, &close).is_err());
}
#[test]
fn bulk_positivity_indicator_ma() {
let open = vec![5278.24, 5314.48, 5357.8, 5343.81, 5341.22, 5353.0, 5409.13];
let previous_close = vec![5283.4, 5291.34, 5354.03, 5352.96, 5346.99, 5360.79, 5375.32];
let signal_period: usize = 5;
assert_eq!(
vec![
(-0.10791117993487043, 0.026244711276039178),
(-0.14531440328757447, 0.01671470717528643),
(0.6289858092169471, 0.05504820197025553)
],
bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::SimpleMovingAverage
)
.unwrap()
);
}
#[test]
fn bulk_positivity_indicator_sma() {
let open = vec![5278.24, 5314.48, 5357.8, 5343.81, 5341.22, 5353.0, 5409.13];
let previous_close = vec![5283.4, 5291.34, 5354.03, 5352.96, 5346.99, 5360.79, 5375.32];
let signal_period: usize = 5;
assert_eq!(
vec![
(-0.10791117993487043, -0.004667175210233987),
(-0.14531440328757447, -0.0374414205397291),
(0.6289858092169471, 0.11452727085189565)
],
bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::SmoothedMovingAverage
)
.unwrap()
);
}
#[test]
fn bulk_positivity_indicator_ema() {
let open = vec![5278.24, 5314.48, 5357.8, 5343.81, 5341.22, 5353.0, 5409.13];
let previous_close = vec![5283.4, 5291.34, 5354.03, 5352.96, 5346.99, 5360.79, 5375.32];
let signal_period: usize = 5;
assert_eq!(
vec![
(-0.10791117993487043, -0.03082127868198756),
(-0.14531440328757447, -0.0713945013484951),
(0.6289858092169471, 0.17175495314835065)
],
bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::ExponentialMovingAverage
)
.unwrap()
);
}
#[test]
fn bulk_positivity_indicator_pma() {
let open = vec![5278.24, 5314.48, 5357.8, 5343.81, 5341.22, 5353.0, 5409.13];
let previous_close = vec![5283.4, 5291.34, 5354.03, 5352.96, 5346.99, 5360.79, 5375.32];
let signal_period: usize = 5;
assert_eq!(
vec![
(-0.10791117993487043, -0.07654434206147799),
(-0.14531440328757447, -0.1152171021135638),
(0.6289858092169471, 0.3001081065924838)
],
bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::PersonalisedMovingAverage {
alpha_num: 5.0,
alpha_den: 4.0
}
)
.unwrap()
);
}
#[test]
fn bulk_positivity_indicator_median() {
let open = vec![5278.24, 5314.48, 5357.8, 5343.81, 5341.22, 5353.0, 5409.13];
let previous_close = vec![5283.4, 5291.34, 5354.03, 5352.96, 5346.99, 5360.79, 5375.32];
let signal_period: usize = 5;
assert_eq!(
vec![
(-0.10791117993487043, -0.0976643827838107),
(-0.14531440328757447, -0.10791117993487043),
(0.6289858092169471, -0.10791117993487043)
],
bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::SimpleMovingMedian
)
.unwrap()
);
}
#[test]
fn bulk_positivity_indicator_mode() {
let open = vec![5278.24, 5314.48, 5357.8, 5343.81, 5341.22, 5353.0, 5409.13];
let previous_close = vec![5283.4, 5291.34, 5354.03, 5352.96, 5346.99, 5360.79, 5375.32];
let signal_period: usize = 5;
assert_eq!(
vec![
(-0.10791117993487043, 0.0),
(-0.14531440328757447, 0.0),
(0.6289858092169471, 0.0)
],
bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::SimpleMovingMode
)
.unwrap()
);
}
#[test]
fn bulk_positivity_indicator_error_length() {
let open = vec![5278.24, 5314.48, 5343.81, 5341.22, 5353.0, 5409.13];
let previous_close = vec![5283.4, 5291.34, 5354.03, 5352.96, 5346.99, 5360.79, 5375.32];
let signal_period: usize = 5;
assert!(bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::SimpleMovingMode,
)
.is_err());
}
#[test]
fn bulk_positivity_indicator_error_empty() {
let open = Vec::new();
let previous_close = Vec::new();
let signal_period: usize = 5;
assert!(bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::SimpleMovingMode,
)
.is_err());
}
#[test]
fn bulk_positivity_indicator_error_period() {
let open = vec![5278.24, 5314.48, 5357.8, 5343.81, 5341.22, 5353.0, 5409.13];
let previous_close = vec![5283.4, 5291.34, 5354.03, 5352.96, 5346.99, 5360.79, 5375.32];
let signal_period: usize = 50;
assert!(bulk::positivity_indicator(
&open,
&previous_close,
signal_period,
crate::ConstantModelType::SimpleMovingMode,
)
.is_err());
}
}