Framed inital data module: equity, crypto, options, price_history. Created traits for data service.

master
Griffiths Lott 3 years ago
commit a7079454a2
  1. 3
      .gitignore
  2. 11
      Cargo.toml
  3. 5
      src/data.rs
  4. 49
      src/data/crypto.rs
  5. 58
      src/data/data_service.rs
  6. 51
      src/data/equity.rs
  7. 193
      src/data/options.rs
  8. 56
      src/data/price_history.rs
  9. 11
      src/lib.rs

3
.gitignore vendored

@ -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" => &timestamp
)?)
}

@ -0,0 +1,11 @@
mod data;
pub type DynResult<T> = Result<T, Box< dyn std::error::Error>>;
#[cfg(test)]
mod tests {
use super::*;
}
Loading…
Cancel
Save