nautilus_model/python/types/
balance.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 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 std::str::FromStr;
17
18use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyvalue_err};
19use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
20
21use crate::{
22    identifiers::InstrumentId,
23    types::{AccountBalance, Currency, MarginBalance, Money},
24};
25
26#[pymethods]
27impl AccountBalance {
28    #[new]
29    fn py_new(total: Money, locked: Money, free: Money) -> PyResult<Self> {
30        Self::new_checked(total, locked, free).map_err(to_pyvalue_err)
31    }
32
33    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
34        match op {
35            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
36            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
37            _ => py.NotImplemented(),
38        }
39    }
40
41    fn __repr__(&self) -> String {
42        format!("{self:?}")
43    }
44
45    fn __str__(&self) -> String {
46        self.to_string()
47    }
48
49    #[staticmethod]
50    #[pyo3(name = "from_dict")]
51    pub fn py_from_dict(values: &Bound<'_, PyDict>) -> PyResult<Self> {
52        let dict = values.as_ref();
53        let currency: String = dict.get_item("currency")?.extract()?;
54        let total_str: String = dict.get_item("total")?.extract()?;
55        let total: f64 = total_str.parse::<f64>().unwrap();
56        let free_str: String = dict.get_item("free")?.extract()?;
57        let free: f64 = free_str.parse::<f64>().unwrap();
58        let locked_str: String = dict.get_item("locked")?.extract()?;
59        let locked: f64 = locked_str.parse::<f64>().unwrap();
60        let currency = Currency::from_str(currency.as_str()).map_err(to_pyvalue_err)?;
61        Self::new_checked(
62            Money::new(total, currency),
63            Money::new(locked, currency),
64            Money::new(free, currency),
65        )
66        .map_err(to_pyvalue_err)
67    }
68
69    #[pyo3(name = "to_dict")]
70    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
71        let dict = PyDict::new(py);
72        dict.set_item("type", stringify!(AccountBalance))?;
73        dict.set_item(
74            "total",
75            format!(
76                "{:.*}",
77                self.total.currency.precision as usize,
78                self.total.as_f64()
79            ),
80        )?;
81        dict.set_item(
82            "locked",
83            format!(
84                "{:.*}",
85                self.locked.currency.precision as usize,
86                self.locked.as_f64()
87            ),
88        )?;
89        dict.set_item(
90            "free",
91            format!(
92                "{:.*}",
93                self.free.currency.precision as usize,
94                self.free.as_f64()
95            ),
96        )?;
97        dict.set_item("currency", self.currency.code.to_string())?;
98        Ok(dict.into())
99    }
100}
101
102#[pymethods]
103impl MarginBalance {
104    #[new]
105    fn py_new(initial: Money, maintenance: Money, instrument: InstrumentId) -> Self {
106        Self::new(initial, maintenance, instrument)
107    }
108    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
109        match op {
110            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
111            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
112            _ => py.NotImplemented(),
113        }
114    }
115
116    fn __repr__(&self) -> String {
117        format!("{self:?}")
118    }
119
120    fn __str__(&self) -> String {
121        self.to_string()
122    }
123
124    #[staticmethod]
125    #[pyo3(name = "from_dict")]
126    pub fn py_from_dict(values: &Bound<'_, PyDict>) -> PyResult<Self> {
127        let dict = values.as_ref();
128        let currency: String = dict.get_item("currency")?.extract()?;
129        let initial_str: String = dict.get_item("initial")?.extract()?;
130        let initial: f64 = initial_str.parse::<f64>().unwrap();
131        let maintenance_str: String = dict.get_item("maintenance")?.extract()?;
132        let maintenance: f64 = maintenance_str.parse::<f64>().unwrap();
133        let instrument_id_str: String = dict.get_item("instrument_id")?.extract()?;
134        let currency = Currency::from_str(currency.as_str()).map_err(to_pyvalue_err)?;
135        let account_balance = Self::new(
136            Money::new(initial, currency),
137            Money::new(maintenance, currency),
138            InstrumentId::from(instrument_id_str.as_str()),
139        );
140        Ok(account_balance)
141    }
142
143    #[pyo3(name = "to_dict")]
144    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
145        let dict = PyDict::new(py);
146        dict.set_item("type", stringify!(MarginBalance))?;
147        dict.set_item(
148            "initial",
149            format!(
150                "{:.*}",
151                self.initial.currency.precision as usize,
152                self.initial.as_f64()
153            ),
154        )?;
155        dict.set_item(
156            "maintenance",
157            format!(
158                "{:.*}",
159                self.maintenance.currency.precision as usize,
160                self.maintenance.as_f64()
161            ),
162        )?;
163        dict.set_item("currency", self.currency.code.to_string())?;
164        dict.set_item("instrument_id", self.instrument_id.to_string())?;
165        Ok(dict.into())
166    }
167}