nautilus_execution/python/
fee.rs1use nautilus_core::python::{to_pyruntime_err, to_pytype_err};
19use nautilus_model::types::Money;
20use pyo3::{IntoPyObjectExt, prelude::*};
21use rust_decimal::Decimal;
22
23use crate::models::fee::{
24 CappedOptionFeeModel, FeeModelAny, FixedFeeModel, MakerTakerFeeModel, PerContractFeeModel,
25 ProbabilityPriceFeeModel, TieredNotionalOptionFeeModel,
26};
27
28#[pymethods]
29#[pyo3_stub_gen::derive::gen_stub_pymethods]
30impl FixedFeeModel {
31 #[new]
33 #[pyo3(signature = (commission, charge_commission_once=None, change_commission_once=None))]
34 fn py_new(
35 commission: Money,
36 charge_commission_once: Option<bool>,
37 change_commission_once: Option<bool>,
38 ) -> PyResult<Self> {
39 let charge_commission_once = resolve_fixed_fee_charge_commission_once(
40 charge_commission_once,
41 change_commission_once,
42 )?;
43 Self::new(commission, charge_commission_once).map_err(to_pyruntime_err)
44 }
45
46 fn __repr__(&self) -> String {
47 format!("{self:?}")
48 }
49}
50
51fn resolve_fixed_fee_charge_commission_once(
52 charge_commission_once: Option<bool>,
53 change_commission_once: Option<bool>,
54) -> PyResult<Option<bool>> {
55 if charge_commission_once.is_some() && change_commission_once.is_some() {
56 return Err(to_pytype_err(
57 "Provide only one of `charge_commission_once` or `change_commission_once`",
58 ));
59 }
60
61 Ok(charge_commission_once.or(change_commission_once))
62}
63
64#[pymethods]
65#[pyo3_stub_gen::derive::gen_stub_pymethods]
66impl MakerTakerFeeModel {
67 #[new]
68 fn py_new() -> Self {
69 Self
70 }
71
72 fn __repr__(&self) -> String {
73 format!("{self:?}")
74 }
75}
76
77#[pymethods]
78#[pyo3_stub_gen::derive::gen_stub_pymethods]
79impl PerContractFeeModel {
80 #[new]
86 fn py_new(commission: Money) -> PyResult<Self> {
87 Self::new(commission).map_err(to_pyruntime_err)
88 }
89
90 fn __repr__(&self) -> String {
91 format!("{self:?}")
92 }
93}
94
95#[pymethods]
96#[pyo3_stub_gen::derive::gen_stub_pymethods]
97impl ProbabilityPriceFeeModel {
98 #[new]
109 fn py_new() -> Self {
110 Self
111 }
112
113 fn __repr__(&self) -> String {
114 format!("{self:?}")
115 }
116}
117
118#[pymethods]
119#[pyo3_stub_gen::derive::gen_stub_pymethods]
120impl CappedOptionFeeModel {
121 #[new]
123 #[pyo3(signature = (maker_rate=None, taker_rate=None, cap_rate=None))]
124 fn py_new(
125 maker_rate: Option<Decimal>,
126 taker_rate: Option<Decimal>,
127 cap_rate: Option<Decimal>,
128 ) -> PyResult<Self> {
129 Self::new(maker_rate, taker_rate, cap_rate).map_err(to_pyruntime_err)
130 }
131
132 fn __repr__(&self) -> String {
133 format!("{self:?}")
134 }
135}
136
137#[pymethods]
138#[pyo3_stub_gen::derive::gen_stub_pymethods]
139impl TieredNotionalOptionFeeModel {
140 #[new]
146 #[pyo3(signature = (maker_rate=None, taker_rate=None))]
147 fn py_new(maker_rate: Option<Decimal>, taker_rate: Option<Decimal>) -> PyResult<Self> {
148 Self::new(maker_rate, taker_rate).map_err(to_pyruntime_err)
149 }
150
151 fn __repr__(&self) -> String {
152 format!("{self:?}")
153 }
154}
155
156pub fn pyobject_to_fee_model_any(obj: &Bound<'_, PyAny>) -> PyResult<FeeModelAny> {
162 if let Ok(m) = obj.extract::<FixedFeeModel>() {
163 return Ok(FeeModelAny::Fixed(m));
164 }
165
166 if let Ok(m) = obj.extract::<MakerTakerFeeModel>() {
167 return Ok(FeeModelAny::MakerTaker(m));
168 }
169
170 if let Ok(m) = obj.extract::<PerContractFeeModel>() {
171 return Ok(FeeModelAny::PerContract(m));
172 }
173
174 if let Ok(m) = obj.extract::<ProbabilityPriceFeeModel>() {
175 return Ok(FeeModelAny::ProbabilityPrice(m));
176 }
177
178 if let Ok(m) = obj.extract::<CappedOptionFeeModel>() {
179 return Ok(FeeModelAny::CappedOption(m));
180 }
181
182 if let Ok(m) = obj.extract::<TieredNotionalOptionFeeModel>() {
183 return Ok(FeeModelAny::TieredNotionalOption(m));
184 }
185
186 let type_name = obj.get_type().name()?;
187 Err(to_pytype_err(format!(
188 "Cannot convert {type_name} to FeeModel"
189 )))
190}
191
192pub fn fee_model_any_to_pyobject(py: Python<'_>, model: &FeeModelAny) -> PyResult<Py<PyAny>> {
198 match model {
199 FeeModelAny::Fixed(model) => model.clone().into_py_any(py),
200 FeeModelAny::MakerTaker(model) => model.clone().into_py_any(py),
201 FeeModelAny::PerContract(model) => model.clone().into_py_any(py),
202 FeeModelAny::ProbabilityPrice(model) => model.clone().into_py_any(py),
203 FeeModelAny::CappedOption(model) => model.clone().into_py_any(py),
204 FeeModelAny::TieredNotionalOption(model) => model.clone().into_py_any(py),
205 }
206}