DEX 微观结构 — Uniswap V2/V3 数学
X·Y=K 推导、Uni V3 集中流动性数学、sqrtPriceX96 / tick / liquidity 关系、virtual reserves
日期: 2026-07-24 方向: 量化 / 微观结构 / 做市 阶段: Phase 2 - 市场微观结构与做市 (Day 75-88) 标签: #DEX #Uniswap #AMM #ConcentratedLiquidity #TickMath
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | X·Y=K 推导、Uni V3 集中流动性数学、sqrtPriceX96 / tick / liquidity 关系、virtual reserves |
| 实操 | Python 实现 V3 swap simulator、tick crossing、liquidity 数学全套 |
| 产出 | uni_v3_math.py (~700 行):精确复刻 Uniswap V3 合约数学 |
这是 Phase 2 最关键一天。把 V3 数学每个公式推到位,下两天 (JIT、CL strategy) 才能讲透。
一、Uniswap V2 — Constant Product
1.1 不变式
池中两资产 reserves (x, y),commit invariant:
$$
\boxed{x \cdot y = k}
$$
每次 swap (Δx_in 加入):
$$
y_{new} = \frac{k}{x + \Delta x_{in}}, \quad \Delta y_{out} = y - y_{new} = y - \frac{k}{x + \Delta x_{in}}
$$
1.2 价格
瞬时价格(marginal price): $$ P = \frac{y}{x} $$
平均成交价(average execution price): $$ P_{avg} = \frac{\Delta y_{out}}{\Delta x_{in}} = \frac{k - y \Delta x / (x+\Delta x)}{\Delta x} \cdot \frac{1}{...} $$
简化:P_avg = y / (x + Δx) = 新 spot price。
1.3 滑点
$$ \text{slippage} = \frac{P - P_{avg}}{P} = \frac{\Delta x}{x + \Delta x} $$
Δx ≪ x 时 ≈ Δx / x。10% pool drain → 10% slippage。
1.4 fee
V2 收 swap 0.3% (= 30 bps):实际 (1 − fee) Δx_in 进入恒等式:
$$ y_{new} = \frac{k}{x + (1−\text{fee}) \Delta x_{in}} $$
收下的 fee 留在池中(增加 reserves),相当于 LP 直接分。
1.5 LP value(无常损失基础)
LP 持仓 = α (x_0, y_0)。如果价格从 P_0 变到 P_1,reserves 调整成 (x_1, y_1) with x_1 y_1 = k_0、y_1/x_1 = P_1。
LP 价值(用 token0 计量): $$ V_{LP}(P) = x + y/P = 2\sqrt{k/P} $$
vs HODL: $$ V_{HODL}(P) = x_0 + y_0/P = x_0 + (P_0 x_0)/P $$
定义 IL: $$ \boxed{IL(P/P_0) = \frac{V_{LP} - V_{HODL}}{V_{HODL}} = \frac{2\sqrt{r}}{1+r} - 1, \quad r = P/P_0} $$
| r | IL |
|---|---|
| 0.5 | -5.7% |
| 0.75 | -1.3% |
| 1.0 | 0% |
| 1.5 | -2.0% |
| 2.0 | -5.7% |
| 4.0 | -20% |
二、Uniswap V3 — Concentrated Liquidity
2.1 关键创新
V2 把流动性均匀分布在 (0, ∞)。V3 让 LP 选 [P_a, P_b] 区间——只在该区间提供流动性。区间外 = 100% 单边 token(如同已经"卖完")。
2.2 数学骨架
定义 sqrt price √P。LP 在 [√P_a, √P_b] 提供 liquidity L。
不变式(在区间内): $$ \boxed{(x + L/\sqrt{P_b})(y + L \sqrt{P_a}) = L^2} $$
或等价地,定义 virtual reserves: $$ x_{virtual} = x + L/\sqrt{P_b}, \quad y_{virtual} = y + L \sqrt{P_a} $$
x_virtual · y_virtual = L²。这就是 V2 形式的 V3——Uniswap 用 virtual reserves 把 V3 数学化简成 V2 形式。
2.3 token amounts in the range
给定区间 [P_a, P_b] 和当前 P,LP 持有:
当 P ≤ P_a(价格在区间下方):100% token0 $$ x = L \cdot \left(\frac{1}{\sqrt{P_a}} - \frac{1}{\sqrt{P_b}}\right), \quad y = 0 $$
当 P ≥ P_b(价格在区间上方):100% token1 $$ x = 0, \quad y = L \cdot (\sqrt{P_b} - \sqrt{P_a}) $$
当 P_a ≤ P ≤ P_b(区间内活跃): $$ \boxed{ x = L \cdot \left(\frac{1}{\sqrt{P}} - \frac{1}{\sqrt{P_b}}\right), \quad y = L \cdot (\sqrt{P} - \sqrt{P_a}) } $$
2.4 mint 时计算 L
LP 想存 Δx, Δy,计算需要的 L:
$$
L_x = \Delta x \cdot \frac{\sqrt{P} \sqrt{P_b}}{\sqrt{P_b} - \sqrt{P}}, \quad
L_y = \frac{\Delta y}{\sqrt{P} - \sqrt{P_a}}
$$
实际 L = min(L_x, L_y)(两侧 token 都不超 user 提供)。
2.5 Tick System
V3 用 ticks 离散化价格。每个 tick i 对应:
$$
P(i) = 1.0001^i
$$
tick spacing 由 fee tier 决定:
| Fee tier | tick spacing | 最小价格步长 |
|---|---|---|
| 0.01% | 1 | 1 bps |
| 0.05% | 10 | 10 bps |
| 0.30% | 60 | ~60 bps |
| 1.00% | 200 | ~200 bps |
LP 位置只能从 tick i_lower 到 i_upper,且必须是 spacing 倍数。
2.6 sqrtPriceX96
合约用 Q64.96 fixed-point 存 √P: $$ \sqrt{P}_{X96} = \sqrt{P} \cdot 2^{96} $$
tick → sqrtPriceX96: $$ \sqrt{P}_{X96}(i) = \sqrt{1.0001^i} \cdot 2^{96} = 1.0001^{i/2} \cdot 2^{96} $$
合约用查找表 + bit shift 算 getSqrtRatioAtTick(i),O(1) 速度。
2.7 swap 内部循环
合约 swap 逻辑:
1. 找 current tick / sqrtPrice
2. 找下一个 active tick(流动性边界)
3. 计算到下一个 tick 的 amount_in / amount_out
4. 如果 amount_in 不够穿越 tick:
- 在当前流动性 L 内 partial swap
- 更新 sqrtPrice(不到 tick 边界)
5. 否则穿越 tick:
- 全部 swap 到 tick 边界
- 在 tick boundary 更新 L(加 / 减 net liquidity at this tick)
- 进入下一个 tick range,goto 3
6. 直到 amount 全部 swap
每个 active tick range 内的数学就是 V2 (virtual reserves)。穿越 tick 时 L 变化。
三、Swap 数学详细
3.1 在区间内 swap (token0 → token1)
输入 Δx,新 √P:
$$
\sqrt{P_{new}} = \frac{L \cdot \sqrt{P}}{L + \Delta x \cdot \sqrt{P}}
$$
输出 Δy:
$$
\Delta y = L \cdot (\sqrt{P} - \sqrt{P_{new}})
$$
3.2 swap (token1 → token0)
$$ \sqrt{P_{new}} = \sqrt{P} + \frac{\Delta y}{L} $$ $$ \Delta x = L \cdot \left(\frac{1}{\sqrt{P}} - \frac{1}{\sqrt{P_{new}}}\right) $$
3.3 到 tick 边界的 amount
如果要 swap 到 √P_target(边界):
$$
\Delta x_{to_target} = L \cdot \left(\frac{1}{\sqrt{P_{target}}} - \frac{1}{\sqrt{P}}\right)
$$
如果用户输入 < 这个,partial swap;否则穿越。
四、代码实现:uni_v3_math.py
"""
uni_v3_math.py — Uniswap V3 完整数学实现
依赖:仅 math/decimal(与合约一致)
"""
from decimal import Decimal, getcontext
import math
getcontext().prec = 80
Q96 = 2**96
Q128 = 2**128
# ----------------------------------------------------------
# 1. tick <-> sqrtPriceX96
# ----------------------------------------------------------
def tick_to_sqrt_price_x96(tick: int) -> int:
"""合约 getSqrtRatioAtTick 的 Python 复刻"""
return int(math.sqrt(1.0001 ** tick) * Q96)
def sqrt_price_x96_to_tick(sqrtP_x96: int) -> int:
"""合约 getTickAtSqrtRatio"""
sqrtP = sqrtP_x96 / Q96
return int(math.floor(math.log(sqrtP**2) / math.log(1.0001)))
def sqrt_price_x96_to_price(sqrtP_x96: int, decimals_diff=0) -> float:
"""price = (sqrtP_x96 / Q96) ** 2 * 10^(decimals_diff)"""
sqrtP = sqrtP_x96 / Q96
return (sqrtP ** 2) * (10 ** decimals_diff)
def price_to_tick(price: float) -> int:
return int(math.floor(math.log(price) / math.log(1.0001)))
# 真实 ETH/USDC 0.05% 池示例
# tick = 200000 ≈ ETH price ~$2000/ETH (decimals 6/18 调整后)
# 测试
def test_tick_math():
tick = 200000
sqrtP = tick_to_sqrt_price_x96(tick)
back = sqrt_price_x96_to_tick(sqrtP)
print(f"tick {tick} -> sqrtPriceX96 {sqrtP}")
print(f" -> back to tick {back}, price={1.0001**tick:.4f}")
# ----------------------------------------------------------
# 2. token amounts from L and range
# ----------------------------------------------------------
def amounts_from_liquidity(L: int, sqrtP_x96: int,
sqrtP_lower_x96: int,
sqrtP_upper_x96: int):
"""
返回 (amount0, amount1) — LP 在当前 P 下的 token amounts
"""
if sqrtP_x96 <= sqrtP_lower_x96:
amount0 = L * Q96 * (sqrtP_upper_x96 - sqrtP_lower_x96) // (sqrtP_lower_x96 * sqrtP_upper_x96)
amount1 = 0
elif sqrtP_x96 >= sqrtP_upper_x96:
amount0 = 0
amount1 = L * (sqrtP_upper_x96 - sqrtP_lower_x96) // Q96
else:
amount0 = L * Q96 * (sqrtP_upper_x96 - sqrtP_x96) // (sqrtP_x96 * sqrtP_upper_x96)
amount1 = L * (sqrtP_x96 - sqrtP_lower_x96) // Q96
return amount0, amount1
def liquidity_from_amounts(amount0: int, amount1: int, sqrtP_x96: int,
sqrtP_lower_x96: int, sqrtP_upper_x96: int):
""" mint 时计算 L """
if sqrtP_x96 <= sqrtP_lower_x96:
L = amount0 * sqrtP_lower_x96 * sqrtP_upper_x96 // (Q96 * (sqrtP_upper_x96 - sqrtP_lower_x96))
return L
elif sqrtP_x96 >= sqrtP_upper_x96:
L = amount1 * Q96 // (sqrtP_upper_x96 - sqrtP_lower_x96)
return L
else:
L0 = amount0 * sqrtP_x96 * sqrtP_upper_x96 // (Q96 * (sqrtP_upper_x96 - sqrtP_x96))
L1 = amount1 * Q96 // (sqrtP_x96 - sqrtP_lower_x96)
return min(L0, L1)
# ----------------------------------------------------------
# 3. swap 数学
# ----------------------------------------------------------
def get_next_sqrt_price_from_amount0(sqrtP_x96: int, L: int, amount0_in: int):
""" token0 进入,价格下移 (P 是 token1/token0, x 增 → P 跌) """
if amount0_in == 0: return sqrtP_x96
numerator = L * sqrtP_x96
denominator = L + (amount0_in * sqrtP_x96 // Q96)
return numerator * Q96 // (numerator + amount0_in * sqrtP_x96)
def get_next_sqrt_price_from_amount1(sqrtP_x96: int, L: int, amount1_in: int):
""" token1 进入,价格上移 """
return sqrtP_x96 + amount1_in * Q96 // L
def compute_swap_step(sqrtP_x96: int, sqrt_target_x96: int,
L: int, amount_remaining: int, fee_pips: int,
zero_for_one: bool):
"""
单步 swap:在当前 tick range 内,要么 swap 完,要么 hit 边界
返回 (new_sqrtP, amount_in, amount_out, fee_amount)
"""
if zero_for_one:
# token0 -> token1, sqrtP 下降, 朝 lower 边界
amount_in_max = max_amount_0_in(sqrtP_x96, sqrt_target_x96, L)
amount_in_after_fee = amount_remaining * (1_000_000 - fee_pips) // 1_000_000
if amount_in_after_fee >= amount_in_max:
# 穿越到 target
new_sqrtP = sqrt_target_x96
amount_in = amount_in_max
amount_out = max_amount_1_out(sqrtP_x96, sqrt_target_x96, L)
else:
# partial swap
new_sqrtP = get_next_sqrt_price_from_amount0(sqrtP_x96, L, amount_in_after_fee)
amount_in = amount_in_after_fee
amount_out = L * (sqrtP_x96 - new_sqrtP) // Q96
fee = (amount_in * fee_pips) // (1_000_000 - fee_pips)
return new_sqrtP, amount_in, amount_out, fee
else:
# token1 -> token0, sqrtP 上升
amount_in_max = max_amount_1_in(sqrtP_x96, sqrt_target_x96, L)
amount_in_after_fee = amount_remaining * (1_000_000 - fee_pips) // 1_000_000
if amount_in_after_fee >= amount_in_max:
new_sqrtP = sqrt_target_x96
amount_in = amount_in_max
amount_out = max_amount_0_out(sqrtP_x96, sqrt_target_x96, L)
else:
new_sqrtP = get_next_sqrt_price_from_amount1(sqrtP_x96, L, amount_in_after_fee)
amount_in = amount_in_after_fee
amount_out = L * Q96 * (new_sqrtP - sqrtP_x96) // (sqrtP_x96 * new_sqrtP)
fee = (amount_in * fee_pips) // (1_000_000 - fee_pips)
return new_sqrtP, amount_in, amount_out, fee
def max_amount_0_in(sqrtP_x96, sqrt_target, L):
return L * Q96 * abs(sqrtP_x96 - sqrt_target) // (sqrtP_x96 * sqrt_target)
def max_amount_1_in(sqrtP_x96, sqrt_target, L):
return L * abs(sqrt_target - sqrtP_x96) // Q96
def max_amount_1_out(sqrtP_x96, sqrt_target, L):
return L * abs(sqrtP_x96 - sqrt_target) // Q96
def max_amount_0_out(sqrtP_x96, sqrt_target, L):
return L * Q96 * abs(sqrt_target - sqrtP_x96) // (sqrtP_x96 * sqrt_target)
# ----------------------------------------------------------
# 4. 完整 swap loop(穿越 ticks)
# ----------------------------------------------------------
class Pool:
"""简化 pool: 多个 tick 边界,每个有 net liquidity ΔL"""
def __init__(self, sqrtP_x96, current_tick, fee_pips, tick_spacing,
active_L, tick_liquidity):
self.sqrtP_x96 = sqrtP_x96
self.tick = current_tick
self.fee = fee_pips
self.spacing = tick_spacing
self.L = active_L # liquidity at current tick
self.tick_liquidity = tick_liquidity # dict: tick -> ΔL net
def swap(self, amount_in: int, zero_for_one: bool):
"""完整 swap:穿越 tick"""
amount_remaining = amount_in
amount_total_out = 0
fee_total = 0
steps = []
while amount_remaining > 0:
# 找下一个 active tick
if zero_for_one:
next_tick = max(t for t in self.tick_liquidity if t < self.tick) \
if any(t < self.tick for t in self.tick_liquidity) else -887272
else:
next_tick = min(t for t in self.tick_liquidity if t > self.tick) \
if any(t > self.tick for t in self.tick_liquidity) else 887272
sqrt_target = tick_to_sqrt_price_x96(next_tick)
new_sqrtP, ain, aout, fee = compute_swap_step(
self.sqrtP_x96, sqrt_target, self.L,
amount_remaining, self.fee, zero_for_one)
amount_remaining -= (ain + fee)
amount_total_out += aout
fee_total += fee
self.sqrtP_x96 = new_sqrtP
steps.append({"new_sqrtP": new_sqrtP, "amount_in": ain,
"amount_out": aout, "fee": fee, "tick": next_tick})
# 是否穿越 tick
if new_sqrtP == sqrt_target:
self.tick = next_tick
self.L += self.tick_liquidity.get(next_tick, 0) * (1 if not zero_for_one else -1)
else:
self.tick = sqrt_price_x96_to_tick(new_sqrtP)
break
return amount_total_out, fee_total, steps
# ----------------------------------------------------------
# 5. 演示:ETH/USDC 0.05% 池
# ----------------------------------------------------------
if __name__ == "__main__":
test_tick_math()
# 一个 ETH/USDC 0.05% 池模拟
# 假设当前 ETH = $2000,tick ≈ -200000(取决于 token order)
# 简化:单 tick range
sqrtP = tick_to_sqrt_price_x96(0) # P = 1
pool = Pool(
sqrtP_x96=sqrtP, current_tick=0, fee_pips=500, tick_spacing=10,
active_L=10**21,
tick_liquidity={-100: 10**21, 100: -10**21} # range [-100, 100]
)
# swap 100 token0 -> token1
out, fee, steps = pool.swap(amount_in=10**18, zero_for_one=True)
print(f"\nSwap 1e18 token0 -> token1: out={out}, fee={fee}")
print(f" Steps: {len(steps)}")
for s in steps:
print(f" {s}")
print(f" New sqrtPriceX96: {pool.sqrtP_x96}, tick: {pool.tick}")
# 测试 amounts_from_liquidity
L = 10**21
sqrtP_l = tick_to_sqrt_price_x96(-100)
sqrtP_u = tick_to_sqrt_price_x96(100)
a0, a1 = amounts_from_liquidity(L, sqrtP, sqrtP_l, sqrtP_u)
print(f"\nLP at tick=0 with L={L} in range [-100,100]:")
print(f" amount0 = {a0}, amount1 = {a1}")
预期输出:
tick 200000 -> sqrtPriceX96 ...
-> back to tick 199999 or 200000, price=485165195.4
Swap 1e18 token0 -> token1: out=996...
...
LP at tick=0 with L=1e21 in range [-100,100]:
amount0 = ...
amount1 = ...
输出数值取决于精度;与 Uniswap 链上结果对比误差应 < 1 wei。
五、真实数据接入
Uniswap V3 Subgraph (官方)
endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3
query example:
{
pools(first: 5, orderBy: totalValueLockedUSD, orderDirection: desc) {
id
token0 { symbol decimals }
token1 { symbol decimals }
feeTier
sqrtPrice
tick
liquidity
totalValueLockedUSD
}
}
返回示例:
{
"data": {
"pools": [
{
"id": "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
"token0": {"symbol":"USDC","decimals":"6"},
"token1": {"symbol":"WETH","decimals":"18"},
"feeTier": "500",
"sqrtPrice": "1842234820392310459203...",
"tick": "201234",
"liquidity": "13245678901234567890",
"totalValueLockedUSD": "189345678.21"
}
]
}
}
Direct on-chain via JSON-RPC
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://eth.llamarpc.com"))
pool_address = "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640" # USDC/WETH 0.05%
abi = [...] # IUniswapV3Pool ABI
pool = w3.eth.contract(address=pool_address, abi=abi)
slot0 = pool.functions.slot0().call()
# slot0 = (sqrtPriceX96, tick, observationIndex, ...)
liquidity = pool.functions.liquidity().call()
Uniswap V3 swap event 链上数据
event Swap(address sender, address recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)
每次 swap 都 emit 完整状态——重建数学几乎无损。
六、CEX vs DEX 对比
| 维度 | CEX LOB | Uniswap V2 | Uniswap V3 | Curve / StableSwap |
|---|---|---|---|---|
| 流动性形式 | 离散挂单 | x·y=k 全区间 | x·y=k 在 [P_a,P_b] | 类似 V2 但 invariant 不同 |
| 滑点 | 看簿 | Δx/(x+Δx) | 同 V2 但用 virtual reserves | 极小(peg 内) |
| LP 收益 | N/A | fee 固定 0.3% | fee tier + 集中 | fee + boost |
| IL 公式 | N/A | 见 §1.5 | 区间内类 V2、区间外 capped | 极小(peg 内) |
| 价格连续度 | tick (1c BTC) | 连续 ($) | 离散 tick | 连续 |
| 更新机制 | 每 ms | 每 swap | 每 swap + tick crossing | 每 swap |
V3 vs V2 是 LP 的"集中下注"
V2: liquidity 全部"白白"在 [0, ∞],包括极不可能价格。 V3: 选 [1900, 2100] 集中 100x 流动性 → 同样 capital,区间内 spread 是 V2 的 1/100。但价格出区间 → 0 fee。
数学等价: $$ L_{V3,在区间内} = L_{V2} \cdot \frac{\sqrt{P_b}}{\sqrt{P_b} - \sqrt{P_a}} \cdot \frac{1}{\sqrt{P_a}} $$
集中度 = √(P_b/P_a) / (√(P_b/P_a) − 1)。±5% 区间集中度 ≈ 41x。
七、风险与陷阱
-
整数除法精度 合约用 Solidity uint256,无小数。Python 实现要用整数除法
//,浮点会有微小偏差导致 swap 数额不匹配。 -
Tick spacing 不对齐 mint 时
(tickLower, tickUpper)必须是 spacing 倍数,否则 revert。 -
流动性边界外 0 收益 V3 LP 在区间外不赚 fee,且 100% 单边 token——相当于"已经卖出",未来反弹也不在仓。
-
Pricing direction confusion
tick = log(price) / log(1.0001)但 price 是哪个 token / 另一个?取决于 token0/token1 的字典序。USDC/WETH 池 token0=USDC、token1=WETH,price = WETH/USDC = ~2000,但 sqrtPrice 表达的是 token1/token0 = USDC per WETH。需要根据 decimals 调整。 -
Sandwich/MEV 影响 swap math 公开 mempool → MEV bot 在你的 swap 前后插单。你的 effective price 会差 50-200 bps。Day 85 详细。
-
L 在 tick crossing 时跳变 一个池可能有多个 LP positions 重叠在 tick 上。穿越 tick 时 L 加 / 减的量是该 tick 上所有 LP 的 net liquidity。
八、关键速查
V2
x · y = k
P = y/x
Δy = y - k/(x + (1-fee)Δx)
slip = Δx/(x+Δx)
IL(r) = 2√r/(1+r) - 1
V3
x_v · y_v = L², x_v = x + L/√P_b, y_v = y + L√P_a
In-range amounts:
x = L (1/√P − 1/√P_b)
y = L (√P − √P_a)
mint:
L = min( Δx · √P√P_b / (√P_b−√P), Δy / (√P−√P_a) )
tick:
P(i) = 1.0001^i
sqrtPriceX96(i) = √(1.0001^i) · 2^96
swap step (token0 in):
√P_new = (L · √P) / (L + Δx · √P)
Δy_out = L (√P − √P_new)
swap step (token1 in):
√P_new = √P + Δy/L
Δx_out = L (1/√P − 1/√P_new)
Tick spacing per fee tier
0.01% → 1 (stable pairs)
0.05% → 10 (ETH/USDC)
0.30% → 60 (default)
1.00% → 200 (exotic)
九、面试题
Q1: 推导 V3 在区间内 LP 持有的 token amounts。
见 §2.3。从 virtual reserves
x_v y_v = L²与 marginal price 关系P = y_v/x_v出发,解出x_v = L/√P, y_v = L√P。然后x = x_v − L/√P_b、y = y_v − L√P_a,代入得公式。
Q2: 为什么 sqrtPriceX96 要用 √P 而不是 P?
A: 让 swap 数学线性化。
Δy = L · Δ(√P)、Δx = L · Δ(1/√P)——L 是常数(在 range 内),所以 swap step 是 √P 域的线性变换。如果用 P 直接,会得到Δy = (L/2)(P + P_new)/√P之类的复杂表达,且无法在 fixed-point 下精确。
Q3: 用 ±10% 集中区间 vs V2 整池,资金效率提升多少? $$ \text{factor} = \frac{\sqrt{1.10}}{\sqrt{1.10} - \sqrt{0.90}} = \frac{1.0488}{0.0997} \approx 10.5x $$
A: 约 10x。±5% 约 20x,±1% 约 100x。代价是出区间风险与 IL 放大。
Q4: V3 LP 在区间外为什么 100% 单边?
A: 假设当前 P > P_b。V3 数学要求池中持有 token0 =
L(1/√P − 1/√P_b)。但 P > P_b → 1/√P < 1/√P_b → 公式给负,意味着 LP 持有 token0 = 0。所有价值在 token1 =L(√P_b − √P_a)上(这是 token1 的"上限量")。LP 已经"卖光" token0,相当于在 P_b 完成了 buy-low/sell-high。
Q5: 集中流动性是不是 LP 必胜?
A: 不是。集中度提高 fee 收入 c 倍,但 IL 在区间内也乘 c。out-of-range 时间 + IL 通常导致 V3 高 fee tier 的 LP 多数亏 USDC vs HODL(Uniswap labs 自家研究)。只有 能预测/实时调整区间 的 active LP 才赚。Day 86 详细策略。
明日预告
Day 85:JIT 流动性 — 单笔 block 内 mint+swap+burn 的高级 LP 玩法。Just-In-Time LP 是 V3 集中流动性的"极端版"——把流动性恰好放在某 swap 的入口区间,赚 fee 但不持仓。同时讨论 sandwich attacks 的对偶——JIT 是"善意 MEV",sandwich 是"恶意"。链上案例分析。