クオンツのためのUniswap v3: 第一原理から理解するConcentrated LiquidityとTick数学
Uniswap v3のLPポジションは、実は指値注文板のレンジオーダーである。2つの価格の間にリクイディティをデポジットするということは、レンジ内で価格が下がる過程で資産を買い、上がる過程で売ることにコミットするということだ——これはCLOB上の指値注文グリッドがやっていることと全く同じである。しかしコントラクト自体は価格も数量も注文板も保存していない。保存しているのはたった3つの数値だけだ: Q64.96固定小数点形式の価格の平方根、整数のティックインデックス、そして集約リクイディティ値である。v3ポジションをマーケットメイキングモデルにおけるクオート——在庫、約定価格、逆選択——と同じように考えたいなら、これらのオンチェーン・プリミティブとそれが表現するトレーディング概念との間を翻訳できなければならない。本稿はUniswap v3のホワイトペーパー(Adams, Zinsmeister, Salem, Keefer, Robinson, 2021)とコアコントラクトに沿って、その翻訳を第一原理から構築し、今やあらゆる真剣なLP議論の出発点となっているLoss-Versus-Rebalancing(LVR)で締めくくる。
x·y = kから仮想リザーブへ
Uniswap v2はconstant productマーケットメーカーである。プールはtoken0のリザーブとtoken1のリザーブを保持し、すべてのスワップにおいて
を強制する。token1建てのtoken0の限界価格はであり、リクイディティは価格軸全体に一様に分散される。これは極端に資本効率が悪い。0.999から1.001の間で取引されるステーブルコインペアは、資本の99%以上を決して約定しない価格帯に配置していることになる。
v3の着想は、各LPが自分の資本をレンジに制限できるようにすることだ。レンジ内部では、そのポジションはv2プールとまったく同じように振る舞わなければならない——同じボンディングカーブ、同じ限界価格付け——ただしそのレンジをカバーするのに必要な分のリザーブだけを使う。ホワイトペーパーはこれを仮想リザーブとして定式化している。ポジションはあたかも曲線上にあるv2型のリザーブを保持しているかのように振る舞う一方、実際のリザーブは仮想リザーブからレンジ境界でポジションが保持するであろう分を差し引いたものになる。
これはホワイトペーパーの式2.2であり、v3において最も重要な単一の式だ。この変換された曲線は軸に接している。では実際のリザーブがゼロになり(ポジションは100% token1)、では実際のリザーブがゼロになる(100% token0)。レンジの外側ではポジションは不活性になる——一方のトークンだけの固定された袋になり、何も稼がない。

リクイディティと呼ばれるパラメータは、に代わる不変量である。と定義され、ホワイトペーパーが明示している通り明快な解釈を持つ——リクイディティとは「価格の平方根の単位あたりの仮想リザーブ」である。レンジ内の任意の価格において、仮想リザーブは
なぜ状態変数は√Pなのか
v3のコアは価格を直接追跡しない。をsqrtPriceX96として追跡する。これは符号なしQ64.96固定小数点数である。
ここでは生の価格、つまりtoken0の基本単位あたりのtoken1の基本単位(小数点桁数を含む、詳細は後述)である。平方根を使う理由はガス代の節約のためではなく、代数的な必然性による。仮想リザーブの恒等式を微分すると、SqrtPriceMathライブラリに実装されている2つの基本的なスワップ方程式が得られる。
どちらのトークン差分も、価格の平方根(またはその逆数)に対して線形であり、が比例定数となる。したがって1つのティック内でのスワップには曲線の反転もニュートン法の反復も不要で、を動かすための1回の乗算と、数量を計算するための2回の乗算だけで済む。スワップが十分大きく価格が初期化済みティックを越える場合、プールはそのティックを通過し、そこで参照されている純リクイディティ(liquidityNet)を加算または減算し、新しい集約で処理を続ける。全体として見ると、プールは区分的constant product AMMである。初期化済みティックの間ではは一定であり、ティックでジャンプする。
マーケットメーカーにとって、これが正しいメンタルモデルだ。プールの集約プロファイルは、DEXにおける板の厚みの等価物である。CLOBの板の厚みが価格レベルごとの数量で示されるのに対し、AMMの厚みはティックごとのであり、その変換はまさに上記のの恒等式そのものだ。
ティック: 対数間隔の価格グリッド
レンジは任意の価格で開始・終了することはできず、ティックにスナップしなければならない。ティックは次の価格に対応する。
つまり各ティックは隣接するティックから絶対値ではなく相対値で1ベーシスポイント離れている。この対数間隔は意図的なものだ。固定加算グリッドでは、$0.0001で取引されるトークンに対しては粗すぎ、$100,000で取引されるトークンに対しては細かすぎることになるが、幾何グリッドならどの価格スケールでも均一な1bpの分解能が得られる。TickMathライブラリはgetSqrtRatioAtTickとgetTickAtSqrtRatioという双方向の変換関数を持ち、指数関数を呼び出す代わりに事前計算された定数に対するビット操作を使う。ティックインデックスはMIN_TICK = -887272とMAX_TICK = 887272で境界づけられており、これは価格レンジをカバーする。uint256演算で存在しうるどんなトークンペアにも十分な広さである。
すべてのティックが使えるわけではない。各手数料ティアはティックスペーシングを課しており、ポジションはそれで割り切れるティックしか使用できない。
| 手数料ティア | ティックスペーシング | 最小レンジ幅 | 典型的な用途 |
|---|---|---|---|
| 0.01% | 1 | 約1bp | ステーブル/ステーブル |
| 0.05% | 10 | 約10bp | ステーブルペア、ETH/ステーブル |
| 0.30% | 60 | 約60bp | メジャー通貨ペア |
| 1.00% | 200 | 約2% | エキゾチック/高ボラティリティ |
0.05%/0.30%/1%のティアは2021年5月のローンチ時に導入され、0.01%ティアは2021年11月のガバナンス投票で追加された。高手数料プールでスペーシングを粗くすることで、通過しうるティック数を抑え、スワップのガスコストを一定の範囲に収めている。
誰もが一度はハマる小数点桁数の罠がある。オンチェーンの生の価格は基本単位におけるtoken0あたりのtoken1である。メインネットのUSDC/WETHプールでは、token0はUSDC(6桁)、token1はWETH(18桁)なので、人間にとっての価格1ETHあたり$3,000は、生の価格でいうと1 USDC単位あたり WETH-weiに相当し、これは以下のティックに位置する。
その時のである。もしあなたのモニタリングダッシュボードがUSDC/WETHで196k付近のティックを表示していたら、その理由がこれで分かるはずだ。そして人間にとっての価格はを必要に応じて逆数化したものになる。
(L, 価格レンジ)からトークン数量を求める: 正確な公式
この節の核心的な成果はこれだ——レンジ上にリクイディティを持つポジションが、現在価格においてどれだけのトークンを保有しているか。スワップ方程式をレンジ全体にわたって積分すると、3つのケースが得られる。
価格がレンジ以下()——ポジションは100% token0:
価格がレンジ以上()——ポジションは100% token1:
価格がレンジ内():
これらはまさにSqrtPriceMath.getAmount0DeltaとgetAmount1Deltaが計算している内容だ(方向づけられた丸め処理付き——コントラクトは常にユーザーに不利な方向へ丸める。これはバックテスターでこの計算を再現する際、wei単位の食い違いに気づいて悩む場合に重要な詳細である)。
from math import sqrt
def position_amounts(L: float, pa: float, pb: float, P: float):
"""Token amounts held by a v3 position (float model; core uses Q96 ints)."""
sa, sb, sp = sqrt(pa), sqrt(pb), sqrt(P)
if P <= pa:
return L * (1/sa - 1/sb), 0.0
if P >= pb:
return 0.0, L * (sb - sa)
return L * (1/sp - 1/sb), L * (sp - sa)
数値例: ETH/USDC、レンジ[2500, 3500]
ETHが USDCで取引されているとして、レンジに1 ETHをデポジットしたいとする。平方根は、、である。
ETHの側からが決まる。
USDCの側はそこから導かれる。
つまりこのポジションをミントするには1 ETH + 3,523.69 USDCが必要で、合計価値は$6,523.69になる。注目すべきは、この2つの側は50/50ではないということだ——その分割比率はがレンジ内のどこに位置するかに依存する(非対称なレンジこそが方向性のある見方を表現する方法であり、あるいは片方のトークンだけの純粋な「レンジオーダー」を配置する方法でもある)。
では価格を境界まで動かしてみよう。
- のとき、ポジションは完全にETHに転換している。 ETH、価値は$5,716.68。あなたは下落過程で1.2867 ETHを、リクイディティ加重平均価格3523.69 / 1.2867 \approx \2,739$で購入したことになる。
- のとき、ポジションは完全にUSDCに転換している。 USDC。あなたは上昇過程でETHを平均$3,240で売却したことになる。
これがレンジオーダーとしての解釈を具体化したものだ——このポジションは、3000から2500に向かう買い注文のはしごと、3000から3500に向かう売り注文のはしごをである、ティックあたりのサイズはに比例する。そして、支払われる対価こそが集中度なのだ。同じ$6,523.69の資本でフルレンジのv2型ポジションを作った場合、となる——集中ポジションはティックあたり12.4倍の厚みを提示し、(レンジ内にある間は)資本1ドルあたり12.4倍のレートで手数料を稼ぐ。
手数料の会計: feeGrowthGlobalとfeeGrowthInside
v3の手数料はポジションに複利で組み込まれない(これはv2からの意図的な決別で、v2では手数料がに再投資されていた)。手数料はトークンごとに並行して蓄積され、その会計処理はO(1)の帳簿管理の小さな傑作であり、すべてのLP分析パイプラインがこれを再実装しているので理解する価値がある。
プールは2つのグローバルアキュムレータ、feeGrowthGlobal0X128とfeeGrowthGlobal1X128を維持している。これらはリクイディティ単位あたりの累積手数料を、プール発足以来Q128.128固定小数点で記録したものだ。各スワップにおいて、手数料額(入力トークンから徴収される)は現在レンジ内にあるで割られ、アキュムレータに加算される。この仕組みがv3経済学の鉄則の背後にある——手数料は、スワップが発生した瞬間にレンジ内にあったリクイディティにのみ発生する。レンジ外のポジションは単に不活性な在庫であるだけでなく、非アクティブな間はまさにゼロを稼ぐ。
グローバルな成長の正しいスライスを有限のレンジに帰属させるため、各初期化済みティックはfeeGrowthOutside0/1X128を保持する。これは現在価格から見てティックの反対側で発生した手数料の成長分である(この値の解釈はティックが通過されるたびに反転し、これがO(1)の仕組みを可能にしている)。そこで、レンジ上にあり現在ティックがであるポジションについて、
各ポジションはスナップショットfeeGrowthInsideLastを保持しており、未回収の手数料は単純に
であり、ポジションが操作されるたびに遅延更新される。実務上の帰結は2つある。第一に、feeGrowthInsideの差分こそが、ポジションの手数料収入を測る唯一の正直な方法である——プールレベルの取引量をサンプリングしてTVLに占める自分のシェアで按分するやり方は、価格がレンジ境界付近をうろつくたびに誤差を生む。第二に、手数料は複利化されずtokensOwedとして滞留するため、実現LPリターンにはv2にはなかったキャッシュドラッグ項が生じる。自動複利化ボールトが存在するのはまさにこれを裁定するためだ(ただし自身の手数料は差し引かれる)。

ペイオフ: 対価をもらいながら保有するショートストラドル(かもしれない)
レンジ内において、価格の関数としてのポジション価値は
に対して凹関数である——の項がすべてを物語っている。レンジの外側では線形になる。下側の傾きは(固定量のETHをロングしている状態)、上側の傾きは0(USDCでフラットな状態)。先ほどの数値例をレンジ全体にわたって計算し、デポジットしたトークンを単純に保有し続けた場合(HODL)と比較してみよう。
| (USDC/ETH) | LP価値 | HODL価値 | 乖離 |
|---|---|---|---|
| 2500 | 5,716.68 | 6,023.69 | −307.02 |
| 2750 | 6,200.4 | 6,273.69 | −73.3 |
| 3000 | 6,523.69 | 6,523.69 | 0 |
| 3250 | 6,706.3 | 6,773.69 | −67.4 |
| 3500 | 6,764.06 | 7,023.69 | −259.63 |
LPは両方向ともHODLに劣後し、ミント時の価格でのみ一致する。上値は頭打ちで下値の参加は増幅される、ストライクにおいて相対価値が最大になる——これはショートストラドルのペイオフである(より正確には、線形の裾野をHODLベンチマークに対してネットした後は、にまたがるストライクを持つオプションのストリップのショートポジションだ)。手数料収入はそのプレミアムに相当する。レンジを狭めるということは、よりタイトなストライクを選ぶということだ——レンジ内にいる間は単位時間あたりのプレミアムが増える一方、価格が動いたときの乖離はより速く、より深くなる。すべてのv3のLPは、それを選んだかどうかにかかわらずショートボラティリティのトレーダーである——これはオーダーブック型マーケットメーカーのためにAvellaneda–Stoikovが定式化した在庫リスクのトレードオフの、オンチェーン版の兄弟にあたる。レンジの幅がクオートするスプレッドの役割を果たしている。

この表におけるギャップの伝統的な呼び名はインパーマネント(ダイバージェンス)ロスだが、IL対HODLは欠陥のあるベンチマークである。それはリクイディティプロバイダーとして被った損失と、いずれにせよ抱えていたであろう市場リスクを混同している。より鋭い分解は、Milionis、Moallemi、Roughgarden、Zhang(2022)による"Automated Market Making and Loss-Versus-Rebalancing"(arXiv:2208.06046)からもたらされる。LPをHODLではなく、常にプールと同じトークン数量を保有しながら、しかしアービトラージャーではなく摩擦のない外部市場価格で取引するリバランシングポートフォリオと比較するのだ。その差分がLVR("lever"と発音)であり、LP損益のうち純粋な逆選択の成分——各価格変動の後にAMMの古いクオートを狩るアービトラージャーに支払われる部分——である。ボラティリティの幾何ブラウン運動の価格の下で、LVRは瞬間的なレートで蓄積する。
著者たちの言葉を借りれば、これは「AMMのためのブラック・ショールズ公式」であり、は現在価格におけるプールの限界的な厚みである。フルレンジのconstant productプールの場合、これは単位時間あたりプール価値の有名なに帰着する。日次ボラティリティ5%であれば、手数料の有無にかかわらず、プールの約3.1bpが毎日アービトラージャーへと流出していく。集中度はを増幅させる——先ほどの12.4倍の厚みの例は、レンジ内にいる間は約12.4倍のLVRマシンでもある。手数料がこの流出を上回らなければポジションは期待値プラスにならない。そしてLVRは("IL"とは異なり)ヘッジ可能で予測可能なランニングコストであり、これこそがLVRを正しい会計単位たらしめている。手数料APR対LVR対実現ボラティリティ、いつLPがデルタヘッジされたショートオプションを上回るかという完全な収益性の計算は、近日公開予定のインパーマネントロスとLVRの深掘り記事のテーマであり、ヘッジのメカニクスについてはv3 LP戦略とヘッジで個別に扱う。
もう一つ、一段深いところにあるコストがある。AMMのクオートは誰かが取引したときにしか更新されないため、すべてのLPポジションは、その取引の周囲でメンプール上で繰り広げられるゲームにもさらされている——あなたの手数料を支払うスワッパーに対するサンドイッチ攻撃、そしてブロック単位であなたのLVRを実現させるアービトラージ・バンドルだ。そのエコシステムについてはMEVとサンドイッチ攻撃の記事でマッピングしている。
オンチェーンでプール状態を読む
これまで述べてきたことはすべて、プールコントラクトに対する3つの安価な呼び出しから観測可能だ。
**slot0()はsqrtPriceX96、現在のtick、オラクル観測インデックス、プロトコル手数料/ロックフラグといったホットな状態を1つのストレージスロットにまとめている。liquidity()は現在レンジ内にある集約を返す——これはTVLではないことに注意。これは現在のティックでアクティブな厚みパラメータであり、価格が初期化済みティックを越えると不連続にジャンプする。ticks(int24)**はティックごとの状態を返す。liquidityGross(そのティックを参照している総)、liquidityNet(左から右へ通過するときに加算される符号付き)、そしてfeeGrowthOutsideアキュムレータだ。初期化済みティックにわたってliquidityNetを反復すること(tickBitmapは170万個すべてをスキャンせずにどのティックが存在するかを教えてくれる)で、厚みプロファイルの全体——つまりあなたの注文板のスナップショット——を再構築できる。
from web3 import Web3
w3 = Web3(Web3.HTTPProvider(RPC_URL))
pool = w3.eth.contract(address=POOL, abi=POOL_ABI) # USDC/WETH 0.05%
sqrt_price_x96, tick, *_ = pool.functions.slot0().call()
L = pool.functions.liquidity().call()
raw_price = (sqrt_price_x96 / 2**96) ** 2 # token1/token0, base units
eth_usdc = 1 / (raw_price * 10**(18 - 6)) # human USDC per ETH
depth_1tick = L * (1.0001**0.5 - 1) * (sqrt_price_x96 / 2**96)
ポジション自体は2つの場所に存在する。コアプールは(owner, tickLower, tickUpper)でポジションをキー管理する——オーナー・レンジの組ごとに1つの集約スロットだ。リテールとほとんどのファンドは代わりに周辺コントラクトのNonfungiblePositionManager(メインネット: 0xC36442b4a4522E871399CD717aBDD847Ab11FE88)を通してミントする。これは各ポジションをERC-721 NFTでラップし、positions(tokenId)が完全なタプル——トークン、手数料ティア、レンジ、、feeGrowthInsideLast、tokensOwed——を返す。ポートフォリオモニタリングにおいては、この1回の呼び出しに加えてプールの現在のfeeGrowthInside(前節と同様にticks()から再計算)があれば、インデクサーに触れることなく時価評価と未収手数料を得られる。もっとも、履歴的なものが必要な場合はスワップイベントのリプレイかサブグラフが必要になる。なぜなら手数料の成長は経路依存であり、チェーンは現在のアキュムレータしか保存していないからだ。
資本を投入する前に内面化しておくべき運用上の詳細が2つある。第一に、ティックスペーシングはあなたの戦略空間を量子化する。0.05%ティアでは10bp幅のレンジを配置でき、真の指値注文に近いものを運用できる(スポットのすぐ上/下でミントし、手数料を回収し、価格が通過した後にバーンする——ホワイトペーパーはこの「レンジオーダー」のユースケースを明示的に想定している)一方、1%ティアでは最小レンジが約2%幅になり、LOBの類推は粗くなる。第二に、レンジオーダーはキャンセル優先権を持たない指値注文である。価格があなたのレンジを越えて、また戻ってくると、あなたは在庫を往復させ(手数料は保持したまま)エッジを失うことになる。パッシブなレンジは撤回できないクオートなのであり、まさにそれゆえリバランシング頻度の問題と、それに答えるためのLVRのレンズが、これほど重要になるのだ。
ここから何が見えてくるか
v3のスタックを圧縮すると: 価格は1.0001の幾何グリッド上のティックであり、厚みはであって平方根の差以外の何物でもない計算でトークン数量に変換でき、手数料はレンジ境界間で差分を取る単位あたりのアキュムレータであり、そして結果として得られるポジションは、名前と公式とarXiv番号を持つランニングコストを抱えたショートボラティリティのレンジオーダーである。これらのプリミティブが手元にあれば、興味深い問いは定量的なものになる——どれくらいの幅にすべきか、どれくらいの頻度でリバランスすべきか、そして与えられたプールとレジームにおいて手数料がLVRを上回るかどうか。これこそが本シリーズが次に向かう先である。
参考文献
- Adams, H., Zinsmeister, N., Salem, M., Keefer, R., Robinson, D. (2021). Uniswap v3 Core (whitepaper).
- Uniswap v3 core libraries:
TickMath.sol,SqrtPriceMath.sol,Position.sol,Tick.sol. - Milionis, J., Moallemi, C., Roughgarden, T., Zhang, A. L. (2022). Automated Market Making and Loss-Versus-Rebalancing. arXiv:2208.06046.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.