Added MA and rsi indicators

dev
Griffiths Lott 3 years ago
parent c3e62e36bb
commit 7fd3d80504
  1. 2
      Cargo.toml
  2. 3
      src/data.rs
  3. 2
      src/data/options.rs
  4. 7
      src/data/quotes.rs
  5. 38
      src/data/timeseries.rs
  6. 2
      src/indicators.rs
  7. 42
      src/indicators/ma.rs
  8. 45
      src/indicators/rsi.rs
  9. 13
      src/lib.rs
  10. 4
      src/order.rs
  11. 112
      src/tests.rs

@ -1,5 +1,5 @@
[package]
name = "RustyTrader"
name = "rusty_trader"
version = "0.1.0"
edition = "2021"

@ -13,9 +13,6 @@ pub trait Volume {
fn volume(&self) -> Option<u64>;
}
pub fn to_datetime(timestamp: i64) {
}
pub enum Period {
Second,

@ -30,7 +30,7 @@ pub enum OptionKind {
Put
}
impl OptionKind {
fn from_str(option_kind: &str) -> Result<OptionKind, OptionContractError>{
pub fn from_str(option_kind: &str) -> Result<OptionKind, OptionContractError>{
let call = Regex::new("(?i)call|(?i)c").expect("call regex failed:\noptions.rs:\t(?i)call|(?i)c");
let put = Regex::new("(?i)put|(?i)p").expect("put regex failed:\noptions.rs:\t(?i)put|(?i)p");

@ -22,6 +22,13 @@ pub trait QuoteData {
fn exchange(&self) -> Option<String>;
// TO DO: Gives the datasource used
}
pub trait HOLC {
fn high(&self) -> f64;
fn open(&self) -> f64;
fn low(&self) -> f64;
fn close(&self) -> f64;
}
///Standard volume information
///
pub trait VolumeData {

@ -1,8 +1,9 @@
use chrono::{DateTime, Utc};
use super::{Volume, TimeStamp, DynResult};
use polars::{prelude::DataFrame, df, prelude::{NamedFrom}};
use super::quotes::HOLC;
#[derive(Clone)]
pub struct Candle {
pub high: f64,
pub open: f64,
@ -22,20 +23,31 @@ impl Candle {
timestamp: data.timestamp(),
}
}
pub fn new(h: f64, o: f64, l: f64, c: f64, v: Option<u64>, t: DateTime<Utc>) -> Self {
Candle {
high : h,
open : o,
low : l,
close: c,
volume : v,
timestamp : t
}
}
}
pub trait HOLC {
fn high(&self) -> f64;
fn open(&self) -> f64;
fn low(&self) -> f64;
fn close(&self) -> f64;
impl HOLC for Candle {
fn high(&self) -> f64 { self.high }
fn open(&self) -> f64 { self.open }
fn low(&self) -> f64 { self.low }
fn close(&self) -> f64 { self.close}
}
pub struct TimeSeries {
candles: Vec<Candle>
}
impl TimeSeries {
fn new(candles: Vec<Candle>) -> Self {
pub fn new(candles: Vec<Candle>) -> Self {
TimeSeries { candles: candles }
}
fn from<C>(data: Vec<C>) -> Self
@ -47,6 +59,12 @@ impl TimeSeries {
}
TimeSeries::new(candles)
}
pub fn into_vec(self) -> Vec<Candle> {
return self.candles
}
pub fn as_vec(&self) -> Vec<Candle>{
return self.candles.clone()
}
}
pub fn create_timeseries_dataframe(timeseries_data: &Vec<Candle>) -> DynResult<DataFrame> {
@ -75,4 +93,8 @@ pub fn create_timeseries_dataframe(timeseries_data: &Vec<Candle>) -> DynResult<D
"TimeStamp" => &timestamp
)?)
}
pub fn sample_timeseries() {
}

@ -0,0 +1,2 @@
pub mod ma;
pub mod rsi;

@ -0,0 +1,42 @@
use crate::data::quotes::HOLC;
pub fn simple_moving_avg<C: HOLC> (data: Vec<C>, period: usize) -> Vec<f64> {
assert!(data.len() >= period, "Cannot create SMA! : Data is shorter than requested period.");
let mut ma_data = vec![];
let n: f64 = period as f64;
// Get the initial average value
let mut avg = data[..period].iter().fold(0.0, |acc, d| acc + d.close()) / n;
ma_data.push(avg);
// calculate the rest of the averages going forward
for (i, d) in data[period..].iter().enumerate() {
// subtract the data that is no longer part of the period
// Not that i corresponds to the index of data 1 period away since we are starting
// iteraction 1 period in. This means it's perfect for getting the oldest data.
avg -= data[i].close()/n;
// add the new data to the average
avg += d.close() / n;
ma_data.push(avg);
}
ma_data
}
fn cur_ema(closing_price: f64, prev_ema: f64, smoothing: f64) -> f64 {
(closing_price * smoothing) + (prev_ema * (1.0 - smoothing))
}
pub fn exponential_moving_average<C: HOLC> (data: Vec<C>, period: usize) -> Vec<f64> {
assert!(data.len() >= period + 1, "EMA requires period + 1 # of datapoints!");
let mut ema_data = vec![];
let n: f64 = period as f64;
// Get the average closing price over the original period
let ma = data[..period].iter().fold(0.0, |acc, d| acc + d.close()) / n;
// Calculates our weighthing factor (using 2 as base)
let smoothing = 2.0 / (n + 1.0);
for (i, d) in data[period..].iter().enumerate() {
// Use the previously calced MA for the first iteration
if i == 0 {ema_data.push(cur_ema(d.close(), ma, smoothing)); continue;}
ema_data.push(cur_ema(d.close(), *ema_data.last().unwrap(), smoothing));
}
ema_data
}

@ -0,0 +1,45 @@
use crate::data::quotes::HOLC;
pub fn relative_strength_index<C: HOLC>(data: Vec<C>, period: usize) -> Vec<f64> {
assert!(data.len() >= period + 2);
let mut rsi_data: Vec<f64> = vec![];
let n = period as f64;
let mut g_avg = 0.0;
let mut l_avg = 0.0;
for (i, d) in data[1..period+1].iter().enumerate() {
let chg = d.close() - data[i].close();
if chg >= 0.0 {g_avg += chg;}
else if chg <= 0.0 { l_avg += chg.abs();}
if i == period -1 {
g_avg /=n;
l_avg /=n;
rsi_data.push(100.0 - (100.0/(1.0+((g_avg)/(l_avg)))));
}
}
// This is magic. Don't touch it. The periods just work.
for (i, d) in data[period+1..].iter().enumerate() {
let chg = d.close() - data[period + i ].close();
if chg > 0.0 {
g_avg = (g_avg * (n - 1.0) + chg) / n;
l_avg = (l_avg * (n - 1.0) + 0.0) / n;
} else if chg < 0.0 {
g_avg = (g_avg * (n - 1.0) + 0.0) / n;
l_avg = (l_avg * (n - 1.0) + chg.abs()) / n;
}
else {
g_avg = (g_avg * (n - 1.0) + 0.0) / n;
l_avg = (l_avg * (n - 1.0) + 0.0) / n;
}
rsi_data.push(100.0 - (100.0/(1.0+((g_avg)/(l_avg)))));
}
rsi_data
}

@ -1,13 +1,6 @@
mod data;
pub mod data;
pub mod indicators;
mod order;
mod tests;
pub type DynResult<T> = Result<T, Box< dyn std::error::Error>>;
#[cfg(test)]
mod tests {
use super::*;
}

@ -6,14 +6,10 @@ enum OrderDirection {
Sell
}
enum CancelCondition {
}
struct Order {
asset: AssetIdentifier,
transaction_price: f64,
quantity: i32,
}

@ -0,0 +1,112 @@
#[cfg(test)]
mod tests {
use crate::data::timeseries::Candle;
use crate::indicators::ma::exponential_moving_average;
use crate::indicators::rsi::relative_strength_index;
use crate::indicators::{ma};
use crate::data::quotes::{HOLC};
use chrono::Utc;
#[test]
fn sma() {
let data = vec![
Candle::new(0.0,0.0,0.0, 4.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 8.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 11.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 8.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 14.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 6.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 6.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 1.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 4.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 4.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 9.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 7.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 12.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 5.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 1.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 10.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 5.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 9.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 6.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 2.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 8.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 9.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 11.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 14.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 9.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 12.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 4.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 6.0, None,Utc::now()),
];
println!("data.len() = {:?}", data.len());
let sma_d = ma::simple_moving_avg(data, 3);
println!("sma_d = {:?}", sma_d);
}
#[test]
fn ema(){
let data = vec![
Candle::new(0.0,0.0,0.0, 4.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 8.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 11.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 8.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 14.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 6.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 6.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 1.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 4.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 4.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 9.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 7.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 12.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 5.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 1.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 10.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 5.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 9.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 6.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 2.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 8.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 9.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 11.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 14.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 9.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 12.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 4.0, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 6.0, None,Utc::now()),
];
let ema_d = exponential_moving_average(data, 3);
println!("ema_d = {:?}", ema_d);
}
#[test]
fn rsi() {
let data = vec![
Candle::new(0.0,0.0,0.0, 140.06, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 144.28, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 147.64, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 150.6, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 151.92, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 154.79, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 152.61, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 150.26, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 150.47, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 146.68, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 145.14, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 148.1, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 148.82, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 148.91, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 147.21, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 142.84, None,Utc::now()),
Candle::new(0.0,0.0,0.0, 145.48, None,Utc::now()),
];
let rsi_d = relative_strength_index(data, 14);
println!("rsi_d = {:?}", rsi_d);
}
}
Loading…
Cancel
Save