diff --git a/Cargo.toml b/Cargo.toml index 8027197..775868f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trading-base" -version = "0.1.0" +version = "0.2.0" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/lib.rs b/src/lib.rs index 44cd3f1..b13efba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use thiserror::Error; mod position_intents; pub use position_intents::{ - AmountSpec, PositionIntent, PositionIntentBuilder, TickerSpec, UpdatePolicy, + Amount, Identifier, PositionIntent, PositionIntentBuilder, UpdatePolicy, }; mod trade_intents; pub use trade_intents::{OrderType, TimeInForce, TradeIntent}; @@ -13,9 +13,9 @@ pub enum Error { #[error( "Non-`Zero` `AmountSpec`s of different type cannot be merged.\nLeft: {0:?}, Right: {1:?}" )] - IncompatibleAmountError(AmountSpec, AmountSpec), + IncompatibleAmountError(Amount, Amount), #[error("Cannot create PositionIntent with `before` < `after`. \nBefore: {0}, After: {1}")] InvalidBeforeAfter(DateTime, DateTime), - #[error("TickerSpec `All` can only be used with the `Dollars` and `Shares` `AmountSpec`s")] + #[error("Identifier `All` can only be used with the `Dollars` and `Shares` `Amount`s")] InvalidCombination, } diff --git a/src/position_intents.rs b/src/position_intents.rs index cb748ba..5f853b5 100644 --- a/src/position_intents.rs +++ b/src/position_intents.rs @@ -15,21 +15,19 @@ pub enum UpdatePolicy { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "snake_case")] -pub enum AmountSpec { +pub enum Amount { Dollars(Decimal), Shares(Decimal), - Percent(Decimal), Zero, } -impl AmountSpec { +impl Amount { pub fn merge(self, other: Self) -> Result { match (self, other) { - (AmountSpec::Dollars(x), AmountSpec::Dollars(y)) => Ok(AmountSpec::Dollars(x + y)), - (AmountSpec::Shares(x), AmountSpec::Shares(y)) => Ok(AmountSpec::Shares(x + y)), - (AmountSpec::Percent(x), AmountSpec::Percent(y)) => Ok(AmountSpec::Percent(x + y)), - (AmountSpec::Zero, AmountSpec::Zero) => Ok(AmountSpec::Zero), - (AmountSpec::Zero, y) => Ok(y), - (x, AmountSpec::Zero) => Ok(x), + (Amount::Dollars(x), Amount::Dollars(y)) => Ok(Amount::Dollars(x + y)), + (Amount::Shares(x), Amount::Shares(y)) => Ok(Amount::Shares(x + y)), + (Amount::Zero, Amount::Zero) => Ok(Amount::Zero), + (Amount::Zero, y) => Ok(y), + (x, Amount::Zero) => Ok(x), (x, y) => Err(Error::IncompatibleAmountError(x, y)), } } @@ -37,12 +35,12 @@ impl AmountSpec { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "lowercase")] -pub enum TickerSpec { +pub enum Identifier { Ticker(String), All, } -impl From for TickerSpec { +impl From for Identifier { fn from(s: T) -> Self { Self::Ticker(s.to_string()) } @@ -52,8 +50,8 @@ impl From for TickerSpec { pub struct PositionIntentBuilder { strategy: String, sub_strategy: Option, - ticker: TickerSpec, - amount: AmountSpec, + identifier: Identifier, + amount: Amount, update_policy: UpdatePolicy, decision_price: Option, limit_price: Option, @@ -104,9 +102,9 @@ impl PositionIntentBuilder { return Err(Error::InvalidBeforeAfter(before, after)); } } - match (self.ticker.clone(), self.amount.clone()) { - (TickerSpec::All, AmountSpec::Dollars(_)) => return Err(Error::InvalidCombination), - (TickerSpec::All, AmountSpec::Shares(_)) => return Err(Error::InvalidCombination), + match (self.identifier.clone(), self.amount.clone()) { + (Identifier::All, Amount::Dollars(_)) => return Err(Error::InvalidCombination), + (Identifier::All, Amount::Shares(_)) => return Err(Error::InvalidCombination), _ => (), } Ok(PositionIntent { @@ -114,7 +112,7 @@ impl PositionIntentBuilder { strategy: self.strategy, sub_strategy: self.sub_strategy, timestamp: Utc::now(), - ticker: self.ticker, + identifier: self.identifier, amount: self.amount, update_policy: self.update_policy, decision_price: self.decision_price, @@ -138,8 +136,8 @@ pub struct PositionIntent { #[serde(skip_serializing_if = "Option::is_none")] pub sub_strategy: Option, pub timestamp: DateTime, - pub ticker: TickerSpec, - pub amount: AmountSpec, + pub identifier: Identifier, + pub amount: Amount, pub update_policy: UpdatePolicy, /// The price at which the decision was made to send a position request. This can be used by /// other parts of the app for execution analysis. This field might also be used for @@ -159,13 +157,13 @@ pub struct PositionIntent { impl PositionIntent { pub fn builder( strategy: impl Into, - ticker: impl Into, - amount: AmountSpec, + identifier: impl Into, + amount: Amount, ) -> PositionIntentBuilder { PositionIntentBuilder { strategy: strategy.into(), sub_strategy: None, - ticker: ticker.into(), + identifier: identifier.into(), amount, update_policy: UpdatePolicy::Update, decision_price: None, @@ -184,7 +182,7 @@ mod test { #[test] fn can_construct_position_intent() { - let builder = PositionIntent::builder("A", "AAPL", AmountSpec::Dollars(Decimal::new(1, 0))); + let builder = PositionIntent::builder("A", "AAPL", Amount::Dollars(Decimal::new(1, 0))); let _intent = builder .sub_strategy("B") .decision_price(Decimal::new(2, 0)) @@ -199,7 +197,7 @@ mod test { #[test] fn can_serialize_and_deserialize() { - let builder = PositionIntent::builder("A", "AAPL", AmountSpec::Shares(Decimal::new(1, 0))); + let builder = PositionIntent::builder("A", "AAPL", Amount::Shares(Decimal::new(1, 0))); let intent = builder .sub_strategy("B") .decision_price(Decimal::new(2, 0))