use analysis::{AnalysisError, Result};
pub fn relative_strength_index(gain: f64, loss: f64) -> Result<f64> {
if gain < 0. {
return Err(AnalysisError::GainLessThanZero);
}
if loss < 0. {
return Err(AnalysisError::LossLessThanZero);
}
Ok(match loss {
0. => 100.,
_ => 100. - 100. / (1. + gain / loss),
})
}
pub fn stochastic_oscillator(close: f64, high: f64, low: f64) -> Result<f64> {
if high < low {
return Err(AnalysisError::HighLessThanLow);
}
if close > high {
return Err(AnalysisError::CloseGreaterThanHigh);
}
if close < low {
return Err(AnalysisError::CloseLessThanLow);
}
Ok(match high == low {
true => 50.,
false => 100. * (close - low) / (high - low),
})
}
pub fn williams_percent_r(close: f64, high: f64, low: f64) -> Result<f64> {
if high < low {
return Err(AnalysisError::HighLessThanLow);
}
if close > high {
return Err(AnalysisError::CloseGreaterThanHigh);
}
if close < low {
return Err(AnalysisError::CloseLessThanLow);
}
Ok(match high == low {
true => -50.,
false => -100. * (high - close) / (high - low),
})
}
#[cfg(test)]
mod tests {
extern crate math;
use analysis::{AnalysisError, Result};
use self::math::round::half_up;
use std::error::Error;
#[test]
fn relative_strength_index() {
let tests: [(f64, f64, Result<f64>); 24] = [
(0.238386, 0.099593, Ok(70.532785)),
(0.221358, 0.112422, Ok(66.318533)),
(0.207690, 0.104392, Ok(66.549817)),
(0.219912, 0.096935, Ok(69.406370)),
(0.204204, 0.103540, Ok(66.355152)),
(0.189618, 0.137451, Ok(57.974923)),
(0.216667, 0.127633, Ok(62.929712)),
(0.204040, 0.118517, Ok(63.257037)),
(0.189466, 0.148508, Ok(56.059342)),
(0.228633, 0.137901, Ok(62.377024)),
(0.212302, 0.175765, Ok(54.707563)),
(0.197137, 0.193832, Ok(50.422668)),
(0.183056, 0.274701, Ok(39.989776)),
(0.180659, 0.255079, Ok(41.460465)),
(0.170598, 0.236859, Ok(41.868958)),
(0.183348, 0.219941, Ok(45.463179)),
(0.170252, 0.286138, Ok(37.304060)),
(0.158091, 0.319821, Ok(33.079521)),
(0.180270, 0.296977, Ok(37.772893)),
(1., 0., Ok(100.)),
(0., 1., Ok(0.)),
(1., 1., Ok(50.)),
(-1., 0., Err(AnalysisError::GainLessThanZero)),
(0., -1., Err(AnalysisError::LossLessThanZero)),
];
for test in &tests {
let result = super::relative_strength_index(test.0, test.1);
match (result, test.2.as_ref()) {
(Ok(val), Ok(exp))
=> assert_eq!(half_up(val, 6), *exp),
(Err(err), Err(exp))
=> assert_eq!(err.description(), exp.description()),
_ => panic!("return type mismatch"),
}
}
}
#[test]
fn stochastic_oscillator() {
let tests: [(f64, f64, f64, Result<f64>); 23] = [
(127.2876, 128.4317, 124.5615, Ok(70.438220)),
(127.1781, 128.4317, 124.5615, Ok(67.608909)),
(128.0138, 128.4317, 124.5615, Ok(89.202108)),
(127.1085, 128.4317, 124.5615, Ok(65.810552)),
(127.7253, 128.4317, 124.5615, Ok(81.747713)),
(127.0587, 128.4317, 124.5615, Ok(64.523797)),
(127.3273, 128.2725, 124.5615, Ok(74.529776)),
(128.7103, 128.7700, 124.5615, Ok(98.581442)),
(127.8745, 129.2873, 124.5615, Ok(70.104533)),
(128.5809, 130.0633, 124.5615, Ok(73.056091)),
(128.6008, 130.0633, 124.5615, Ok(73.417791)),
(127.9342, 130.0633, 124.5715, Ok(61.231290)),
(128.1133, 130.0633, 125.0689, Ok(60.956271)),
(127.5960, 130.0633, 125.9245, Ok(40.386102)),
(127.5960, 130.0633, 125.9245, Ok(40.386102)),
(128.6904, 130.0633, 125.9245, Ok(66.828549)),
(128.2725, 130.0633, 125.9245, Ok(56.731420)),
(100., 100., 100., Ok(50.)),
(100., 100., 0., Ok(100.)),
(0., 100., 0., Ok(0.)),
(100., 0., 100., Err(AnalysisError::HighLessThanLow)),
(100., 0., 0., Err(AnalysisError::CloseGreaterThanHigh)),
(0., 100., 100., Err(AnalysisError::CloseLessThanLow)),
];
for test in &tests {
let result = super::stochastic_oscillator(test.0, test.1, test.2);
match (result, test.3.as_ref()) {
(Ok(val), Ok(exp))
=> assert_eq!(half_up(val, 6), *exp),
(Err(err), Err(exp))
=> assert_eq!(err.description(), exp.description()),
_ => panic!("return type mismatch"),
}
}
}
#[test]
fn williams_percent_r() {
let tests: [(f64, f64, f64, Result<f64>); 23] = [
(127.2876, 128.4317, 124.5615, Ok(-29.561780)),
(127.1781, 128.4317, 124.5615, Ok(-32.391091)),
(128.0138, 128.4317, 124.5615, Ok(-10.797891)),
(127.1085, 128.4317, 124.5615, Ok(-34.189447)),
(127.7253, 128.4317, 124.5615, Ok(-18.252287)),
(127.0587, 128.4317, 124.5615, Ok(-35.476203)),
(127.3273, 128.2725, 124.5615, Ok(-25.470224)),
(128.7103, 128.7700, 124.5615, Ok(-1.418558)),
(127.8745, 129.2873, 124.5615, Ok(-29.895467)),
(128.5809, 130.0633, 124.5615, Ok(-26.943909)),
(128.6008, 130.0633, 124.5615, Ok(-26.582209)),
(127.9342, 130.0633, 124.5715, Ok(-38.768710)),
(128.1133, 130.0633, 125.0689, Ok(-39.043729)),
(127.5960, 130.0633, 125.9245, Ok(-59.613898)),
(127.5960, 130.0633, 125.9245, Ok(-59.613898)),
(128.6904, 130.0633, 125.9245, Ok(-33.171451)),
(128.2725, 130.0633, 125.9245, Ok(-43.268580)),
(100., 100., 100., Ok(-50.)),
(100., 100., 0., Ok(0.)),
(0., 100., 0., Ok(-100.)),
(100., 0., 100., Err(AnalysisError::HighLessThanLow)),
(100., 0., 0., Err(AnalysisError::CloseGreaterThanHigh)),
(0., 100., 100., Err(AnalysisError::CloseLessThanLow)),
];
for test in &tests {
let result = super::williams_percent_r(test.0, test.1, test.2);
match (result, test.3.as_ref()) {
(Ok(val), Ok(exp))
=> assert_eq!(half_up(val, 6), *exp),
(Err(err), Err(exp))
=> assert_eq!(err.description(), exp.description()),
_ => panic!("return type mismatch"),
}
}
}
}