解析 GMX V1

  • blgamann
  • 发布于 2024-01-20 22:30
  • 阅读 60

GMX 是一个去中心化的现货和永续交易平台,支持低交易费用和零价格影响交易。文章详细介绍了 GMX 的多资产池、Chainlink 预言机和流动性提供者的奖励机制,并阐明了价格设定、交易执行和流动性管理等复杂的交易机制。

GMX 是 去中心化现货和永续交易所,支持 低交易费用无滑点兑换交易

交易的支持来自于 独特的多资产池,流动性提供者从做市、交换费用和杠杆交易中赚取费用。

动态定价由 Chainlink Oracles领先交易所的价格聚合 支持。

GMX 是 押注抵押代币的指数代币价格上涨(long)或下跌(short)的交易

GMX 提供杠杆(最多 50 倍)。

价格

在 GMX 中,指数代币的价格决定了盈亏(Profit and Loss)。假设某人在当前 1 ETH = $300 的情况下打开了一个多头头寸,当 1 ETH = $330 时获利,当 1 ETH = $270 时则亏损(反之,空头头寸的盈亏与多头头寸相反)。因此,GMX 必须使用准确且有效的价格

GMX 从 Chainlink、AMM 以及自有的 Keeper 获取代币价格。但实际上,GMX 只使用 Chainlink 和自有 Keeper 的价格。Keeper 是 执行特定任务(发送交易)的自动化机器人。GMX 存在两种类型的 Keeper:Price feed keeper 和 Position keeper。

  • Price feed keeper:定期提交价格以供交换
  • Position keeper:执行头寸时提交价格

setFastPrice

在 GMX 中,Chainlink 的价格是 refPrice(或 primaryPrice)。而自有 Keeper 的价格是 fastPrice(或 secondaryPrice)。fastPrice 是 Binance、Bitfinex 和 Coinbase 价格的中间值。fastPrice 由 Price feed keeper 在短时间周期内设定(refPrice 则由 Chainlink 自行设定)。

Price feed keeper 是发送交易的机器人,而 updater 是交易签名者。updater 所设定的 fastPrice 可能无效。为了防范这一风险,存在 signer,signer 执行监视角色(disableFastPrice)。

每当 fastPrice 被新设置时,都会比较 refPrice 的价格变动量(refDeltaAmount)和 fastPrice 的价格变动量(fastDeltaAmount)。如果 fastPrice 的价格变动量大于 refPrice 的价格变动量,并且超过阈值(maxCumulativeDeltaDiffs),则会触发特定事件(MaxCumulativeDeltaDiffExceeded)。

fastPrice 被设置的时点(lastUpdatedAt 和 lastUpdatedBlock)会被始终记录下来。

getPrice

GMX 的价格有最小值和最大值。

在打开空头头寸或关闭多头头寸时使用 minPrice,在打开多头头寸或关闭空头头寸时使用 maxPrice。

GMX 首先从 Chainlink 获取价格。Chainlink 的价格每次新设置时都会增加回合数。也就是说,不同回合的价格是不同的。如果 priceSampleSpace 为 3,则查询最近的 3 个回合。在 minPrice 的情况下,使用这 3 个回合中最小的值,而在 maxPrice 的情况下,使用最大的值。

从 Chainlink 获取的价格被用作 refPrice(不使用 AMM 的价格)。如果 fastPrice 在特定时期(priceDuration, maxPriceUpdateDelay)内没有被设置,则应用带有价差(spreadBasisPointsIfChainError 或 spreadBasisPointsInactive)的价格(min/max )。在这种情况下使用的当前价格为 refPrice 的价格。否则,将使用当前价格的 fastPrice。

当强制应用价差时,将使用在 refPrice 和 fastPrice 之间的较小值作为 minPrice,较大的值作为 maxPrice。强制应用价差的情况如下:

  1. spreadEnable 被设为 true(由 Gov 设定): 强制应用价差
  2. disableFastPriceVoteCount(由 Signer)> minAuthorization: 监视者(signer)的报告
  3. cumulativeFastDelta > cumulativeRefDelta && cumulativeFastDelta — cumulativeRefDelta > maxCumulativeDeltaDiffs: fastPrice 是无效的价格变动量
  4. | fastPrice — refPrice | * BASIS_POINTS_DIVISOR / refPrice > maxDeviationBasisPoints: refPrice 和 fastPrice 价格差异过大

LP

用户的头寸获利时,GMX 必须进行支付。GMX 为此需要流动性。

GMX 的 LP(流动性提供者)因以下原因向 GMX 提供流动性。

  1. 交易池收益:如果用户的头寸亏损,交易池则盈利。在这种情况下,LP 也会获利。
  2. GMX 中产生的收费:GMX 有 depositFee、swapFee、marginFee(positionFee + fundingFee)和 liquidationFee。这些费用的一部分归 LP 所有。

addLiquidity

LP 通过 GlpManager 合约提供流动性。当 LP 提供 1 ETH(\$300)的流动性时,1 ETH 将通过 GlpManager 转移到 Vault。Vault 将以 ETH 的价格(\$300)铸造相同价值的 USDG(300),并支付给 GlpManager。GlpManager 根据 GLP 的价格(\$1)计算出以 USDG 价值铸造的 GLP 数量(300 个),并支付给 LP。

buyUSDG

LP 的流动性在 poolAmount、usdgAmount 和 feeReserve 中记录。LP 的流动性在实际的 poolAmount 中存储大量储存量(amountInAfterFee),从而应用 depositFee。

GMX 为每种代币管理 poolAmount、usdgAmount、feeReserved、reservedAmount 和 guaranteedUsd。poolAmount 和 usdgAmount 为 deposit,feeReserved 为费用,而 poolAmount — reservedAmount + guranteedUsd 与全球多头 PnL 相关。

glpPrice

为了计算 GLP 的价格,首先需要计算 AUM(Assets under management)。AUM 是 GMX 内所有代币的 poolAmount ± global long PnL ± global short PnL。将 AUM 除以 GLP 的 totalSupply 即可得到 GLP 的价格。将 usdgAmount 除以 glpPrice,可以得到可以铸造的 GLP 的总数量。

Swap

GMX 内存在流动性,因此也可以进行交换。交换时仅应用 swapFee。

Position

开立头寸分为请求(request)和执行(execute)两个步骤。当用户请求开立头寸时,Position keeper 同时执行该头寸(setFastPrice)。如果该请求无效,Position keeper 将使其无效(cancel)。

在 GMX 中,开立头寸需要抵押代币和指数代币(标的)。抵押代币是字面意思上的抵押代币,而指数代币是价格变化的对象。 多头头寸的抵押代币必须与指数代币相同。空头头寸的抵押代币应为稳定币。

在 GMX 中,头寸分为 increase/decrease。

  • increase:开立头寸 / 增加头寸
  • decrease:关闭头寸 / 减少头寸

increasePosition

新开头寸时,用于计算头寸和全球 PnL 的值会发生变化。

假设 Alice 将 2 ETH(1 ETH = \$300)作为抵押,开立 \$6000 的多头头寸。

如果 ETH 的价格超过 \$300(position.averagePrice),则 Alice 开始了一场获胜的游戏。如果 ETH 的价格达到 \$330,Alice 将会有 6000(position.size)* 10% 的利润。position.reserveAmount 是 GMX 为借出 $6000 而单独预留的 ETH 数量。通过此方式,确保无论 ETH 的价格上升多少,总有支付的办法。

ETH 全球多头 PnL 计算为 poolAmount — reservedAmount + guaranteedUsd。guaranteedUsd 等于 position.size — position.collateral。最终,ETH 全球多头 PnL 为 poolAmount — reservedAmount + Σ(position.size — position.collateral)。当 Alice 的头寸被开立时,将影响 2 ETH — 20 ETH + (\$6000 — \$594) 的 ETH 全球多头 PnL。如果 ETH 的价格上涨,由于 -18 ETH 的价值增加,ETH 的 AUM 会减少。换言之,Alice 所获利润将导致 GMX 亏损。

假设 Alice 使用 600 DAI 作为抵押,开立 \$6000 的空头头寸。与开立多头头寸时不同,开立空头头寸时,DAI 的 poolAmount 不会发生变化。相反,计算全球空头 PnL 的值(globalShortSize 和 globalShortAveragePrice)会发生变化。

由于 DAI 是稳定币,因此在计算 DAI 的 AUM 时,只会反映 poolAmount。反之,乙烯的价格变动,已被计入定价变化后的 PnL。

decreasePosition

可以减少抵押的大小和头寸的大小。在此过程中,将计算头寸的 PnL 并进行处理。

长头寸的 PnL = 头寸的当前价值 — 头寸的入场价值

= 头寸的代币大小 * 当前市场价格 — 头寸的代币大小 * avgEntryPrice

短头寸的 PnL = 入场价值 — 头寸的当前价值

= 头寸的代币大小 * avgEntryPrice — 头寸的代币大小 * 当前市场价格

假设要全部关闭头寸(sizeDelta = $6000),总共可能会有四种情况。

  • 多头头寸和价格上涨的情况(\$300 -> \$315:增幅 0.05%)

:usdOut = 300(PnL)+ 600(抵押)-> 从 ETH poolAmount 中支付给 Alice \$300(PnL)。

  • 多头头寸和价格下跌的情况(\$300 -> \$285:降幅 0.05%)

:usdOut = -300(PnL)+ 600(抵押)-> 从抵押中扣除 PnL,Alice 获得 \$300。

  • 空头头寸和价格下跌的情况(\$300 -> $285:降幅 0.05%)

:usdOut = 300(PnL)+ 600(抵押)-> 从 DAI poolAmount 中支付给 Alice \$300(PnL)。

  • 空头头寸和价格上涨的情况(\$300 -> \$315:增幅 0.05%)

:usdOut = -300(PnL)+ 600(抵押)-> 从抵押中扣除 PnL, Alice 获得 $300。DAI 的 poolAmount 增加了 PnL (开立多头头寸时,抵押代币已体现在 poolAmount 中,而开立空头头寸时,未能体现在 poolAmount 中。为了反映 GMX 的利润,pıpnd需要在 poolAmount 中反映相应的 PnL)

当 PnL 值为负时,如果该值大于抵押值,则该头寸将被强制平仓。

global short PnL

全球多头 PnL 通过每种非稳定代币的 poolAmount — reservedAmount + guranteedUsd(= Σ(position.size — position.collateral))来获取。全球空头 PnL 利用 ShortsTracker 合约的 globalShortSize 和 globalShortAveragePrice 进行计算。

GMX V1 使用 ShortsTracker 合约的 globalShortAveragePrice。原因是 Vault 的 globalShortAveragePrice 计算错误被发现

globalShortAveragePrice

空头平均价格(globalShortAveragePrice)在每次增减空头头寸时更新。

为了计算空头平均价格,首先计算 realisedPnl。头寸大小为 position-size-delta 的 PnL,则头寸变化大小的 PnL 为 position-sizeDelta-deltaposition-sizeDelta-delta 是 realisedPnl。realisedPnl 仅在减少头寸时计算。

delta 是通过当前总空头头寸大小和先前的空头平均价格计算得到的 PnL。我们称之为 short-size-delta。在减小头寸时,将 short-size-deltaposition-sizeDelta-delta 作差,该 delta 带有 nextShort-size-delta

当新的空头头寸被开篇时,新的空头平均价格(globalShortAveragePrice)为 nextPrice * nextSize / nextSize ± nextShort-size-delta

费用

GMX V1 中存在多种费用。包括 depositFee、swapFee、marginFee(positionFee、fundingFee)、liquidationFee。

positionFee

BASIS_POINTS_DIVISOR 为 10000。marginFeeBasisPoints 为 10。positionFee 为 sizeDelta 的 0.1%。

fundingFee

cumulativeFundingRates 每 8 小时(fundingInterval)更新一次。cumulativeFundingRates 是 fundingRate 的累积值。它在 buyUSDG、sellUSDG、swap、increasePosition、decreasePosition、liquidationPosition 时被调用的 updateCumulativeFundingRate 函数中更新。

fundingRate 的计算如下:fundingRate = fundingRateFactor(600) * (reservedAmount / poolAmount)* intervals。此值由已借出代币的数量(reservedAmount)和当前可用代币的数量(poolAmount)的比率决定。

在经过 8 小时后,增加一个新头寸(increasePosition)。

  1. 调用 updateCumulativeFundingRate 函数以更新 cumulativeFundingRates 值。
  2. 计算 fundingFee。fundingFee = (cumulativeFundingRates — position.entryFundingRate)/ FUNDING_RATE_PRECISION * position.size。FUNDING_RATE_PRECISION 为 1000000。当前 position.size 为 0,因此 fundingFee 为 0。
  3. 计算 position.entryFundingRate。此值为更新后的 cumulativeFundingRates 值。

fundingFee 随时间增加, 因为 cumulativeFundingRates 值每 8 小时(fundingInterval) 증가,因此 cumulativeFundingRates — position.entryFundingRate 也会持续增加。

经过 16 小时后,增大头寸的尺寸(increasePosition)。

  1. 调用 updateCumulativeFundingRate 函数以更新 cumulativeFundingRates 值。
  2. 计算 fundingFee。fundingFee = (cumulativeFundingRates — position.entryFundingRate)/ FUNDING_RATE_PRECISION * position.size。FUNDING_RATE_PRECISION 为 1000000。在 16 小时内,当前 cumulativeFundingRates 值与 position.entryFundingRate 之间的差异为 interval 2。因此在此基础上产生 fundingFee。
  3. 现在更新时间 cumulativeFundingRates 值的 position.entryFundingRate。

liquidationFee

执行 liquidatePosition 时,将计算 marginFee(fundingFee + positionFee)。

remainingCollateral 是 position.collateral — PnL。当 remainingCollateral 小于 marginFee 时,返回 remainingCollateral;如果小于 marginFee + liquidationFeeUsd,则返回 marginFee。

附录

(1) long/short 参数分析

  • path:path 的长度最大为 2。 _path[_path.length - 1] 为抵押代币。如果 path = [DAI, WETH],则表示 DAI 被交换成 WETH。
  • indexToken:索引代币的地址。
  • amountIn:path[0] 的数量。作为抵押代币的量。
  • minOut:若 path 的长度为 2,则表示交换。为 swapOut 的 minOut 值。
  • sizeDelta:头寸规模的变化量,单位为美元。
  • isLong:选择 long 或 short。
  • acceptablePrice:当前指数代币的价格。
  • executionFee:执行头寸时需要的费用。头寸分为请求/执行两阶段,由 Position keeper 执行,将费用支付给 Position keeper。

(2) 交易池收益 = LP 收益​ 示例

1/ addLiquidity, buyUsdg(1 ETH = $300)

10 ETH → 3000 USDG(1 ETH = $300)

ETH

- poolAmount: 10 ETH

- usdgAmount: 3000 USDG

- glpPrice: 1$

- glpAmountToMint: 3,000

2/ open long position(1 ETH = $300)

1 ETH / 600$(2x 杠杆)

ETH

- poolAmount: 11 ETH

- usdgAmount: 3000 USDG

- reserveAmount: 2 ETH

- guranteedUsd: 600$ - 300$ = 300$

AUM: 11 ETH - 2 ETH + 300 \$ = 9 ETH * 300 + 300 = 2700$ + 300$ = 3000$

glpPrice: 3,000\$ / 3000 = 1\$

3/ price down(1 ETH = $270)

AUM: 11 ETH - 2 ETH + 300\$ = 9 ETH * 270 + 300\$ = 2430\$ + 300\$ = 2730\$

glpPrice: 2,730\$ / 3000 = 0.91\$

4/ removeLiquidity / sellUsdg(1 ETH = \$270)

glpAmountToBurn: 3,000

AUM: 11 ETH - 2 ETH + 300\$ = 9 ETH * 270 + 300\$ = 2430\$ + 300\$ = 2730\$

glpPrice: 2730\$ / 3000 = 0.91\$

usdgAmount: glpAmountToBurn * glpPrice = 3000 * 0.91\$ = 2730\$

ETH

- poolAmount = 11 - 10.11111111 ETH = 0.88888889 ETH

- usdgAmount = 3000 - 2730 = 270

transferOut = 2730\$ / 270\$ = 10.11111111 ETH (2729.9999997\$)

从 LP 的角度看,持有更大量的 ETH(10 -> 10.11111111),但由于 ETH 价格下跌,发生了 IL(Impermanent Loss),导致损失($3000 -> 2729.9999997$)。

之所以称之为无常损失(impermanent loss),是因为这种在价格波动较大时发生的潜在损失在价格回到原始货币价值时可以恢复,因此使用如此名称。

例如:

- 1.25 倍的价格变动造成相对于 HODL 的 0.6% 损失

- 1.50 倍的价格变动造成相对于 HODL 的 2.0% 损失

- 1.75 倍的价格变动造成相对于 HODL 的 3.8% 损失

- 2 倍的价格变动造成相对于 HODL 的 5.7% 损失

- 3 倍的价格变动造成相对于 HODL 的 13.4% 损失

- 4 倍的价格变动造成相对于 HODL 的 20.0% 损失

- 5 倍的价格变动造成相对于 HODL 的 25.5% 损失

https://pintail.medium.com/uniswap-a-good-deal-for-liquidity-providers-104c0b6816f2

GLP 的价格不受 GLP 发放/燃烧的影响。也不受交易员开立或平仓的影响。仅根据指数代币价格所产生的盈亏影响其价格。

参考资料

  • 原文链接: medium.com/@blgamann/gmx...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
blgamann
blgamann
江湖只有他的大名,没有他的介绍。