master
commit
a7079454a2
@ -0,0 +1,3 @@ |
||||
/target |
||||
/Cargo.lock |
||||
to-do.txt |
||||
@ -0,0 +1,11 @@ |
||||
[package] |
||||
name = "RustyTrader" |
||||
version = "0.1.0" |
||||
edition = "2021" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
chrono = "0.4.23" |
||||
regex = "1.7.0" |
||||
polars = "0.25.1" |
||||
@ -0,0 +1,5 @@ |
||||
mod equity; |
||||
mod crypto; |
||||
mod options; |
||||
mod price_history; |
||||
mod data_service; |
||||
@ -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,193 @@ |
||||
use std::fmt::Display; |
||||
use chrono::{TimeZone, LocalResult, DateTime}; |
||||
use polars::{prelude::DataFrame, df, prelude::{NamedFrom}}; |
||||
use regex::Regex; |
||||
|
||||
|
||||
use crate::DynResult; |
||||
|
||||
#[derive(Debug)] |
||||
pub struct OptionContractError { |
||||
error: String |
||||
} |
||||
impl OptionContractError { |
||||
pub fn new(error_msg: &str) -> Self { |
||||
OptionContractError { error: error_msg.to_owned() } |
||||
} |
||||
} |
||||
impl Display for OptionContractError { |
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||
write!(f, "OptionContract Error Placeholder") |
||||
} |
||||
} |
||||
impl std::error::Error for OptionContractError {} |
||||
|
||||
pub enum OptionKind { |
||||
Call, |
||||
Put |
||||
} |
||||
impl OptionKind { |
||||
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"); |
||||
|
||||
if call.find(option_kind).is_some() { |
||||
return Ok(OptionKind::Call) |
||||
} else if put.find(option_kind).is_some() { |
||||
return Ok(OptionKind::Put)} |
||||
|
||||
return Err(OptionContractError::new(&format!("Could not convert {option_kind} to an OptionKind (Call/Put)"))) |
||||
} |
||||
} |
||||
impl ToString for OptionKind { |
||||
fn to_string(&self) -> String { |
||||
match &self { |
||||
Self::Call => "Call".to_string(), |
||||
Self::Put => "Put".to_string() |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
pub struct OptionContract { |
||||
pub symbol: String, |
||||
pub underlying: String, |
||||
pub exchange: Option<String>, |
||||
pub kind: OptionKind, |
||||
pub strike_price: f64, |
||||
pub expiration: i64, // mls since epoch
|
||||
pub dte: u32, // days to expiration time of quote
|
||||
pub bid: f64, |
||||
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> |
||||
} |
||||
impl OptionContract { |
||||
pub fn expiration_as_date(&self) -> LocalResult<DateTime<chrono::Utc>> { |
||||
chrono::Utc.timestamp_millis_opt(self.expiration) |
||||
} |
||||
} |
||||
|
||||
|
||||
pub trait ToOptionContract { |
||||
fn to_option_contract(&self) -> DynResult<OptionContract>; |
||||
} |
||||
|
||||
pub struct Greeks { |
||||
delta: Option<f64>, |
||||
gamma: Option<f64>, |
||||
theta: Option<f64>, |
||||
vega: Option<f64>, |
||||
rho: Option<f64>, |
||||
} |
||||
|
||||
|
||||
|
||||
pub type OptionChainDataFrame = DataFrame;
|
||||
pub fn create_optionchain_dataframe(options: Vec<OptionContract>) -> DynResult<OptionChainDataFrame> { |
||||
let mut symbols = vec![];
|
||||
let mut underlyings = vec![] ; |
||||
let mut exchanges = vec![] ; |
||||
let mut kinds = vec![] ; |
||||
let mut strike_prices = vec![] ; |
||||
let mut expirations = vec![] ; |
||||
let mut dtes = vec![] ; |
||||
let mut bids = vec![] ; |
||||
let mut mids = vec![] ; |
||||
let mut asks = vec![] ; |
||||
let mut lasts = vec![] ; |
||||
let mut bid_sizes = vec![] ; |
||||
let mut ask_sizes = vec![] ; |
||||
let mut volumes = vec![] ; |
||||
let mut highs = vec![] ; |
||||
let mut opens = vec![] ; |
||||
let mut lows = vec![] ; |
||||
let mut closes = vec![] ; |
||||
let mut deltas = vec![]; |
||||
let mut gammas = vec![] ; |
||||
let mut thetas = vec![] ; |
||||
let mut vegas = vec![] ; |
||||
let mut rhos = vec![] ; |
||||
let mut volatilitys = vec![] ; |
||||
for op in options { |
||||
symbols.push(op.symbol); |
||||
underlyings.push(op.underlying); |
||||
exchanges.push(op.exchange); |
||||
kinds.push(op.kind.to_string()); |
||||
strike_prices.push(op.strike_price); |
||||
expirations.push(op.expiration); |
||||
dtes.push(op.dte); |
||||
bids.push(op.bid); |
||||
mids.push(op.mid); |
||||
asks.push(op.ask); |
||||
lasts.push(op.last); |
||||
bid_sizes.push(op.bid_size); |
||||
ask_sizes.push(op.ask_size); |
||||
volumes.push(op.volume); |
||||
highs.push(op.high); |
||||
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 |
||||
}); |
||||
volatilitys.push(op.volatility); |
||||
} |
||||
Ok(df!( |
||||
"Symbol" => &symbols, |
||||
"Underlying" => &underlyings, |
||||
"Exchange" => &exchanges, |
||||
"Kind" => &kinds, |
||||
"StrikePrice" => &strike_prices, |
||||
"Expiration" => &expirations, |
||||
"DaysToExpiry" => &dtes, |
||||
"Bid" => &bids, |
||||
"Mid" => &mids, |
||||
"Ask" => &asks, |
||||
"Last" => &lasts, |
||||
"BidSize" => &bid_sizes, |
||||
"AskSize" => &ask_sizes, |
||||
"Volume" => &volumes, |
||||
"High" => &highs, |
||||
"Open" => &opens, |
||||
"Low" => &lows, |
||||
"Close" => &closes, |
||||
"Delta" => &deltas, |
||||
"Gamma" => &gammas, |
||||
"Theta" => &thetas, |
||||
"Vegas" => &vegas, |
||||
"Rho" => &rhos, |
||||
"Volatility" => &volatilitys, |
||||
|
||||
)?) |
||||
} |
||||
@ -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 |
||||
)?) |
||||
|
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
mod data; |
||||
|
||||
pub type DynResult<T> = Result<T, Box< dyn std::error::Error>>; |
||||
|
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
|
||||
|
||||
} |
||||
Loading…
Reference in new issue