use super::super::{ComboLeg, Contract, Currency, DeltaNeutralContract, Exchange, OptionRight, SecurityIdType, SecurityType, Symbol};
use crate::Error;
#[derive(Clone, Debug, Default)]
#[must_use = "ContractBuilder does nothing until you call .build()"]
pub struct ContractBuilder {
pub(crate) contract_id: Option<i32>,
pub(crate) symbol: Option<String>,
pub(crate) security_type: Option<SecurityType>,
pub(crate) last_trade_date_or_contract_month: Option<String>,
pub(crate) strike: Option<f64>,
pub(crate) right: Option<OptionRight>,
pub(crate) multiplier: Option<String>,
pub(crate) exchange: Option<String>,
pub(crate) currency: Option<String>,
pub(crate) local_symbol: Option<String>,
pub(crate) primary_exchange: Option<String>,
pub(crate) trading_class: Option<String>,
pub(crate) include_expired: Option<bool>,
pub(crate) security_id_type: Option<SecurityIdType>,
pub(crate) security_id: Option<String>,
pub(crate) combo_legs_description: Option<String>,
pub(crate) combo_legs: Option<Vec<ComboLeg>>,
pub(crate) delta_neutral_contract: Option<DeltaNeutralContract>,
pub(crate) last_trade_date: Option<time::Date>,
pub(crate) issuer_id: Option<String>,
pub(crate) description: Option<String>,
}
impl ContractBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn contract_id(mut self, contract_id: i32) -> Self {
self.contract_id = Some(contract_id);
self
}
pub fn symbol<S: Into<String>>(mut self, symbol: S) -> Self {
self.symbol = Some(symbol.into());
self
}
pub fn security_type(mut self, security_type: SecurityType) -> Self {
self.security_type = Some(security_type);
self
}
pub fn last_trade_date_or_contract_month<S: Into<String>>(mut self, date: S) -> Self {
self.last_trade_date_or_contract_month = Some(date.into());
self
}
pub fn strike(mut self, strike: f64) -> Self {
self.strike = Some(strike);
self
}
pub fn right(mut self, right: OptionRight) -> Self {
self.right = Some(right);
self
}
pub fn multiplier<S: Into<String>>(mut self, multiplier: S) -> Self {
self.multiplier = Some(multiplier.into());
self
}
pub fn exchange<S: Into<String>>(mut self, exchange: S) -> Self {
self.exchange = Some(exchange.into());
self
}
pub fn currency<S: Into<String>>(mut self, currency: S) -> Self {
self.currency = Some(currency.into());
self
}
pub fn local_symbol<S: Into<String>>(mut self, local_symbol: S) -> Self {
self.local_symbol = Some(local_symbol.into());
self
}
pub fn primary_exchange<S: Into<String>>(mut self, primary_exchange: S) -> Self {
self.primary_exchange = Some(primary_exchange.into());
self
}
pub fn trading_class<S: Into<String>>(mut self, trading_class: S) -> Self {
self.trading_class = Some(trading_class.into());
self
}
pub fn include_expired(mut self, include_expired: bool) -> Self {
self.include_expired = Some(include_expired);
self
}
pub fn security_id_type(mut self, security_id_type: SecurityIdType) -> Self {
self.security_id_type = Some(security_id_type);
self
}
pub fn security_id<S: Into<String>>(mut self, security_id: S) -> Self {
self.security_id = Some(security_id.into());
self
}
pub fn combo_legs_description<S: Into<String>>(mut self, combo_legs_description: S) -> Self {
self.combo_legs_description = Some(combo_legs_description.into());
self
}
pub fn combo_legs(mut self, combo_legs: Vec<ComboLeg>) -> Self {
self.combo_legs = Some(combo_legs);
self
}
pub fn delta_neutral_contract(mut self, delta_neutral_contract: DeltaNeutralContract) -> Self {
self.delta_neutral_contract = Some(delta_neutral_contract);
self
}
pub fn last_trade_date(mut self, date: time::Date) -> Self {
self.last_trade_date = Some(date);
self
}
pub fn issuer_id<S: Into<String>>(mut self, issuer_id: S) -> Self {
self.issuer_id = Some(issuer_id.into());
self
}
pub fn description<S: Into<String>>(mut self, description: S) -> Self {
self.description = Some(description.into());
self
}
pub fn stock<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
Self::new()
.symbol(symbol)
.security_type(SecurityType::Stock)
.exchange(exchange)
.currency(currency)
}
pub fn option<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
Self::new()
.symbol(symbol)
.security_type(SecurityType::Option)
.exchange(exchange)
.currency(currency)
}
pub fn futures<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
Self::new()
.symbol(symbol)
.security_type(SecurityType::Future)
.exchange(exchange)
.currency(currency)
}
pub fn continuous_futures<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
Self::new()
.symbol(symbol)
.security_type(SecurityType::ContinuousFuture)
.exchange(exchange)
.currency(currency)
}
pub fn crypto<S: Into<String>>(symbol: S, exchange: S, currency: S) -> Self {
Self::new()
.symbol(symbol)
.security_type(SecurityType::Crypto)
.exchange(exchange)
.currency(currency)
}
pub fn build(self) -> Result<Contract, Error> {
if self.symbol.is_none() && self.local_symbol.is_none() && self.contract_id.is_none() {
return Err(Error::InvalidArgument("Symbol, local_symbol, or contract_id is required".into()));
}
let security_type = self.security_type.clone().unwrap_or_default();
if security_type == SecurityType::Option || security_type == SecurityType::FuturesOption {
if self.strike.is_none() {
return Err(Error::InvalidArgument("Strike price is required for options".into()));
}
if let Some(strike) = self.strike {
if strike < 0.0 {
return Err(Error::InvalidArgument("Strike price cannot be negative".into()));
}
}
if self.right.is_none() {
return Err(Error::InvalidArgument(
"Right (OptionRight::Call or OptionRight::Put) is required for options".into(),
));
}
if self.last_trade_date_or_contract_month.is_none() {
return Err(Error::InvalidArgument("Expiration date is required for options".into()));
}
}
if (security_type == SecurityType::Future || security_type == SecurityType::FuturesOption) && self.last_trade_date_or_contract_month.is_none()
{
return Err(Error::InvalidArgument("Contract month is required for futures".into()));
}
Ok(Contract {
contract_id: self.contract_id.unwrap_or(0),
symbol: Symbol::from(self.symbol.unwrap_or_default()),
security_type,
last_trade_date_or_contract_month: self.last_trade_date_or_contract_month.unwrap_or_default(),
strike: self.strike.unwrap_or(0.0),
right: self.right,
multiplier: self.multiplier.unwrap_or_default(),
exchange: Exchange::from(self.exchange.unwrap_or_default()),
currency: Currency::from(self.currency.unwrap_or_default()),
local_symbol: self.local_symbol.unwrap_or_default(),
primary_exchange: Exchange::from(self.primary_exchange.unwrap_or_default()),
trading_class: self.trading_class.unwrap_or_default(),
include_expired: self.include_expired.unwrap_or(false),
security_id_type: self.security_id_type,
security_id: self.security_id.unwrap_or_default(),
combo_legs_description: self.combo_legs_description.unwrap_or_default(),
combo_legs: self.combo_legs.unwrap_or_default(),
delta_neutral_contract: self.delta_neutral_contract,
last_trade_date: self.last_trade_date,
issuer_id: self.issuer_id.unwrap_or_default(),
description: self.description.unwrap_or_default(),
})
}
}
#[cfg(test)]
mod tests;