Skip to content

Calculating dynamic fee and fee collected per Swap event #336

@WooSungD

Description

@WooSungD

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()
        )

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions