Liquidity providing in Uniswap v3 (3)
👉 Learn from other LPs! Join our Discord LP Cafe!
Where does the money come from?
Sometimes it’s good to ask very basic questions, like a 5 years old child:
– Dad, where do the money on Uniswap come from?
– Money comes from fees.
– But where does the fees come from?
– Fees come from swaps, people are swapping one token to another, and pay some fees to do this
– But why people are swapping tokens?
Well, that is a good question and it deserves a better answer than “because that’s how it goes”. The first answer that comes to mind is quite obviously: “because sometimes people want to buy or sell some token”. I call those trades “retail trades”. The thing is, Uniswap is by not the only place someone can trade tokens. For some, more obscure tokens, it can have almost monopolistic position (until token gets really popular), but there are dozens of centralized and decentralized exchanges where you can exchange eg. ETH for USDT. Even at Uniswap you can use v1, v2 and v3. And then, within v3 for many pairs you have 3 pools, with different fees, different liquidity and different price at any given moment.
There are also “automated retail traders”. Some systems can use Uniswap as its source of liquidity. Examples include “index funds” which hold some allocations of some tokens, and periodically rebalance its holdings using Uniswap.
There are privileged pools, which get extra volume. When someone wants to buy some token for eg. USDC and there’s no token/USDC pool, USDC is first exchanged into ETH (using one of ETH/USDC pools), then ETH is exchanged for this token, using token/ETH pool. LPs in ETH/USDC pool enjoy more fees because of this.
What if I told you… that none of it matters (too much)?
Next very basic question that can be asked is “how one can know things about Uniswap”. Well, of course the best way to do it is to analyse the data. But sometimes I’m just too lazy to write code, and I just watch. I watch my positions, I watch price in my pools and at the same time I watch prices on some big exchange like Coinbase or Binance. This is a very time consuming process, and perhaps not the best way to analyse any complex system, but in my opinion it’s a pretty good start.
And I see some patterns. For example I noticed that in ETH/USDT pool, where I have some positions open, my fees grow in very uneven way. It looks like this: for many minutes (sometimes it’s 2 minutes, sometimes it’s even 20 minutes) fees stay almost the same. When I check swaps, there are not many of them, and even if there are some, the amounts exchanged are low. Then suddenly big swap arrives (50 or even 100+ ETH) and my fees grow significantly. The picture I see on Binance or Coinbase is very different. Trades are much more uniformly distributed in time, and also much less variable in size.
The price also behave in a very different fashion on big CEXes. On CEXes it fluctuates a lot. 5 USDC up, 7 USDC down, 2 USDC up, 5 USDC down (and all those moves consist of even smaller moves). On Uniswap v3 price jumps and then stays almost the same for some time. Those jumps are the big trades, which are also a vast majority of my income. They happen when price on broad market differs from price in pool by ~0.7%.
Those big trades are arbitrage trades, profiting from price difference between Uniswap and “broad market”. And it was pretty shocking to me that perhaps more than 90% of my fees comes from those trades. I don’t think I’m exaggerating (I’m talking here about ETH/USDT pool, I guess other ETH/stablecoin pools may be similar, but eg. stablecoin/stablecoin pools are very different).
Arbitrage. So what?
Traders generate trades, trades generate volume, volume generates fees, fees generate LPs driving Lambos. Why split hair in two and distinguish between “retail volume” and “arbitrage volume”?
Those two types of trades have some very distinct properties. I’ll focus on arbitrage, since it makes most money for LPs (at least on ETH/stablecoin pairs)
Volatility of broader markets triggers those trades, so we can say that volatility itself generates trades. Perhaps no one want to buy some ETH at some particular moment, but if those ETH can be sold elsewher for 1% higher price? You can be sure that someone will jump in and profit from this trade. Even if no one would use Uniswap to buy/sell ETH, there will be still some (actually a lot of) volume on Uniswap and fees for us LPs. Just because crypto is volatile.
While it’s of course important that the price stays within the range of our positions, sometimes it’s not as profitable as one can think. I watched price staying in very tight range for hours and I though about those lucky LPs providing liquidity just to this narrow range, but then I saw that they probably made most of their money when price entered that range and exited it. Long minutes in range meant that the volume (consisting mostly of retail trades) was very low. Calm, sideways market: no IL but also no fees.
The most important conclusion is that — if my hypothesis about arbitrage trades making most of volume is true — it doesn’t really matter if you provide liquidity in highly competitive range or you’re the sole LP in that range. Why?Read on.
Return of the backtests
As I mentioned before, price watching is time consuming and far from the most efficient method of getting insights about Uniswap. First thing I did when Uniswap v3 was introduced, was to write a backtester.
It works like this: it takes past price data/volume from Uniswap v2 from one week, then for the same week it allocates some capital in some range (range can be defined by simple +/- 20% of price at the start of the week or it can be something more sophisticated, like Bollinger Bands). Then it checks (trade by trade) if trade fits into range (accruing fees if if it does). Next week comes, capital is taken out of pool, rebalanced (by default it’s allocated in the middle of the range, so we need two tokens of the same value) and added put into new range and so on and so on.
At the end of the run various numbers are generated by my script, including of course profit/loss, IL, APY and many more.
This was quite an effort to write such script (BTW: I made it public) but after few days doubts started to grow in my head. I wondered if results I’m getting will have any meaning in a very different world of v3. In my backtester you have to specify how much capital you need to achieve some share in pool (eg. you need 10 ETH to have 1% in pool), so that it knows your share in pool profits as well. You can easily estimate this number from uniswap.info (v2). It allows to calculate how efficient is your capital, how much fees it generates per invested ETH (or USD).
In v3 this is not so simple anymore. There’s this “fee multiplier” concept (check part 1 of my series) which promises you much more fees if your range is narrow. But what does it mean “fee multiplier is 10x”? It’s ten times more than if everyone else in the pool used range like in v2: from 0 to infinity. Which is not the case in v3 at all, you’re not the only one player who can get this boost. Typical liquidity graph looks like this:
So you can expect much more liquidity at some ranges (typically around the current price), and you may find that your 10x multiplier is actually giving you less share in your range than you’d have in v2, because (hypothetical example) most others LPs have even higher multiplier. Also inside bounds of your range, in some areas you can have more (or less) competition. When you think of it, you may come to the conclusion that my backtest assumption that eg. 10 ETH gives 1% in pool is totally useless. It doesn’t really matter how much you put into whole pool, what matter is how much share you have in the actual tick (or ticks — I haven’t covered ticks, but you’ll find pretty good explanation in the whitepaper). And during backtest you can’t really know how other pool participants will behave, how much liquidity would be competing with your liquidity, so how can you calculate your fees?
That’s why I almost abandoned my backtesting software. While it provided interesting insights into IL and effect of rebalancing (you can see some example results in part 2) I considered it useless in estimating profits from fees (that’s why I switched fees off in the example).
What if I told you (again)… that none of it matters (too much)?
While the volume of retail trades is very hard to predict (someone may buy $1M USD worth of some token, just because he thinks all dog named coins are cute), volume of arbitrage is very predictable. Given some price difference between Uniswap and other exchange, knowing the price impact/slippage on both exchanges, fees and so on, one can calculate the exact amount that can be arbitraged to maximise the profit. Fees are more or less constant, so let’s dig in price impact. It depends on one variable only: liquidity. If liquidity is high, you can do large trades without moving the price (arbitrageur would say: “liquidity is high, so I can buy lot of coins, and those coins are still cheap, so I can still make profit by selling it somewhere else”).
This means that volume of arbitrage trades depends on liquidity.
Why this so important? Because this also mean that no matter how “congested” is your price range, you’ll get the same amount of arbitrage volume (and fees) and it depends only on the amount of liquidity you provided (not on your share in the range). It’s possible to calculate how big the trade has to be to move the price of eg. 1 ETH in some tick from 2500 to 2505. If there’s 1000 ETH in this tick, this trade can be 1000x bigger (roughly). So it seems that if most trades are arbitrage trades, it doesn’t really matter what other LPs do, how they distribute their liquidity along the price curve.
The implications of this finding are huge. First, my backtests are not that useless as I thought. Second, you can try to predict volume from price action (and if you know the volume, you know the fees, and if you know the fees, you know whether you’ll drive Lambo or used bicycle – which can be healthier for you and the planet, by the way). I don’t have a chart for v3, but look at v2:
I see a lot of correlation between volatility (height of bars) and volume. Particularly during calmer, sideways markets. During big drops this changes a bit. Some people are panicking and selling, some people are sensing a cheap coin and buying. Gas fees can skyrocket making some arbitrage trades not profitable anymore. What’s more, arbitrage is not instant. During this specific drop I saw price of ETH on Uniswap v3 going down to 2728 USDT, while at the same moment it was 2660 on Binance. 68 USDT of difference was not arbitraged (remember that you can’t do an on-chain trade in any arbitrary moment, a block has to be mined). I’d also repeat once again that I’m talking here about ETH/USDT and ETH/USDC pairs. Other pairs have very different (lower) share of arbitrage trades.
That’s it for now, thanks for reading this. If you’d like to hang out with other LPs, tool providers and AMM geeks, join the discussion at LP Cafe!