Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
m5l14i11 committed Jul 14, 2023
1 parent 77bd76b commit e05b7d7
Show file tree
Hide file tree
Showing 21 changed files with 247 additions and 384 deletions.
3 changes: 0 additions & 3 deletions ta_lib/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 53 additions & 13 deletions ta_lib/core/src/math.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
use crate::series::Series;

impl Series<f64> {
fn ew<F>(&self, period: usize, alpha_fn: F) -> Self
where
F: Fn(usize) -> f64,
{
let len = self.len();

let alpha = alpha_fn(period);

let one_minus_alpha = 1.0 - alpha;

let mut ew = Series::empty(len);

if let Some(val) = self[0] {
ew[0] = Some(val);
}

for i in 1..len {
let ew_prev = ew[i - 1].unwrap_or(0.0);
if let Some(val) = self[i] {
ew[i] = Some(alpha * val + one_minus_alpha * ew_prev);
}
}

ew
}

pub fn max(&self, scalar: f64) -> Self {
self.fmap(|val| val.map(|v| v.max(scalar)).or(Some(scalar)))
}
Expand All @@ -21,23 +47,37 @@ impl Series<f64> {
}

pub fn sum(&self, period: usize) -> Self {
self.window(period, |window, _, _| window.iter().sum())
self.sliding_map(period, |window, _, _| {
Some(window.iter().filter_map(|v| *v).sum())
})
}

pub fn mean(&self, period: usize) -> Self {
self.window(period, |window, size, _| {
window.iter().sum::<f64>() / size as f64
pub fn ma(&self, period: usize) -> Self {
self.sliding_map(period, |window, size, _| {
Some(window.iter().filter_map(|v| *v).sum::<f64>() / size as f64)
})
}

pub fn std(&self, period: usize) -> Self {
let mean = self.mean(period);
pub fn ema(&self, period: usize) -> Self {
self.ew(period, |period| 2.0 / (period as f64 + 1.0))
}

pub fn smma(&self, period: usize) -> Self {
self.ew(period, |period| 1.0 / (period as f64))
}

self.window(period, |window, size, i| {
let mean_val = mean[i].unwrap_or(0.0);
let variance =
window.iter().map(|&v| (v - mean_val).powi(2)).sum::<f64>() / size as f64;
variance.sqrt()
pub fn std(&self, period: usize) -> Self {
let ma = self.ma(period);

self.sliding_map(period, |window, size, i| {
let ma_val = ma[i].unwrap_or(0.0);
let variance = window
.iter()
.filter_map(|v| *v)
.map(|v| (v - ma_val).powi(2))
.sum::<f64>()
/ size as f64;
Some(variance.sqrt())
})
}
}
Expand Down Expand Up @@ -135,12 +175,12 @@ mod tests {
}

#[test]
fn test_mean() {
fn test_ma() {
let source = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let expected = vec![Some(1.0), Some(1.5), Some(2.0), Some(3.0), Some(4.0)];
let series = Series::from(&source);

let result = series.mean(3);
let result = series.ma(3);

assert_eq!(result, expected);
}
Expand Down
55 changes: 27 additions & 28 deletions ta_lib/core/src/series.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ impl<T: Clone> Series<T> {
Series { data }
}

pub fn sliding_map<U, F>(&self, period: usize, mut f: F) -> Series<U>
where
F: FnMut(&[Option<T>], usize, usize) -> Option<U>,
U: Clone,
{
let len = self.len();
let mut result = Series::<U>::empty(len);
let mut window = vec![None; period];
let mut pos = 0;

for i in 0..len {
window[pos] = self[i].clone();

let size = (i + 1).min(period);

result[i] = f(&window[0..size], size, i);

pos = (pos + 1) % period;
}

result
}

pub fn empty(length: usize) -> Self {
Self {
data: vec![None; length],
Expand Down Expand Up @@ -66,37 +89,13 @@ impl<T> IndexMut<usize> for Series<T> {
}

impl Series<f64> {
pub fn window<F>(&self, period: usize, f: F) -> Self
where
F: Fn(&[f64], usize, usize) -> f64,
{
let len = self.len();
let mut result = Self::empty(len);
let mut window = vec![0.0; period];
let mut pos = 0;

for i in 0..len {
if let Some(value) = self[i] {
window[pos] = value;

let size = (i + 1).min(period);

result[i] = Some(f(&window[0..size], size, i));

pos = (pos + 1) % period;
}
}

result
}

fn extreme_value<F>(&self, period: usize, comparison: F) -> Self
where
F: Fn(&f64, &f64) -> bool,
F: Fn(Option<&f64>, Option<&f64>) -> bool,
{
self.window(period, |window, _, _| {
window.iter().fold(f64::NAN, |acc, x| {
if acc.is_nan() || comparison(&x, &acc) {
self.sliding_map(period, |window, _, _| {
window.iter().fold(None, |acc, x| {
if acc.is_none() || comparison(x.as_ref(), acc.as_ref()) {
*x
} else {
acc
Expand Down
1 change: 0 additions & 1 deletion ta_lib/momentum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ edition = "2021"

[dependencies]
core = { path = "../core" }
overlap = { path = "../overlap" }
price = { path = "../price" }
utils = { path = "../utils" }
12 changes: 6 additions & 6 deletions ta_lib/momentum/src/aosc.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use core::series::Series;

pub fn aosc(hl2: &[f64], short_period: usize, long_period: usize) -> Series<f64> {
pub fn aosc(hl2: &[f64], short_period: usize, long_period: usize) -> Vec<f64> {
let hl2 = Series::from(hl2);

let ao_short = hl2.mean(short_period);
let ao_long = hl2.mean(long_period);
let ao_short = hl2.ma(short_period);
let ao_long = hl2.ma(long_period);

let aosc = ao_short - &ao_long;

aosc
aosc.into()
}

#[cfg(test)]
Expand All @@ -23,10 +23,10 @@ mod tests {
let hl2 = median_price(&high, &low);
let short_period = 2;
let long_period = 4;
let expected_result = vec![Some(0.0), Some(0.0), Some(0.5), Some(1.0), Some(1.0)];
let expected = vec![0.0, 0.0, 0.5, 1.0, 1.0];

let result = aosc(&hl2, short_period, long_period);

assert_eq!(result, expected_result);
assert_eq!(result, expected);
}
}
99 changes: 32 additions & 67 deletions ta_lib/momentum/src/macd.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
use core::series::Series;
use overlap::ema::ema;

pub fn macd(
source: &[f64],
fast_period: usize,
slow_period: usize,
signal_period: usize,
) -> (Series<f64>, Series<f64>, Series<f64>) {
let ema_fast = ema(source, fast_period);
let ema_slow = ema(source, slow_period);
) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
let source = Series::from(source);

let ema_fast = source.ema(fast_period);
let ema_slow = source.ema(slow_period);

let macd_line = ema_fast - &ema_slow;
let macd_line_vec: Vec<f64> = macd_line.clone().into();

let signal_line = ema(&macd_line_vec, signal_period);
let signal_line = macd_line.ema(signal_period);

let histogram = &macd_line - &signal_line;

(macd_line, signal_line, histogram)
(macd_line.into(), signal_line.into(), histogram.into())
}

#[cfg(test)]
Expand All @@ -32,78 +32,43 @@ mod tests {
let signal_period = 4;
let epsilon = 0.001;
let expected_macd_line = vec![
Some(0.0),
Some(0.33333),
Some(0.72222),
Some(1.0648),
Some(1.334877),
Some(1.035751),
Some(0.596751),
Some(0.184292),
Some(-0.150576),
Some(-0.403769),
0.0, 0.33333, 0.72222, 1.0648, 1.334877, 1.035751, 0.596751, 0.184292, -0.150576,
-0.403769,
];
let expected_signal_line = vec![
Some(0.0),
Some(0.13333),
Some(0.36888),
Some(0.6472),
Some(0.9223),
Some(0.9676),
Some(0.8193),
Some(0.5653),
Some(0.2789),
Some(0.0058),
0.0, 0.13333, 0.36888, 0.6472, 0.9223, 0.9676, 0.8193, 0.5653, 0.2789, 0.0058,
];
let expected_histogram = vec![
Some(0.0),
Some(0.1999),
Some(0.3533),
Some(0.4175),
Some(0.4125),
Some(0.068),
Some(-0.2222),
Some(-0.381),
Some(-0.4295),
Some(-0.4096),
0.0, 0.1999, 0.3533, 0.4175, 0.4125, 0.068, -0.2222, -0.381, -0.4295, -0.4096,
];

let (macd_line, signal_line, histogram) =
macd(&source, fast_period, slow_period, signal_period);

for i in 0..source.len() {
match (macd_line[i], expected_macd_line[i]) {
(Some(a), Some(b)) => {
assert!((a - b).abs() < epsilon, "at position {}: {} != {}", i, a, b)
}
(None, None) => {}
_ => panic!(
"at position {}: {:?} != {:?}",
i, macd_line[i], expected_macd_line[i]
),
}
assert!(
(macd_line[i] - expected_macd_line[i]).abs() < epsilon,
"at position {}: {} != {}",
i,
macd_line[i],
expected_macd_line[i]
);

match (signal_line[i], expected_signal_line[i]) {
(Some(a), Some(b)) => {
assert!((a - b).abs() < epsilon, "at position {}: {} != {}", i, a, b)
}
(None, None) => {}
_ => panic!(
"at position {}: {:?} != {:?}",
i, signal_line[i], expected_signal_line[i]
),
}
assert!(
(signal_line[i] - expected_signal_line[i]).abs() < epsilon,
"at position {}: {} != {}",
i,
signal_line[i],
expected_signal_line[i]
);

match (histogram[i], expected_histogram[i]) {
(Some(a), Some(b)) => {
assert!((a - b).abs() < epsilon, "at position {}: {} != {}", i, a, b)
}
(None, None) => {}
_ => panic!(
"at position {}: {:?} != {:?}",
i, histogram[i], expected_histogram[i]
),
}
assert!(
(histogram[i] - expected_histogram[i]).abs() < epsilon,
"at position {}: {} != {}",
i,
histogram[i],
expected_histogram[i]
);
}
}
}
Loading

0 comments on commit e05b7d7

Please sign in to comment.