Skip to main content

nautilus_model/python/types/
currency.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 std::str::FromStr;
17
18use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
19use pyo3::{IntoPyObjectExt, prelude::*};
20
21use crate::{enums::CurrencyType, types::Currency};
22
23#[pymethods]
24#[pyo3_stub_gen::derive::gen_stub_pymethods]
25impl Currency {
26    /// Represents a medium of exchange in a specified denomination with a fixed decimal precision.
27    ///
28    /// Handles up to `FIXED_PRECISION` decimals of precision.
29    #[new]
30    fn py_new(
31        code: &str,
32        precision: u8,
33        iso4217: u16,
34        name: &str,
35        currency_type: CurrencyType,
36    ) -> PyResult<Self> {
37        Self::new_checked(code, precision, iso4217, name, currency_type).map_err(to_pyvalue_err)
38    }
39
40    fn __reduce__(&self, py: Python) -> PyResult<Py<PyAny>> {
41        let unpickle = py.get_type::<Self>().getattr("_unpickle")?;
42        let args = (
43            self.code.to_string(),
44            self.precision,
45            self.iso4217,
46            self.name.to_string(),
47            self.currency_type.to_string(),
48        )
49            .into_py_any(py)?;
50        (unpickle, args).into_py_any(py)
51    }
52
53    #[staticmethod]
54    fn _unpickle(
55        code: &str,
56        precision: u8,
57        iso4217: u16,
58        name: &str,
59        currency_type_str: &str,
60    ) -> PyResult<Self> {
61        let currency_type = CurrencyType::from_str(currency_type_str).map_err(to_pyvalue_err)?;
62        Self::new_checked(code, precision, iso4217, name, currency_type).map_err(to_pyvalue_err)
63    }
64
65    fn __repr__(&self) -> String {
66        format!("{self:?}")
67    }
68
69    fn __str__(&self) -> &'static str {
70        self.code.as_str()
71    }
72
73    #[getter]
74    #[pyo3(name = "code")]
75    fn py_code(&self) -> &'static str {
76        self.code.as_str()
77    }
78
79    #[getter]
80    #[pyo3(name = "precision")]
81    fn py_precision(&self) -> u8 {
82        self.precision
83    }
84
85    #[getter]
86    #[pyo3(name = "iso4217")]
87    fn py_iso4217(&self) -> u16 {
88        self.iso4217
89    }
90
91    #[getter]
92    #[pyo3(name = "name")]
93    fn py_name(&self) -> &'static str {
94        self.name.as_str()
95    }
96
97    #[getter]
98    #[pyo3(name = "currency_type")]
99    fn py_currency_type(&self) -> CurrencyType {
100        self.currency_type
101    }
102
103    /// Checks if the currency identified by the given `code` is a fiat currency.
104    ///
105    /// # Errors
106    ///
107    /// Returns an error if:
108    /// - A currency with the given `code` does not exist.
109    /// - There is a failure acquiring the lock on the currency map.
110    #[staticmethod]
111    #[pyo3(name = "is_fiat")]
112    fn py_is_fiat(code: &str) -> PyResult<bool> {
113        Self::is_fiat(code).map_err(to_pyvalue_err)
114    }
115
116    /// Checks if the currency identified by the given `code` is a cryptocurrency.
117    ///
118    /// # Errors
119    ///
120    /// Returns an error if:
121    /// - If a currency with the given `code` does not exist.
122    /// - If there is a failure acquiring the lock on the currency map.
123    #[staticmethod]
124    #[pyo3(name = "is_crypto")]
125    fn py_is_crypto(code: &str) -> PyResult<bool> {
126        Self::is_crypto(code).map_err(to_pyvalue_err)
127    }
128
129    #[staticmethod]
130    #[pyo3(name = "is_commodity_backed")]
131    fn py_is_commodidity_backed(code: &str) -> PyResult<bool> {
132        Self::is_commodity_backed(code).map_err(to_pyvalue_err)
133    }
134
135    #[staticmethod]
136    #[pyo3(name = "from_str")]
137    #[pyo3(signature = (value, strict = false))]
138    fn py_from_str(value: &str, strict: bool) -> PyResult<Self> {
139        match Self::from_str(value) {
140            Ok(currency) => Ok(currency),
141            Err(e) => {
142                if strict {
143                    Err(to_pyvalue_err(e))
144                } else {
145                    Self::new_checked(value, 8, 0, value, CurrencyType::Crypto)
146                        .map_err(to_pyvalue_err)
147                }
148            }
149        }
150    }
151
152    /// Register the given `currency` in the internal currency map.
153    ///
154    /// - If `overwrite` is `true`, any existing currency will be replaced.
155    /// - If `overwrite` is `false` and the currency already exists, the operation is a no-op.
156    ///
157    /// # Errors
158    ///
159    /// Returns an error if there is a failure acquiring the lock on the currency map.
160    #[staticmethod]
161    #[pyo3(name = "register")]
162    #[pyo3(signature = (currency, overwrite = false))]
163    fn py_register(currency: Self, overwrite: bool) -> PyResult<()> {
164        Self::register(currency, overwrite).map_err(to_pyruntime_err)
165    }
166}