Compare commits
4 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c3e1b50b5f | 3 years ago |
|
|
7fd3d80504 | 3 years ago |
|
|
c3e62e36bb | 3 years ago |
|
|
4fb67445a9 | 3 years ago |
@ -1,11 +1,29 @@ |
|||||||
mod equity; |
pub mod quotes; |
||||||
mod crypto; |
pub mod options; |
||||||
mod options; |
pub mod timeseries; |
||||||
mod price_history; |
|
||||||
mod data_service; |
use chrono::{DateTime, Utc}; |
||||||
|
use super::DynResult; |
||||||
pub enum AssetDataObject { |
|
||||||
Equity(equity::Equity), |
|
||||||
Crypro(crypto::Crypto), |
pub trait TimeStamp { |
||||||
OptionContract(options::OptionContract), |
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,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" => ×tamp |
|
||||||
)?) |
|
||||||
|
|
||||||
} |
|
||||||
@ -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" => ×tamp |
||||||
|
)?) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
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,47 +1,15 @@ |
|||||||
use crate::data::AssetDataObject; |
use crate::data::quotes::AssetIdentifier; |
||||||
|
|
||||||
|
|
||||||
pub enum Direction { |
enum OrderDirection { |
||||||
Buy, |
Buy, |
||||||
Sell |
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…
Reference in new issue