Module orca_whirlpool.internal.utils.price_math

Expand source code
import math
from decimal import Decimal
from .q64_fixed_point_math import Q64FixedPointMath
from .tick_util import TickUtil


def mul_shift(a: int, b: int, shift: int) -> int:
    return (a * b) >> shift


def tick_index_to_sqrt_price_positive(tick_index: int) -> int:
    tick_index_shifted = tick_index

    if tick_index_shifted & 1 == 0:
        ratio = 79228162514264337593543950336  # 0
    else:
        ratio = 79232123823359799118286999567  # 1

    precalculated_factor = [
        79236085330515764027303304731,  # 2
        79244008939048815603706035061,  # 4
        79259858533276714757314932305,  # 8
        79291567232598584799939703904,  # ...
        79355022692464371645785046466,
        79482085999252804386437311141,
        79736823300114093921829183326,
        80248749790819932309965073892,
        81282483887344747381513967011,
        83390072131320151908154831281,
        87770609709833776024991924138,
        97234110755111693312479820773,
        119332217159966728226237229890,
        179736315981702064433883588727,
        407748233172238350107850275304,
        2098478828474011932436660412517,
        55581415166113811149459800483533,
        38992368544603139932233054999993551,  # 262144
    ]

    for i in range(len(precalculated_factor)):
        tick_index_shifted = tick_index_shifted >> 1
        if tick_index_shifted & 1 != 0:
            ratio = mul_shift(ratio, precalculated_factor[i], 96)

    return mul_shift(ratio, 1, 32)


def tick_index_to_sqrt_price_negative(tick_index: int) -> int:
    tick_index_shifted = abs(tick_index)

    if tick_index_shifted & 1 == 0:
        ratio = 18446744073709551616  # 0
    else:
        ratio = 18445821805675392311  # 1

    precalculated_factor = [
        18444899583751176498,  # 2
        18443055278223354162,  # 4
        18439367220385604838,  # 8
        18431993317065449817,  # ...
        18417254355718160513,
        18387811781193591352,
        18329067761203520168,
        18212142134806087854,
        17980523815641551639,
        17526086738831147013,
        16651378430235024244,
        15030750278693429944,
        12247334978882834399,
        8131365268884726200,
        3584323654723342297,
        696457651847595233,
        26294789957452057,
        37481735321082,  # 262144
    ]

    for i in range(len(precalculated_factor)):
        tick_index_shifted = tick_index_shifted >> 1
        if tick_index_shifted & 1 != 0:
            ratio = mul_shift(ratio, precalculated_factor[i], 64)

    return ratio


class PriceMath:
    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#sqrtPriceX64ToPrice
    # https://github.com/orca-so/whirlpools/blob/main/sdk/src/utils/public/price-math.ts#L22
    @staticmethod
    def sqrt_price_x64_to_price(sqrt_price_x64: int, decimals_a: int, decimals_b: int) -> Decimal:
        decimal_adjust = Decimal(10)**(decimals_a - decimals_b)
        price = Q64FixedPointMath.x64int_to_decimal(sqrt_price_x64) ** 2 * decimal_adjust
        return price

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#sqrtPriceX64ToTickIndex
    # https://github.com/orca-so/whirlpools/blob/2df89bb/sdk/src/utils/public/price-math.ts#L49
    @staticmethod
    def sqrt_price_x64_to_tick_index(sqrt_price_x64: int) -> int:
        # rough calculation
        shift_64 = 2**64
        sqrt_price = sqrt_price_x64 / shift_64
        price = sqrt_price**2
        tick_index = math.floor(math.log(price) / math.log(1.0001))

        # adjust to exact result
        while PriceMath.tick_index_to_sqrt_price_x64(tick_index)   >  sqrt_price_x64: tick_index = tick_index - 1
        while PriceMath.tick_index_to_sqrt_price_x64(tick_index+1) <= sqrt_price_x64: tick_index = tick_index + 1
        return tick_index

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#tickIndexToSqrtPriceX64
    # https://github.com/orca-so/whirlpools/blob/2df89bb/sdk/src/utils/public/price-math.ts#L36
    @staticmethod
    def tick_index_to_sqrt_price_x64(tick_index: int) -> int:
        if tick_index > 0:
            return tick_index_to_sqrt_price_positive(tick_index)
        else:
            return tick_index_to_sqrt_price_negative(tick_index)

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#tickIndexToPrice
    # https://github.com/orca-so/whirlpools/blob/7b9ec35/sdk/src/utils/public/price-math.ts#L101
    @staticmethod
    def tick_index_to_price(tick_index: int, decimals_a: int, decimals_b: int) -> Decimal:
        return PriceMath.sqrt_price_x64_to_price(
            PriceMath.tick_index_to_sqrt_price_x64(tick_index),
            decimals_a,
            decimals_b
        )

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#priceToSqrtPriceX64
    # https://github.com/orca-so/whirlpools/blob/7b9ec35/sdk/src/utils/public/price-math.ts#L18
    @staticmethod
    def price_to_sqrt_price_x64(price: Decimal, decimals_a: int, decimals_b: int) -> int:
        decimal_adjust = Decimal(10)**(decimals_a - decimals_b)
        return Q64FixedPointMath.decimal_to_x64int((price / decimal_adjust).sqrt())

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#priceToTickIndex
    # https://github.com/orca-so/whirlpools/blob/7b9ec35/sdk/src/utils/public/price-math.ts#L109
    @staticmethod
    def price_to_tick_index(price: Decimal, decimals_a: int, decimals_b: int) -> int:
        return PriceMath.sqrt_price_x64_to_tick_index(
            PriceMath.price_to_sqrt_price_x64(price, decimals_a, decimals_b)
        )

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#priceToInitializableTickIndex
    # https://github.com/orca-so/whirlpools/blob/7b9ec35/sdk/src/utils/public/price-math.ts#L115
    @staticmethod
    def price_to_initializable_tick_index(price: Decimal, decimals_a: int, decimals_b: int, tick_spacing: int) -> int:
        return TickUtil.get_initializable_tick_index(
            PriceMath.price_to_tick_index(price, decimals_a, decimals_b),
            tick_spacing
        )

Functions

def mul_shift(a: int, b: int, shift: int) ‑> int
Expand source code
def mul_shift(a: int, b: int, shift: int) -> int:
    return (a * b) >> shift
def tick_index_to_sqrt_price_negative(tick_index: int) ‑> int
Expand source code
def tick_index_to_sqrt_price_negative(tick_index: int) -> int:
    tick_index_shifted = abs(tick_index)

    if tick_index_shifted & 1 == 0:
        ratio = 18446744073709551616  # 0
    else:
        ratio = 18445821805675392311  # 1

    precalculated_factor = [
        18444899583751176498,  # 2
        18443055278223354162,  # 4
        18439367220385604838,  # 8
        18431993317065449817,  # ...
        18417254355718160513,
        18387811781193591352,
        18329067761203520168,
        18212142134806087854,
        17980523815641551639,
        17526086738831147013,
        16651378430235024244,
        15030750278693429944,
        12247334978882834399,
        8131365268884726200,
        3584323654723342297,
        696457651847595233,
        26294789957452057,
        37481735321082,  # 262144
    ]

    for i in range(len(precalculated_factor)):
        tick_index_shifted = tick_index_shifted >> 1
        if tick_index_shifted & 1 != 0:
            ratio = mul_shift(ratio, precalculated_factor[i], 64)

    return ratio
def tick_index_to_sqrt_price_positive(tick_index: int) ‑> int
Expand source code
def tick_index_to_sqrt_price_positive(tick_index: int) -> int:
    tick_index_shifted = tick_index

    if tick_index_shifted & 1 == 0:
        ratio = 79228162514264337593543950336  # 0
    else:
        ratio = 79232123823359799118286999567  # 1

    precalculated_factor = [
        79236085330515764027303304731,  # 2
        79244008939048815603706035061,  # 4
        79259858533276714757314932305,  # 8
        79291567232598584799939703904,  # ...
        79355022692464371645785046466,
        79482085999252804386437311141,
        79736823300114093921829183326,
        80248749790819932309965073892,
        81282483887344747381513967011,
        83390072131320151908154831281,
        87770609709833776024991924138,
        97234110755111693312479820773,
        119332217159966728226237229890,
        179736315981702064433883588727,
        407748233172238350107850275304,
        2098478828474011932436660412517,
        55581415166113811149459800483533,
        38992368544603139932233054999993551,  # 262144
    ]

    for i in range(len(precalculated_factor)):
        tick_index_shifted = tick_index_shifted >> 1
        if tick_index_shifted & 1 != 0:
            ratio = mul_shift(ratio, precalculated_factor[i], 96)

    return mul_shift(ratio, 1, 32)

Classes

class PriceMath
Expand source code
class PriceMath:
    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#sqrtPriceX64ToPrice
    # https://github.com/orca-so/whirlpools/blob/main/sdk/src/utils/public/price-math.ts#L22
    @staticmethod
    def sqrt_price_x64_to_price(sqrt_price_x64: int, decimals_a: int, decimals_b: int) -> Decimal:
        decimal_adjust = Decimal(10)**(decimals_a - decimals_b)
        price = Q64FixedPointMath.x64int_to_decimal(sqrt_price_x64) ** 2 * decimal_adjust
        return price

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#sqrtPriceX64ToTickIndex
    # https://github.com/orca-so/whirlpools/blob/2df89bb/sdk/src/utils/public/price-math.ts#L49
    @staticmethod
    def sqrt_price_x64_to_tick_index(sqrt_price_x64: int) -> int:
        # rough calculation
        shift_64 = 2**64
        sqrt_price = sqrt_price_x64 / shift_64
        price = sqrt_price**2
        tick_index = math.floor(math.log(price) / math.log(1.0001))

        # adjust to exact result
        while PriceMath.tick_index_to_sqrt_price_x64(tick_index)   >  sqrt_price_x64: tick_index = tick_index - 1
        while PriceMath.tick_index_to_sqrt_price_x64(tick_index+1) <= sqrt_price_x64: tick_index = tick_index + 1
        return tick_index

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#tickIndexToSqrtPriceX64
    # https://github.com/orca-so/whirlpools/blob/2df89bb/sdk/src/utils/public/price-math.ts#L36
    @staticmethod
    def tick_index_to_sqrt_price_x64(tick_index: int) -> int:
        if tick_index > 0:
            return tick_index_to_sqrt_price_positive(tick_index)
        else:
            return tick_index_to_sqrt_price_negative(tick_index)

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#tickIndexToPrice
    # https://github.com/orca-so/whirlpools/blob/7b9ec35/sdk/src/utils/public/price-math.ts#L101
    @staticmethod
    def tick_index_to_price(tick_index: int, decimals_a: int, decimals_b: int) -> Decimal:
        return PriceMath.sqrt_price_x64_to_price(
            PriceMath.tick_index_to_sqrt_price_x64(tick_index),
            decimals_a,
            decimals_b
        )

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#priceToSqrtPriceX64
    # https://github.com/orca-so/whirlpools/blob/7b9ec35/sdk/src/utils/public/price-math.ts#L18
    @staticmethod
    def price_to_sqrt_price_x64(price: Decimal, decimals_a: int, decimals_b: int) -> int:
        decimal_adjust = Decimal(10)**(decimals_a - decimals_b)
        return Q64FixedPointMath.decimal_to_x64int((price / decimal_adjust).sqrt())

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#priceToTickIndex
    # https://github.com/orca-so/whirlpools/blob/7b9ec35/sdk/src/utils/public/price-math.ts#L109
    @staticmethod
    def price_to_tick_index(price: Decimal, decimals_a: int, decimals_b: int) -> int:
        return PriceMath.sqrt_price_x64_to_tick_index(
            PriceMath.price_to_sqrt_price_x64(price, decimals_a, decimals_b)
        )

    # https://orca-so.github.io/whirlpools/classes/PriceMath.html#priceToInitializableTickIndex
    # https://github.com/orca-so/whirlpools/blob/7b9ec35/sdk/src/utils/public/price-math.ts#L115
    @staticmethod
    def price_to_initializable_tick_index(price: Decimal, decimals_a: int, decimals_b: int, tick_spacing: int) -> int:
        return TickUtil.get_initializable_tick_index(
            PriceMath.price_to_tick_index(price, decimals_a, decimals_b),
            tick_spacing
        )

Static methods

def price_to_initializable_tick_index(price: decimal.Decimal, decimals_a: int, decimals_b: int, tick_spacing: int) ‑> int
Expand source code
@staticmethod
def price_to_initializable_tick_index(price: Decimal, decimals_a: int, decimals_b: int, tick_spacing: int) -> int:
    return TickUtil.get_initializable_tick_index(
        PriceMath.price_to_tick_index(price, decimals_a, decimals_b),
        tick_spacing
    )
def price_to_sqrt_price_x64(price: decimal.Decimal, decimals_a: int, decimals_b: int) ‑> int
Expand source code
@staticmethod
def price_to_sqrt_price_x64(price: Decimal, decimals_a: int, decimals_b: int) -> int:
    decimal_adjust = Decimal(10)**(decimals_a - decimals_b)
    return Q64FixedPointMath.decimal_to_x64int((price / decimal_adjust).sqrt())
def price_to_tick_index(price: decimal.Decimal, decimals_a: int, decimals_b: int) ‑> int
Expand source code
@staticmethod
def price_to_tick_index(price: Decimal, decimals_a: int, decimals_b: int) -> int:
    return PriceMath.sqrt_price_x64_to_tick_index(
        PriceMath.price_to_sqrt_price_x64(price, decimals_a, decimals_b)
    )
def sqrt_price_x64_to_price(sqrt_price_x64: int, decimals_a: int, decimals_b: int) ‑> decimal.Decimal
Expand source code
@staticmethod
def sqrt_price_x64_to_price(sqrt_price_x64: int, decimals_a: int, decimals_b: int) -> Decimal:
    decimal_adjust = Decimal(10)**(decimals_a - decimals_b)
    price = Q64FixedPointMath.x64int_to_decimal(sqrt_price_x64) ** 2 * decimal_adjust
    return price
def sqrt_price_x64_to_tick_index(sqrt_price_x64: int) ‑> int
Expand source code
@staticmethod
def sqrt_price_x64_to_tick_index(sqrt_price_x64: int) -> int:
    # rough calculation
    shift_64 = 2**64
    sqrt_price = sqrt_price_x64 / shift_64
    price = sqrt_price**2
    tick_index = math.floor(math.log(price) / math.log(1.0001))

    # adjust to exact result
    while PriceMath.tick_index_to_sqrt_price_x64(tick_index)   >  sqrt_price_x64: tick_index = tick_index - 1
    while PriceMath.tick_index_to_sqrt_price_x64(tick_index+1) <= sqrt_price_x64: tick_index = tick_index + 1
    return tick_index
def tick_index_to_price(tick_index: int, decimals_a: int, decimals_b: int) ‑> decimal.Decimal
Expand source code
@staticmethod
def tick_index_to_price(tick_index: int, decimals_a: int, decimals_b: int) -> Decimal:
    return PriceMath.sqrt_price_x64_to_price(
        PriceMath.tick_index_to_sqrt_price_x64(tick_index),
        decimals_a,
        decimals_b
    )
def tick_index_to_sqrt_price_x64(tick_index: int) ‑> int
Expand source code
@staticmethod
def tick_index_to_sqrt_price_x64(tick_index: int) -> int:
    if tick_index > 0:
        return tick_index_to_sqrt_price_positive(tick_index)
    else:
        return tick_index_to_sqrt_price_negative(tick_index)