ledger_models/fintekkers/wrappers/models/
price.rs

1use std::hash::{Hash, Hasher};
2use rust_decimal::Decimal;
3
4use crate::fintekkers::models::price::PriceProto;
5use crate::fintekkers::models::security::SecurityProto;
6use crate::fintekkers::models::util::DecimalValueProto;
7
8use crate::fintekkers::wrappers::models::security::{SecurityProtoBuilder, SecurityWrapper};
9use crate::fintekkers::wrappers::models::utils::datetime::LocalTimestampWrapper;
10use crate::fintekkers::wrappers::models::utils::decimal::DecimalWrapper;
11use crate::fintekkers::wrappers::models::utils::errors::Error;
12use crate::fintekkers::wrappers::models::utils::uuid_wrapper::UUIDWrapper;
13
14//Imports below are for RawDataModelObject related macro. IDE might not complain if you remove
15//them but will fail at compile time
16use prost::Message;
17use crate::fintekkers::wrappers::models::raw_datamodel_object::RawDataModelObject;
18use crate::raw_data_model_object_trait;
19
20#[derive(Clone, Default, Debug)]
21pub struct PriceWrapper {
22    pub proto: PriceProto,
23}
24
25///
26/// When a PriceWrapper is created directly from the proto, we have to
27/// synthesize the wrappers on demand. Ideally higher level wrappers
28/// refer to lower level wrappers via a reference. For instance a SecurityWrapper
29/// would be created and passed into the PriceWrapper as a reference. This would
30/// avoid creation of additional memory in the case where we know that securities
31/// will outlive the price. Example hierarchy.
32///
33/// PriceWrapper:
34///     SecurityWrapper
35///     UUIDWrapper
36///     etc
37///
38/// For now we will just create wrappers when accessor methods are called. E.g.
39/// 'uuid_wrapper(&self)' will create a UUIDWrapper when accessed by cloning the
40/// uuid.
41///
42/// In the longer-term we could optimize for memory by providing price/portfolio/
43/// security/etc caches. When a PriceWrapper is created a reference to a SecurityWrapper
44/// would be passed in, rather than a full wrapper object. The reference to the security
45/// would be owned by the cached, which could use reference counting to decide when to
46/// free up the memory. That way when there is a security and a price on a security, only
47/// one security is held in memory.
48///
49impl PriceWrapper {
50    pub fn new(proto:PriceProto) -> Self {
51        PriceWrapper {
52            proto
53        }
54    }
55
56    pub fn uuid_wrapper(&self) -> UUIDWrapper {
57        UUIDWrapper::new(self.proto.uuid.as_ref().unwrap().clone())
58    }
59
60    pub fn security_wrapper(&self) -> SecurityWrapper {
61        let security_proto = self.proto.security.clone().unwrap();
62        SecurityWrapper {
63            proto: security_proto
64        }
65    }
66}
67
68raw_data_model_object_trait!(PriceWrapper);
69
70impl From<PriceWrapper> for PriceProto {
71    fn from(wrapper:PriceWrapper) -> PriceProto {
72        wrapper.proto
73    }
74}
75
76impl Hash for PriceWrapper {
77    fn hash<H: Hasher>(&self, state: &mut H) {
78        self.proto.uuid.as_ref().unwrap().raw_uuid.hash(state);
79    }
80}
81
82impl PartialEq for PriceWrapper {
83    fn eq(&self, other: &Self) -> bool {
84        self.proto.uuid.as_ref() == other.proto.uuid.as_ref()
85    }
86}
87impl Eq for PriceWrapper {}
88
89pub struct PriceProtoBuilder {
90    as_of: LocalTimestampWrapper,
91    valid_from: LocalTimestampWrapper,
92    valid_to: Option<LocalTimestampWrapper>,
93
94    object_class: String,
95    version: String,
96    is_link: bool,
97
98    uuid: UUIDWrapper,
99    security: Option<SecurityProto>,
100    price: Option<DecimalWrapper>
101}
102
103impl PriceProtoBuilder {
104    pub fn new() -> Self {
105        Self {
106            as_of: LocalTimestampWrapper::now(),
107            valid_from: LocalTimestampWrapper::now(),
108            valid_to: None,
109
110            //This is currently hardcoded, this will change in future versions
111            object_class: "Security".to_string(),
112            //The version is hardcoded, this will change in future versions
113            version: "0.0.1".to_string(),
114            is_link: false,
115
116            uuid: UUIDWrapper::new_random(),
117            security: None,
118            price: None,
119        }
120    }
121
122    pub fn as_of(mut self, as_of: LocalTimestampWrapper) -> Self {
123        self.as_of = as_of.into();
124        self
125    }
126
127    pub fn valid_from(mut self, valid_from: LocalTimestampWrapper) -> Self {
128        self.valid_from = valid_from.into();
129        self
130    }
131
132    pub fn valid_to(mut self, valid_to: LocalTimestampWrapper) -> Self {
133        self.valid_to = valid_to.into();
134        self
135    }
136
137    pub fn object_class(mut self, object_class: String) -> Self {
138        self.object_class = object_class;
139        self
140    }
141
142    pub fn version(mut self, version: String) -> Self {
143        self.version = version;
144        self
145    }
146
147    pub fn is_link(mut self, is_link: bool) -> Self {
148        self.is_link = is_link;
149        self
150    }
151
152    pub fn uuid(mut self, uuid: UUIDWrapper) -> Self {
153        self.uuid = uuid;
154        self
155    }
156
157    pub fn security(mut self, security: SecurityProto) -> Self {
158        self.security = security.into();
159        self
160    }
161
162    pub fn price(mut self, price: DecimalWrapper) -> Self {
163        self.price = price.into();
164        self
165    }
166
167    pub fn build(self) -> Result<PriceProto, Error> {
168        let valid_to = match self.valid_to {
169            Some(..) => Some(self.valid_to.unwrap().proto),
170            None => None
171        };
172
173        Ok(PriceProto {
174            as_of: Some(self.as_of.into()),
175            valid_from: Some(self.valid_from.into()),
176            valid_to,
177
178            object_class: self.object_class,
179            version: self.version,
180            is_link: self.is_link,
181
182            uuid: Some(self.uuid.into()),
183            price: Some(DecimalValueProto {
184                arbitrary_precision_value: self.price.unwrap().to_string()
185            }),
186            security: Some(
187                self.security.unwrap()
188            ),
189        })
190    }
191
192    pub fn dummy_price_wrapper(&self, price_decimal: Decimal) -> PriceWrapper {
193        let security_proto = SecurityProtoBuilder::new()
194            .settlement_currency("CAD".to_string())
195            .asset_class("Asset Class".to_string())
196            .build().unwrap();//.expect("Could not build security");
197
198        let price_proto = PriceProtoBuilder::new()
199            .price(DecimalWrapper::from(price_decimal))
200            .security(
201                security_proto
202            )
203            .build().unwrap();//.expect("Could not build security");
204
205        PriceWrapper{ proto: price_proto }
206    }
207}
208
209#[cfg(test)]
210mod test {
211    use rust_decimal_macros::dec;
212
213    use super::PriceProtoBuilder;
214
215    #[test]
216    fn test_proto_to_date() {
217        let number = dec!(-1.23);
218
219        let price_proto = PriceProtoBuilder::new()
220            .dummy_price_wrapper(number)
221            .proto;
222
223        let price = price_proto.price.unwrap();
224        let price_str = price.arbitrary_precision_value;
225
226        assert_eq!(price_str, number.to_string());
227    }
228}