Skip to main content

nautilus_model/python/events/portfolio/
snapshot.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::{UUID4, python::IntoPyObjectNautilusExt};
17use pyo3::{basic::CompareOp, prelude::*};
18
19use crate::{
20    enums::AccountType,
21    events::PortfolioSnapshot,
22    identifiers::AccountId,
23    types::{AccountBalance, Currency, MarginBalance, Money},
24};
25
26#[pymethods]
27#[pyo3_stub_gen::derive::gen_stub_pymethods]
28impl PortfolioSnapshot {
29    /// Represents a point-in-time snapshot of portfolio state for a single account,
30    /// emitted periodically while the account holds open positions.
31    ///
32    /// Unlike `AccountState`, which fires only on
33    /// balance or margin changes, `PortfolioSnapshot` carries a continuous
34    /// mark-to-market view by folding open-position valuations into the totals.
35    /// Totals span every venue the account holds positions on, so multi-venue
36    /// accounts (e.g., a prime broker routing across exchanges) produce a single
37    /// account-wide snapshot rather than per-venue slices.
38    #[expect(clippy::too_many_arguments)]
39    #[new]
40    #[pyo3(signature = (account_id, account_type, balances, margins, unrealized_pnls, realized_pnls, total_equity, event_id, ts_event, ts_init, base_currency=None))]
41    fn py_new(
42        account_id: AccountId,
43        account_type: AccountType,
44        balances: Vec<AccountBalance>,
45        margins: Vec<MarginBalance>,
46        unrealized_pnls: Vec<Money>,
47        realized_pnls: Vec<Money>,
48        total_equity: Vec<Money>,
49        event_id: UUID4,
50        ts_event: u64,
51        ts_init: u64,
52        base_currency: Option<Currency>,
53    ) -> Self {
54        Self::new(
55            account_id,
56            account_type,
57            base_currency,
58            balances,
59            margins,
60            unrealized_pnls,
61            realized_pnls,
62            total_equity,
63            event_id,
64            ts_event.into(),
65            ts_init.into(),
66        )
67    }
68
69    #[getter]
70    fn account_id(&self) -> AccountId {
71        self.account_id
72    }
73
74    #[getter]
75    fn account_type(&self) -> AccountType {
76        self.account_type
77    }
78
79    #[getter]
80    fn base_currency(&self) -> Option<Currency> {
81        self.base_currency
82    }
83
84    #[getter]
85    fn balances(&self) -> Vec<AccountBalance> {
86        self.balances.clone()
87    }
88
89    #[getter]
90    fn margins(&self) -> Vec<MarginBalance> {
91        self.margins.clone()
92    }
93
94    #[getter]
95    fn unrealized_pnls(&self) -> Vec<Money> {
96        self.unrealized_pnls.clone()
97    }
98
99    #[getter]
100    fn realized_pnls(&self) -> Vec<Money> {
101        self.realized_pnls.clone()
102    }
103
104    #[getter]
105    fn total_equity(&self) -> Vec<Money> {
106        self.total_equity.clone()
107    }
108
109    #[getter]
110    fn event_id(&self) -> UUID4 {
111        self.event_id
112    }
113
114    #[getter]
115    fn ts_event(&self) -> u64 {
116        self.ts_event.as_u64()
117    }
118
119    #[getter]
120    fn ts_init(&self) -> u64 {
121        self.ts_init.as_u64()
122    }
123
124    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
125        match op {
126            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
127            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
128            _ => py.NotImplemented(),
129        }
130    }
131
132    fn __repr__(&self) -> String {
133        format!("{self:?}")
134    }
135
136    fn __str__(&self) -> String {
137        self.to_string()
138    }
139}