forked from greyblake/ta-rs
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added DI-/DI+, DM-/DM+, DX and ADX. Needs verification & tests and so…
…me docs.
- Loading branch information
1 parent
47c0213
commit 55ea749
Showing
8 changed files
with
960 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
use crate::{errors::Result, indicators::DirectionalMovementIndex, High, Next, Period, Reset}; | ||
#[cfg(feature = "serde")] | ||
use serde::{Deserialize, Serialize}; | ||
use std::fmt; | ||
|
||
/// Average Directional Index (ADX) | ||
/// | ||
/// A direction indicator, originally developed by J. Welles Wilder. The | ||
/// average directional movement index is an N-sample smoothed moving average of | ||
/// a combination of positive & negative directional indicator (DI) values. | ||
/// | ||
/// # Parameters | ||
/// | ||
/// * `period` - Smoothing period (samples) of SDM and ATR (nonzero integer) | ||
/// used in the DIs. | ||
/// | ||
/// # Links | ||
/// | ||
/// * [Averager directional movement index, Wikipedia](https://en.wikipedia.org/wiki/Average_directional_movement_index) | ||
#[doc(alias = "ADX")] | ||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] | ||
#[derive(Debug, Clone)] | ||
pub struct AverageDirectionalIndex { | ||
previous: f64, | ||
dx: DirectionalMovementIndex, | ||
} | ||
|
||
impl AverageDirectionalIndex { | ||
pub fn new(period: usize) -> Result<Self> { | ||
Ok(Self { | ||
previous: 0.0, | ||
dx: DirectionalMovementIndex::new(period)?, | ||
}) | ||
} | ||
} | ||
|
||
impl Period for AverageDirectionalIndex { | ||
fn period(&self) -> usize { | ||
self.dx.period() | ||
} | ||
} | ||
|
||
impl Next<f64> for AverageDirectionalIndex { | ||
type Output = f64; | ||
|
||
fn next(&mut self, input: f64) -> Self::Output { | ||
let current = self.dx.next(input); | ||
let adx = (self.previous * (self.dx.period() - 1) as f64 + current) / self.dx.period() as f64; | ||
self.previous = current; | ||
|
||
adx | ||
} | ||
} | ||
|
||
impl<T: High> Next<&T> for AverageDirectionalIndex { | ||
type Output = f64; | ||
|
||
fn next(&mut self, input: &T) -> Self::Output { | ||
self.next(input.high()) | ||
} | ||
} | ||
|
||
impl Reset for AverageDirectionalIndex { | ||
fn reset(&mut self) { | ||
self.previous = 0.0; | ||
self.dx.reset() | ||
} | ||
} | ||
|
||
impl Default for AverageDirectionalIndex { | ||
fn default() -> Self { | ||
Self::new(14).unwrap() | ||
} | ||
} | ||
|
||
impl fmt::Display for AverageDirectionalIndex { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "ADX({})", self.period()) | ||
} | ||
} | ||
|
||
// TODO: implement AverageDirectionalIndexDetailed where next() returns a tuple | ||
// of (DI-, ADX, DI+) | ||
|
||
/* | ||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::test_helper::*; | ||
test_indicator!(ExponentialMovingAverage); | ||
#[test] | ||
fn test_new() { | ||
assert!(ExponentialMovingAverage::new(0).is_err()); | ||
assert!(ExponentialMovingAverage::new(1).is_ok()); | ||
} | ||
#[test] | ||
fn test_next() { | ||
let mut ema = ExponentialMovingAverage::new(3).unwrap(); | ||
assert_eq!(ema.next(2.0), 2.0); | ||
assert_eq!(ema.next(5.0), 3.5); | ||
assert_eq!(ema.next(1.0), 2.25); | ||
assert_eq!(ema.next(6.25), 4.25); | ||
let mut ema = ExponentialMovingAverage::new(3).unwrap(); | ||
let bar1 = Bar::new().close(2); | ||
let bar2 = Bar::new().close(5); | ||
assert_eq!(ema.next(&bar1), 2.0); | ||
assert_eq!(ema.next(&bar2), 3.5); | ||
} | ||
#[test] | ||
fn test_reset() { | ||
let mut ema = ExponentialMovingAverage::new(5).unwrap(); | ||
assert_eq!(ema.next(4.0), 4.0); | ||
ema.next(10.0); | ||
ema.next(15.0); | ||
ema.next(20.0); | ||
assert_ne!(ema.next(4.0), 4.0); | ||
ema.reset(); | ||
assert_eq!(ema.next(4.0), 4.0); | ||
} | ||
#[test] | ||
fn test_default() { | ||
ExponentialMovingAverage::default(); | ||
} | ||
#[test] | ||
fn test_display() { | ||
let ema = ExponentialMovingAverage::new(7).unwrap(); | ||
assert_eq!(format!("{}", ema), "EMA(7)"); | ||
} | ||
} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
use crate::{ | ||
errors::Result, | ||
indicators::{ | ||
AverageTrueRange, SmoothedNegativeDirectionalMovement, SmoothedPositiveDirectionalMovement, | ||
}, | ||
High, Next, Period, Reset, | ||
}; | ||
#[cfg(feature = "serde")] | ||
use serde::{Deserialize, Serialize}; | ||
use std::fmt; | ||
|
||
/// Negative Directional Indicator (DI-). | ||
/// | ||
/// A downtrend indicator, originally developed by J. Welles Wilder. The | ||
/// negative directional indicator is an N-sample smoothed moving average of the | ||
/// smoothed negative directional movement (SDM-) values normalized by the | ||
/// average true range (ATR). | ||
/// | ||
/// # Formula | ||
/// | ||
/// DI- = SDM-<sub>t</sub> / ATR(period)<sub>t</sub> | ||
/// | ||
/// Where: | ||
/// | ||
/// * _SDM-(period)<sub>t</sub>_ – [Smoothed negative directional | ||
/// movement](crate::indicators::SmoothedNegativeDirectionalMovement) over | ||
/// _period_ at time _t_. | ||
/// * _ATR(period)<sub>t</sub>_ – [Averag true | ||
/// range](crate::indicators::AverageTrueRange) over _period_ at time _t_. | ||
/// | ||
/// # Parameters | ||
/// | ||
/// * `period` - Smoothing period (number of samples) of SDM- and ATR (positive | ||
/// integer). | ||
/// | ||
/// # Links | ||
/// | ||
/// * [Average directional movement index, Wikipedia](https://en.wikipedia.org/wiki/Average_directional_movement_index) | ||
#[doc(alias = "DI-")] | ||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] | ||
#[derive(Debug, Clone)] | ||
pub struct NegativeDirectionalIndicator { | ||
sndm: SmoothedNegativeDirectionalMovement, | ||
atr: AverageTrueRange, | ||
} | ||
|
||
impl NegativeDirectionalIndicator { | ||
pub fn new(period: usize) -> Result<Self> { | ||
Ok(Self { | ||
sndm: SmoothedNegativeDirectionalMovement::new(period)?, | ||
atr: AverageTrueRange::new(period)?, | ||
}) | ||
} | ||
} | ||
|
||
impl Period for NegativeDirectionalIndicator { | ||
fn period(&self) -> usize { | ||
self.sndm.period() | ||
} | ||
} | ||
|
||
impl Next<f64> for NegativeDirectionalIndicator { | ||
type Output = f64; | ||
|
||
fn next(&mut self, input: f64) -> Self::Output { | ||
100.0 * (self.sndm.next(input) / self.atr.next(input)) | ||
} | ||
} | ||
|
||
impl<T: High> Next<&T> for NegativeDirectionalIndicator { | ||
type Output = f64; | ||
|
||
fn next(&mut self, input: &T) -> Self::Output { | ||
self.next(input.high()) | ||
} | ||
} | ||
|
||
impl Reset for NegativeDirectionalIndicator { | ||
fn reset(&mut self) { | ||
self.sndm.reset(); | ||
self.atr.reset(); | ||
} | ||
} | ||
|
||
impl Default for NegativeDirectionalIndicator { | ||
fn default() -> Self { | ||
Self::new(14).unwrap() | ||
} | ||
} | ||
|
||
impl fmt::Display for NegativeDirectionalIndicator { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "DI-({})", self.sndm.period()) | ||
} | ||
} | ||
|
||
/// Positive Directional Indicator (DI+). | ||
/// | ||
/// An uptrend indicator, originally developed by J. Welles | ||
/// Wilder. The positive directional indicator is an N-sample smoothed moving | ||
/// average of the smoothed positive directional movement (SDM+) values | ||
/// normalized by the average true range (ATR). | ||
/// | ||
/// # Formula | ||
/// | ||
/// DI+(period)<sub>t</sub> = SDM+(period)<sub>t</sub> / ATR(period)<sub>t</sub> | ||
/// | ||
/// Where: | ||
/// | ||
/// * _SDM+(period)<sub>t</sub>_ – [Smoothed positive directional | ||
/// movement](crate::indicators::SmoothedPositiveDirectionalMovement) over | ||
/// _period_ at time _t_. | ||
/// * _ATR(period)<sub>t</sub>_ – [Averag true | ||
/// range](crate::indicators::AverageTrueRange) over _period_ at time _t_. | ||
/// | ||
/// # Parameters | ||
/// | ||
/// * `period` - Smoothing period (number of samples) of SDM+ and ATR (positive | ||
/// integer). | ||
/// | ||
/// # Links | ||
/// | ||
/// * [Average directional movement index, Wikipedia](https://en.wikipedia.org/wiki/Average_directional_movement_index) | ||
#[doc(alias = "DI+")] | ||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] | ||
#[derive(Debug, Clone)] | ||
pub struct PositiveDirectionalIndicator { | ||
spdm: SmoothedPositiveDirectionalMovement, | ||
atr: AverageTrueRange, | ||
} | ||
|
||
impl PositiveDirectionalIndicator { | ||
pub fn new(period: usize) -> Result<Self> { | ||
Ok(Self { | ||
spdm: SmoothedPositiveDirectionalMovement::new(period)?, | ||
atr: AverageTrueRange::new(period)?, | ||
}) | ||
} | ||
} | ||
|
||
impl Period for PositiveDirectionalIndicator { | ||
fn period(&self) -> usize { | ||
self.spdm.period() | ||
} | ||
} | ||
|
||
impl Next<f64> for PositiveDirectionalIndicator { | ||
type Output = f64; | ||
|
||
fn next(&mut self, input: f64) -> Self::Output { | ||
100.0 * (self.spdm.next(input) / self.atr.next(input)) | ||
} | ||
} | ||
|
||
impl<T: High> Next<&T> for PositiveDirectionalIndicator { | ||
type Output = f64; | ||
|
||
fn next(&mut self, input: &T) -> Self::Output { | ||
self.next(input.high()) | ||
} | ||
} | ||
|
||
impl Reset for PositiveDirectionalIndicator { | ||
fn reset(&mut self) { | ||
self.spdm.reset(); | ||
self.atr.reset(); | ||
} | ||
} | ||
|
||
impl Default for PositiveDirectionalIndicator { | ||
fn default() -> Self { | ||
Self::new(14).unwrap() | ||
} | ||
} | ||
|
||
impl fmt::Display for PositiveDirectionalIndicator { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "DI+({})", self.spdm.period()) | ||
} | ||
} | ||
|
||
/* | ||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::test_helper::*; | ||
test_indicator!(AverageTrueRange); | ||
#[test] | ||
fn test_new() { | ||
assert!(AverageTrueRange::new(0).is_err()); | ||
assert!(AverageTrueRange::new(1).is_ok()); | ||
} | ||
#[test] | ||
fn test_next() { | ||
let mut atr = AverageTrueRange::new(3).unwrap(); | ||
let bar1 = Bar::new().high(10).low(7.5).close(9); | ||
let bar2 = Bar::new().high(11).low(9).close(9.5); | ||
let bar3 = Bar::new().high(9).low(5).close(8); | ||
assert_eq!(atr.next(&bar1), 2.5); | ||
assert_eq!(atr.next(&bar2), 2.25); | ||
assert_eq!(atr.next(&bar3), 3.375); | ||
} | ||
#[test] | ||
fn test_reset() { | ||
let mut atr = AverageTrueRange::new(9).unwrap(); | ||
let bar1 = Bar::new().high(10).low(7.5).close(9); | ||
let bar2 = Bar::new().high(11).low(9).close(9.5); | ||
atr.next(&bar1); | ||
atr.next(&bar2); | ||
atr.reset(); | ||
let bar3 = Bar::new().high(60).low(15).close(51); | ||
assert_eq!(atr.next(&bar3), 45.0); | ||
} | ||
#[test] | ||
fn test_default() { | ||
AverageTrueRange::default(); | ||
} | ||
#[test] | ||
fn test_display() { | ||
let indicator = AverageTrueRange::new(8).unwrap(); | ||
assert_eq!(format!("{}", indicator), "ATR(8)"); | ||
} | ||
}*/ |
Oops, something went wrong.