Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/indicators/exponential_moving_average.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use crate::models::candlestick::Candlestick;

pub struct ExponentialMovingAverage {
period: usize,
count: usize,
k: f64,
current: f64,
is_new: bool,
results: Box<[f64]>,
}

#[allow(unused)]
Expand All @@ -17,7 +19,9 @@ impl ExponentialMovingAverage {
period,
k: weight,
current: 0.0,
count: 0,
is_new: true,
results: vec![0.0; period].into_boxed_slice(),
}
}

Expand All @@ -26,7 +30,9 @@ impl ExponentialMovingAverage {
period,
k: 2.0 / (period + 1) as f64,
current: 0.0,
count: 0,
is_new: true,
results: vec![0.0; period].into_boxed_slice(),
}
}

Expand All @@ -40,20 +46,69 @@ impl ExponentialMovingAverage {

pub fn reset(&mut self) {
self.current = 0.0;
self.count = 0;
self.is_new = true;

for i in 0..self.period {
self.results[i] = 0.0;
}
}

pub fn turns_up(&self) -> bool {
if self.period < 3 {
unimplemented!();
}

assert!(self.count >= 3, "The ExponentialMovingAverage turns_up method needs to have at least 3 candle period.");

let last_but_one_value = self.results[2];
let last_value = self.results[1];
let current_value = self.results[0];

if self.count >= 3 && last_but_one_value >= last_value && current_value > last_value {
return true;
}
false
}

pub fn turns_down(&self) -> bool {
if self.period < 3 {
unimplemented!();
}

assert!(self.count >= 3, "The ExponentialMovingAverage turns_down method needs to have at least 3 candle period");

let last_but_one_value = self.results[2];
let last_value = self.results[1];
let current_value = self.results[0];

if self.count >= 3 && last_but_one_value <= last_value && current_value < last_value {
return true;
}
false
}

}

impl Next<f64> for ExponentialMovingAverage {
type Output = f64;

fn next(&mut self, close_value: f64) -> Self::Output {

if self.count < self.period {
self.count += 1;
}

if self.is_new {
self.is_new = false;
self.current = close_value;
} else {
self.current = self.k * close_value + (1.0 - self.k) * self.current;
}

self.results.rotate_right(1);
self.results[0] = self.current;

self.current
}
}
Expand Down
122 changes: 120 additions & 2 deletions src/indicators/simple_moving_average.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,72 @@ impl SimpleMovingAverage {
})
}

pub fn trending_up(&mut self) -> bool {
if self.period < 3 {
unimplemented!();
}
assert!(self.count >= 3, "The SimpleMovingAverage needs to have at least 3 valid values for function trending_up().");

let last_but_one_value = self.results[2];
let last_value = self.results[1];
let current_value = self.results[0];

if last_but_one_value < last_value && last_value < current_value {
return true;
}
false
}

pub fn trending_down(&mut self) -> bool {
if self.period < 3 {
unimplemented!();
}
assert!(self.count >= 3, "The SimpleMovingAverage needs to have at least 3 valid values for function trending_down().");

let last_but_one_value = self.results[2];
let last_value = self.results[1];
let current_value = self.results[0];

if last_but_one_value > last_value && last_value > current_value {
return true;
}
false
}

pub fn turns_up(&self) -> bool {
if self.period < 3 {
unimplemented!();
}

assert!(self.count >= 3, "The SimpleMovingAverage needs to have at least 3 valid values for function turns_up().");

let last_but_one_value = self.results[2];
let last_value = self.results[1];
let current_value = self.results[0];

if self.count >= 3 && last_but_one_value >= last_value && current_value > last_value {
return true;
}
false
}

pub fn turns_down(&self) -> bool {
if self.period < 3 {
unimplemented!();
}

assert!(self.count >= 3, "The SimpleMovingAverage needs to have at least 3 valid values for function turns_down().");

let last_but_one_value = self.results[2];
let last_value = self.results[1];
let current_value = self.results[0];

if self.count >= 3 && last_but_one_value <= last_value && current_value < last_value {
return true;
}
false
}

pub fn value(&self) -> f64 {
self.current_value
}
Expand Down Expand Up @@ -83,6 +149,7 @@ impl SimpleMovingAverage {
self.current_value = 0.0;
for i in 0..self.period {
self.deque[i] = 0.0;
self.results[i] = 0.0;
}
}
}
Expand All @@ -99,7 +166,6 @@ impl Next<f64> for SimpleMovingAverage {
fn next(&mut self, input: f64) -> Self::Output {
let old_val = self.deque[self.index];
self.deque[self.index] = input;
let initial_index = self.index;


self.index = if self.index + 1 < self.period {
Expand All @@ -114,7 +180,10 @@ impl Next<f64> for SimpleMovingAverage {

self.sum = self.sum - old_val + input;
self.current_value = self.sum / (self.count as f64);
self.results[initial_index] = self.current_value;

self.results.rotate_right(1);
self.results[0] = self.current_value;

self.current_value
}
}
Expand Down Expand Up @@ -214,6 +283,55 @@ mod tests {
assert_eq!(sma.max_value_on_period(), 31.0);
}

#[test]
fn test_turn_up(){
let mut sma = SimpleMovingAverage::new(3).unwrap();
sma.next(3.0);
sma.next(1.0);
sma.next(5.0);
assert_eq!(sma.turns_up(), true);

sma.next(6.0);
assert_eq!(sma.turns_up(), false);
//
sma.next(7.0);
assert_eq!(sma.turns_up(), false);

sma.next(1.0);
assert_eq!(sma.turns_up(), false);

sma.next(6.0);
assert_eq!(sma.turns_up(), false);

sma.next(10.0);
assert_eq!(sma.turns_up(), true);
}

#[test]
fn test_turn_down(){
// Values of result [ 3, 2, 3, 4, 5, 4.66, 4.66, 2,66 ]
let mut sma = SimpleMovingAverage::new(3).unwrap();
sma.next(3.0);
sma.next(1.0);
sma.next(5.0);
assert_eq!(sma.turns_down(), false);

sma.next(6.0);
assert_eq!(sma.turns_down(), false);
//
sma.next(7.0);
assert_eq!(sma.turns_down(), false);

sma.next(1.0);
assert_eq!(sma.turns_down(), true);

sma.next(6.0);
assert_eq!(sma.turns_down(), false);

sma.next(1.0);
assert_eq!(sma.turns_down(), true);
}

fn new_candle(close: f64) -> Candlestick {
Candlestick{
open: 0.0,
Expand Down
22 changes: 10 additions & 12 deletions src/indicators/volume_weighted_average_price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use ta::{Close, High, Low, Next, Volume};
pub struct VolumeWeightedAveragePrice {
index: usize,
current_value: f64,
cumulative_typical_price: f64,
cumulative_price_volume: f64,
cumulative_volume: f64
}

Expand All @@ -26,7 +26,7 @@ impl VolumeWeightedAveragePrice {
Ok(Self {
index: 0,
current_value: 0.0,
cumulative_typical_price: 0.0,
cumulative_price_volume: 0.0,
cumulative_volume: 0.0
})
}
Expand All @@ -42,7 +42,7 @@ impl VolumeWeightedAveragePrice {
pub fn reset(&mut self) {
self.index = 0;
self.current_value = 0.0;
self.cumulative_typical_price = 0.0;
self.cumulative_price_volume = 0.0;
self.cumulative_volume = 0.0;
}
}
Expand All @@ -51,11 +51,11 @@ impl<T: High + Low + Close + Volume> Next<&T> for VolumeWeightedAveragePrice {
type Output = f64;

fn next(&mut self, input: &T) -> Self::Output {
let typical_price = (input.high() + input.low() + input.close()) / 3.0;
self.cumulative_typical_price += typical_price;
let typical_price_volume = ((input.high() + input.low() + input.close()) / 3.0) * input.volume();
self.cumulative_price_volume += typical_price_volume;
self.cumulative_volume += input.volume();

self.current_value = self.cumulative_typical_price * input.volume() / self.cumulative_volume;
self.current_value = self.cumulative_price_volume / self.cumulative_volume;

self.index += 1;
self.current_value
Expand All @@ -78,7 +78,7 @@ mod tests {
fn test_new() {
let vwap = VolumeWeightedAveragePrice::new().unwrap();
assert_eq!(vwap.index, 0);
assert_eq!(vwap.cumulative_typical_price, 0.0);
assert_eq!(vwap.cumulative_price_volume, 0.0);
assert_eq!(vwap.cumulative_volume, 0.0);
}

Expand All @@ -87,10 +87,8 @@ mod tests {
let test_data = vec![
// high, low , close, volume, expected
(150.5, 145.0, 148.0, 5000, 147.83),
(155.0, 147.5, 153.0, 4000, 133.19),
(160.0, 150.0, 155.0, 6000, 181.87),
(158.5, 151.0, 154.5, 4500, 140.62),
(162.0, 153.5, 159.0, 5500, 168.85)
(155.0, 147.5, 153.0, 4000, 149.61),
(160.0, 150.0, 155.0, 6000, 151.77)
];
let mut expected_index = 0usize;

Expand Down Expand Up @@ -121,7 +119,7 @@ mod tests {
vwap.reset();

assert_eq!(vwap.current_value, 0.0);
assert_eq!(vwap.cumulative_typical_price, 0.0);
assert_eq!(vwap.cumulative_price_volume, 0.0);
assert_eq!(vwap.cumulative_volume, 0.0);
}
}
2 changes: 2 additions & 0 deletions src/services/trailing_stop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use crate::models::side::Side;
///
/// **important:** you must call the function [TrailingStop::should_close_position] with every new price update
/// to allow the trailing stop to properly **adjust** the desired **stop loss**

#[derive(Clone)]
pub struct TrailingStop {
entry_price: f64,
stop_loss_price: f64,
Expand Down
1 change: 1 addition & 0 deletions src/strategies/custom/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod dummy_strategy;
pub mod stormer_scalper_strategy;
pub mod vwap_strategy;
Loading