Skip to content

Commit

Permalink
Merge pull request #2 from Overmuse/SR/remove_percent
Browse files Browse the repository at this point in the history
SR/remove percent
  • Loading branch information
SebRollen authored Jul 26, 2021
2 parents a082d3e + 9f61154 commit c5dcb43
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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<Utc>, DateTime<Utc>),
#[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,
}
46 changes: 22 additions & 24 deletions src/position_intents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,32 @@ 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<Self, Error> {
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)),
}
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TickerSpec {
pub enum Identifier {
Ticker(String),
All,
}

impl<T: ToString> From<T> for TickerSpec {
impl<T: ToString> From<T> for Identifier {
fn from(s: T) -> Self {
Self::Ticker(s.to_string())
}
Expand All @@ -52,8 +50,8 @@ impl<T: ToString> From<T> for TickerSpec {
pub struct PositionIntentBuilder {
strategy: String,
sub_strategy: Option<String>,
ticker: TickerSpec,
amount: AmountSpec,
identifier: Identifier,
amount: Amount,
update_policy: UpdatePolicy,
decision_price: Option<Decimal>,
limit_price: Option<Decimal>,
Expand Down Expand Up @@ -104,17 +102,17 @@ 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 {
id: Uuid::new_v4(),
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,
Expand All @@ -138,8 +136,8 @@ pub struct PositionIntent {
#[serde(skip_serializing_if = "Option::is_none")]
pub sub_strategy: Option<String>,
pub timestamp: DateTime<Utc>,
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
Expand All @@ -159,13 +157,13 @@ pub struct PositionIntent {
impl PositionIntent {
pub fn builder(
strategy: impl Into<String>,
ticker: impl Into<TickerSpec>,
amount: AmountSpec,
identifier: impl Into<Identifier>,
amount: Amount,
) -> PositionIntentBuilder {
PositionIntentBuilder {
strategy: strategy.into(),
sub_strategy: None,
ticker: ticker.into(),
identifier: identifier.into(),
amount,
update_policy: UpdatePolicy::Update,
decision_price: None,
Expand All @@ -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))
Expand All @@ -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))
Expand Down

0 comments on commit c5dcb43

Please sign in to comment.