nautilus_model/python/defi/
data.rs1use std::{
19 collections::hash_map::DefaultHasher,
20 hash::{Hash, Hasher},
21 str::FromStr,
22 sync::Arc,
23};
24
25use alloy_primitives::Address;
26use nautilus_core::python::to_pyvalue_err;
27use pyo3::{basic::CompareOp, prelude::*};
28
29use crate::{
30 defi::{
31 Chain, Dex,
32 chain::Blockchain,
33 data::{Block, PoolLiquidityUpdate, PoolLiquidityUpdateType, PoolSwap},
34 },
35 enums::OrderSide,
36 identifiers::InstrumentId,
37 types::{Price, Quantity},
38};
39
40#[pymethods]
41impl PoolSwap {
42 #[new]
43 #[allow(clippy::too_many_arguments)]
44 fn py_new(
45 chain: Chain,
46 dex: Dex,
47 instrument_id: InstrumentId,
48 pool_address: String,
49 block: u64,
50 transaction_hash: String,
51 transaction_index: u32,
52 log_index: u32,
53 timestamp: u64,
54 sender: String,
55 side: OrderSide,
56 size: Quantity,
57 price: Price,
58 ) -> PyResult<Self> {
59 let sender = sender.parse().map_err(to_pyvalue_err)?;
60 Ok(Self::new(
61 Arc::new(chain),
62 Arc::new(dex),
63 instrument_id,
64 Address::from_str(&pool_address).map_err(to_pyvalue_err)?,
65 block,
66 transaction_hash,
67 transaction_index,
68 log_index,
69 Some(timestamp.into()),
70 sender,
71 side,
72 size,
73 price,
74 ))
75 }
76
77 #[getter]
78 #[pyo3(name = "chain")]
79 fn py_chain(&self) -> PyResult<Chain> {
80 Ok(self.chain.as_ref().clone())
81 }
82
83 #[getter]
84 #[pyo3(name = "dex")]
85 fn py_dex(&self) -> PyResult<Dex> {
86 Ok(self.dex.as_ref().clone())
87 }
88
89 #[getter]
90 #[pyo3(name = "instrument_id")]
91 fn py_instrument_id(&self) -> InstrumentId {
92 self.instrument_id
93 }
94
95 #[getter]
96 #[pyo3(name = "pool_address")]
97 fn py_pool_address(&self) -> String {
98 self.pool_address.to_string()
99 }
100
101 #[getter]
102 #[pyo3(name = "block")]
103 fn py_block(&self) -> u64 {
104 self.block
105 }
106
107 #[getter]
108 #[pyo3(name = "transaction_hash")]
109 fn py_transaction_hash(&self) -> &str {
110 &self.transaction_hash
111 }
112
113 #[getter]
114 #[pyo3(name = "transaction_index")]
115 fn py_transaction_index(&self) -> u32 {
116 self.transaction_index
117 }
118
119 #[getter]
120 #[pyo3(name = "log_index")]
121 fn py_log_index(&self) -> u32 {
122 self.log_index
123 }
124
125 #[getter]
126 #[pyo3(name = "sender")]
127 fn py_sender(&self) -> String {
128 self.sender.to_string()
129 }
130
131 #[getter]
132 #[pyo3(name = "side")]
133 fn py_side(&self) -> OrderSide {
134 self.side
135 }
136
137 #[getter]
138 #[pyo3(name = "size")]
139 fn py_size(&self) -> Quantity {
140 self.size
141 }
142
143 #[getter]
144 #[pyo3(name = "price")]
145 fn py_price(&self) -> Price {
146 self.price
147 }
148
149 #[getter]
150 #[pyo3(name = "timestamp")]
151 fn py_timestamp(&self) -> Option<u64> {
152 self.timestamp.map(|x| x.as_u64())
153 }
154
155 #[getter]
156 #[pyo3(name = "ts_init")]
157 fn py_ts_init(&self) -> Option<u64> {
158 self.ts_init.map(|x| x.as_u64())
159 }
160
161 fn __str__(&self) -> String {
162 self.to_string()
163 }
164
165 fn __repr__(&self) -> String {
166 format!("{self:?}")
167 }
168
169 fn __hash__(&self) -> u64 {
170 let mut hasher = DefaultHasher::new();
171 self.chain.chain_id.hash(&mut hasher);
172 self.transaction_hash.hash(&mut hasher);
173 self.log_index.hash(&mut hasher);
174 hasher.finish()
175 }
176
177 fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
178 match op {
179 CompareOp::Eq => self == other,
180 CompareOp::Ne => self != other,
181 _ => panic!("Unsupported comparison for PoolSwap"),
182 }
183 }
184}
185
186#[pymethods]
187impl PoolLiquidityUpdate {
188 #[new]
189 #[allow(clippy::too_many_arguments)]
190 fn py_new(
191 chain: Chain,
192 dex: Dex,
193 instrument_id: InstrumentId,
194 pool_address: String,
195 kind: PoolLiquidityUpdateType,
196 block: u64,
197 transaction_hash: String,
198 transaction_index: u32,
199 log_index: u32,
200 sender: Option<String>,
201 owner: String,
202 position_liquidity: Quantity,
203 amount0: Quantity,
204 amount1: Quantity,
205 tick_lower: i32,
206 tick_upper: i32,
207 timestamp: u64,
208 ) -> PyResult<Self> {
209 let sender = sender
210 .map(|s| s.parse())
211 .transpose()
212 .map_err(to_pyvalue_err)?;
213 let owner = owner.parse().map_err(to_pyvalue_err)?;
214 Ok(Self::new(
215 Arc::new(chain),
216 Arc::new(dex),
217 instrument_id,
218 Address::from_str(&pool_address).map_err(to_pyvalue_err)?,
219 kind,
220 block,
221 transaction_hash,
222 transaction_index,
223 log_index,
224 sender,
225 owner,
226 position_liquidity,
227 amount0,
228 amount1,
229 tick_lower,
230 tick_upper,
231 Some(timestamp.into()),
232 ))
233 }
234
235 #[getter]
236 #[pyo3(name = "chain")]
237 fn py_chain(&self) -> PyResult<Chain> {
238 Ok(self.chain.as_ref().clone())
239 }
240
241 #[getter]
242 #[pyo3(name = "dex")]
243 fn py_dex(&self) -> PyResult<Dex> {
244 Ok(self.dex.as_ref().clone())
245 }
246
247 #[getter]
248 #[pyo3(name = "instrument_id")]
249 fn py_instrument_id(&self) -> crate::identifiers::InstrumentId {
250 self.instrument_id
251 }
252
253 #[getter]
254 #[pyo3(name = "pool_address")]
255 fn py_pool_address(&self) -> String {
256 self.pool_address.to_string()
257 }
258
259 #[getter]
260 #[pyo3(name = "kind")]
261 fn py_kind(&self) -> PoolLiquidityUpdateType {
262 self.kind
263 }
264
265 #[getter]
266 #[pyo3(name = "block")]
267 fn py_block(&self) -> u64 {
268 self.block
269 }
270
271 #[getter]
272 #[pyo3(name = "transaction_hash")]
273 fn py_transaction_hash(&self) -> &str {
274 &self.transaction_hash
275 }
276
277 #[getter]
278 #[pyo3(name = "transaction_index")]
279 fn py_transaction_index(&self) -> u32 {
280 self.transaction_index
281 }
282
283 #[getter]
284 #[pyo3(name = "log_index")]
285 fn py_log_index(&self) -> u32 {
286 self.log_index
287 }
288
289 #[getter]
290 #[pyo3(name = "sender")]
291 fn py_sender(&self) -> Option<String> {
292 self.sender.map(|s| s.to_string())
293 }
294
295 #[getter]
296 #[pyo3(name = "owner")]
297 fn py_owner(&self) -> String {
298 self.owner.to_string()
299 }
300
301 #[getter]
302 #[pyo3(name = "position_liquidity")]
303 fn py_position_liquidity(&self) -> Quantity {
304 self.position_liquidity
305 }
306
307 #[getter]
308 #[pyo3(name = "amount0")]
309 fn py_amount0(&self) -> Quantity {
310 self.amount0
311 }
312
313 #[getter]
314 #[pyo3(name = "amount1")]
315 fn py_amount1(&self) -> Quantity {
316 self.amount1
317 }
318
319 #[getter]
320 #[pyo3(name = "tick_lower")]
321 fn py_tick_lower(&self) -> i32 {
322 self.tick_lower
323 }
324
325 #[getter]
326 #[pyo3(name = "tick_upper")]
327 fn py_tick_upper(&self) -> i32 {
328 self.tick_upper
329 }
330
331 #[getter]
332 #[pyo3(name = "timestamp")]
333 fn py_timestamp(&self) -> Option<u64> {
334 self.timestamp.map(|x| x.as_u64())
335 }
336
337 #[getter]
338 #[pyo3(name = "ts_init")]
339 fn py_ts_init(&self) -> Option<u64> {
340 self.ts_init.map(|x| x.as_u64())
341 }
342
343 fn __str__(&self) -> String {
344 self.to_string()
345 }
346
347 fn __repr__(&self) -> String {
348 format!("{self:?}")
349 }
350
351 fn __hash__(&self) -> u64 {
352 let mut hasher = DefaultHasher::new();
353 self.chain.chain_id.hash(&mut hasher);
354 self.transaction_hash.hash(&mut hasher);
355 self.log_index.hash(&mut hasher);
356 hasher.finish()
357 }
358
359 fn __richcmp__(&self, other: &Self, op: pyo3::pyclass::CompareOp) -> bool {
360 match op {
361 CompareOp::Eq => self == other,
362 CompareOp::Ne => self != other,
363 _ => panic!("Unsupported comparison for PoolLiquidityUpdate"),
364 }
365 }
366}
367
368#[pymethods]
369impl Block {
370 #[getter]
371 #[pyo3(name = "chain")]
372 fn py_chain(&self) -> Option<Blockchain> {
373 self.chain
374 }
375
376 #[getter]
377 #[pyo3(name = "hash")]
378 fn py_hash(&self) -> &str {
379 &self.hash
380 }
381
382 #[getter]
383 #[pyo3(name = "number")]
384 fn py_number(&self) -> u64 {
385 self.number
386 }
387
388 #[getter]
389 #[pyo3(name = "parent_hash")]
390 fn py_parent_hash(&self) -> &str {
391 &self.parent_hash
392 }
393
394 #[getter]
395 #[pyo3(name = "miner")]
396 fn py_miner(&self) -> &str {
397 &self.miner
398 }
399
400 #[getter]
401 #[pyo3(name = "gas_limit")]
402 fn py_gas_limit(&self) -> u64 {
403 self.gas_limit
404 }
405
406 #[getter]
407 #[pyo3(name = "gas_used")]
408 fn py_gas_used(&self) -> u64 {
409 self.gas_used
410 }
411
412 #[getter]
413 #[pyo3(name = "base_fee_per_gas")]
414 fn py_base_fee_per_gas(&self) -> Option<String> {
415 self.base_fee_per_gas.map(|x| x.to_string())
416 }
417
418 #[getter]
419 #[pyo3(name = "blob_gas_used")]
420 fn py_blob_gas_used(&self) -> Option<String> {
421 self.blob_gas_used.map(|x| x.to_string())
422 }
423
424 #[getter]
425 #[pyo3(name = "excess_blob_gas")]
426 fn py_excess_blob_gas(&self) -> Option<String> {
427 self.excess_blob_gas.map(|x| x.to_string())
428 }
429
430 #[getter]
431 #[pyo3(name = "l1_gas_price")]
432 fn py_l1_gas_price(&self) -> Option<String> {
433 self.l1_gas_price.map(|x| x.to_string())
434 }
435
436 #[getter]
437 #[pyo3(name = "l1_gas_used")]
438 fn py_l1_gas_used(&self) -> Option<u64> {
439 self.l1_gas_used
440 }
441
442 #[getter]
443 #[pyo3(name = "l1_fee_scalar")]
444 fn py_l1_fee_scalar(&self) -> Option<u64> {
445 self.l1_fee_scalar
446 }
447
448 #[getter]
449 #[pyo3(name = "timestamp")]
450 fn py_timestamp(&self) -> u64 {
451 self.timestamp.as_u64()
452 }
453
454 fn __str__(&self) -> String {
455 self.to_string()
456 }
457
458 fn __repr__(&self) -> String {
459 format!("{self:?}")
460 }
461
462 fn __hash__(&self) -> u64 {
463 let mut hasher = DefaultHasher::new();
464 self.hash.hash(&mut hasher);
465 hasher.finish()
466 }
467}