Try   HackMD

Tick Choice Rule

Outlines a strategy for maximizing expected yield on active liquidity for Uniswap-style concentrated liquidity LPs.

TL;DR

  • This note gives guidance on what concentrated liquidity tick width to choose for +EV LPing with no external hedging, ideal for vaults.
  • Strategy consists of providing liquidity over a fixed time period, and rebalancing around the current pool tick once this time period elapses.
  • Rebalance swaps at the end of each period are executed through the pool itself, assuming virtual pool liquidity outside of the LP's contribution is approximately constant over the period.
  • Yields an optimization problem where the LP must balance gains from a larger share of pro-rata swap fees with potential losses due to a higher probability of inactive liquidity and larger rebalancing costs. i.e. Maximize with respect to half tick width
    Δ
    the value of the LP position after the next rebalance swap:

E0[Vτ(Δ)]

  • Provided script optimize.py uses scipy to determine the solution numerically, given the complexity of full expressions.
  • Ignoring drift, +EV solutions appear to exist when the Uniswap V2 limit of infinite tick width is also +EV. To first order assuming GBM, this occurs when the fee volume per unit of pool virtual liquidity
    θ
    outside of the LP's own physical contribution
    l
    satisfies

θ>σ28(l+1)

  • Suggested vault strategy is then relatively simple. Choose full tick width when fee volume
    θ
    is less than the bound
    (σ2/8)(l+1)
    to minimize IL. However, when
    θ
    exceeds the bound, concentrate liquidity around the critical half tick width
    Δc
    (calculated by optimize.py) that solves

0=ΔE0[Vτ]|Δ=Δc

  • Using the Ethereum mainnet Uniswap V3 USDC/ETH 5 bps pool as an example, a tick width of about ~ +/- 15% around the current price appears optimal under GBM assuming a 7 day rebalance period, LP providing $10M of physical liquidity to the pool with existing virtual liquidity of about $1.6B, and 24h fees of approximately $500K. Expected yield would be close to 37 bps over the 7 days.

Intro

Given a pair of tokens

(X,Y) an LP is comfortable providing liquidity for on Uniswap V3 (or V4 in the future), what should the LP choose as their tick range to maximize expected yield?

This is an inherently probabilistic question relying on the nature of the stochastic price process

pt for the pool. The tradeoff the LP must consider is that a tighter tick range leads to a larger share of the currently active liquidity and thus a larger share of pro-rata swap fees. However, the downside to a tighter tick range is the higher probability of their liquidity quickly becoming inactive in addition to larger rebalancing costs (i.e. realized IL + swap fees, slippage) as price significantly deviates.

I'll start general by not assuming any particular model for the stochastic price process. Then I'll focus on results for standard but unrealistic GBM.

The LP employs a strategy of deploying liquidity in a set tick range over fixed periods of time

[ti,ti+τ], rebalancing around the current price once another period has elapsed. The problem reduces to the LP setting an optimal half tick width
Δ
given a chosen rebalance period
τ
, liquidity to deploy
l
, as well as existing pool conditions (i.e. liquidity, volume, and price history).

From a vault perspective, this is easier to implement with keepers vs what I've seen from existing work on this topic where LPs rebalance only when price moves out of a range. The latter strategy seems prone to LPs chasing price. Gas costs are less variable in the former as one roughly knows when the rebalance transactions will happen regardless of where the price has gone. It's also easier to analyze analytically.

Setup

Assume the LP provides liquidity in a set tick range over a fixed period of time

τ, and rebalances to a new "optimal" tick range every time this period has elapsed. To optimize over the next period
0t<τ
, take the LP to enter a range position at the beginning of the period between prices
[pa,pb]
.

The LP aims to maximize their excess fee revenues relative to principal losses caused by rebalancing costs. The value of the LP's position will be the principal plus cumulative fee revenues gained over the period less swap fees and slippage incurred on the portion of principal that needs to be rebalanced at the end of the period due to swaps on the pool.

For each time period

tit<ti+τ, the same optimization procedure also takes place with the LP rebalancing to a new price range
[pai,pbi]
. I'll assume the time-dependence of the stochastic process doesn't change.

I'll examine portfolio values assuming

Y as the quote currency. I break down the value of the portfolio at the end of the period into two components:

  • ψτ
    : value of accumulated fee revenues over the period
    [0,τ]
  • ρτ
    : value of the principal tokens used for LPing after performing the rebalancing swap at
    τ

From this portfolio value expression,

Vτ=ψτ+ρτ

I calculate the yield of the LP's portfolio value over the period. I then maximize the expected value of the yield to determine an appropriate choice of tick width.

I take the LP to be providing active liquidity in an on-chain vault that does not hedge on other venues. Information the vault has access to is constained to the individual pool the LP is providing liquidity on.

Fee Revenues

The pool sees

Y token volume in of
vtY
with fees at fee tier
f
taken on that volume of

vtYfdt

Cumulative fees between rebalances taken by the pool are

0τdtvtYf

But the LP only sees a fraction of these fees based on whether the current price is in range

paptpb at the time the swaps occur. If not in range, the LP receives no fees. If in range, the liquidity is active and the LP receives a pro-rata share based on the existing virtual liquidity at the current tick range.

Assume before the LP provides liquidity, the virtual liquidity on the pool is

L. Virtual liquidity on the pool is taken to remain constant throughout the rebalance period, outside of the LP's actions. The LP's share of virtual liquidity after they add
δL
to the pool will be

δLL+δL

Further, assume token volumes in are roughly constant throughout the rebalance period, with

v0=v0Yp0v0X

Taking the external active liquidity and volumes to each be roughly constant between rebalances avoids estimating liquidity and volume distributions.

The LP receives fees on volume at time

t of

dψtY=v0fdtδLL+δL1paptpb

where

1A is the indicator function. Assume the LP provides liquidity symmetrically around the current tick range when rebalancing, such that

pbp0=p0pa=eΔ

for a full tick range width of

2Δ in natural log terms. One finds their contribution to Uniswap-style virtual liquidity will be

δL=δx0δy01eΔ/2

for

(δx0,δy0) initial token amounts sent to the pool.

Y cumulative fees received by the LP at the end of the rebalance period are then

ψτY=δy0θ1eΔ/2+l0τdt1paptpb

where I've introduced shorthand

θ=v0fLp0l=δy0Lp0

for fee volume per unit of external virtual liquidity (excluding the LP) and the amount of physical tokens the LP contributes to the pool per unit of external virtual liquidity, respectively.

Note that the "payoff" of the future LP fee revenues looks like a portfolio of double digital options with expiries at each successive time

t between rebalances. This is useful context when thinking through replication strategies for any derivative protocols aiming to tokenize these fee streams and sell them off to interested buyers.

I assume the LP holds the accumulated fees until the end of the rebalance period, at which point they reinvest the fees into principal for the next period. Accumulated

X token fees are then valued at the price at the end of the rebalance period.

Total value of the fees accumulated in quote terms at the end of the period will be

ψτ=ψτY+pτψτXψτY[1+pτp0]

given prior assumptions on volume.

Principal Losses

As a Uniswap V3 liquidity provider, token balances attributed to the LP change as price moves. At any time

t before the rebalance swap happens, the LP has principal token balances in the pool of

δxt=δL[1pa1pb]1pt<pa+δL[1pt1pb]1paptpbδyt=δL[pbpa]1pt>pb+δL[ptpa]1paptpb

At rebalance time

τ, the LP removes all of their liquidity and swaps a portion of the returned principal through the same pool. The LP then adds the rebalanced liquidity to the pool. I assume arbitraguers immediately return the price back to its pre-swap value of
pτ
, after the LP rebalances but prior to the LP adding liquidity again to simplify things.

Post rebalance swap, the LP holds token balances of

δxτ=δxτ+ϵτX1pτ>0ϵτX(1+f)1pτ>0δyτ=δyτϵτY(1+f)1pτ>0+ϵτY1pτ<0

Principal balances after the swap must satisfy

pτδxτ=δyτ

for the LP to rebalance around the current price at the end of the period. The LP removes their full liquidity contribution then swaps through the remaining external virtual liquidity

L. The pool pre- and post-swap obeys the Uniswap invariant

L2=x~τy~τ=(x~τϵτX)(y~τ±ϵτY)

where

x~τ=Lpτy~τ=Lpτ

are virtual token balances ignoring possible changes in external liquidity distribution between ticks (roughly ok for smaller LP sizes).

±, signs are dictated by whether
pτ>p0
or
pτ<p0
. Expanding terms to second order in
f,(ϵ/x~),(δx/x)
, I find

δyτy~τ12[δyτy~τ+δxτx~τ]12|δyτy~τδxτx~τ|[f2+14|δyτy~τδxτx~τ|]

and

δyτy~τ+δxτx~τ=l[eΔ/2+1][1pτ<papτp0+1pτ>pbp0pτ]+1papτpbl1eΔ/2[2eΔ/2(p0pτ+pτp0)]
δyτy~τδxτx~τ=l[eΔ/2+1][1pτ>pbp0pτ1pτ<papτp0]+1papτpbleΔ/21[pτp0p0pτ]

This leads to a principal value at the end of the period post-swap of

ρτy~τ{δyτy~τ+δxτx~τ|δyτy~τδxτx~τ|[f2+14|δyτy~τδxτx~τ|]}

Each term represents

  • δyτ+pτδxτ
    : principal before rebalancing
  • (f/2)|δyτpτδxτ|
    : swap fees paid on rebalance
  • |δyτpτδxτ|2/(4y~τ)
    : slippage paid on rebalance

EV for LPs

The LP optimizes their expected gains at the end of the period from information known at the beginning of the period

F0, i.e.

E0[Vτ]

Assume the price process abides by GBM

dpt=μptdt+σptdWtpt=p0eμt+σWtμ=μσ2/2

where

Wt is a Wiener process. I'll make use of the identity that the expectation of the indicator function is the probability of the associated event occurring, as e.g.

E0[1papτpb]=P0[Δln(pτ/p0)Δ]=Φ(Δμτστ)Φ(Δμτστ)

Φ(z) is the standard normal CDF.

To simplify expressions, let

dt+=Δμtσtdt=Δμtσt

I find for the fee revenue term (see Appendix A)

E0[ψτ]=δy0θ1eΔ/2+l0τdt{Φ(dt+)Φ(dt)+eμτ[Φ(dt+σt)Φ(dtσt)]}

and below for the principal value terms (see Appendix B).

Principal before rebalance swap:

E0[δyτ+pτδxτ]=δy0{[eΔ/2+1][eμτΦ(dτστ)+1Φ(dτ+)]+11eΔ/2[2e12(μ14σ2)τ(Φ(dτ+στ/2)Φ(dτστ/2))eΔ/2(Φ(dτ+)Φ(dτ)+eμτ[Φ(dτ+στ)Φ(dτστ)])]}

Swap fees paid on rebalance:

E0[(f/2)|δyτpτδxτ|]=δy0(f/2){[eΔ/2+1][eμτΦ(dτστ)+1Φ(dτ+)]+1eΔ/21[eμτ(Φ(dτ+στ)+Φ(dτστ)2Φ(μτ/(στ)στ))+2Φ(μτ/(στ))Φ(dτ+)Φ(dτ)]}

Slippage paid on rebalance:

E0[|δyτpτδxτ|2/(4y~τ)]=δy0(l/4)e12(μ+34σ2)τ{[eΔ/2+1]2[eμτ(1Φ(dτ++στ/2))+eμτΦ(dτ3στ/2)]1[eΔ/21]2[eμτ(Φ(dτ+3στ/2)Φ(dτ3στ/2))+eμτ(Φ(dτ++στ/2)Φ(dτ+στ/2))2e12σ2τ(Φ(dτ+στ/2)Φ(dτστ/2))]}

Hard to have clarity about these expressions from just looking at them. Desmos plots make life easier:

Solid black line is the expected LP value

E0[Vτ] at the end of the period as a function of half tick width
Δ
, given strategist set parameters for liquidity provided
l
and rebalance period length
τ
as well as assumed pool fee volume
θ
. Solid blue line is the associated expected yield relative to initial principal
yτ=E0[Vτ]/V01
. This is also given as a function of half tick width
Δ
, but scaled by a multiplier of 100 to express in percentage points.

Optimization for half tick width choice reduces to finding the critical point

Δc

0=ΔE0[Vτ]|Δ=Δc

at which the yield peaks. Script optimize.py in the Kodiak simulations repo uses the scipy.optimize package to find this value.

When ignoring drift

μ=0, expected yield appears to peak with the same sign as expected yield when
Δ
. In the Uniswap V2 limit with zero drift, expected yield is positive when fee volume exceeds

l+1τ[1e18σ2τ]

or to first order

θ>σ28(l+1)

I used the Ethereum mainnet Uniswap V3 USDC/ETH 5 bps pool history to fit and set parameters in the Desmos plot above, ignoring drift param results. For a 7 day rebalance period providing about $10M of physical liquidity to a pool with virtual liquidity of about $1.6B and assuming 24h fees of about $500K, a tick width of approximately 0.14 * 2 / log(1.0001) = 2800 (i.e. ~ +/- 15% around current price) appears optimal under GBM with an expected yield at the end of the period after rebalancing of 37 bps.

References

Appendix

A. Derivation of
E0[ψτ]

Need to take the expectation of

E0[ψτ]=δy0θ1eΔ/2+l0τdtE0[1paptpb(1pτ/p0)]

From earlier assumptions of GBM

pτ/p0=eμτ+σWτ

and symmetric LP tick range

1paptpb=1Δln(pt/p0)Δ=1ΔμtσWtΔμtσ

Which means

E0[ψτ]=δy0θ1eΔ/2+l0τdt{E0[1ΔμtσWtΔμtσ]eμτE0[1ΔμtσWtΔμtσeσWτ]}

But given the Gaussian increments of the Wiener process,

E0[1ΔμtσWtΔμtσ]=P0[ΔμtσWtΔμtσ]=Φ(Δμtσt)Φ(Δμtσt)=Φ(dt+)Φ(dt)

Need to be a bit more careful with the second term to separate out independent r.v.s. Using Wiener process properties of stationary and independent increments

E0[1ΔμtσWtΔμtσeσWτ]=E0[1ΔμtσWtΔμtσeσ(WτWt+Wt)]=E0[1ΔμtσWtΔμtσeσWteσWτt]=E0[1ΔμtσWtΔμtσeσWt]E0[eσWτt]=e12σ2(τt)E0[1ΔμtσWtΔμtσeσWt]=e12σ2(τt)dtdt+dz2πe12z2+σtz=e12σ2(τt)dtdt+dz2πe12(z22σtz+σ2tσ2t)=e12σ2τdtdt+dz2πe12(zσt)2=e12σ2τdtσtdt+σtdz2πe12z2=e12σ2τ[Φ(dt+σt)Φ(dtσt)]

Leads to

E0[ψτ]=δy0θ1eΔ/2+l0τdt{Φ(dt+)Φ(dt)eμτ[Φ(dt+σt)Φ(dtσt)]}

B. Derivation of
E0[ρτ]

To second order in

f,(ϵ/x~),(δx/x), have

E0[ρτ]E0[δyτ+pτδxτ]E0[(f/2)|δyτpτδxτ|]E0[|δyτpτδxτ|2/(4y~τ)]

I'll divide into separate sections for each term.

Principal before rebalancing

From the end of principal losses section,

E0[δyτ+pτδxτ]=δy0E0[[eΔ/2+1][1pτ<papτp0+1pτ>pb]+1papτpb1eΔ/2[2pτp0eΔ/2(1+pτp0)]]=δy0{[eΔ/2+1][E0[1pτ<pa(pτ/p0)]+E0[1pτ>pb]]+11eΔ/2[2E0[1papτpbpτ/p0]eΔ/2E0[1papτpb(1+pτ/p0)]]}

as

y~τl=δy0pτ/p0.

Taking each expectation separately,

E0[1pτ>pb]=P0[pτ>pb]=P0[ln(pτ/p0)>Δ]=P0[Wτ>(Δμτ)/σ]=1P0[Wτ(Δμτ)/σ]=1Φ(dτ+)

E0[1pτ<pa(pτ/p0)]=E0[1Wτ<(Δμτ)/σeμτ+σWτ]=eμτdτdz2πe12z2+σtz=eμτdτdz2πe12(z22σtz+σ2τσ2τ)=eμτdτdz2πe12(zστ)2=eμτdτστdz2πe12z2=eμτΦ(dτστ)

E0[1papτpbpτ/p0]=E0[1ΔμτσWτΔμτσe12(μτ+σWτ)]=e12μτdτdτ+dz2πe12z2+12στz=e12μτdτdτ+dz2πe12(z2στz+14σ2τ14σ2τ)=e12(μ12σ2)τ+18σ2τdτdτ+dz2πe12(z12στ)2=e12(μ14σ2)τdτ12στdτ+12στdz2πe12z2=e12(μ14σ2)τ[Φ(dτ+στ/2)Φ(dτστ/2)]

E0[1papτpb(1+pτ/p0)]=E0[1ΔμτσWτΔμτσ(1+eμτ+σWτ)]=Φ(dτ+)Φ(dτ)+eμτE0[1ΔμτσWτΔμτσeσWτ]=Φ(dτ+)Φ(dτ)+eμτdτdτ+dz2πe12z2+στz=Φ(dτ+)Φ(dτ)+eμτdτdτ+dz2πe12(z2+στz+σ2τσ2τ)=Φ(dτ+)Φ(dτ)+eμτdτdτ+dz2πe12(zστ)2=Φ(dτ+)Φ(dτ)+eμτ[Φ(dτ+στ)Φ(dτστ)]

Leads to

E0[δyτ+pτδxτ]=δy0{[eΔ/2+1][eμτΦ(dτστ)+1Φ(dτ+)]+11eΔ/2[2e12(μ14σ2)τ[Φ(dτ+στ/2)Φ(dτστ/2)]eΔ/2(Φ(dτ+)Φ(dτ)+eμτ[Φ(dτ+στ)Φ(dτστ)])]}

Swap fees paid on rebalance

Also have at the end of principal losses section,

E0[(f/2)|δyτpτδxτ|]=δy0(f/2)E0[|[eΔ/2+1][1pτ>pb1pτ<papτp0]+1papτpb1eΔ/21[pτp01]|]=δy0(f/2){[eΔ/2+1][E0[1pτ>pb]+E0[1pτ<pa(pτ/p0)]]+1eΔ/21E0[1papτpb|pτ/p01|]}

First two expectations can be found in the prior section. Last expectation separates into two terms

E0[1papτpb|pτ/p01|]=E0[1papτp0(1pτ/p0)]+E0[1p0pτpb(pτ/p01)]

which individually evaluate to

E0[1papτp0(1pτ/p0)]=E0[1ΔμτσWτμτσ(1eμτ+σWτ)]=E0[1ΔμτσWτμτσ]eμτE0[1ΔμτσWτμτσeσWτ]=Φ(μτ/σ)Φ(dτ)eμτdτμτ/σdz2πe12z2+στz=Φ(μτ/σ)Φ(dτ)eμτdτμτ/σdz2πe12(zστ)2=Φ(μτ/σ)Φ(dτ)eμτ[Φ(μτ/σστ)Φ(dτστ)]

E0[1p0pτpb(pτ/p01)]=E0[1μτσWτΔμτσ(eμτ+σWτ1)]=eμτE0[1μτσWτΔμτσeσWτ][Φ(dτ+)Φ(μτ/σ)]=eμτμτ/σdτ+dz2πe12z2+στz[Φ(dτ+)Φ(μτ/σ)]=eμτ[Φ(dτ+στ)Φ(μτ/σστ)][Φ(dτ+)Φ(μτ/σ)]

Leads to

E0[(f/2)|δyτpτδxτ|]=δy0(f/2){[eΔ/2+1][1Φ(dτ+)+eμτΦ(dτστ)]+1eΔ/21[2Φ(μτ/σ)Φ(dτ+)Φ(dτ)+eμτ(Φ(dτ+στ)+Φ(dτστ)2Φ(μτ/σστ))]}

Slippage paid on rebalance

Finally,

E0[|δyτpτδxτ|2/(4y~τ)]=δy0(l/4)E0[pτp0{[eΔ/2+1]2[1pτ>pbp0pτ+1pτ<papτp0]+1papτpb[eΔ/21]2[pτp0p0pτ]2}]=δy0(l/4){[eΔ/2+1]2[E0[1pτ>pb(p0/pτ)1/2]+E0[1pτ<pa(pτ/p0)3/2]]+1[eΔ/21]2[E0[1papτpb(pτ/p0)3/2]+E0[1papτpb(p0/pτ)1/2]2E0[1papτpb(pτ/p0)1/2]]}

Taking expectations separately,

E0[1pτ>pb(p0/pτ)1/2]=E0[1Wτ>Δμτσe12μτ12σWτ]=e12μτdτ+dz2πe12z212στz=e12μτdτ+dz2πe12(z2+στz+14σ2τ14σ2τ)=e12(μ12σ2)τ+18σ2τdτ+dz2πe12(z+12στ)2=e12(μ+34σ2)τeμτ[1Φ(dτ++στ/2)]

E0[1pτ<pa(pτ/p0)3/2]=e32μτE0[1Wτ<Δμτσe32σWτ]=e32μτdτdz2πe12z2+32στz=e32μτdτdz2πe12(z23στz+94σ2τ94σ2τ)=e32(μ12σ2)τ+98σ2τdτdz2πe12(z3στ/2)2=e12(μ+34σ2)τeμτΦ(dτ3στ/2)

E0[1papτpb(pτ/p0)3/2]=e32μτE0[1ΔμτσWτΔμτσe32σWτ]=e32μτdτdτ+dz2πe12z2+32στz=e12(μ+34σ2)τeμτ[Φ(dτ+3στ/2)Φ(dτ3στ/2)]

E0[1papτpb(p0/pτ)1/2]=E0[1ΔμτσWτΔμτσe12μτ12σWτ]=e12μτdτdτ+dz2πe12z212στz=e12(μ+34σ2)τeμτ[Φ(dτ++στ/2)Φ(dτ+στ/2)]

E0[1papτpb(pτ/p0)1/2]=E0[1ΔμτσWτΔμτσe12μτ+12σWτ]=e12μτdτdτ+dz2πe12z2+12στz=e12μτdτdτ+dz2πe12(z2στz+14σ2τ14σ2τ)=e12(μ12σ2)τ+18σ2τdτdτ+dz2πe12(zστ/2)2=e12(μ+34σ2)τe12σ2τ[Φ(dτ+στ/2)Φ(dτστ/2)]

Leads to

E0[|δyτpτδxτ|2/(4y~τ)]=δy0(l/4)e12(μ+34σ2)τ{[eΔ/2+1]2[eμτ[1Φ(dτ++στ/2)]+eμτΦ(dτ3στ/2)]+1[eΔ/21]2[eμτ[Φ(dτ+3στ/2)Φ(dτ3στ/2)]+eμτ[Φ(dτ++στ/2)Φ(dτ+στ/2)]2e12σ2τ[Φ(dτ+στ/2)Φ(dτστ/2)]]}