-
Notifications
You must be signed in to change notification settings - Fork 10
Description
This issue outlines the calculation method required to calculate the dynamic fee and fees collected per Swap
event on CLPool
contract, given dynamic fee parameters and tick.
The explanation will include examples from Python code used in the backtesting framework.
Instead of using tick
parameter from the Swap
event log directly, it needs to be derived from sqrtPriceX96
using the following formula:
tick = 2* ((sqrtPriceX96 / (2**96)) / math.log(1.0001))
Once derived tick
value is calculated, tick value for each block is required and use previous value if it doesn't exist for a particular block
df = complete_blocks.join(df, on="block", how="left").with_columns(
pl.col("*").forward_fill()
)
Calculate TWAVG of tick by using mean of ticks per block, given window size.
# Step 1: Aggregate by unique blocks
block_avg = (
self.tick_history.groupby("block")
.agg(pl.col("tick").mean().alias("block_mean"))
.sort("block")
)
# Step 2: Create a rolling mean of ticks with the specified window size
time_weighted_average_tick = block_avg.select(
"block",
pl.col("block_mean")
.rolling_mean(window_size=self.config.twavg_window_size)
.alias("time_weighted_average_tick"),
).sort("block")
# Step 3: Join the time-weighted average tick back to the original tick history
# This ensures each row in tick_history gets the corresponding time-weighted average
self.tick_history = self.tick_history.join(
time_weighted_average_tick.select(["block", "time_weighted_average_tick"]),
on="block",
how="left",
)
Calculate final dynamic fee component and final fee by using formulae
# Calculate the dynamic component using numpy for vector operations
dynamic_component = (
self.config.k_value
* self.calculate_absolute_tick_deviation()
/ SCALING_PRECISION_FOR_DYNAMIC_FEE
)
# Calculate the dynamic fee using numpy and convert back to polars series
dynamic_fee_values = np.minimum(
self.config.fee_base + dynamic_component, self.config.fee_cap
)
Fees collected in USD can be calculated (in token1
) using the formula below:
(price
here is calculated using tick
value)
self.total_fees_collected = (
(
abs(self.tick_history["amount0"])
/ 1e18
* self.tick_history["dynamic_fee"]
* self.tick_history["price"]
)
.fill_nan(0)
.sum()
)