Compare commits

...

4 Commits
master ... dev

  1. 5
      Cargo.toml
  2. 38
      src/data.rs
  3. 49
      src/data/crypto.rs
  4. 58
      src/data/data_service.rs
  5. 51
      src/data/equity.rs
  6. 165
      src/data/options.rs
  7. 56
      src/data/price_history.rs
  8. 67
      src/data/quotes.rs
  9. 131
      src/data/timeseries.rs
  10. 3
      src/indicators.rs
  11. 42
      src/indicators/ma.rs
  12. 35
      src/indicators/macd.rs
  13. 45
      src/indicators/rsi.rs
  14. 12
      src/lib.rs
  15. 44
      src/order.rs
  16. 126
      src/tests.rs

@ -1,5 +1,5 @@
[package]
name = "RustyTrader"
name = "rusty_trader"
version = "0.1.0"
edition = "2021"
@ -8,4 +8,5 @@ edition = "2021"
[dependencies]
chrono = "0.4.23"
regex = "1.7.0"
polars = "0.25.1"
polars = "0.25.1"
probability = "0.20.3"

@ -1,11 +1,29 @@
mod equity;
mod crypto;
mod options;
mod price_history;
mod data_service;
pub enum AssetDataObject {
Equity(equity::Equity),
Crypro(crypto::Crypto),
OptionContract(options::OptionContract),
pub mod quotes;
pub mod options;
pub mod timeseries;
use chrono::{DateTime, Utc};
use super::DynResult;
pub trait TimeStamp {
fn timestamp(&self) -> DateTime<Utc>;
}
pub trait Volume {
fn volume(&self) -> Option<u64>;
}
pub enum Period {
Second,
Minute,
Hour,
Day,
TradeDay,
Week,
TradeWeek,
Month,
Year,
Ytd,
AssetLife
}

@ -1,49 +0,0 @@
use std::fmt::Display;
use crate::DynResult;
#[derive(Debug)]
pub struct CryptoError {
_error: String
}
impl CryptoError {
pub fn new(error_msg: &str) -> Self {
CryptoError { _error: error_msg.to_owned() }
}
}
impl Display for CryptoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Crypto Error Placeholder")
}
}
impl std::error::Error for CryptoError {}
#[derive(Debug)]
pub struct Crypto {
pub symbol: String,
pub exchange: Option<String>,
pub base_crypto: String, // being purchased (top)
pub quote_crypto: String,
pub bid: f64,
pub mid: f64,
pub ask: f64,
pub last: Option<f64>,
pub bid_size: Option<usize>,
pub ask_size: Option<usize>,
pub volume: Option<usize>,
pub high: Option<f64>,
pub open: Option<f64>,
pub low: Option<f64>,
pub close: Option<f64>,
pub marginable: Option<bool>,
pub quote_time: i64
}
impl Crypto {
}
pub trait ToCrypto {
fn to_crypto(&self) -> DynResult<Crypto>;
}

@ -1,58 +0,0 @@
use crate::data::*;
use crate::DynResult;
/*
A data service must implement at lease one of the following traits
Important to note that Paramaters can be typed as Option<P> during implemenation
and that the actual strucut/type passed into these is flexable. This should allow
each services library to implement a paramates trait that works for them, or
leave paramaters as 'None'
This way you can lock down what paramaters are required for each indiviudual service
while still allowing the RustyTrade framework to reliably obtain the expected struct
from each trait interface.
struct ExamplePriceHistoryParams {
start_date: i64,
end_date: i64
}
struct ExampleDataService {};
impl GetPriceHistory for ExampleDataService {
type Paramaters = ExamplePriceHistoryParmas;
fn get_price_history(&mut self, paramaters: Self::Paramaters)-> DynResult<PriceHistory> {
}
}
*/
pub trait GetEquity {
type Paramaters;
fn get_equity(&mut self, paramaters: Self::Paramaters) -> DynResult<equity::Equity>;
}
pub trait GetCrypto {
type Paramaters;
fn get_crypto(&mut self, paramaters: Self::Paramaters) -> DynResult<crypto::Crypto>;
}
pub trait GetOptionContract {
type Paramaters;
fn get_option_contract(&mut self, paramaters: Self::Paramaters) -> DynResult<options::OptionContract>;
}
pub trait GetOptionChain {
type Paramaters;
fn get_options_chain_vec(&mut self, paramaters: Self::Paramaters) -> DynResult<Vec<options::OptionContract>>;
fn get_options_chain_dataframe(&mut self, paramaters: Self::Paramaters) -> DynResult<options::OptionChainDataFrame>;
}
pub trait GetPriceHistory{
type Paramaters;
fn get_price_history(&mut self, paramaters: Self::Paramaters)-> DynResult<price_history::PriceHistory>;
}

@ -1,51 +0,0 @@
use std::fmt::Display;
use crate::DynResult;
#[derive(Debug)]
pub struct EquityError {
error: String
}
impl EquityError {
pub fn new(error_msg: &str) -> Self {
EquityError { error: error_msg.to_owned() }
}
}
impl Display for EquityError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Equity Error Placeholder")
}
}
impl std::error::Error for EquityError {}
#[derive(Debug)]
pub struct Equity {
pub symbol: String,
pub cusip : Option<String>,
pub exchange: Option<String>,
pub bid: f64,
pub mid: f64,
pub ask: f64,
pub last: Option<f64>,
pub bid_size: Option<usize>,
pub ask_size: Option<usize>,
pub volume: Option<usize>,
pub high: Option<f64>,
pub open: Option<f64>,
pub low: Option<f64>,
pub close: Option<f64>,
pub shortable: Option<bool>,
pub marginable: Option<bool>,
pub quote_time: i64
}
impl Equity {
}
pub trait ToEquity {
fn to_equity(&self) -> DynResult<Equity>;
}

@ -1,11 +1,13 @@
use std::fmt::Display;
use chrono::{TimeZone, LocalResult, DateTime};
use chrono::{TimeZone, DateTime, Utc};
use polars::{prelude::DataFrame, df, prelude::{NamedFrom}};
use regex::Regex;
use crate::DynResult;
use super::quotes::{QuoteData, AssetIdentifier, VolumeData, PriceSpreadData};
#[derive(Debug)]
pub struct OptionContractError {
error: String
@ -22,12 +24,13 @@ impl Display for OptionContractError {
}
impl std::error::Error for OptionContractError {}
#[derive(Debug, Clone)]
pub enum OptionKind {
Call,
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");
@ -61,25 +64,125 @@ pub struct OptionContract {
pub mid: f64,
pub ask: f64,
pub last: Option<f64>,
pub bid_size: Option<u64>,
pub ask_size: Option<u64>,
pub volume: Option<u64>,
pub high: Option<f64>,
pub open: Option<f64>,
pub low: Option<f64>,
pub close: Option<f64>,
pub greeks: Option<Greeks>,
pub volatility: Option<f64>
pub bid_size: u64,
pub ask_size: u64,
pub last_size: u64,
pub volume: u64,
pub high: f64,
pub open: f64,
pub low: f64,
pub close: f64,
pub greeks: Greeks,
pub volatility: f64,
pub quote_time: i64
}
impl QuoteData for OptionContract {
fn asset_symbol(&self) -> AssetIdentifier {
AssetIdentifier::Ticker(self.symbol.clone())
}
fn exchange(&self) -> Option<String> {
self.exchange.clone()
}
fn quote_time(&self) -> DateTime<chrono::Utc> {
Utc.timestamp_millis_opt(self.quote_time).unwrap()
}
fn retrieve_time(&self) ->DateTime<Utc> {
self.quote_time()
}
}
impl VolumeData for OptionContract {
fn ask_size(&self) -> u64 {
self.ask_size
}
fn bid_size(&self) -> u64 {
self.bid_size
}
fn last_size(&self) -> Option<u64> {
Some(self.last_size)
}
fn total_volume(&self) -> u64 {
self.volume
}
fn volume_period(&self) -> super::Period {
super::Period::Day
}
}
impl PriceSpreadData for OptionContract {
fn ask(&self) -> f64 {
self.ask
}
fn bid(&self) -> f64 {
self.bid
}
fn mid(&self) -> f64 {
self.mid
}
}
impl OptionContract {
pub fn expiration_as_date(&self) -> LocalResult<DateTime<chrono::Utc>> {
chrono::Utc.timestamp_millis_opt(self.expiration)
impl OptionsData for OptionContract {
fn days_to_expirations(&self) -> i32 {
self.dte as i32
}
fn expiration(&self) -> DateTime<Utc> {
Utc.timestamp_millis_opt(self.expiration).unwrap()
}
fn implied_volatility(&self) -> Option<f64> {
Some(self.volatility)
}
fn underlying(&self) -> AssetIdentifier {
AssetIdentifier::Ticker(self.underlying.clone())
}
fn option_type(&self) -> OptionKind {
self.kind.clone()
}
fn strike_price(&self) -> f64 {
self.strike_price
}
}
impl GreeksData for OptionContract {
fn delta(&self) -> Option<f64> {
self.greeks.delta
}
fn gamma(&self) -> Option<f64> {
self.greeks.gamma
}
fn theta(&self) -> Option<f64> {
self.greeks.theta
}
fn rho(&self) -> Option<f64> {
self.greeks.rho
}
fn vega(&self) -> Option<f64> {
self.greeks.vega
}
fn valid_greek(greek: f64) -> bool {
greek >= -1.0 && greek <= 1.0
}
}
pub trait ToOptionContract {
fn to_option_contract(&self) -> DynResult<OptionContract>;
pub trait OptionsData {
/// Gives the underlying asset of an options
fn underlying(&self) -> AssetIdentifier;
/// Returns the options type
fn option_type(&self) -> OptionKind;
/// Returns strike price
fn strike_price(&self) -> f64;
fn expiration(&self) -> DateTime<Utc>;
fn days_to_expirations(&self) -> i32;
fn implied_volatility(&self) -> Option<f64>;
}
/// Methods for accessing Greeks
pub trait GreeksData {
fn delta(&self) -> Option<f64>;
fn gamma(&self) -> Option<f64>;
fn theta(&self) -> Option<f64>;
fn vega(&self) -> Option<f64>;
fn rho(&self) -> Option<f64>;
fn valid_greek(greek: f64) -> bool {
greek >= -1.0 && greek <= 1.0
}
}
pub struct Greeks {
@ -137,30 +240,12 @@ pub fn create_optionchain_dataframe(options: Vec<OptionContract>) -> DynResult<O
opens.push(op.open);
lows.push(op.low);
closes.push(op.close);
deltas.push(match &op.greeks {
Some(greeks) => greeks.delta,
None => None
});
deltas.push(match &op.greeks {
Some(greeks) => greeks.delta,
None => None
});
gammas.push(match &op.greeks {
Some(greeks) => greeks.gamma,
None => None
});
thetas.push(match &op.greeks {
Some(greeks) => greeks.theta,
None => None
});
vegas.push(match &op.greeks {
Some(greeks) => greeks.vega,
None => None
});
rhos.push(match &op.greeks {
Some(greeks) => greeks.rho,
None => None
});
deltas.push(op.greeks.delta);
deltas.push(op.greeks.delta);
gammas.push(op.greeks.gamma);
thetas.push(op.greeks.theta);
vegas.push(op.greeks.vega);
rhos.push(op.greeks.rho);
volatilitys.push(op.volatility);
}
Ok(df!(

@ -1,56 +0,0 @@
use polars::{prelude::DataFrame, df, prelude::{NamedFrom}};
use crate::DynResult;
pub struct Candle {
pub high: f64,
pub open: f64,
pub low: f64,
pub close: f64,
pub volume: Option<u64>,
pub timestamp: i64 // mls since epoch
}
impl Candle {
pub fn new(high: f64, open: f64, low: f64, close: f64, volume: Option<u64>, timestamp: i64) -> Self {
Candle { high, open, low, close, volume, timestamp }
}
}
pub struct PriceHistory {
symbol: String,
candles: Vec<Candle>
}
pub trait ToPriceHistory {
fn to_pricehistorty(&self) -> DynResult<PriceHistory>;
}
pub fn create_timeseries_dataframe(timeseries_data: &Vec<Candle>) -> DynResult<DataFrame> {
let mut high = vec![];
let mut open = vec![];
let mut low = vec![];
let mut close = vec![];
let mut volume = vec![];
let mut timestamp = vec![];
for candle in timeseries_data {
high.push(candle.high);
open.push(candle.open);
low.push(candle.low);
close.push(candle.close);
volume.push(candle.volume);
timestamp.push(candle.timestamp);
}
Ok(df!(
"High" => &high,
"Open" => &open,
"Low" => &low,
"Close"=> &close,
"Volume"=> &volume,
"TimeStamp" => &timestamp
)?)
}

@ -0,0 +1,67 @@
use chrono::{Utc, DateTime};
use super::Period;
use super::options::OptionKind;
pub enum AssetIdentifier{
Ticker(String),
Cusip(String),
Other(String)
}
///Information about what this quote it, where it came from and when
///
pub trait QuoteData {
/// Gives the symbol/ticker/cusip of this asset
fn asset_symbol(&self) -> AssetIdentifier;
/// Gives the timestamp of when the quote data is valid
fn quote_time(&self) -> DateTime<Utc>;
/// Gives the timestamp of when the quote was created
fn retrieve_time(&self) ->DateTime<Utc>;
/// Gives the exchange which the data was sources from
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 {
/// Gives the period over which this volume is valid
fn volume_period(&self) -> Period;
/// Total volume seem in that period
fn total_volume(&self) -> u64;
/// Gives the current bid size of the asset
fn bid_size(&self) -> u64;
// Gives the current as size of the asset
fn ask_size(&self) -> u64;
// Gives the size of the last trade
fn last_size(&self) -> Option<u64>;
}
///Standard information about price spread
///
pub trait PriceSpreadData {
/// Give the bid price of an asset
fn bid(&self) -> f64;
/// Gives the mid price of an asset
fn mid(&self) -> f64;
/// Gives the ask price of an asset
fn ask(&self) -> f64;
}
pub trait Marginable {
/// Gives the margin cost as a yearly interest cost. Returns none if not marginable
fn margin_cost(&self) -> Option<f64>;
/// Returns true if we can use margin on this instrument
fn is_marginable(&self) -> bool;
}
pub trait CryptoData {
fn base_crypto(&self) -> String;
fn quote_crypto(&self) -> String;
}

@ -0,0 +1,131 @@
use chrono::{DateTime, Utc};
use super::{Volume, TimeStamp, DynResult};
use polars::{prelude::DataFrame, df, prelude::{NamedFrom}};
use super::quotes::HOLC;
use probability::prelude::*;
#[derive(Clone, Debug)]
pub struct Candle {
pub high: f64,
pub open: f64,
pub low: f64,
pub close: f64,
pub volume: Option<u64>,
pub timestamp: DateTime<Utc>
}
impl Candle {
fn from<SourceData>(data: SourceData) -> Self
where SourceData: HOLC + TimeStamp + Volume {
Candle { high: data.high(),
open: data.open(),
low: data.low(),
close: data.close(),
volume: data.volume(),
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
}
}
}
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}
}
impl TimeStamp for Candle {
fn timestamp(&self) -> DateTime<Utc> {
self.timestamp
}
}
pub struct TimeSeries {
pub candles: Vec<Candle>
}
impl TimeSeries {
pub fn new(candles: Vec<Candle>) -> Self {
TimeSeries { candles: candles }
}
fn from<C>(data: Vec<C>) -> Self
where C: HOLC + TimeStamp + Volume
{
let mut candles: Vec<Candle> = Vec::new();
for d in data {
candles.push(Candle::from(d))
}
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 generate_random(n_datapoints: usize, mut start_price: f64) -> Self {
let mut data: Vec<Candle> = vec![];
let inter_day_dist = Gaussian::new(1.0043,0.021);
let mut source = source::default(420);
let mut inter_sampler = Independent(&inter_day_dist, &mut source);
let intra_day_dist = Gaussian::new(1.003,0.011);
let mut source2 = source::default(69);
let mut intra_sampler = Independent(&intra_day_dist, &mut source2);
let day_moves = inter_sampler.take(n_datapoints).collect::<Vec<_>>();
let intraday_moves = intra_sampler.take(n_datapoints * 3).collect::<Vec<_>>();
for n in 0..n_datapoints {
let close = start_price * day_moves[n];
data.push( Candle::new(start_price * intraday_moves[n], start_price, start_price * intraday_moves[n+1], close, Some((18183.0 * intraday_moves[n+3]) as u64), Utc::now()));
start_price = close;
}
TimeSeries { candles: data }
}
}
pub fn create_timeseries_dataframe(timeseries_data: &Vec<Candle>) -> DynResult<DataFrame> {
let mut high = vec![];
let mut open = vec![];
let mut low = vec![];
let mut close = vec![];
let mut volume = vec![];
let mut timestamp = vec![];
for candle in timeseries_data {
high.push(candle.high);
open.push(candle.open);
low.push(candle.low);
close.push(candle.close);
volume.push(candle.volume);
timestamp.push(candle.timestamp.timestamp_millis());
}
Ok(df!(
"High" => &high,
"Open" => &open,
"Low" => &low,
"Close"=> &close,
"Volume"=> &volume,
"TimeStamp" => &timestamp
)?)
}
pub fn sample_timeseries() {
}

@ -0,0 +1,3 @@
pub mod ma;
pub mod rsi;
pub mod macd;

@ -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,35 @@
use crate::indicators::ma::exponential_moving_average;
use crate::data::quotes::HOLC;
use crate::data::TimeStamp;
pub struct MACD {
upperBand: Vec<f64>,
lowerBand: Vec<f64>,
signalLine: Vec<f64>,
}
impl MACD {
pub fn new<C: HOLC + TimeStamp + Clone>(data: Vec<C>, upperBand: Option<usize>, lowerBand: Option<usize>, signalBand: Option<usize>) {
// This is going to need a custom implementation. It's not efficent to clone and iterate through the data 3 seperate times
let ub = upperBand.unwrap_or(26);
let lb = lowerBand.unwrap_or(12);
let sb = signalBand.unwrap_or(9);
let u_ema = exponential_moving_average(data.clone(), ub);
let l_ema = exponential_moving_average(data.clone(), lb);
let s_ema = exponential_moving_average(data.clone(), sb);
println!("u_ema = {:?}", u_ema);
println!("u_ema.len() = {:?}", u_ema.len());
println!("l_ema = {:?}", l_ema);
println!("l_ema.len() = {:?}", l_ema.len());
println!("s_ema = {:?}", s_ema);
println!("s_ema.len() = {:?}", s_ema.len());
}
}
fn moving_average_convergence_divergence<C: HOLC>(data: Vec<C>) {
}

@ -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,12 +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::*;
}

@ -1,47 +1,15 @@
use crate::data::AssetDataObject;
use crate::data::quotes::AssetIdentifier;
pub enum Direction {
enum OrderDirection {
Buy,
Sell
}
impl ToString for Direction {
fn to_string(&self) -> String {
match &self {
Self::Buy => "Buy".to_string(),
Self::Sell => "Sell".to_string()
}
}
}
pub enum OrderType {
Market,
Limit,
Stop,
TrailingStop
}
impl ToString for OrderType {
fn to_string(&self) -> String {
match &self {
Self::Market => "Market".to_string(),
Self::Limit => "Limit".to_string(),
Self::Stop => "Stop".to_string(),
Self::TrailingStop => "TrailingStop".to_string()
}
}
}
pub struct SimpleOrder {
pub asset: AssetDataObject,
pub price_to_pay: f64,
pub direction: Direction,
pub order_type: OrderType,
pub good_until: i64,
pub approved: bool
}
pub struct CompoundOrder {
pub orders: Vec<SimpleOrder>,
pub approved: bool
struct Order {
asset: AssetIdentifier,
transaction_price: f64,
quantity: i32,
}

@ -0,0 +1,126 @@
#[cfg(test)]
mod tests {
use crate::data::timeseries::{Candle, TimeSeries};
use crate::indicators::ma::exponential_moving_average;
use crate::indicators::rsi::relative_strength_index;
use crate::indicators::macd::MACD;
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);
}
#[test]
fn candle_gen() {
let data = TimeSeries::generate_random(50, 100.0);
for c in data.candles.iter() {
println!("c = {:?}", c);
}
}
#[test]
fn macd(){
let data = TimeSeries::generate_random(50, 100.0);
let x = MACD::new(data.candles, None,None,None);
}
}
Loading…
Cancel
Save