Source code for bittensor.utils.liquidity
"""
This module provides utilities for managing liquidity positions and price conversions in the Bittensor network. The
module handles conversions between TAO and Alpha tokens while maintaining precise calculations for liquidity
provisioning and fee distribution.
"""
import math
from typing import Any
from dataclasses import dataclass
from bittensor.utils.balance import Balance, fixed_to_float
# These three constants are unchangeable at the level of Uniswap math
MIN_TICK = -887272
MAX_TICK = 887272
PRICE_STEP = 1.0001
[docs]
@dataclass
class LiquidityPosition:
id: int
price_low: Balance # RAO
price_high: Balance # RAO
liquidity: Balance # TAO + ALPHA (sqrt by TAO balance * Alpha Balance -> math under the hood)
fees_tao: Balance # RAO
fees_alpha: Balance # RAO
netuid: int
[docs]
def to_token_amounts(
self, current_subnet_price: Balance
) -> tuple[Balance, Balance]:
"""Convert a position to token amounts.
Arguments:
current_subnet_price: current subnet price in Alpha.
Returns:
tuple[int, int]:
Amount of Alpha in liquidity
Amount of TAO in liquidity
Liquidity is a combination of TAO and Alpha depending on the price of the subnet at the moment.
"""
sqrt_price_low = math.sqrt(self.price_low)
sqrt_price_high = math.sqrt(self.price_high)
sqrt_current_subnet_price = math.sqrt(current_subnet_price)
if sqrt_current_subnet_price < sqrt_price_low:
amount_alpha = self.liquidity * (1 / sqrt_price_low - 1 / sqrt_price_high)
amount_tao = 0
elif sqrt_current_subnet_price > sqrt_price_high:
amount_alpha = 0
amount_tao = self.liquidity * (sqrt_price_high - sqrt_price_low)
else:
amount_alpha = self.liquidity * (
1 / sqrt_current_subnet_price - 1 / sqrt_price_high
)
amount_tao = self.liquidity * (sqrt_current_subnet_price - sqrt_price_low)
return Balance.from_rao(int(amount_alpha), self.netuid), Balance.from_rao(
int(amount_tao)
)
[docs]
def price_to_tick(price: float) -> int:
"""Converts a float price to the nearest Uniswap V3 tick index."""
if price <= 0:
raise ValueError(f"Price must be positive, got `{price}`.")
tick = int(math.log(price) / math.log(PRICE_STEP))
if not (MIN_TICK <= tick <= MAX_TICK):
raise ValueError(
f"Resulting tick {tick} is out of allowed range ({MIN_TICK} to {MAX_TICK})"
)
return tick
[docs]
def tick_to_price(tick: int) -> float:
"""Convert an integer Uniswap V3 tick index to float price."""
if not (MIN_TICK <= tick <= MAX_TICK):
raise ValueError("Tick is out of allowed range")
return PRICE_STEP**tick
[docs]
def get_fees(
current_tick: int,
tick: dict,
tick_index: int,
quote: bool,
global_fees_tao: float,
global_fees_alpha: float,
above: bool,
) -> float:
"""Returns the liquidity fee."""
tick_fee_key = "fees_out_tao" if quote else "fees_out_alpha"
tick_fee_value = fixed_to_float(tick.get(tick_fee_key))
global_fee_value = global_fees_tao if quote else global_fees_alpha
if above:
return (
global_fee_value - tick_fee_value
if tick_index <= current_tick
else tick_fee_value
)
return (
tick_fee_value
if tick_index <= current_tick
else global_fee_value - tick_fee_value
)
[docs]
def get_fees_in_range(
quote: bool,
global_fees_tao: float,
global_fees_alpha: float,
fees_below_low: float,
fees_above_high: float,
) -> float:
"""Returns the liquidity fee value in a range."""
global_fees = global_fees_tao if quote else global_fees_alpha
return global_fees - fees_below_low - fees_above_high
# Calculate fees for a position
[docs]
def calculate_fees(
position: dict[str, Any],
global_fees_tao: float,
global_fees_alpha: float,
tao_fees_below_low: float,
tao_fees_above_high: float,
alpha_fees_below_low: float,
alpha_fees_above_high: float,
netuid: int,
) -> tuple[Balance, Balance]:
fee_tao_agg = get_fees_in_range(
quote=True,
global_fees_tao=global_fees_tao,
global_fees_alpha=global_fees_alpha,
fees_below_low=tao_fees_below_low,
fees_above_high=tao_fees_above_high,
)
fee_alpha_agg = get_fees_in_range(
quote=False,
global_fees_tao=global_fees_tao,
global_fees_alpha=global_fees_alpha,
fees_below_low=alpha_fees_below_low,
fees_above_high=alpha_fees_above_high,
)
fee_tao = fee_tao_agg - fixed_to_float(position["fees_tao"])
fee_alpha = fee_alpha_agg - fixed_to_float(position["fees_alpha"])
liquidity_frac = position["liquidity"]
fee_tao = liquidity_frac * fee_tao
fee_alpha = liquidity_frac * fee_alpha
return Balance.from_rao(int(fee_tao)), Balance.from_rao(int(fee_alpha), netuid)