Uniswap v3 Math Insights— Part 1 of 6

🦄 ck.eth 🦄
10 min readMay 3, 2023

--

New discoveries with Uniswap v3

The advancement of Uniswap v3 has given birth to a universal AMM capable of replicating a variety of intriguing payoff structures[1]. Here we’ll go over an example of one such unique payoff that provides the use of concentrated liquidity.

The TLDR:

  • Tools like Uniswap Analytics and Yewbow can help discover trading volume where LPing fees are generated.
  • The statistical tool of recurrence plots (code available in appendix) can spot patterns of price ranges to help find recurrent points that minimize divergence loss.
  • +/- 84% price boundary range optimizes for non-directional maximum range to avoid falling out of bounds while minimizing the use of capital (capital efficiency) for LP positions.

Quick Visual Review of v3:
Let’s visualize how an LP position on Uniswap v3 behaves with a little simulation courtesy of Lambert’s desmos[2] file:

If the price fluctuates between the boundaries, the total value of an LP position grows with varying fees.

A regular Uniswap v3 payoff structure is concave (in the shape of a hill). It makes sense to enter and exit at the same ratio as you entered while staying within the lower and upper boundaries, but what happens as you stay in an LP position for a longer time? Suppose you enter an LP position of X/ETH where X is a token that correlates with ETH. Sometimes it outperforms ETH and sometimes it underperforms as others buy and sell X in the market.

The more one waits, the more likely one is to diverge from the current entry price.

As observed in the bulging of the distribution animation above the more time elapses, the more the probability space of X/ETH expands (volatility evolves with the square root of time). This makes it more likely that you have a divergence loss if you exit at a random point in time when the X/ETH ratio does not match the one you entered with. On the other hand, you are earning fees as an LP while slipping from this divergent hill. This is the main tradeoff that LPs face.
How does one stay on the hill to avoid divergence loss? Mathematically, one aims for an X/Y pool that has high fees, high trading activity, and a high likelihood of revisiting the original price to be able to close an LP position.
The X token also has to be carefully studied in terms of protocol risk (Euler recently was hacked despite six professional audits), team/due diligence risk (scams/frauds/rug patterns are worth studying) because if something risky happens to X and the price of X crashes, then the LP position becomes largely allocated into X as price diverges.

The essence of LPing on Uniswap then becomes:

1). Figure out where trading volume is occurring.

2). Figure out where the price reverts to the original ratio to minimize divergence loss. I call this ratio the recurrence point.

3). Pick an optimal range to minimize use of capital and maximize range. As a basic strategy we will find with some calculus that +/- 84% is a limit for our upper and lower boundaries immaterial of the nature of the stochastic process when we are unsure of our price direction.

Let’s dive into each one.

1 — Trading Volume Analytics
On Uniswap an LP receives fees only when a trade takes place. More trades logically take place around assets which have volume and are volatile in their price.

  • Determining whether a token has volume can be done with the Uniswap Analytics tool https://info.uniswap.org/#/pools and to sort it by the “volume” column.
  • Another interesting approach is to sort by “implied volatility” through the Yewbow tool https://info.yewbow.org/#/pools. Implied volatility (IV) looks at the relationship between fees and volume relative to total volume locked at the current price. Note that this does not mean that these pairs’ risk/return ratio is justified. It’s important to note that relying just on implied volatility can be dangerous because assets that promise excessive fee returns are most likely too good to be true. As such, very excessive IV should be looked at with careful suspicion and due diligence performed on the token pair.

Finding pools with high fees and volume is not enough though. One has to factor into account the dreaded divergence loss.

2 — Recurrence Points
As an LP one would want to maximize the chance of recurrence / price reversion to minimize divergence loss. This means that one starts with a certain ratio of X/ETH such as 2/1 and would want to revisit 2/1 again at some point in the future. A useful tool in statistics for discovering recurrence is to look at a recurrence plot[3].
These plots allow us to see when a system revisits a particular point, such as a ratio, again in time. Let’s do a few examples followed by an LP example and how these recurrence plots can help us.

2.1 — Simple Recurrence Model
Let’s look at a recurrence plot with a case of a simple system involving a swinging pendulum that dampens over time (blue line). One could think of this simple model as the normalization after a market crash, or the discovery of an equilibrium price after the launch of a new protocol token (such as the Arbitrum airdrop), or the hack of a protocol with its impact on the price. Here our system is predictable, non-chaotic, but it also does not generate more LP fees as it becomes more predictable as time increases and the system settles in place. No price movement — no LP fees.

The recent launch of the Arbitrum token exhibits a similar oscillating and then dampening pattern.

The recurrence plot on the right captures this pattern, the yellower the color the more likely a point is revisited in time. As the system settles in a particular range, notice how it becomes bright yellow further to the top right as divergence decreases.

2.2 — Chaotic Recurrence Model
The simplicity of the above model is not an accurate description of the reality of price dynamics though. We must first note that when people buy and sell a token, the quantity that is bought impacts the price non-linearly. We also must note that since the blockchain is transparent, as soon as the price moves, other traders may act, LPs also notice this and may add or remove liquidity. The action of adding or removing liquidity has again a different price impact when another trade takes place. Price impact starts to impact price impact, it gets self-referential in its nature. The simplest model that is non-linear and self-referential is called a logistic map and it gives birth to chaos.

A non-linear function loops on itself leading to chaos: https://en.wikipedia.org/wiki/Logistic_map

Note though that despite the chaos of this system, we had moments where the system revisited our recurrence point. Let’s use the recurrence plot to take a look at what happens as we increase a particular parameter r of the logistic map equation x[n+1] = rx(1-x).

Despite the onset of chaos, we still observe bright spots in the lower right.

Observe how recurrence does not fully vanish. We still retain some yellow areas as the system loops on itself, showing that even in a chaotic system, we still can approach close to the reference point with which we started. An LP would be intrigued to hear this. Bearing in mind the nature of chaos, let’s look at a complex, non-linear system such as crypto with a live example.

2.3 — Reality of Recurrence
When we look at ETH/USD since 2017, we find that the recurrence plot barely lights up except for the bear market interval.

The recurrence plot is largely dark. No wonder LPs have trouble with ETHUSD, the numeraire (denominator) is a stable coin. In these cases more advanced models like ARIMA + GARCH need to be used to predict ETHUSD (but that’s for another article) and shorter time periods with frequent exit/entry must be used. Instead, for neutral and passive LPing we can look at pools where the numeraire drifts with the X token. Let’s look at the largest such case — ETH/BTC.

The recurrence points instantly light up (see appendix if you want to play around with recurrence plot code). We can see a lot of recurrence in-between the 250–1250 time steps and 1300–2000!

But an LP may be worried about both ETH and BTC declining. For example, we could have a shock like COVID impacting both ETH and BTC value. In such a case we can again use our handy recurrence plot to find a combination where both tokens drift together and also do not fall in price — stablecoins. Let’s look at a recurrence plot of USDC/USDT below.

We find that there’s a lot of recurrence. In such cases we can take advantage of this to concentrate liquidity with uniswap v3.

3 — Optimal Range Selection with Uniswap v3

When one concentrates the upper and lower boundaries (also known as P_b and P_a or simply b and a), what one does is steepen the hill of an LP payoff structure and in the extreme cases transforms the divergent hill into a cliff, courtesy of Lambert’s desmos file:

Wide boundaries result in a smoother LP value shift upon price fluctuation.
Highly concentrated boundaries result in a cliff-like shape. The longer one holds such a position, the more likely one is to wind up outside the bounds and not earn any LP fees.

Given the boundary constraint we have to pick an optimal range that ensures we balance out the maximum possible range so we can still earn fees while using capital as efficiently as possible. Hayden Adams provided a formula for capital efficiency for Uniswap v3:

For the derivation of the above formula see Lambert’s link: https://twitter.com/guil_lambert/status/1507163985531047938

If we take the lower boundary price (P_a) and the upper boundary price (P_b) and move them simultaneously away from the current price by a percent x:

We rewrite the formula as:

Take the derivative of the function with respect to x:

We can graph this relationship and notice that there is a peak of f’(x) at 84% with a capital efficiency of 2.18x. What does this mean, exactly? It means that we can use around twice as little capital as long as we are in the +/-84% range.

Desmos interactive link: https://www.desmos.com/calculator/ysv2j74j6k

This also means that as we start to increase our upper and lower boundaries in order to capture as much of the range as possible, we notice that going beyond 84% becomes marginally pointless if we’re uncertain of price direction. For every 1% increase beyond, we get less of a bang for the buck, instead 84% becomes a limit for an LP strategy for balancing our price escaping the boundaries and capital efficiency.
Such a conservative strategy can make sense for LPs who are worried about rapid price moves while receiving staking rewards for LPing. Note that this strategy is not aimed for maximizing fees, but rather making sure one stays within a range while using as little capital as possible.

Disclaimer: This research is for general information purposes only. The Uniswap Foundation was kind enough to sponsor the publication of previously private research. It does not constitute investment advice or a recommendation or solicitation to buy or sell any investment and should not be used in the evaluation of the merits of making any investment decision. It should not be relied upon for accounting, legal or tax advice or investment recommendations. This post reflects the current opinions of the author. The opinions reflected herein are subject to change without being updated.

Appendix

Code for Recurrence plots in Python.

import math
import numpy as np
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as animation

datausdc=yf.download("USDC-USD", start="2017-01-01", end="2023-04-21")
datausdt=yf.download("USDT-USD", start="2017-01-01", end="2023-04-21")

data3=datausdc/datausdt

dat=data3['Close']
dat = pd.to_numeric(dat, errors='coerce')
dat=dat.dropna()

x = np.array(dat.values)

fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(10,5))

# Plot the time series in the first subplot
ax1.plot(range(len(x)), x, 'b-', linewidth=.5)
ax1.set_xlabel('Time')
ax1.set_ylabel('USDC/USDT Price')
ax1.set_title('USDC/USDT Fluctuations since 2017')
n_end=len(x)

# Create a recurrence plot in the second subplot
R = np.zeros((n_end, n_end))
for i in range(n_end):
for j in range(i, n_end):
if abs(x[i] - x[j]) < 0.01:
R[i, j] = 1
R[j, i] = 1
ax2.imshow(R, cmap='viridis', origin='lower', vmin=0, vmax=1)
ax2.set_xlabel('Time step')
ax2.set_ylabel('Time step')
ax2.set_title('Recurrence Plot of USDC/USDT')

# Display the plots
plt.show()
plt.savefig("usdc/usdt.jpg", dpi=100)

--

--