nautilus_model/instruments/
binary_option.rs1use std::hash::{Hash, Hasher};
17
18use nautilus_core::{
19 correctness::{check_equal_u8, 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::{check_positive_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 BinaryOption {
46 pub id: InstrumentId,
48 pub raw_symbol: Symbol,
50 pub asset_class: AssetClass,
52 pub currency: Currency,
54 pub activation_ns: UnixNanos,
56 pub expiration_ns: UnixNanos,
58 pub price_precision: u8,
60 pub size_precision: u8,
62 pub price_increment: Price,
64 pub size_increment: Quantity,
66 pub margin_init: Decimal,
68 pub margin_maint: Decimal,
70 pub maker_fee: Decimal,
72 pub taker_fee: Decimal,
74 pub outcome: Option<Ustr>,
76 pub description: Option<Ustr>,
78 pub max_quantity: Option<Quantity>,
80 pub min_quantity: Option<Quantity>,
82 pub max_notional: Option<Money>,
84 pub min_notional: Option<Money>,
86 pub max_price: Option<Price>,
88 pub min_price: Option<Price>,
90 pub ts_event: UnixNanos,
92 pub ts_init: UnixNanos,
94}
95
96impl BinaryOption {
97 #[allow(clippy::too_many_arguments)]
103 pub fn new_checked(
104 id: InstrumentId,
105 raw_symbol: Symbol,
106 asset_class: AssetClass,
107 currency: Currency,
108 activation_ns: UnixNanos,
109 expiration_ns: UnixNanos,
110 price_precision: u8,
111 size_precision: u8,
112 price_increment: Price,
113 size_increment: Quantity,
114 outcome: Option<Ustr>,
115 description: Option<Ustr>,
116 max_quantity: Option<Quantity>,
117 min_quantity: Option<Quantity>,
118 max_notional: Option<Money>,
119 min_notional: Option<Money>,
120 max_price: Option<Price>,
121 min_price: Option<Price>,
122 margin_init: Option<Decimal>,
123 margin_maint: Option<Decimal>,
124 maker_fee: Option<Decimal>,
125 taker_fee: Option<Decimal>,
126 ts_event: UnixNanos,
127 ts_init: UnixNanos,
128 ) -> anyhow::Result<Self> {
129 check_equal_u8(
130 price_precision,
131 price_increment.precision,
132 stringify!(price_precision),
133 stringify!(price_increment.precision),
134 )?;
135 check_equal_u8(
136 size_precision,
137 size_increment.precision,
138 stringify!(size_precision),
139 stringify!(size_increment.precision),
140 )?;
141 check_positive_price(price_increment.raw, stringify!(price_increment.raw))?;
142 check_positive_quantity(size_increment.raw, stringify!(size_increment.raw))?;
143
144 Ok(Self {
145 id,
146 raw_symbol,
147 asset_class,
148 currency,
149 activation_ns,
150 expiration_ns,
151 price_precision,
152 size_precision,
153 price_increment,
154 size_increment,
155 margin_init: margin_init.unwrap_or_default(),
156 margin_maint: margin_maint.unwrap_or_default(),
157 maker_fee: maker_fee.unwrap_or_default(),
158 taker_fee: taker_fee.unwrap_or_default(),
159 outcome,
160 description,
161 max_quantity,
162 min_quantity,
163 max_notional,
164 min_notional,
165 max_price,
166 min_price,
167 ts_event,
168 ts_init,
169 })
170 }
171
172 #[allow(clippy::too_many_arguments)]
174 pub fn new(
175 id: InstrumentId,
176 raw_symbol: Symbol,
177 asset_class: AssetClass,
178 currency: Currency,
179 activation_ns: UnixNanos,
180 expiration_ns: UnixNanos,
181 price_precision: u8,
182 size_precision: u8,
183 price_increment: Price,
184 size_increment: Quantity,
185 outcome: Option<Ustr>,
186 description: Option<Ustr>,
187 max_quantity: Option<Quantity>,
188 min_quantity: Option<Quantity>,
189 max_notional: Option<Money>,
190 min_notional: Option<Money>,
191 max_price: Option<Price>,
192 min_price: Option<Price>,
193 margin_init: Option<Decimal>,
194 margin_maint: Option<Decimal>,
195 maker_fee: Option<Decimal>,
196 taker_fee: Option<Decimal>,
197 ts_event: UnixNanos,
198 ts_init: UnixNanos,
199 ) -> Self {
200 Self::new_checked(
201 id,
202 raw_symbol,
203 asset_class,
204 currency,
205 activation_ns,
206 expiration_ns,
207 price_precision,
208 size_precision,
209 price_increment,
210 size_increment,
211 outcome,
212 description,
213 max_quantity,
214 min_quantity,
215 max_notional,
216 min_notional,
217 max_price,
218 min_price,
219 margin_init,
220 margin_maint,
221 maker_fee,
222 taker_fee,
223 ts_event,
224 ts_init,
225 )
226 .expect(FAILED)
227 }
228}
229
230impl PartialEq<Self> for BinaryOption {
231 fn eq(&self, other: &Self) -> bool {
232 self.id == other.id
233 }
234}
235
236impl Eq for BinaryOption {}
237
238impl Hash for BinaryOption {
239 fn hash<H: Hasher>(&self, state: &mut H) {
240 self.id.hash(state);
241 }
242}
243
244impl Instrument for BinaryOption {
245 fn into_any(self) -> InstrumentAny {
246 InstrumentAny::BinaryOption(self)
247 }
248
249 fn id(&self) -> InstrumentId {
250 self.id
251 }
252
253 fn raw_symbol(&self) -> Symbol {
254 self.raw_symbol
255 }
256
257 fn asset_class(&self) -> AssetClass {
258 self.asset_class
259 }
260
261 fn instrument_class(&self) -> InstrumentClass {
262 InstrumentClass::BinaryOption
263 }
264
265 fn underlying(&self) -> Option<Ustr> {
266 None
267 }
268
269 fn base_currency(&self) -> Option<Currency> {
270 None
271 }
272
273 fn quote_currency(&self) -> Currency {
274 self.currency
275 }
276
277 fn settlement_currency(&self) -> Currency {
278 self.currency
279 }
280
281 fn isin(&self) -> Option<Ustr> {
282 None
283 }
284
285 fn exchange(&self) -> Option<Ustr> {
286 None
287 }
288
289 fn option_kind(&self) -> Option<OptionKind> {
290 None
291 }
292
293 fn is_inverse(&self) -> bool {
294 false
295 }
296
297 fn price_precision(&self) -> u8 {
298 self.price_precision
299 }
300
301 fn size_precision(&self) -> u8 {
302 self.size_precision
303 }
304
305 fn price_increment(&self) -> Price {
306 self.price_increment
307 }
308
309 fn size_increment(&self) -> Quantity {
310 self.size_increment
311 }
312
313 fn multiplier(&self) -> Quantity {
314 Quantity::from(1)
315 }
316
317 fn lot_size(&self) -> Option<Quantity> {
318 Some(Quantity::from(1))
319 }
320
321 fn max_quantity(&self) -> Option<Quantity> {
322 self.max_quantity
323 }
324
325 fn min_quantity(&self) -> Option<Quantity> {
326 self.min_quantity
327 }
328
329 fn max_price(&self) -> Option<Price> {
330 self.max_price
331 }
332
333 fn min_price(&self) -> Option<Price> {
334 self.min_price
335 }
336
337 fn ts_event(&self) -> UnixNanos {
338 self.ts_event
339 }
340
341 fn ts_init(&self) -> UnixNanos {
342 self.ts_init
343 }
344
345 fn strike_price(&self) -> Option<Price> {
346 None
347 }
348
349 fn activation_ns(&self) -> Option<UnixNanos> {
350 Some(self.activation_ns)
351 }
352
353 fn expiration_ns(&self) -> Option<UnixNanos> {
354 Some(self.expiration_ns)
355 }
356
357 fn max_notional(&self) -> Option<Money> {
358 self.max_notional
359 }
360
361 fn min_notional(&self) -> Option<Money> {
362 self.min_notional
363 }
364}
365
366#[cfg(test)]
370mod tests {
371 use rstest::rstest;
372
373 use crate::instruments::{stubs::*, BinaryOption};
374
375 #[rstest]
376 fn test_equality(binary_option: BinaryOption) {
377 let cloned = binary_option;
378 assert_eq!(binary_option, cloned);
379 }
380}