From e1c879e0171e4943d85cbf70031bc2f0cc0f077e Mon Sep 17 00:00:00 2001 From: TP Date: Tue, 4 Oct 2022 23:46:08 -0400 Subject: [PATCH] working on instrument structs --- rust/aat-core/src/core/instrument/base.rs | 562 ++++++---------------- rust/aat-core/src/core/instrument/bond.rs | 38 ++ rust/aat-core/src/core/instrument/mod.rs | 2 + 3 files changed, 195 insertions(+), 407 deletions(-) diff --git a/rust/aat-core/src/core/instrument/base.rs b/rust/aat-core/src/core/instrument/base.rs index 0ab6eecb..65e59dd7 100644 --- a/rust/aat-core/src/core/instrument/base.rs +++ b/rust/aat-core/src/core/instrument/base.rs @@ -11,33 +11,43 @@ pub struct InstrumentBaseData { instrument_type: InstrumentType, } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct InstrumentData { - base: InstrumentBaseData, +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct InstrumentExtraData { + currency: Optional, + underlying: Optional, // trading_day: TradingDay, broker_exchange: Optional, broker_id: Optional, - // currency: Optional, - currency: Optional, - // underlying: Optional, - underlying: Optional, - // leg1: Optional, + price_increment: Optional, + unit_value: Optional, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct InstrumentLegData { leg1: Optional, - // leg2: Optional, leg2: Optional, - leg1_side: Optional, leg2_side: Optional, +} +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct InstrumentDerivativeData { expiration: Optional>, contract_month: Optional, - price_increment: Optional, - unit_value: Optional, option_type: Optional, } +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct InstrumentData { + // TODO enum out with the supported real types + base: InstrumentBaseData, + extra: InstrumentExtraData, + legs: InstrumentLegData, + derivative: InstrumentDerivativeData, +} + pub trait Instrument { fn get_id(&self) -> Id; fn get_type(&self) -> InstrumentType; @@ -45,10 +55,45 @@ pub trait Instrument { fn get_exchange(&self) -> ExchangeType; } +pub trait InstrumentExtra { + fn get_currency(&self) -> Optional; + fn get_underlying(&self) -> Optional; + // trading_day: TradingDay, + fn get_broker_exchange(&self) -> Optional; + fn get_broker_id(&self) -> Optional; + fn get_price_increment(&self) -> Optional; + fn get_unit_value(&self) -> Optional; +} + +pub trait InstrumentLeg { + fn get_leg1(&self) -> Optional; + fn get_leg2(&self) -> Optional; + fn get_leg1_side(&self) -> Optional; + fn get_leg2_side(&self) -> Optional; +} + +pub trait InstrumentDerivative { + fn get_expiration(&self) -> Optional>; + fn get_contract_month(&self) -> Optional; + fn get_option_type(&self) -> Optional; +} + pub trait HasInstrumentBaseData { fn get_base(&self) -> &InstrumentBaseData; } +pub trait HasInstrumentExtraData { + fn get_extra(&self) -> &InstrumentExtraData; +} + +pub trait HasInstrumentLegData { + fn get_legs(&self) -> &InstrumentLegData; +} + +pub trait HasInstrumentDerivativeData { + fn get_derivative(&self) -> &InstrumentDerivativeData; +} + impl Instrument for T { fn get_id(&self) -> Id { self.get_base().id @@ -67,6 +112,70 @@ impl Instrument for T { } } +impl InstrumentExtra for T { + fn get_currency(&self) -> Optional { + self.get_extra().currency + } + + fn get_underlying(&self) -> Optional { + self.get_extra().underlying + } + + fn get_broker_exchange(&self) -> Optional { + self.get_extra().broker_exchange + } + + fn get_broker_id(&self) -> Optional { + self.get_extra().broker_id.clone() + // let extra = self.get_extra(); + // match &extra.broker_id { + // Some(s) => Some(*s.clne), + // _ => None, + + // } + } + + fn get_price_increment(&self) -> Optional { + self.get_extra().price_increment + } + + fn get_unit_value(&self) -> Optional { + self.get_extra().unit_value + } +} + +impl InstrumentLeg for T { + fn get_leg1(&self) -> Optional { + self.get_legs().leg1 + } + + fn get_leg2(&self) -> Optional { + self.get_legs().leg2 + } + + fn get_leg1_side(&self) -> Optional { + self.get_legs().leg1_side + } + + fn get_leg2_side(&self) -> Optional { + self.get_legs().leg2_side + } +} + +impl InstrumentDerivative for T { + fn get_expiration(&self) -> Optional> { + self.get_derivative().expiration + } + + fn get_contract_month(&self) -> Optional { + self.get_derivative().contract_month + } + + fn get_option_type(&self) -> Optional { + self.get_derivative().option_type + } +} + impl PartialEq for InstrumentBaseData { fn eq(&self, other: &Self) -> bool { self.id == other.id @@ -75,6 +184,40 @@ impl PartialEq for InstrumentBaseData { impl Eq for InstrumentBaseData {} +impl Default for InstrumentExtraData { + fn default() -> Self { + Self { + currency: None, + underlying: None, + broker_exchange: None, + broker_id: None, + price_increment: None, + unit_value: None, + } + } +} + +impl Default for InstrumentLegData { + fn default() -> Self { + Self { + leg1: None, + leg2: None, + leg1_side: None, + leg2_side: None, + } + } +} + +impl Default for InstrumentDerivativeData { + fn default() -> Self { + Self { + contract_month: None, + expiration: None, + option_type: None, + } + } +} + /**********************************/ #[cfg(test)] use std::default::default; @@ -93,398 +236,3 @@ mod base_instrument_data_tests { assert_eq!((d2 == d3), false); } } - -// impl Instrument { -// pub fn new() -> Instrument { - -// } -// } - -// def __init__( -// self, -// name: str, -// type: InstrumentType = InstrumentType.EQUITY, -// exchange: ExchangeType = ExchangeType(""), -// **kwargs: Union[ -// str, # key -// "Instrument", # instrument -// Side, # side -// ExchangeType, # exchange -// TradingDay, # trading calendar -// ], -// ) -> None: -// """construct a new instrument instance - -// Args: -// name (str): the asset's common name, relative to whatever -// the exchange's standard is - -// type (InstrumentType): the instrument type, dictates the required -// extra kwargs - -// exchange (ExchangeType): the exchange the instrument can be traded -// through -// Kwargs: -// trading_day (TradingDay): per-exchange trading hours -// Applies to: All - -// broker_exchange (str): Underlying exchange to use (e.g. not aat.exchange, -// but real exchange in cases where aat is wrapping a -// broker like IB, TDA, etc) -// Applies to: All - -// broker_id (str): Broker's id if available -// Applies to: All - -// currency (Instrument): Underlying currency -// Applies to: All - -// underlying (Instrument): the underlying asset -// Applies to: OPTION, FUTURE, FUTURESOPTION - -// leg1 (Instrument): -// Applies to: PAIR, SPREAD - -// leg2 (Instrument): -// Applies to: PAIR, SPREAD - -// leg1_side (Side): -// Applies to: SPREAD - -// leg2_side (Side): -// Applies to: SPREAD - -// """ - -// # Validation -// assert isinstance(name, str) -// assert isinstance(type, InstrumentType) -// assert isinstance(exchange, ExchangeType) or not exchange - -// # Required fields -// self.__name = name # noop if already exists - -// if hasattr(self, "_Instrument__type"): -// assert self.__type == type -// else: -// self.__type = type - -// # FIXME ugly -// if ( -// exchange -// and hasattr(self, "_Instrument__exchanges") -// and exchange not in self.__exchanges -// ): -// # append this exchange to list of available -// self.__exchanges.append(exchange) - -// # set attribute -// self.__exchange = exchange - -// elif exchange: -// # create new list with this one -// self.__exchanges = [exchange] - -// # set attribute -// self.__exchange = exchange - -// elif hasattr(self, "_Instrument__exchanges"): -// # do nothing -// pass - -// else: -// # no exchange known and no exchange provided -// self.__exchanges = [] - -// # set attribute -// self.__exchange = exchange - -// # Optional Fields -// if hasattr(self, "_Instrument__trading_day") and self.__trading_day is not None: -// assert kwargs.get( -// "trading_day" -// ) is None or self.__trading_day == kwargs.get("trading_day") -// else: -// self.__trading_day: Optional[TradingDay] = cast( -// TradingDay, kwargs.get("trading_day") -// ) - -// if ( -// hasattr(self, "_Instrument__broker_exchange") -// and self.__broker_exchange is not None -// ): -// assert kwargs.get( -// "broker_exchange" -// ) is None or self.__broker_exchange == kwargs.get("broker_exchange") -// else: -// self.__broker_exchange: Optional[ExchangeType] = cast( -// ExchangeType, kwargs.get("broker_exchange") -// ) - -// if hasattr(self, "_Instrument__broker_id") and self.__broker_id is not None: -// assert kwargs.get("broker_id") is None or self.__broker_id == kwargs.get( -// "broker_id" -// ) -// else: -// self.__broker_id: str = cast(str, kwargs.get("broker_id")) - -// if hasattr(self, "_Instrument__currency") and self.__currency is not None: -// assert kwargs.get("currency") is None or self.__currency == kwargs.get( -// "currency" -// ) -// else: -// self.__currency: "Instrument" = cast("Instrument", kwargs.get("currency")) - -// if hasattr(self, "_Instrument__underlying") and self.__underlying is not None: -// assert kwargs.get("underlying") is None or self.__underlying == kwargs.get( -// "underlying" -// ) -// else: -// self.__underlying: Optional["Instrument"] = cast( -// "Instrument", kwargs.get("underlying") -// ) - -// if hasattr(self, "_Instrument__leg1") and self.__leg1 is not None: -// assert kwargs.get("leg1") is None or self.__leg1 == kwargs.get("leg1") -// else: -// self.__leg1: Optional["Instrument"] = cast("Instrument", kwargs.get("leg1")) - -// if hasattr(self, "_Instrument__leg2") and self.__leg2 is not None: -// assert kwargs.get("leg2") is None or self.__leg2 == kwargs.get("leg2") -// else: -// self.__leg2: Optional["Instrument"] = cast("Instrument", kwargs.get("leg2")) - -// if hasattr(self, "_Instrument__leg1_side") and self.__leg1_side is not None: -// assert kwargs.get("leg1_side") is None or self.__leg1_side == kwargs.get( -// "leg1_side" -// ) -// else: -// self.__leg1_side: Optional[Side] = kwargs.get("leg1_side") - -// if hasattr(self, "_Instrument__leg2_side") and self.__leg2_side is not None: -// assert kwargs.get("leg2_side") is None or self.__leg2_side == kwargs.get( -// "leg2_side" -// ) -// else: -// self.__leg2_side: Optional[Side] = kwargs.get("leg2_side") - -// if hasattr(self, "_Instrument__expiration"): -// assert kwargs.get("expiration") is None or self.__expiration == kwargs.get( -// "expiration" -// ) -// else: -// self.__expiration: datetime = cast(datetime, kwargs.get("expiration")) - -// if hasattr(self, "_Instrument__price_increment"): -// assert kwargs.get( -// "price_increment" -// ) is None or self.__price_increment == kwargs.get("price_increment") -// elif kwargs.get("price_increment") is not None: -// self.__price_increment = float( -// kwargs.get("price_increment") # type: ignore -// ) -// else: -// self.__price_increment = None - -// if hasattr(self, "_Instrument__unit_value"): -// assert kwargs.get("unit_value") is None or self.__unit_value == kwargs.get( -// "unit_value" -// ) -// elif kwargs.get("unit_value") is not None: -// self.__unit_value = float(kwargs.get("unit_value")) # type: ignore -// else: -// self.__unit_value = None - -// if hasattr(self, "_Instrument__option_type"): -// assert kwargs.get( -// "option_type" -// ) is None or self.__option_type == kwargs.get("option_type") -// else: -// self.__option_type: OptionType = cast(OptionType, kwargs.get("option_type")) - -// # Optional Fields Validation -// assert isinstance(self.__broker_exchange, (None.__class__, str)) -// assert isinstance(self.__broker_id, (None.__class__, str)) -// assert isinstance(self.__currency, (None.__class__, Instrument)) -// assert isinstance(self.__underlying, (None.__class__, Instrument)) -// assert isinstance(self.__leg1, (None.__class__, Instrument)) -// assert isinstance(self.__leg2, (None.__class__, Instrument)) -// assert isinstance(self.__leg1_side, (None.__class__, Side)) -// assert isinstance(self.__leg2_side, (None.__class__, Side)) -// assert isinstance(self.__expiration, (None.__class__, datetime)) -// assert isinstance(self.__unit_value, (None.__class__, float)) -// assert isinstance(self.__price_increment, (None.__class__, float)) -// assert isinstance(self.__option_type, (None.__class__, OptionType)) - -// # install into instrumentdb, noop if already there -// self._instrumentdb.add(self) - -// # ******** # -// # Readonly # -// # ******** # -// @property -// def name(self) -> str: -// return self.__name - -// @property -// def type(self) -> InstrumentType: -// return self.__type - -// @property -// def exchanges(self) -> List[ExchangeType]: -// return self.__exchanges - -// @property -// def exchange(self) -> ExchangeType: -// return self.__exchange - -// def tradingLines(self, exchange: ExchangeType = None) -> List["Instrument"]: -// """Returns other exchanges that the same instrument trades on - -// Returns: -// exchange (ExchangeType): Exchange to filter by -// """ -// return self._instrumentdb.instruments(self.name, self.type, exchange) - -// def synthetics( -// self, type: InstrumentType = None, exchange: ExchangeType = None -// ) -> List["Instrument"]: -// """Returns other instruments with the same name - -// Returns: -// type (InstrumentType): instrument type to filter by, e.g. for an equity, filter to only get ADRs -// exchange (ExchangeType): Exchange to filter by -// """ -// return self._instrumentdb.instruments(self.name, type, exchange) - -// # ******** # -// # Optional # -// # ******** # -// @property -// def tradingDay(self) -> TradingDay: -// return self.__trading_day - -// @property -// def brokerExchange(self) -> Optional[ExchangeType]: -// return self.__broker_exchange - -// @property -// def brokerId(self) -> Optional[str]: -// return self.__broker_id - -// @property -// def currency(self) -> Optional["Instrument"]: -// return self.__currency - -// @property -// def underlying(self) -> Optional["Instrument"]: -// return self.__underlying - -// @property -// def leg1(self) -> Optional["Instrument"]: -// return self.__leg1 - -// @property -// def leg2(self) -> Optional["Instrument"]: -// return self.__leg2 - -// @property -// def leg1Side(self) -> Optional[Side]: -// return self.__leg1_side - -// @property -// def leg2Side(self) -> Optional[Side]: -// return self.__leg2_side - -// @property -// def expiration(self) -> Optional[datetime]: -// return self.__expiration - -// @property -// def unitValue(self) -> Optional[float]: -// return self.__unit_value - -// @property -// def priceIncrement(self) -> Optional[float]: -// return self.__price_increment - -// @property -// def optionType(self) -> Optional[OptionType]: -// return self.__option_type - -// def __eq__(self, other: object) -> bool: -// if other is None: -// return False -// if not isinstance(other, Instrument): -// raise TypeError() -// return self.name == other.name and self.type == other.type - -// def __hash__(self) -> int: -// return hash(str(self)) - -// def json(self) -> dict: -// return { -// "name": self.name, -// "type": self.type.value, -// "exchanges": [v.json() for v in self.exchanges] if self.exchanges else [], -// "broker_exchange": self.brokerExchange, -// "broker_id": self.brokerId, -// "currency": self.currency.json() if self.currency else "", -// "underlying": self.underlying.json() if self.underlying else "", -// "leg1": self.leg1.json() if self.leg1 else "", -// "leg2": self.leg2.json() if self.leg2 else "", -// "leg1_side": self.leg1Side.value if self.leg1Side else "", -// "leg2_side": self.leg2Side.value if self.leg2Side else "", -// "expiration": self.expiration.timestamp() if self.expiration else "", -// "price_increment": self.priceIncrement or "", -// "unit_value": self.unitValue or "", -// "option_type": self.optionType.value if self.optionType else "", -// } - -// @staticmethod -// def fromJson(jsn: dict) -> "Instrument": -// kwargs = {} -// kwargs["name"] = jsn["name"] -// kwargs["type"] = InstrumentType(jsn["type"]) -// kwargs["exchanges"] = [ExchangeType.fromJson(e) for e in jsn["exchanges"]] - -// if "broker_exchange" in jsn and jsn["broker_exchange"]: -// kwargs["broker_exchange"] = jsn["broker_exchange"] - -// if "broker_id" in jsn and jsn["broker_id"]: -// kwargs["broker_id"] = jsn["broker_id"] - -// if "currency" in jsn and jsn["currency"]: -// kwargs["currency"] = Instrument.fromJson(jsn["currency"]) - -// if "underlying" in jsn and jsn["underlying"]: -// kwargs["underlying"] = Instrument.fromJson(jsn["underlying"]) - -// if "leg1" in jsn and jsn["leg1"]: -// kwargs["leg1"] = Instrument.fromJson(jsn["leg1"]) - -// if "leg2" in jsn and jsn["leg2"]: -// kwargs["leg2"] = Instrument.fromJson(jsn["leg2"]) - -// if "leg1_side" in jsn and jsn["leg1_side"]: -// kwargs["leg1_side"] = Side(jsn["leg1_side"]) - -// if "leg2_side" in jsn and jsn["leg2_side"]: -// kwargs["leg2_side"] = Side(jsn["leg2_side"]) - -// if "expiration" in jsn and jsn["expiration"]: -// kwargs["expiration"] = datetime.fromtimestamp(jsn["expiration"]) - -// if "price_increment" in jsn and jsn["price_increment"]: -// kwargs["price_increment"] = jsn["price_increment"] - -// if "unit_value" in jsn and jsn["unit_value"]: -// kwargs["unit_value"] = jsn["unit_value"] - -// if "option_type" in jsn and jsn["option_type"]: -// kwargs["option_type"] = OptionType(jsn["option_type"]) - -// return Instrument(**kwargs) - -// def __repr__(self) -> str: -// return f"Instrument({self.name}-{self.type})" diff --git a/rust/aat-core/src/core/instrument/bond.rs b/rust/aat-core/src/core/instrument/bond.rs index e69de29b..4564cb7f 100644 --- a/rust/aat-core/src/core/instrument/bond.rs +++ b/rust/aat-core/src/core/instrument/bond.rs @@ -0,0 +1,38 @@ +use super::{ + HasInstrumentBaseData, HasInstrumentDerivativeData, HasInstrumentExtraData, + HasInstrumentLegData, InstrumentBaseData, InstrumentDerivativeData, InstrumentExtraData, + InstrumentLegData, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +struct Bond { + base: InstrumentBaseData, + extra: InstrumentExtraData, + legs: InstrumentLegData, + derivative: InstrumentDerivativeData, +} + +impl HasInstrumentBaseData for Bond { + fn get_base(&self) -> &InstrumentBaseData { + &self.base + } +} + +impl HasInstrumentExtraData for Bond { + fn get_extra(&self) -> &InstrumentExtraData { + &self.extra + } +} + +impl HasInstrumentLegData for Bond { + fn get_legs(&self) -> &InstrumentLegData { + &self.legs + } +} + +impl HasInstrumentDerivativeData for Bond { + fn get_derivative(&self) -> &InstrumentDerivativeData { + &self.derivative + } +} diff --git a/rust/aat-core/src/core/instrument/mod.rs b/rust/aat-core/src/core/instrument/mod.rs index cbcb6ac7..a645b13f 100644 --- a/rust/aat-core/src/core/instrument/mod.rs +++ b/rust/aat-core/src/core/instrument/mod.rs @@ -1,3 +1,5 @@ mod base; +mod bond; pub use base::*; +pub use bond::*;