nautilus_model/instruments/
binary_option.rs1use std::hash::{Hash, Hasher};
17
18use nautilus_core::{
19 UnixNanos,
20 correctness::{FAILED, check_equal_u8},
21};
22use rust_decimal::Decimal;
23use serde::{Deserialize, Serialize};
24use ustr::Ustr;
25
26use super::{Instrument, any::InstrumentAny};
27use crate::{
28 enums::{AssetClass, InstrumentClass, OptionKind},
29 identifiers::{InstrumentId, Symbol},
30 types::{
31 currency::Currency,
32 money::Money,
33 price::{Price, check_positive_price},
34 quantity::{Quantity, check_positive_quantity},
35 },
36};
37
38#[repr(C)]
40#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
41#[cfg_attr(
42 feature = "python",
43 pyo3::pyclass(module = "posei_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)]
106 pub fn new_checked(
107 instrument_id: InstrumentId,
108 raw_symbol: Symbol,
109 asset_class: AssetClass,
110 currency: Currency,
111 activation_ns: UnixNanos,
112 expiration_ns: UnixNanos,
113 price_precision: u8,
114 size_precision: u8,
115 price_increment: Price,
116 size_increment: Quantity,
117 outcome: Option<Ustr>,
118 description: Option<Ustr>,
119 max_quantity: Option<Quantity>,
120 min_quantity: Option<Quantity>,
121 max_notional: Option<Money>,
122 min_notional: Option<Money>,
123 max_price: Option<Price>,
124 min_price: Option<Price>,
125 margin_init: Option<Decimal>,
126 margin_maint: Option<Decimal>,
127 maker_fee: Option<Decimal>,
128 taker_fee: Option<Decimal>,
129 ts_event: UnixNanos,
130 ts_init: UnixNanos,
131 ) -> anyhow::Result<Self> {
132 check_equal_u8(
133 price_precision,
134 price_increment.precision,
135 stringify!(price_precision),
136 stringify!(price_increment.precision),
137 )?;
138 check_equal_u8(
139 size_precision,
140 size_increment.precision,
141 stringify!(size_precision),
142 stringify!(size_increment.precision),
143 )?;
144 check_positive_price(price_increment, stringify!(price_increment))?;
145 check_positive_quantity(size_increment, stringify!(size_increment))?;
146
147 Ok(Self {
148 id: instrument_id,
149 raw_symbol,
150 asset_class,
151 currency,
152 activation_ns,
153 expiration_ns,
154 price_precision,
155 size_precision,
156 price_increment,
157 size_increment,
158 margin_init: margin_init.unwrap_or_default(),
159 margin_maint: margin_maint.unwrap_or_default(),
160 maker_fee: maker_fee.unwrap_or_default(),
161 taker_fee: taker_fee.unwrap_or_default(),
162 outcome,
163 description,
164 max_quantity,
165 min_quantity,
166 max_notional,
167 min_notional,
168 max_price,
169 min_price,
170 ts_event,
171 ts_init,
172 })
173 }
174
175 #[allow(clippy::too_many_arguments)]
181 pub fn new(
182 instrument_id: InstrumentId,
183 raw_symbol: Symbol,
184 asset_class: AssetClass,
185 currency: Currency,
186 activation_ns: UnixNanos,
187 expiration_ns: UnixNanos,
188 price_precision: u8,
189 size_precision: u8,
190 price_increment: Price,
191 size_increment: Quantity,
192 outcome: Option<Ustr>,
193 description: Option<Ustr>,
194 max_quantity: Option<Quantity>,
195 min_quantity: Option<Quantity>,
196 max_notional: Option<Money>,
197 min_notional: Option<Money>,
198 max_price: Option<Price>,
199 min_price: Option<Price>,
200 margin_init: Option<Decimal>,
201 margin_maint: Option<Decimal>,
202 maker_fee: Option<Decimal>,
203 taker_fee: Option<Decimal>,
204 ts_event: UnixNanos,
205 ts_init: UnixNanos,
206 ) -> Self {
207 Self::new_checked(
208 instrument_id,
209 raw_symbol,
210 asset_class,
211 currency,
212 activation_ns,
213 expiration_ns,
214 price_precision,
215 size_precision,
216 price_increment,
217 size_increment,
218 outcome,
219 description,
220 max_quantity,
221 min_quantity,
222 max_notional,
223 min_notional,
224 max_price,
225 min_price,
226 margin_init,
227 margin_maint,
228 maker_fee,
229 taker_fee,
230 ts_event,
231 ts_init,
232 )
233 .expect(FAILED)
234 }
235}
236
237impl PartialEq<Self> for BinaryOption {
238 fn eq(&self, other: &Self) -> bool {
239 self.id == other.id
240 }
241}
242
243impl Eq for BinaryOption {}
244
245impl Hash for BinaryOption {
246 fn hash<H: Hasher>(&self, state: &mut H) {
247 self.id.hash(state);
248 }
249}
250
251impl Instrument for BinaryOption {
252 fn into_any(self) -> InstrumentAny {
253 InstrumentAny::BinaryOption(self)
254 }
255
256 fn id(&self) -> InstrumentId {
257 self.id
258 }
259
260 fn raw_symbol(&self) -> Symbol {
261 self.raw_symbol
262 }
263
264 fn asset_class(&self) -> AssetClass {
265 self.asset_class
266 }
267
268 fn instrument_class(&self) -> InstrumentClass {
269 InstrumentClass::BinaryOption
270 }
271
272 fn underlying(&self) -> Option<Ustr> {
273 None
274 }
275
276 fn base_currency(&self) -> Option<Currency> {
277 None
278 }
279
280 fn quote_currency(&self) -> Currency {
281 self.currency
282 }
283
284 fn settlement_currency(&self) -> Currency {
285 self.currency
286 }
287
288 fn isin(&self) -> Option<Ustr> {
289 None
290 }
291
292 fn exchange(&self) -> Option<Ustr> {
293 None
294 }
295
296 fn option_kind(&self) -> Option<OptionKind> {
297 None
298 }
299
300 fn is_inverse(&self) -> bool {
301 false
302 }
303
304 fn price_precision(&self) -> u8 {
305 self.price_precision
306 }
307
308 fn size_precision(&self) -> u8 {
309 self.size_precision
310 }
311
312 fn price_increment(&self) -> Price {
313 self.price_increment
314 }
315
316 fn size_increment(&self) -> Quantity {
317 self.size_increment
318 }
319
320 fn multiplier(&self) -> Quantity {
321 Quantity::from(1)
322 }
323
324 fn lot_size(&self) -> Option<Quantity> {
325 Some(Quantity::from(1))
326 }
327
328 fn max_quantity(&self) -> Option<Quantity> {
329 self.max_quantity
330 }
331
332 fn min_quantity(&self) -> Option<Quantity> {
333 self.min_quantity
334 }
335
336 fn max_price(&self) -> Option<Price> {
337 self.max_price
338 }
339
340 fn min_price(&self) -> Option<Price> {
341 self.min_price
342 }
343
344 fn ts_event(&self) -> UnixNanos {
345 self.ts_event
346 }
347
348 fn ts_init(&self) -> UnixNanos {
349 self.ts_init
350 }
351
352 fn strike_price(&self) -> Option<Price> {
353 None
354 }
355
356 fn activation_ns(&self) -> Option<UnixNanos> {
357 Some(self.activation_ns)
358 }
359
360 fn expiration_ns(&self) -> Option<UnixNanos> {
361 Some(self.expiration_ns)
362 }
363
364 fn max_notional(&self) -> Option<Money> {
365 self.max_notional
366 }
367
368 fn min_notional(&self) -> Option<Money> {
369 self.min_notional
370 }
371}
372
373#[cfg(test)]
377mod tests {
378 use rstest::rstest;
379
380 use crate::instruments::{BinaryOption, stubs::*};
381
382 #[rstest]
383 fn test_equality(binary_option: BinaryOption) {
384 let cloned = binary_option;
385 assert_eq!(binary_option, cloned);
386 }
387}