Curve A 参数五论之二:调整 A 带来的曲线变化及损益

( 本人twitter:@observerdq

2020 年 4 月 17 日,离 312 已一个多月,DAI 依然是正向脱锚状态,价格在 1.02 左右。

Michael(Curve 创始人)在推特上发起了一个投票,看大家是否支持把 Compound pool 的 A 参数从 900 调到 400。

49 人参与了投票,但回复者仅一人。8 个小时后,Michael 把 A 调到了 400,又发了一则推特,0 回复。

在那个当下,能真正参与讨论这个议题的,寥寥无几。Michael 可能沉浸在一个人的试验之中,调试参数、观测、改进,在 Telegram 的只言片语中,我感受到了他的兴致和乐趣。

回到这次 A 参数的调整,带来了日化约 0.1% 的损失。

我的这一篇,就是想讲清楚 A 调整了之后,到底意味着什么?为什么会带来损失?

不同 A 值对应曲线的一个图示

我挑选了一组实际参数设定,进行图示呈现。本文的讨论对象是 Curve V1 pool,曲线公式见 V1 白皮书

黑色曲线对应的参数为:A = 10     D = 20,000

当前池子处于黑色点,X_token、Y_token 的数量分别为 2500 / 18105,价格约为 1 X_token = 1.36 Y_token。

此刻将 A 降为3,曲线将变成红色线。这条曲线的参数为:A = 3     D = 19,022

比较红黑二曲线,很明显的,黑色曲线中趋近直线的比例更多,这也是 A 之核心作用,A 越大,价格 1 附近的流动性越多;黑色曲线的 A(10)高于红色曲线的 A(3)。相应地,远离价格 1 的区间的流动性,则是红色曲线要多过黑色曲线。

在那则推特投票的时刻,DAI 有些脱锚,如果是使用低 A 值的红色曲线,就能比高 A 值的黑色曲线提供更多的流动性,这也是 Michael 想调整 A 的原因,他希望池子能捕获更多手续费。

我们再回到将 A 降为 3 带来的变化,降为 3 的那个瞬间,池子内两种 token 的数量并没有变化,但 D 值发生了变化。此外,图中还能看出,当前点的切线斜率发生了变化,曲线形状亦发生了变化。

以下专门探讨这些变化。切线斜率、曲线形状的变化,若换一角度考察,会更加直观。D 值的变化,则涉及到了池子损益评估的话题。

订单簿视角来看 A 值调整后的变化

Curve 池子可以从订单簿的角度去理解。单个池子,根据其 A、D 参数的不同,对应着在所有价格点上的挂单数量的不同分布,并且所有的挂单是联动的、整体性的存在。

上一节的案例里,当 A 从 10 降为 3 的瞬间,可以理解为所有的挂单瞬间做了一次整体性大调整,从黑色的一组挂单集合转换成了红色的挂单集合。

先对上图做一些基本说明。

横轴是价格,1.00 代表 1 X_token = [ 1.00 ~ 1.01 ) Y_token,1.01 代表 1 X_token = [ 1.01 ~ 1.02 ) Y_token,以此类推。为了节省空间,我将价格 < 1.00 的挂单略去了。

竖轴表示 Y_token的数量(即对 X_token 的买单挂单)。黑色部分在价格 > 1.36 的区间没有数据,这是因为在黑色曲线上的当前价格是 1.36,> 1.36 意味着 X_token 的更高价卖单挂单。这张图仅考察使用 Y_token 进行买入挂单的部分,因此 > 1.36 的部分无数据。

我们看黑红的对比。首先,当前价格发生了变化,对应着前一节提到的切线斜率的变化。A 调整后,当前价格瞬间变动到了 1.92,红色部分的挂单延展到了 1.92。也就是说,有一定数量的 Y_token 在 1.36 - 1.92 的价格区间内分批挂出了对 X_token 的买单。

这其实是很奇怪的变化,一个 DEX 的当前价格在没有 swap 的情况下,竟然发生了变化。我们立刻可以想到,这将形成和市场价格的价差,我们假设套利者会瞬间介入把价格打到 1.36。

此外,可以看到从 1.00 - 1.36 的每一个价格点上的挂单数量都有所不同,整体而言,高位挂单的数量更多。这对应着上一节提到的曲线形状的变化。

A 调整带来的损益 - 最直接的计算方式

Michael 的推提及了 Compound pool A 参数的调低会带来一些损失( ‘wipe one day’s profits’ )。在讨论背后的数学关系之前,需要先把“损失/损益”定义清楚。

There are 2 ways to measure profit - with and without impermanent loss. - Michael

按照最直接的想法,定义损益很简单,将两个 token 的价值折算成 U 或是其中任意一个(X_token 亦或 Y_token),二维转一维,然后对比 A 调整前后的总价值。这也是 Michael 提到的 ‘with impermanent loss’。

不妨先简要讨论这个意义上的损益。

 为求简化,所有的讨论暂且假设手续费 = 0。

还是基于前文一直用的示例,A 调低后,X_token 的瞬时价格变高,套利者介入,把价格打回 A 调整前的价格,这便会带来损失。道理很简单,上一节提到,A 调低后,相当于按照高于市场价的价格挂了很多对 X_token 的买单,这些不正常的买单被套利者吃掉,这必然会带来损失。

从曲线图上看得会更加清晰。

黑色点和黑色曲线是调整前池子的状态,对应价格为 1.36 。A 调低后,池子按红色曲线运行,调低的瞬间,池子价格变成 1.92,套利者介入使得池子的状态很快从黑色点移到红色点,对应着 1.36 的价格。

需要比较 A 调整前后的池子总价值,方法比较简单。

  • 先看 A 调整后的总价值(套利者介入后)。找出红色曲线在红色点处的切线和 Y 轴的交点,即为两种 token 按现价折算成单一 Y_token 的数量。
  • 再看 A 调整前的总价值。找出黑色曲线在黑色点处的切线和 Y 轴的交点。因为 A 调整前后的价格是一样的(套利者介入后),因此这条切线和第一步里的切线平行。

  • 显然,黑色切线与 Y 轴的交点更高,也就是 A 调整前的总价值更高。A 调低带来了总价值的损失。

以上讨论仅限于 A 调低后的瞬时损失,还算简单。但若把追踪时间拉长,想探讨后续不同价格走势下 A 调低所带来的长期损益,就变得有些复杂。这取决于价格往更加脱锚的方向去发展、还是回归完美的锚定,A 调整后的总价值可能会不如不调整 A 的状态、也有可能会高过。这里暂不展开,本文的目的仅在简要演示一番 A 调整是如何影响到池子总价值的,并不求完整系统的论述。

剔除无常的损益评估

按照两个 token 的当前价格折算,也就是考虑进无常,这是最直观的度量方法,但这种方法较为麻烦。Curve 引入了另一种独特的损益评估法,剔除了无常的因素,简化了计算,在大部分情况下也能够适用。

这就是 D 值,D 值是 Curve 曲线公式在 A 之外的另一核心参数。我们在 Curve 官网每个池子里看到的 virtual price 即为根据 D 值计算得出。

D 值,就是当池子价格是 1(完美锚定)的时候,池子内两种 token 的总数量。因为此刻价格是 1,因此可以把两种 token 的数量简单相加。池子价格等于 1 的点,即为曲线和 x=y 这条直线的交点。

回到前述示例,A 调低以后,很显然,D 值变小了。因此,长期来讲如果价格能够恢复锚定的话,D 值的变化能够反映出因为 A 调整带来的损失。

我上文使用的示例中,当前价格是 1.36,这其实是比较极端的情况。我们看 Curve V1 类池子,比如主流的稳定币/ LSD 池子,价格都不会偏离 1 太多。在价格接近 1 的时候,无常的影响很小,因此可以直接用 D 值的变化来近似反映损益。

D 值,作为一维的度量衡,且作为池子的参数之一,便于计算、便于追踪历史数值,比较适合用来近似地评估损益。

结语

A 参数的调整相当于在订单簿所有价格点上的挂单的一次重排,改变了当前价格点,改变了 D 值,带来了损益。

因此,A 参数的一次性大幅调整有一种突兀感,甚至有一种瑕疵感。白皮书内对 A 的动态管理并无涉及,或许是在 Curve 上线实际运行了一段时间后,Michael 才渐渐认识到了对 A 参数的调整方式需要修正。在宣布 Compound pool A 参数调整完毕的那则推特下,Michael 跟了一句评论。后续新版本的池子,A 参数的调整改成了一段时间渐变完成的模式。

老池子 A 参数的一次性调整方式仅仅是瑕疵感么?没这么简单,背后还深藏了一个可被攻击的点。幸运的是,发现这个脆弱点的是白帽(对协议理解的深度真是天外有天)。后续会单写一篇讲述这个攻击方法。

Subscribe to observerdq
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.