From e6d78c9358eead1e1a22f55df6492297e4c66ef9 Mon Sep 17 00:00:00 2001 From: Filip Macek Date: Fri, 15 Mar 2024 09:48:00 +0100 Subject: [PATCH] Equity pyo3 Rust Cython conversion (#1542) --- .../adapters/src/databento/decode.rs | 8 +++++++ nautilus_core/model/src/instruments/equity.rs | 13 +++++++++++ nautilus_core/model/src/instruments/stubs.rs | 6 ++++- .../model/src/python/instruments/equity.rs | 13 +++++++++++ nautilus_trader/core/nautilus_pyo3.pyi | 2 ++ nautilus_trader/model/instruments/equity.pyx | 18 ++++++++++----- .../model/instruments/test_equity_pyo3.py | 22 +++++++++++++++---- tests/unit_tests/model/test_instrument.py | 4 ++++ 8 files changed, 75 insertions(+), 11 deletions(-) diff --git a/nautilus_core/adapters/src/databento/decode.rs b/nautilus_core/adapters/src/databento/decode.rs index 0c259c8e4c1..ce213526c9e 100644 --- a/nautilus_core/adapters/src/databento/decode.rs +++ b/nautilus_core/adapters/src/databento/decode.rs @@ -198,6 +198,10 @@ pub fn decode_equity_v1( currency, currency.precision, decode_price(msg.min_price_increment, currency.precision)?, + None, // TBD + None, // TBD + None, // TBD + None, // TBD Some(Quantity::new(msg.min_lot_size_round_lot.into(), 0)?), None, // TBD None, // TBD @@ -743,6 +747,10 @@ pub fn decode_equity( currency, currency.precision, decode_price(msg.min_price_increment, currency.precision)?, + None, // TBD + None, // TBD + None, // TBD + None, // TBD Some(Quantity::new(msg.min_lot_size_round_lot.into(), 0)?), None, // TBD None, // TBD diff --git a/nautilus_core/model/src/instruments/equity.rs b/nautilus_core/model/src/instruments/equity.rs index 3cda216dfd5..5010444c4ad 100644 --- a/nautilus_core/model/src/instruments/equity.rs +++ b/nautilus_core/model/src/instruments/equity.rs @@ -20,6 +20,7 @@ use std::{ use anyhow::Result; use nautilus_core::time::UnixNanos; +use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use ustr::Ustr; @@ -45,6 +46,10 @@ pub struct Equity { pub currency: Currency, pub price_precision: u8, pub price_increment: Price, + pub maker_fee: Decimal, + pub taker_fee: Decimal, + pub margin_init: Decimal, + pub margin_maint: Decimal, pub lot_size: Option, pub max_quantity: Option, pub min_quantity: Option, @@ -63,6 +68,10 @@ impl Equity { currency: Currency, price_precision: u8, price_increment: Price, + maker_fee: Option, + taker_fee: Option, + margin_init: Option, + margin_maint: Option, lot_size: Option, max_quantity: Option, min_quantity: Option, @@ -78,6 +87,10 @@ impl Equity { currency, price_precision, price_increment, + maker_fee: maker_fee.unwrap_or(0.into()), + taker_fee: taker_fee.unwrap_or(0.into()), + margin_init: margin_init.unwrap_or(0.into()), + margin_maint: margin_maint.unwrap_or(0.into()), lot_size, max_quantity, min_quantity, diff --git a/nautilus_core/model/src/instruments/stubs.rs b/nautilus_core/model/src/instruments/stubs.rs index 6542226b8cd..584e3f83114 100644 --- a/nautilus_core/model/src/instruments/stubs.rs +++ b/nautilus_core/model/src/instruments/stubs.rs @@ -278,7 +278,11 @@ pub fn equity_aapl() -> Equity { Currency::from("USD"), 2, Price::from("0.01"), - Some(Quantity::from(1)), + None, + None, + None, + None, + None, None, None, None, diff --git a/nautilus_core/model/src/python/instruments/equity.rs b/nautilus_core/model/src/python/instruments/equity.rs index 604a740e431..ea6c8a1d855 100644 --- a/nautilus_core/model/src/python/instruments/equity.rs +++ b/nautilus_core/model/src/python/instruments/equity.rs @@ -23,6 +23,7 @@ use nautilus_core::{ time::UnixNanos, }; use pyo3::{basic::CompareOp, prelude::*, types::PyDict}; +use rust_decimal::Decimal; use ustr::Ustr; use crate::{ @@ -43,6 +44,10 @@ impl Equity { price_increment: Price, ts_event: UnixNanos, ts_init: UnixNanos, + maker_fee: Option, + taker_fee: Option, + margin_init: Option, + margin_maint: Option, isin: Option, lot_size: Option, max_quantity: Option, @@ -57,6 +62,10 @@ impl Equity { currency, price_precision, price_increment, + maker_fee, + taker_fee, + margin_init, + margin_maint, lot_size, max_quantity, min_quantity, @@ -185,6 +194,10 @@ impl Equity { dict.set_item("price_increment", self.price_increment.to_string())?; dict.set_item("ts_event", self.ts_event)?; dict.set_item("ts_init", self.ts_init)?; + dict.set_item("maker_fee", self.maker_fee.to_string())?; + dict.set_item("taker_fee", self.taker_fee.to_string())?; + dict.set_item("margin_init", self.margin_init.to_string())?; + dict.set_item("margin_maint", self.margin_maint.to_string())?; match &self.isin { Some(value) => dict.set_item("isin", value.to_string())?, None => dict.set_item("isin", py.None())?, diff --git a/nautilus_trader/core/nautilus_pyo3.pyi b/nautilus_trader/core/nautilus_pyo3.pyi index 85efa8a60b8..ee7a99997d1 100644 --- a/nautilus_trader/core/nautilus_pyo3.pyi +++ b/nautilus_trader/core/nautilus_pyo3.pyi @@ -1206,6 +1206,8 @@ class Equity: max_price: Price | None = None, min_price: Price | None = None, ) -> None: ... + @classmethod + def from_dict(cls, values: dict[str, str]) -> Equity: ... @property def id(self) -> InstrumentId: ... @property diff --git a/nautilus_trader/model/instruments/equity.pyx b/nautilus_trader/model/instruments/equity.pyx index 49cc7e7e543..823e5bacdf9 100644 --- a/nautilus_trader/model/instruments/equity.pyx +++ b/nautilus_trader/model/instruments/equity.pyx @@ -91,6 +91,8 @@ cdef class Equity(Instrument): margin_maint: Decimal | None = None, maker_fee: Decimal | None = None, taker_fee: Decimal | None = None, + max_quantity: Quantity | None = None, + min_quantity: Quantity | None = None, dict info = None, ): if isin is not None: @@ -108,8 +110,8 @@ cdef class Equity(Instrument): size_increment=Quantity.from_int_c(1), multiplier=Quantity.from_int_c(1), lot_size=lot_size, - max_quantity=None, - min_quantity=Quantity.from_int_c(1), + max_quantity=max_quantity, + min_quantity=min_quantity, max_notional=None, min_notional=None, max_price=None, @@ -135,10 +137,10 @@ cdef class Equity(Instrument): price_increment=Price.from_str(values["price_increment"]), lot_size=Quantity.from_str(values["lot_size"]), isin=values.get("isin"), # Can be None, - margin_init=Decimal(values.get("margin_init", 0)), # Can be None, - margin_maint=Decimal(values.get("margin_maint", 0)), # Can be None, - maker_fee=Decimal(values.get("maker_fee", 0)), # Can be None, - taker_fee=Decimal(values.get("taker_fee", 0)), # Can be None, + margin_init=Decimal(values.get("margin_init", 0)) if values.get("margin_init") is not None else None, + margin_maint=Decimal(values.get("margin_maint", 0)) if values.get("margin_maint") is not None else None, + maker_fee=Decimal(values.get("maker_fee", 0)) if values.get("maker_fee") is not None else None, + taker_fee=Decimal(values.get("taker_fee", 0)) if values.get("taker_fee") is not None else None, ts_event=values["ts_event"], ts_init=values["ts_init"], ) @@ -159,6 +161,10 @@ cdef class Equity(Instrument): "margin_maint": str(obj.margin_maint), "maker_fee": str(obj.maker_fee), "taker_fee": str(obj.taker_fee), + "min_price": str(obj.min_price) if obj.min_price is not None else None, + "max_price": str(obj.max_price) if obj.max_price is not None else None, + "max_quantity": str(obj.max_quantity) if obj.max_quantity is not None else None, + "min_quantity": str(obj.min_quantity) if obj.min_quantity is not None else None, "ts_event": obj.ts_event, "ts_init": obj.ts_init, } diff --git a/tests/unit_tests/model/instruments/test_equity_pyo3.py b/tests/unit_tests/model/instruments/test_equity_pyo3.py index f39cf00454a..9ddf809f637 100644 --- a/tests/unit_tests/model/instruments/test_equity_pyo3.py +++ b/tests/unit_tests/model/instruments/test_equity_pyo3.py @@ -13,9 +13,9 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- -from nautilus_trader.core.nautilus_pyo3 import Equity +from nautilus_trader.core import nautilus_pyo3 from nautilus_trader.core.nautilus_pyo3 import InstrumentId -from nautilus_trader.model.instruments import Equity as LegacyEquity +from nautilus_trader.model.instruments import Equity from nautilus_trader.test_kit.rust.instruments_pyo3 import TestInstrumentProviderPyo3 @@ -38,7 +38,7 @@ def test_hash(): def test_to_dict(): result = _AAPL_EQUITY.to_dict() - assert Equity.from_dict(result) == _AAPL_EQUITY + assert nautilus_pyo3.Equity.from_dict(result) == _AAPL_EQUITY assert result == { "type": "Equity", "id": "AAPL.XNAS", @@ -47,6 +47,10 @@ def test_to_dict(): "currency": "USD", "price_precision": 2, "price_increment": "0.01", + "maker_fee": "0", + "taker_fee": "0", + "margin_init": "0", + "margin_maint": "0", "lot_size": "100", "max_quantity": None, "min_quantity": None, @@ -58,6 +62,16 @@ def test_to_dict(): def test_legacy_equity_from_pyo3(): - equity = LegacyEquity.from_pyo3(_AAPL_EQUITY) + equity = Equity.from_pyo3(_AAPL_EQUITY) assert equity.id.value == "AAPL.XNAS" + + +def test_pyo3_cython_conversion(): + equity_pyo3 = TestInstrumentProviderPyo3.aapl_equity() + equity_pyo3_dict = equity_pyo3.to_dict() + equity_cython = Equity.from_pyo3(equity_pyo3) + equity_cython_dict = Equity.to_dict(equity_cython) + equity_pyo3_back = nautilus_pyo3.Equity.from_dict(equity_cython_dict) + assert equity_cython_dict == equity_pyo3_dict + assert equity_pyo3 == equity_pyo3_back diff --git a/tests/unit_tests/model/test_instrument.py b/tests/unit_tests/model/test_instrument.py index 9fd524c39fa..d53d804328a 100644 --- a/tests/unit_tests/model/test_instrument.py +++ b/tests/unit_tests/model/test_instrument.py @@ -237,6 +237,10 @@ def test_equity_instrument_to_dict(self): "price_precision": 2, "price_increment": "0.01", "lot_size": "100", + "max_price": None, + "max_quantity": None, + "min_price": None, + "min_quantity": None, "isin": "US0378331005", "margin_init": "0", "margin_maint": "0",