nautilus_model/instruments/
equity.rs1use std::hash::{Hash, Hasher};
17
18use nautilus_core::{
19 correctness::{check_equal_u8, check_valid_string_optional, FAILED},
20 UnixNanos,
21};
22use rust_decimal::Decimal;
23use serde::{Deserialize, Serialize};
24use ustr::Ustr;
25
26use super::{any::InstrumentAny, Instrument};
27use crate::{
28 enums::{AssetClass, InstrumentClass, OptionKind},
29 identifiers::{InstrumentId, Symbol},
30 types::{
31 currency::Currency,
32 money::Money,
33 price::{check_positive_price, Price},
34 quantity::Quantity,
35 },
36};
37
38#[repr(C)]
40#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
41#[cfg_attr(
42 feature = "python",
43 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
44)]
45pub struct Equity {
46 pub id: InstrumentId,
48 pub raw_symbol: Symbol,
50 pub isin: Option<Ustr>,
52 pub currency: Currency,
54 pub price_precision: u8,
56 pub price_increment: Price,
58 pub margin_init: Decimal,
60 pub margin_maint: Decimal,
62 pub maker_fee: Decimal,
64 pub taker_fee: Decimal,
66 pub lot_size: Option<Quantity>,
68 pub max_quantity: Option<Quantity>,
70 pub min_quantity: Option<Quantity>,
72 pub max_price: Option<Price>,
74 pub min_price: Option<Price>,
76 pub ts_event: UnixNanos,
78 pub ts_init: UnixNanos,
80}
81
82impl Equity {
83 #[allow(clippy::too_many_arguments)]
89 pub fn new_checked(
90 id: InstrumentId,
91 raw_symbol: Symbol,
92 isin: Option<Ustr>,
93 currency: Currency,
94 price_precision: u8,
95 price_increment: Price,
96 lot_size: Option<Quantity>,
97 max_quantity: Option<Quantity>,
98 min_quantity: Option<Quantity>,
99 max_price: Option<Price>,
100 min_price: Option<Price>,
101 margin_init: Option<Decimal>,
102 margin_maint: Option<Decimal>,
103 maker_fee: Option<Decimal>,
104 taker_fee: Option<Decimal>,
105 ts_event: UnixNanos,
106 ts_init: UnixNanos,
107 ) -> anyhow::Result<Self> {
108 check_valid_string_optional(isin.map(|u| u.as_str()), stringify!(isin))?;
109 check_equal_u8(
110 price_precision,
111 price_increment.precision,
112 stringify!(price_precision),
113 stringify!(price_increment.precision),
114 )?;
115 check_positive_price(price_increment.raw, stringify!(price_increment.raw))?;
116
117 Ok(Self {
118 id,
119 raw_symbol,
120 isin,
121 currency,
122 price_precision,
123 price_increment,
124 lot_size,
125 max_quantity,
126 min_quantity,
127 max_price,
128 min_price,
129 margin_init: margin_init.unwrap_or_default(),
130 margin_maint: margin_maint.unwrap_or_default(),
131 maker_fee: maker_fee.unwrap_or_default(),
132 taker_fee: taker_fee.unwrap_or_default(),
133 ts_event,
134 ts_init,
135 })
136 }
137
138 #[allow(clippy::too_many_arguments)]
140 pub fn new(
141 id: InstrumentId,
142 raw_symbol: Symbol,
143 isin: Option<Ustr>,
144 currency: Currency,
145 price_precision: u8,
146 price_increment: Price,
147 lot_size: Option<Quantity>,
148 max_quantity: Option<Quantity>,
149 min_quantity: Option<Quantity>,
150 max_price: Option<Price>,
151 min_price: Option<Price>,
152 margin_init: Option<Decimal>,
153 margin_maint: Option<Decimal>,
154 maker_fee: Option<Decimal>,
155 taker_fee: Option<Decimal>,
156 ts_event: UnixNanos,
157 ts_init: UnixNanos,
158 ) -> Self {
159 Self::new_checked(
160 id,
161 raw_symbol,
162 isin,
163 currency,
164 price_precision,
165 price_increment,
166 lot_size,
167 max_quantity,
168 min_quantity,
169 max_price,
170 min_price,
171 margin_init,
172 margin_maint,
173 maker_fee,
174 taker_fee,
175 ts_event,
176 ts_init,
177 )
178 .expect(FAILED)
179 }
180}
181
182impl PartialEq<Self> for Equity {
183 fn eq(&self, other: &Self) -> bool {
184 self.id == other.id
185 }
186}
187
188impl Eq for Equity {}
189
190impl Hash for Equity {
191 fn hash<H: Hasher>(&self, state: &mut H) {
192 self.id.hash(state);
193 }
194}
195
196impl Instrument for Equity {
197 fn into_any(self) -> InstrumentAny {
198 InstrumentAny::Equity(self)
199 }
200
201 fn id(&self) -> InstrumentId {
202 self.id
203 }
204
205 fn raw_symbol(&self) -> Symbol {
206 self.raw_symbol
207 }
208
209 fn asset_class(&self) -> AssetClass {
210 AssetClass::Equity
211 }
212
213 fn instrument_class(&self) -> InstrumentClass {
214 InstrumentClass::Spot
215 }
216 fn underlying(&self) -> Option<Ustr> {
217 None
218 }
219
220 fn base_currency(&self) -> Option<Currency> {
221 None
222 }
223
224 fn quote_currency(&self) -> Currency {
225 self.currency
226 }
227
228 fn settlement_currency(&self) -> Currency {
229 self.currency
230 }
231
232 fn isin(&self) -> Option<Ustr> {
233 self.isin
234 }
235
236 fn option_kind(&self) -> Option<OptionKind> {
237 None
238 }
239
240 fn exchange(&self) -> Option<Ustr> {
241 None
242 }
243
244 fn strike_price(&self) -> Option<Price> {
245 None
246 }
247
248 fn activation_ns(&self) -> Option<UnixNanos> {
249 None
250 }
251
252 fn expiration_ns(&self) -> Option<UnixNanos> {
253 None
254 }
255
256 fn is_inverse(&self) -> bool {
257 false
258 }
259
260 fn price_precision(&self) -> u8 {
261 self.price_precision
262 }
263
264 fn size_precision(&self) -> u8 {
265 0
266 }
267
268 fn price_increment(&self) -> Price {
269 self.price_increment
270 }
271
272 fn size_increment(&self) -> Quantity {
273 Quantity::from(1)
274 }
275
276 fn multiplier(&self) -> Quantity {
277 Quantity::from(1)
278 }
279
280 fn lot_size(&self) -> Option<Quantity> {
281 self.lot_size
282 }
283
284 fn max_quantity(&self) -> Option<Quantity> {
285 self.max_quantity
286 }
287
288 fn min_quantity(&self) -> Option<Quantity> {
289 self.min_quantity
290 }
291
292 fn max_notional(&self) -> Option<Money> {
293 None
294 }
295
296 fn min_notional(&self) -> Option<Money> {
297 None
298 }
299
300 fn max_price(&self) -> Option<Price> {
301 self.max_price
302 }
303
304 fn min_price(&self) -> Option<Price> {
305 self.min_price
306 }
307
308 fn ts_event(&self) -> UnixNanos {
309 self.ts_event
310 }
311
312 fn ts_init(&self) -> UnixNanos {
313 self.ts_init
314 }
315}
316
317#[cfg(test)]
321mod tests {
322 use rstest::rstest;
323
324 use crate::instruments::{stubs::*, Equity};
325
326 #[rstest]
327 fn test_equality(equity_aapl: Equity) {
328 let cloned = equity_aapl;
329 assert_eq!(equity_aapl, cloned);
330 }
331}