Skip to main content

nautilus_model/python/reports/
fill.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use nautilus_core::{
17    UUID4,
18    python::{IntoPyObjectNautilusExt, serialization::from_dict_pyo3},
19};
20use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
21
22use crate::{
23    enums::{LiquiditySide, OrderSide},
24    identifiers::{AccountId, ClientOrderId, InstrumentId, PositionId, TradeId, VenueOrderId},
25    reports::fill::FillReport,
26    types::{Money, Price, Quantity},
27};
28
29#[pymethods]
30#[pyo3_stub_gen::derive::gen_stub_pymethods]
31impl FillReport {
32    /// Represents a fill report of a single order execution.
33    #[new]
34    #[expect(clippy::too_many_arguments)]
35    #[pyo3(signature = (
36        account_id,
37        instrument_id,
38        venue_order_id,
39        trade_id,
40        order_side,
41        last_qty,
42        last_px,
43        commission,
44        liquidity_side,
45        ts_event,
46        ts_init,
47        client_order_id=None,
48        venue_position_id=None,
49        avg_px=None,
50        report_id=None,
51    ))]
52    fn py_new(
53        account_id: AccountId,
54        instrument_id: InstrumentId,
55        venue_order_id: VenueOrderId,
56        trade_id: TradeId,
57        order_side: OrderSide,
58        last_qty: Quantity,
59        last_px: Price,
60        commission: Money,
61        liquidity_side: LiquiditySide,
62        ts_event: u64,
63        ts_init: u64,
64        client_order_id: Option<ClientOrderId>,
65        venue_position_id: Option<PositionId>,
66        avg_px: Option<rust_decimal::Decimal>,
67        report_id: Option<UUID4>,
68    ) -> Self {
69        let mut report = Self::new(
70            account_id,
71            instrument_id,
72            venue_order_id,
73            trade_id,
74            order_side,
75            last_qty,
76            last_px,
77            commission,
78            liquidity_side,
79            client_order_id,
80            venue_position_id,
81            ts_event.into(),
82            ts_init.into(),
83            report_id,
84        );
85        report.avg_px = avg_px;
86        report
87    }
88
89    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
90        match op {
91            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
92            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
93            _ => py.NotImplemented(),
94        }
95    }
96
97    fn __repr__(&self) -> String {
98        self.to_string()
99    }
100
101    fn __str__(&self) -> String {
102        self.to_string()
103    }
104
105    #[getter]
106    #[pyo3(name = "account_id")]
107    const fn py_account_id(&self) -> AccountId {
108        self.account_id
109    }
110
111    #[getter]
112    #[pyo3(name = "instrument_id")]
113    const fn py_instrument_id(&self) -> InstrumentId {
114        self.instrument_id
115    }
116
117    #[getter]
118    #[pyo3(name = "venue_order_id")]
119    const fn py_venue_order_id(&self) -> VenueOrderId {
120        self.venue_order_id
121    }
122
123    #[getter]
124    #[pyo3(name = "trade_id")]
125    const fn py_trade_id(&self) -> TradeId {
126        self.trade_id
127    }
128
129    #[getter]
130    #[pyo3(name = "order_side")]
131    const fn py_order_side(&self) -> OrderSide {
132        self.order_side
133    }
134
135    #[getter]
136    #[pyo3(name = "last_qty")]
137    const fn py_last_qty(&self) -> Quantity {
138        self.last_qty
139    }
140
141    #[getter]
142    #[pyo3(name = "last_px")]
143    const fn py_last_px(&self) -> Price {
144        self.last_px
145    }
146
147    #[getter]
148    #[pyo3(name = "commission")]
149    const fn py_commission(&self) -> Money {
150        self.commission
151    }
152
153    #[getter]
154    #[pyo3(name = "liquidity_side")]
155    const fn py_liquidity_side(&self) -> LiquiditySide {
156        self.liquidity_side
157    }
158
159    #[getter]
160    #[pyo3(name = "avg_px")]
161    fn py_avg_px(&self) -> Option<rust_decimal::Decimal> {
162        self.avg_px
163    }
164
165    #[getter]
166    #[pyo3(name = "report_id")]
167    const fn py_report_id(&self) -> UUID4 {
168        self.report_id
169    }
170
171    #[getter]
172    #[pyo3(name = "ts_event")]
173    const fn py_ts_event(&self) -> u64 {
174        self.ts_event.as_u64()
175    }
176
177    #[getter]
178    #[pyo3(name = "ts_init")]
179    const fn py_ts_init(&self) -> u64 {
180        self.ts_init.as_u64()
181    }
182
183    #[getter]
184    #[pyo3(name = "client_order_id")]
185    const fn py_client_order_id(&self) -> Option<ClientOrderId> {
186        self.client_order_id
187    }
188
189    #[getter]
190    #[pyo3(name = "venue_position_id")]
191    const fn py_venue_position_id(&self) -> Option<PositionId> {
192        self.venue_position_id
193    }
194
195    /// Creates a `FillReport` from a Python dictionary.
196    ///
197    /// # Errors
198    ///
199    /// Returns a Python exception if conversion from dict fails.
200    #[staticmethod]
201    #[pyo3(name = "from_dict")]
202    pub fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
203        from_dict_pyo3(py, values)
204    }
205
206    /// Converts the `FillReport` to a Python dictionary.
207    ///
208    /// # Errors
209    ///
210    /// Returns a Python exception if conversion to dict fails.
211    #[pyo3(name = "to_dict")]
212    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
213        let dict = PyDict::new(py);
214        dict.set_item("type", stringify!(FillReport))?;
215        dict.set_item("account_id", self.account_id.to_string())?;
216        dict.set_item("instrument_id", self.instrument_id.to_string())?;
217        dict.set_item("venue_order_id", self.venue_order_id.to_string())?;
218        dict.set_item("trade_id", self.trade_id.to_string())?;
219        dict.set_item("order_side", self.order_side.to_string())?;
220        dict.set_item("last_qty", self.last_qty.to_string())?;
221        dict.set_item("last_px", self.last_px.to_string())?;
222        dict.set_item("commission", self.commission.to_string())?;
223        dict.set_item("liquidity_side", self.liquidity_side.to_string())?;
224        match &self.avg_px {
225            Some(avg_px) => dict.set_item("avg_px", avg_px)?,
226            None => dict.set_item("avg_px", py.None())?,
227        }
228        dict.set_item("report_id", self.report_id.to_string())?;
229        dict.set_item("ts_event", self.ts_event.as_u64())?;
230        dict.set_item("ts_init", self.ts_init.as_u64())?;
231
232        match &self.client_order_id {
233            Some(id) => dict.set_item("client_order_id", id.to_string())?,
234            None => dict.set_item("client_order_id", py.None())?,
235        }
236
237        match &self.venue_position_id {
238            Some(id) => dict.set_item("venue_position_id", id.to_string())?,
239            None => dict.set_item("venue_position_id", py.None())?,
240        }
241
242        Ok(dict.into())
243    }
244}