金融市场与金融工具 / M29

自动做市:沿报价曲线交换,怎样算实际得到多少

从集中区间库存推导一次带费用的有限交换、成交均价与限价余量;费用增长和池内L单独核算。

Agent Markdown ↓

一、报价不一定来自一排买卖订单

订单簿告诉我们各价格上可成交的数量;自动做市也可以把可成交条件写进一条随库存移动的曲线。二者都要回答同一问题:我送入一笔确定数量,实际得到多少,成交后别人还面对什么价格?只读开头的边际价格,再乘整个数量,通常遗漏了自己的交易会改变库存这件事。

本篇采用Uniswap v3的集中流动性机制和固定v1.0.0代码作原件。它是一种有特定版本的机制,不把v3称为“最新协议”,也没有取得某个真实池的区块、tick或链上余额。下面的数值是连续实数教学例,用来把一次交换算完整;实际合约还有整数舍入、tick间距和结算条件。123

二、真实库存与虚拟库存,为什么要同时存在

令 \(P\) 表示一单位X的Y价格,流动性为 \(L\),允许提供流动性的价格范围为 \([P_a,P_b]\)。在当前价格位于区间内部时,v3可以用虚拟储备

\[ x_v=\frac{L}{\sqrt P},\qquad y_v=L\sqrt P \]

描述局部曲线,因而 \(x_vy_v=L^2\)。但某个有限区间头寸真正需要的代币是

\[ \begin{aligned} x&=L\left(\frac1{\sqrt P}-\frac1{\sqrt{P_b}}\right),\\ y&=L\left(\sqrt P-\sqrt{P_a}\right). \end{aligned} \]

虚拟储备是表达价格曲线的变量,不是池子另外收到的代币;真实库存则把区间之外不提供的流动性扣掉。用真实的 \(x,y\) 直接套 \(xy=L^2\) 会错,正确的平移关系是

\[ \left(x+\frac{L}{\sqrt{P_b}}\right) \left(y+L\sqrt{P_a}\right)=L^2. \]

这也解释了集中流动性的设计:只在一段价格范围内承诺交换,可以用较少实际库存支持该区间的局部流动性;代价是出了区间以后,头寸不会继续在外面提供同样的双币交换。它不是无条件更高的投资回报。1

设 \(L=1000,P=1,P_a=0.81,P_b=1.21\),平方根为1、0.9、1.1。初始虚拟X、Y各1000;真实X为 \(1000(1-1/1.1)=90.9090909\),真实Y为100。本默认例只有这一位活跃LP,因此头寸L等于全池active L。 后面单独的费用分配例会明确换成多位LP的环境。

三、送入50Y:先扣费,再沿曲线移动

固定手续费率 \(\phi=0.003\),协议分成取零,费用不自动复投进流动性。用户送入50Y,其中0.15Y是费用,真正推动曲线的是49.85Y。令 \(s=\sqrt P\),区间内 \(y_v=Ls\),因此

\[ \begin{aligned} \Delta y&=50(1-0.003)=49.85,\\ s_1&=s_0+\Delta y/L=1.04985,\\ P_1&=s_1^2=1.1021850225,\\ X_{\rm out}&=L\left(\frac1{s_0}-\frac1{s_1}\right)\\ &\approx47.4829737582. \end{aligned} \]

最后的边际价格约1.102185 Y/X,但这笔交易的含费平均执行价是 \(50/47.4829737582\approx1.0530090271\) Y/X。它既不是初始边际价1,也不是最终边际价。沿曲线看,后面买到的X越来越贵;这个平均价概括的是整段交换。3

从微积分也能看出差别:在活跃区间内 \(dy=-P\,dx\),边际价格是瞬时的比率,而有限成交量来自这条关系在交易路径上的积分。这里直接用两个端点的虚拟库存差算出X输出,等价于做完整个积分,不是用一个端点价格乘数量。

成交后真实X约43.4261171509,真实Y为149.85,另有费用0.15Y。检查守恒:用户拿走47.4829737582X,加池内43.4261171509X,恢复初始90.9090909091X;用户50Y中49.85进入曲线库存,0.15进费用账,没有凭空少钱或多出L。

四、送入150Y:不能越过用户自己设的价格上限

这一次仍用相同初始状态,但用户最多愿意把价格推到1.21,恰好也等于当前区间上界。要把 \(\sqrt P\) 从1推到1.1,只需要净输入 \(1000(1.1-1)=100\) Y。因此含费输入的上限是

\[ \begin{aligned} Y_{\rm consumed}&=100/(1-0.003)\approx100.3009027081,\\ \mathrm{Fee}&\approx0.3009027081,\\ Y_{\rm unused}&=150-100.3009027081\approx49.6990972919. \end{aligned} \]

到上限时,X输出90.9090909091,真实库存X为0、Y为200,费用另计。用户未被消耗的49.6990972919Y不是手续费;含费平均执行价应以实际消耗100.3009027081除X输出,约1.1033099298,而不是用最初指定的150去除。

单次从相同初始状态出发指定Y输入实际消耗Y费用Y未用Y得到X最后边际价
区间内成交50500.15047.482973761.10218502
达到用户价格上限150100.300902710.3009027149.6990972990.909090911.21

这两行是两笔独立地从同一个初始状态开始的例子,不是先做50、再从新库存接着做150。

价格上限和区间边界在本例数值相同,但规则职责不同。达到本次用户的price limit就停止,即使外面还有别人的活跃流动性。 若用户事先允许更远的价格、首先碰到的只是一个已初始化tick,代码才会先按该tick的净流动性变化更新active L,再用新的L处理剩余量。不能跨tick之后仍拿旧L外推,更不能因为存在另一LP而越过这次用户的限价。23

这里不再添加一组跨tick成交数据;读者只需把“用户不许再走”和“这段流动性已走完、后面要换L”这两个原因分开。

五、费用按活跃L分配,不把一位LP当全池

白皮书把fee growth描述为每单位流动性的累计费用;在其式(6.9)的印刷表达与这个单位有差异时,应回到固定代码看实际含义。v1.0.0的Pool.swap先处理协议分成,再用 feeAmount × Q128 / state.liquidity 更新累计量。\(Q128\) 是定点数刻度;忽略数值编码后,经济关系就是分给LP的费用除以当时全池活跃L12

现在另开一个独立分配例:池active L=1000,其中我们考察的头寸L=100,其他LP有900;合计可分LP费用为3Y。于是

\[ \Delta g=3/1000=0.003\ \text{Y/单位L},\qquad \text{该头寸所得}=100\Delta g=0.3\ \text{Y}. \]

这不是把刚才唯一的L1000头寸偷偷缩成100,也不是说刚才50Y输入产生了3Y手续费。两个例子各负责一次交换和比例分配,金额身份必须保留。若某段价格上头寸已经不活跃,它不会继续按原L分到该段的新交换费用;历史累积费用则仍是另外的资产余额。4

六、让曲线、库存和现金同时变化

从50Y输入开始,读三条不同的线或标记:边际价格、含费平均执行价、用户价格上限。增加至150,观察输出X达到本区间可交付库存以后,实际消耗不再增加、未用输入出现。价格上限保持1.21,实验不会把“更多输入”解释为授权越价。再展开费用分配卡,确认它仍是另一个L100占池L1000的例子。

这一步只讨论一次兑换的金额和交换后的库存。是否值得长期提供流动性,还要选路径、费用、成本及比较基准;“相对一直持币的变化”和“相对自融资再平衡的变化”不是同一个问题,本篇不把一次交换的手续费自动升级成LP长期收益。

七、检验:每一笔代币去了哪里

题一:开头边际价为1,为什么50Y得不到50X?

解析:先扣0.15Y费用,且49.85净输入沿曲线推高价格;后面的X不是都按开头价格卖给你。完整端点差给47.4829737582X;只用1乘整个输入同时忽略了费用和价格路径。

题二:150Y指定输入的那一行,平均价为什么不能用150除90.9090909?

解析:只有100.3009027081被消耗,49.6990972919没有用于成交。含费平均价的分子是实际花掉的代币,未用额不是成本,更不是手续费。

题三:在1.21之外出现另一位LP,原150Y订单能否自动继续?

解析:不能,1.21也是用户价格上限。只有另一个允许更远价格的指令,才可能先跨tick、更新active L、再继续处理剩余数量。区间外有流动性不等于用户授权越价。

题四:池active L=1000,某LP L=100,LP费用合计3Y,为什么该LP不能得3Y?

解析:它只提供其中十分之一的活跃L;每L费用为0.003Y,所得0.3Y。分母应是全池活跃L,不是该LP自己的L。费用单独累计,也没有自动变成额外L。

题五:上界时真实X为零,虚拟X仍为1000/1.1,这些虚拟X能继续交给交易者吗?

解析:不能。虚拟储备是表达曲线的平移变量,不是可交付资产;本区间真实X已用完。把虚拟储备当余额,会同时违反库存守恒、区间约束和用户限价。


  1. Uniswap Labs contributors,Uniswap v3 Core,March 2021。原文。定位:§2–3.2集中流动性/区间;§6.1–6.3库存、手续费及区间内交换;PDF p6式6.9的每单位L口径。采用范围:集中区间的真实/虚拟储备与有限交换;手续费增长公式以固定代码作局部单位校正。 ↩︎ ↩︎ ↩︎

  2. Uniswap Labs contributors,Uniswap v3-core v1.0.0 — UniswapV3Pool.swap,git tag v1.0.0。原文。定位:固定tag v1.0.0 UniswapV3Pool.sol swap L596–788,特别while/价格目标、feeGrowth除active L、跨initialized tick更新L。采用范围:用户限价停止、活动L、fee与跨tick顺序的原始代码;非部署审计。 ↩︎ ↩︎ ↩︎

  3. Uniswap Labs contributors,Uniswap v3-core v1.0.0 — SwapMath.sol,git tag v1.0.0。原文。定位:固定tag v1.0.0 SwapMath.sol全文;amountRemainingLessFee、target与feeAmount。采用范围:一步精确输入中净输入/fee和target限制的原始实现。 ↩︎ ↩︎ ↩︎

  4. Uniswap Labs contributors,Concentrated Liquidity,current docs retrieved 2026-09-21。原文。定位:How concentrated liquidity works;Active liquidity;Ticks全文;v3/v4定位,未采用营销绩效数。采用范围:当前集中流动性概念说明,未声称任何已部署池状态。 ↩︎

动手试一试

有限交换:gross、net、费用和限价余量

这篇词条与哪些内容相连

图中汇集本篇及其段落的直接关系,具体使用位置见下方列表。箭头按关系名称阅读。例如“需要先修”指向需要掌握的内容;推荐阅读顺序另见本领域路线。

逐条查看关系与依据

KEEP ASKING

把这一页,变成一堂课。

专属 Prompt 与同源材料会一并复制。Agent 先读取指定文献,再围绕本页的材料、推导和练习展开教学。

READ TOGETHER

一起读,也一起把问题说清楚。

关于整篇的想法留在这里;某个推导、算例或表格的问题,可以点小节旁的“章说”,在原处展开讨论。

使用 GitHub 账号参与

本篇讨论

搜索笔记

让 Agent 教我

这里包含专属 Prompt 及本次学习范围的正文、案例、来源和图表说明,分支内容以当前选择为准。复制后粘贴到你常用的 Agent。