use alloy_primitives::{U160, U256};
use nautilus_core::UnixNanos;
use serde::{Deserialize, Serialize};
use crate::{
defi::{
data::block::BlockPosition, pool_analysis::position::PoolPosition, tick_map::tick::PoolTick,
},
identifiers::InstrumentId,
};
pub const PROTOCOL_FEE_BASIS_POINTS_DENOMINATOR: u32 = 10_000;
#[cfg_attr(
feature = "python",
pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model", from_py_object)
)]
#[cfg_attr(
feature = "python",
pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PoolSnapshot {
pub instrument_id: InstrumentId,
pub state: PoolState,
pub positions: Vec<PoolPosition>,
pub ticks: Vec<PoolTick>,
pub analytics: PoolAnalytics,
pub block_position: BlockPosition,
#[serde(default)]
pub ts_event: UnixNanos,
#[serde(default)]
pub ts_init: UnixNanos,
}
impl PoolSnapshot {
#[must_use]
#[expect(clippy::too_many_arguments)]
pub fn new(
instrument_id: InstrumentId,
state: PoolState,
positions: Vec<PoolPosition>,
ticks: Vec<PoolTick>,
analytics: PoolAnalytics,
block_position: BlockPosition,
ts_event: UnixNanos,
ts_init: UnixNanos,
) -> Self {
Self {
instrument_id,
state,
positions,
ticks,
analytics,
block_position,
ts_event,
ts_init,
}
}
}
#[cfg_attr(
feature = "python",
pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model", from_py_object)
)]
#[cfg_attr(
feature = "python",
pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
)]
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PoolState {
pub current_tick: i32,
pub price_sqrt_ratio_x96: U160,
pub liquidity: u128,
pub protocol_fees_token0: U256,
pub protocol_fees_token1: U256,
pub fee_protocol: u8,
#[serde(default)]
pub fee_protocol0_basis_points: Option<u32>,
#[serde(default)]
pub fee_protocol1_basis_points: Option<u32>,
pub fee_growth_global_0: U256,
pub fee_growth_global_1: U256,
}
impl PoolState {
#[must_use]
pub fn new(protocol_fees_token0: U256, protocol_fees_token1: U256, fee_protocol: u8) -> Self {
Self {
current_tick: 0,
price_sqrt_ratio_x96: U160::ZERO,
liquidity: 0,
protocol_fees_token0,
protocol_fees_token1,
fee_protocol,
fee_protocol0_basis_points: None,
fee_protocol1_basis_points: None,
fee_growth_global_0: U256::ZERO,
fee_growth_global_1: U256::ZERO,
}
}
#[must_use]
pub const fn uniswap_v3_fee_protocol(&self, zero_for_one: bool) -> u8 {
if zero_for_one {
self.fee_protocol % 16
} else {
self.fee_protocol >> 4
}
}
#[must_use]
pub const fn fee_protocol_basis_points(&self, zero_for_one: bool) -> Option<u32> {
if zero_for_one {
self.fee_protocol0_basis_points
} else {
self.fee_protocol1_basis_points
}
}
pub fn set_uniswap_v3_fee_protocol(&mut self, fee_protocol: u8) {
self.fee_protocol = fee_protocol;
self.fee_protocol0_basis_points = None;
self.fee_protocol1_basis_points = None;
}
pub fn set_protocol_fee_basis_points(&mut self, fee_protocol0: u32, fee_protocol1: u32) {
self.fee_protocol = 0;
self.fee_protocol0_basis_points = Some(fee_protocol0);
self.fee_protocol1_basis_points = Some(fee_protocol1);
}
}
impl Default for PoolState {
fn default() -> Self {
Self {
current_tick: 0,
price_sqrt_ratio_x96: U160::ZERO,
liquidity: 0,
protocol_fees_token0: U256::ZERO,
protocol_fees_token1: U256::ZERO,
fee_protocol: 0,
fee_protocol0_basis_points: None,
fee_protocol1_basis_points: None,
fee_growth_global_0: U256::ZERO,
fee_growth_global_1: U256::ZERO,
}
}
}
#[cfg_attr(
feature = "python",
pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model", from_py_object)
)]
#[cfg_attr(
feature = "python",
pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
)]
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PoolAnalytics {
pub total_amount0_deposited: U256,
pub total_amount1_deposited: U256,
pub total_amount0_collected: U256,
pub total_amount1_collected: U256,
pub total_swaps: u64,
pub total_mints: u64,
pub total_burns: u64,
pub total_fee_collects: u64,
pub total_flashes: u64,
pub liquidity_utilization_rate: f64,
}
impl Default for PoolAnalytics {
fn default() -> Self {
Self {
total_amount0_deposited: U256::ZERO,
total_amount1_deposited: U256::ZERO,
total_amount0_collected: U256::ZERO,
total_amount1_collected: U256::ZERO,
total_swaps: 0,
total_mints: 0,
total_burns: 0,
total_fee_collects: 0,
total_flashes: 0,
liquidity_utilization_rate: 0.0,
}
}
}