nautilus_model/python/defi/
profiler.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
16//! Python bindings for DeFi pool profiler.
17
18use std::str::FromStr;
19
20use alloy_primitives::{U160, U256};
21use nautilus_core::python::to_pyvalue_err;
22use pyo3::prelude::*;
23
24use crate::{
25    defi::{
26        Pool,
27        pool_analysis::{PoolProfiler, quote::SwapQuote},
28    },
29    identifiers::InstrumentId,
30};
31
32#[pymethods]
33impl PoolProfiler {
34    #[getter]
35    #[pyo3(name = "pool")]
36    fn py_pool(&self) -> Pool {
37        self.pool.as_ref().clone()
38    }
39
40    #[getter]
41    #[pyo3(name = "instrument_id")]
42    fn py_instrument_id(&self) -> InstrumentId {
43        self.pool.instrument_id
44    }
45
46    #[getter]
47    #[pyo3(name = "is_initialized")]
48    fn py_is_initialized(&self) -> bool {
49        self.is_initialized
50    }
51
52    #[getter]
53    #[pyo3(name = "current_tick")]
54    fn py_current_tick(&self) -> i32 {
55        self.state.current_tick
56    }
57
58    #[getter]
59    #[pyo3(name = "price_sqrt_ratio_x96")]
60    fn py_price_sqrt_ratio_x96(&self) -> String {
61        self.state.price_sqrt_ratio_x96.to_string()
62    }
63
64    #[getter]
65    #[pyo3(name = "total_amount0_deposited")]
66    fn py_total_amount0_deposited(&self) -> String {
67        self.analytics.total_amount0_deposited.to_string()
68    }
69
70    #[getter]
71    #[pyo3(name = "total_amount1_deposited")]
72    fn py_total_amount1_deposited(&self) -> String {
73        self.analytics.total_amount1_deposited.to_string()
74    }
75
76    #[getter]
77    #[pyo3(name = "total_amount0_collected")]
78    fn py_total_amount0_collected(&self) -> String {
79        self.analytics.total_amount0_collected.to_string()
80    }
81
82    #[getter]
83    #[pyo3(name = "total_amount1_collected")]
84    fn py_total_amount1_collected(&self) -> String {
85        self.analytics.total_amount1_collected.to_string()
86    }
87
88    #[getter]
89    #[pyo3(name = "protocol_fees_token0")]
90    fn py_protocol_fees_token0(&self) -> String {
91        self.state.protocol_fees_token0.to_string()
92    }
93
94    #[getter]
95    #[pyo3(name = "protocol_fees_token1")]
96    fn py_protocol_fees_token1(&self) -> String {
97        self.state.protocol_fees_token1.to_string()
98    }
99
100    #[getter]
101    #[pyo3(name = "fee_protocol")]
102    fn py_fee_protocol(&self) -> u8 {
103        self.state.fee_protocol
104    }
105
106    #[pyo3(name = "get_active_liquidity")]
107    fn py_get_active_liquidity(&self) -> u128 {
108        self.get_active_liquidity()
109    }
110
111    #[pyo3(name = "get_active_tick_count")]
112    fn py_get_active_tick_count(&self) -> usize {
113        self.get_active_tick_count()
114    }
115
116    #[pyo3(name = "get_total_tick_count")]
117    fn py_get_total_tick_count(&self) -> usize {
118        self.get_total_tick_count()
119    }
120
121    #[pyo3(name = "get_total_active_positions")]
122    fn py_get_total_active_positions(&self) -> usize {
123        self.get_total_active_positions()
124    }
125
126    #[pyo3(name = "get_total_inactive_positions")]
127    fn py_get_total_inactive_positions(&self) -> usize {
128        self.get_total_inactive_positions()
129    }
130
131    #[pyo3(name = "estimate_balance_of_token0")]
132    fn py_estimate_balance_of_token0(&self) -> String {
133        self.estimate_balance_of_token0().to_string()
134    }
135
136    #[pyo3(name = "estimate_balance_of_token1")]
137    fn py_estimate_balance_of_token1(&self) -> String {
138        self.estimate_balance_of_token1().to_string()
139    }
140
141    #[pyo3(name = "get_total_liquidity")]
142    fn py_get_total_liquidity_all_positions(&self) -> String {
143        self.get_total_liquidity().to_string()
144    }
145
146    #[pyo3(name = "liquidity_utilization_rate")]
147    fn py_liquidity_utilization_rate(&self) -> f64 {
148        self.liquidity_utilization_rate()
149    }
150
151    #[pyo3(name = "swap_exact_in")]
152    fn py_swap_exact_in(
153        &self,
154        amount_in: &str,
155        zero_for_one: bool,
156        sqrt_price_limit_x96: Option<&str>,
157    ) -> PyResult<SwapQuote> {
158        let amount_in = U256::from_str(amount_in).map_err(to_pyvalue_err)?;
159        let sqrt_price_limit = match sqrt_price_limit_x96 {
160            Some(limit_str) => Some(U160::from_str(limit_str).map_err(to_pyvalue_err)?),
161            None => None,
162        };
163
164        self.swap_exact_in(amount_in, zero_for_one, sqrt_price_limit)
165            .map_err(to_pyvalue_err)
166    }
167
168    #[pyo3(name = "swap_exact_out")]
169    fn py_swap_exact_out(
170        &self,
171        amount_out: &str,
172        zero_for_one: bool,
173        sqrt_price_limit_x96: Option<&str>,
174    ) -> PyResult<SwapQuote> {
175        let amount_out = U256::from_str(amount_out).map_err(to_pyvalue_err)?;
176        let sqrt_price_limit = match sqrt_price_limit_x96 {
177            Some(limit_str) => Some(U160::from_str(limit_str).map_err(to_pyvalue_err)?),
178            None => None,
179        };
180
181        self.swap_exact_out(amount_out, zero_for_one, sqrt_price_limit)
182            .map_err(to_pyvalue_err)
183    }
184}