From 4fb67445a95014c01a4c713eed3b23969e9ee8dd Mon Sep 17 00:00:00 2001 From: Griffiths Lott Date: Thu, 1 Dec 2022 19:29:50 -0500 Subject: [PATCH] Overhauled data module to revolve around traits rather than standardized models | Added timeseries/candle struct and traits --- src/data.rs | 41 +++++++--- src/data/crypto.rs | 49 ------------ src/data/data_service.rs | 58 -------------- src/data/equity.rs | 51 ------------ src/data/options.rs | 163 +++++++++++++++++++++++++++++--------- src/data/price_history.rs | 56 ------------- src/data/quotes.rs | 60 ++++++++++++++ src/data/timeseries.rs | 78 ++++++++++++++++++ src/lib.rs | 5 +- src/order.rs | 47 ----------- 10 files changed, 296 insertions(+), 312 deletions(-) delete mode 100644 src/data/crypto.rs delete mode 100644 src/data/data_service.rs delete mode 100644 src/data/equity.rs delete mode 100644 src/data/price_history.rs create mode 100644 src/data/quotes.rs create mode 100644 src/data/timeseries.rs diff --git a/src/data.rs b/src/data.rs index 3ed7675..3b2e988 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,11 +1,32 @@ -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; +} +pub trait Volume { + fn volume(&self) -> Option; +} + +pub fn to_datetime(timestamp: i64) { + +} + +pub enum Period { + Second, + Minute, + Hour, + Day, + TradeDay, + Week, + TradeWeek, + Month, + Year, + Ytd, + AssetLife } \ No newline at end of file diff --git a/src/data/crypto.rs b/src/data/crypto.rs deleted file mode 100644 index 5f6fae9..0000000 --- a/src/data/crypto.rs +++ /dev/null @@ -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, - pub base_crypto: String, // being purchased (top) - pub quote_crypto: String, - pub bid: f64, - pub mid: f64, - pub ask: f64, - pub last: Option, - pub bid_size: Option, - pub ask_size: Option, - pub volume: Option, - pub high: Option, - pub open: Option, - pub low: Option, - pub close: Option, - pub marginable: Option, - pub quote_time: i64 -} -impl Crypto { - -} - - - - -pub trait ToCrypto { - fn to_crypto(&self) -> DynResult; -} \ No newline at end of file diff --git a/src/data/data_service.rs b/src/data/data_service.rs deleted file mode 100644 index c7718b4..0000000 --- a/src/data/data_service.rs +++ /dev/null @@ -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

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 { - - } -} - -*/ -pub trait GetEquity { - type Paramaters; - fn get_equity(&mut self, paramaters: Self::Paramaters) -> DynResult; -} -pub trait GetCrypto { - type Paramaters; - fn get_crypto(&mut self, paramaters: Self::Paramaters) -> DynResult; -} -pub trait GetOptionContract { - type Paramaters; - fn get_option_contract(&mut self, paramaters: Self::Paramaters) -> DynResult; -} -pub trait GetOptionChain { - type Paramaters; - fn get_options_chain_vec(&mut self, paramaters: Self::Paramaters) -> DynResult>; - fn get_options_chain_dataframe(&mut self, paramaters: Self::Paramaters) -> DynResult; -} -pub trait GetPriceHistory{ - type Paramaters; - fn get_price_history(&mut self, paramaters: Self::Paramaters)-> DynResult; -} - diff --git a/src/data/equity.rs b/src/data/equity.rs deleted file mode 100644 index 38f7520..0000000 --- a/src/data/equity.rs +++ /dev/null @@ -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, - pub exchange: Option, - pub bid: f64, - pub mid: f64, - pub ask: f64, - pub last: Option, - pub bid_size: Option, - pub ask_size: Option, - pub volume: Option, - pub high: Option, - pub open: Option, - pub low: Option, - pub close: Option, - pub shortable: Option, - pub marginable: Option, - pub quote_time: i64 -} -impl Equity { - -} - - - - - - -pub trait ToEquity { - fn to_equity(&self) -> DynResult; -} \ No newline at end of file diff --git a/src/data/options.rs b/src/data/options.rs index 986d299..80b8e61 100644 --- a/src/data/options.rs +++ b/src/data/options.rs @@ -1,11 +1,13 @@ use std::fmt::Display; -use chrono::{TimeZone, LocalResult, DateTime}; +use chrono::{TimeZone, LocalResult, 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,6 +24,7 @@ impl Display for OptionContractError { } impl std::error::Error for OptionContractError {} +#[derive(Debug, Clone)] pub enum OptionKind { Call, Put @@ -61,25 +64,125 @@ pub struct OptionContract { pub mid: f64, pub ask: f64, pub last: Option, - pub bid_size: Option, - pub ask_size: Option, - pub volume: Option, - pub high: Option, - pub open: Option, - pub low: Option, - pub close: Option, - pub greeks: Option, - pub volatility: Option + 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 { + self.exchange.clone() + } + fn quote_time(&self) -> DateTime { + Utc.timestamp_millis_opt(self.quote_time).unwrap() + } + fn retrieve_time(&self) ->DateTime { + 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 { + 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> { - 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.timestamp_millis_opt(self.expiration).unwrap() + } + fn implied_volatility(&self) -> Option { + 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 { + self.greeks.delta + } + fn gamma(&self) -> Option { + self.greeks.gamma + } + fn theta(&self) -> Option { + self.greeks.theta + } + fn rho(&self) -> Option { + self.greeks.rho + } + fn vega(&self) -> Option { + self.greeks.vega + } + fn valid_greek(greek: f64) -> bool { + greek >= -1.0 && greek <= 1.0 } } -pub trait ToOptionContract { - fn to_option_contract(&self) -> DynResult; +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; + fn days_to_expirations(&self) -> i32; + fn implied_volatility(&self) -> Option; +} + + +/// Methods for accessing Greeks +pub trait GreeksData { + fn delta(&self) -> Option; + fn gamma(&self) -> Option; + fn theta(&self) -> Option; + fn vega(&self) -> Option; + fn rho(&self) -> Option; + 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) -> DynResult 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!( diff --git a/src/data/price_history.rs b/src/data/price_history.rs deleted file mode 100644 index 7f6ebf3..0000000 --- a/src/data/price_history.rs +++ /dev/null @@ -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, - pub timestamp: i64 // mls since epoch -} -impl Candle { - pub fn new(high: f64, open: f64, low: f64, close: f64, volume: Option, timestamp: i64) -> Self { - Candle { high, open, low, close, volume, timestamp } - } -} - -pub struct PriceHistory { - symbol: String, - candles: Vec -} - -pub trait ToPriceHistory { - fn to_pricehistorty(&self) -> DynResult; -} - -pub fn create_timeseries_dataframe(timeseries_data: &Vec) -> DynResult { - 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 - )?) - -} \ No newline at end of file diff --git a/src/data/quotes.rs b/src/data/quotes.rs new file mode 100644 index 0000000..5ac4935 --- /dev/null +++ b/src/data/quotes.rs @@ -0,0 +1,60 @@ +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; + /// Gives the timestamp of when the quote was created + fn retrieve_time(&self) ->DateTime; + /// Gives the exchange which the data was sources from + fn exchange(&self) -> Option; + // TO DO: Gives the datasource used +} +///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; +} +///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; + /// 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; +} diff --git a/src/data/timeseries.rs b/src/data/timeseries.rs new file mode 100644 index 0000000..8dba9e1 --- /dev/null +++ b/src/data/timeseries.rs @@ -0,0 +1,78 @@ +use chrono::{DateTime, Utc}; +use super::{Volume, TimeStamp, DynResult}; +use polars::{prelude::DataFrame, df, prelude::{NamedFrom}}; + + +pub struct Candle { + pub high: f64, + pub open: f64, + pub low: f64, + pub close: f64, + pub volume: Option, + pub timestamp: DateTime +} +impl Candle { + fn from(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 trait HOLC { + fn high(&self) -> f64; + fn open(&self) -> f64; + fn low(&self) -> f64; + fn close(&self) -> f64; +} + +pub struct TimeSeries { + candles: Vec +} +impl TimeSeries { + fn new(candles: Vec) -> Self { + TimeSeries { candles: candles } + } + fn from(data: Vec) -> Self + where C: HOLC + TimeStamp + Volume + { + let mut candles: Vec = Vec::new(); + for d in data { + candles.push(Candle::from(d)) + } + TimeSeries::new(candles) + } +} + +pub fn create_timeseries_dataframe(timeseries_data: &Vec) -> DynResult { + 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 + )?) + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index eee7e66..e4e6dc7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,8 @@ pub type DynResult = Result>; #[cfg(test)] mod tests { - use super::*; - + use super::*; + + } diff --git a/src/order.rs b/src/order.rs index ce75df1..e69de29 100644 --- a/src/order.rs +++ b/src/order.rs @@ -1,47 +0,0 @@ -use crate::data::AssetDataObject; - - -pub enum Direction { - 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, - pub approved: bool - -} \ No newline at end of file