Compare commits
No commits in common. 'dev' and 'master' have entirely different histories.
@ -1,29 +1,11 @@ |
|||||||
pub mod quotes; |
mod equity; |
||||||
pub mod options; |
mod crypto; |
||||||
pub mod timeseries; |
mod options; |
||||||
|
mod price_history; |
||||||
use chrono::{DateTime, Utc}; |
mod data_service; |
||||||
use super::DynResult; |
|
||||||
|
pub enum AssetDataObject { |
||||||
|
Equity(equity::Equity), |
||||||
pub trait TimeStamp { |
Crypro(crypto::Crypto), |
||||||
fn timestamp(&self) -> DateTime<Utc>; |
OptionContract(options::OptionContract), |
||||||
} |
|
||||||
pub trait Volume { |
|
||||||
fn volume(&self) -> Option<u64>; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
pub enum Period { |
|
||||||
Second, |
|
||||||
Minute, |
|
||||||
Hour, |
|
||||||
Day, |
|
||||||
TradeDay, |
|
||||||
Week, |
|
||||||
TradeWeek, |
|
||||||
Month, |
|
||||||
Year, |
|
||||||
Ytd, |
|
||||||
AssetLife |
|
||||||
} |
} |
||||||
@ -0,0 +1,49 @@ |
|||||||
|
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>; |
||||||
|
} |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
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>; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,51 @@ |
|||||||
|
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>; |
||||||
|
} |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
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 |
||||||
|
)?) |
||||||
|
|
||||||
|
} |
||||||
@ -1,67 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
@ -1,131 +0,0 @@ |
|||||||
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() { |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
pub mod ma; |
|
||||||
pub mod rsi; |
|
||||||
pub mod macd; |
|
||||||
@ -1,42 +0,0 @@ |
|||||||
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 |
|
||||||
} |
|
||||||
@ -1,35 +0,0 @@ |
|||||||
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>) { |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,45 +0,0 @@ |
|||||||
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,6 +1,12 @@ |
|||||||
pub mod data; |
mod data; |
||||||
pub mod indicators; |
|
||||||
mod order; |
mod order; |
||||||
mod tests; |
|
||||||
|
|
||||||
pub type DynResult<T> = Result<T, Box< dyn std::error::Error>>; |
pub type DynResult<T> = Result<T, Box< dyn std::error::Error>>; |
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use super::*; |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|||||||
@ -1,15 +1,47 @@ |
|||||||
use crate::data::quotes::AssetIdentifier; |
use crate::data::AssetDataObject; |
||||||
|
|
||||||
|
|
||||||
enum OrderDirection { |
pub enum Direction { |
||||||
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, |
|
||||||
} |
} |
||||||
@ -1,126 +0,0 @@ |
|||||||
#[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