这个 Solana 池子到底能吃下多少?

TVL 对你这笔交易几乎什么都说明不了。你那个规模上的深度、以及你将要出场的那一侧的深度,才是要紧的数字。

BoltTx Team··15 min read
solana流动性深度仓位规模amm交易机器人

一个池子显示着很大的锁仓总额,而你的交易照样把价格推动了好几个百分点。那个数字是真的 —— 它只是在回答另一个问题。

★TVL 描述的是这个池子。深度描述的是这个池子对你的规模做了什么。★

为什么 TVL 是错的那个数字

锁仓总额把两侧储备加在一起。而一笔交易只和这一对里的一侧打交道 —— 具体地说,★你的买入受限于你要收到的那个代币的储备。★

// ★一个"很大"的池子,可能在你需要的那一侧很薄。★
const reserveSol   = 500_000n * LAMPORTS_PER_SOL;   // 很深
const reserveToken = 1_000n * 10n ** 9n;            // ★很薄★

买入代币会抽走代币储备。一个 SOL 很多、代币很少的池子,不管头条数字多好看,给你的成交都很差。

这种不对称对出场同样要紧。 一个你轻松进去的仓位,在其它所有人做同样的事、把另一侧储备抽薄之后,可能就难出来了。

深度是一条曲线,不是一个数字

诚实地回答"这能吃下多少",是在几个规模上分别算冲击,而不是去找一个单一阈值:

function impactAt(amountIn, reserveIn, reserveOut, feeBps) {
  const inAfterFee = amountIn * (10_000n - feeBps) / 10_000n;
  const out = (inAfterFee * reserveOut) / (reserveIn + inAfterFee);
  const spot = (inAfterFee * reserveOut) / reserveIn;    // ★零冲击价格★
  return Number(spot - out) / Number(spot);
}

for (const size of [0.1, 0.5, 1, 5, 10]) {
  console.log(size, impactAt(toLamports(size), reserveIn, reserveOut, 25n));
}

★输出是一条曲线,而它的形状就是答案。★ 一个冲击在 5 SOL 之前都很平、之后陡然上升的池子,可用规模大约就是 5 SOL —— 这是任何单一 TVL 数字都传达不了的事实。

恒定乘积的冲击增长快于线性,所以规模翻倍,冲击不止翻倍。这就是"干脆少下点"比大多数人以为的更有效的原因。

如果你的规模算对了、交易还是会错过,免费领个 BoltTx key 改一行就能测测提交路径。

按出场定规模,不是按进场

★代价最大的那个错误,是按"我能买进多少"来定仓位规模。★

// ★错的问题:我能进去多少?★
const maxEntry = sizeForImpact(reserves, MAX_IMPACT);

// ★对的问题:我能出来多少?★
const maxExit = sizeForImpact(reservesAfterEntry, MAX_IMPACT);
const size = min(maxEntry, maxExit);

进场会把储备推向对你不利的方向。 ★你自己的买入,让你将来要卖出的那一侧变薄了★ —— 所以在同样的名义规模下,出场永远比进场更难。

★一个能轻松吃下你进场的池子,可能吃不下你的出场 —— 而你会在最糟的时刻发现这件事。★

集中流动性改变了这套算术

不是每个池子都是恒定乘积。 集中流动性池把流动性放在若干价格区间里,产生的行为上面那个公式描述不了。

★在一个活跃区间内部,深度可以远好于同等 TVL 的恒定乘积池。而在区间之外,可能什么都没有。★

实际后果是: 一个集中流动性池的深度,取决于当前价格相对于做市商选择的那些区间落在哪里。现在测出的深度不能预测价格移动之后的深度,因为那次移动可能把价格推出有流动性的区间。

套用任何公式之前,先确认你在交易的是哪种池子。 对池子账户调 getAccountInfo,返回的 owner 字段会告诉你它属于哪个程序。

聚合器隐藏了什么

聚合器路由通过拆分到多个场所报出一个好价格,这确实有用,同时也遮住了一个风险。

★你可能是从五个池子进去,却要从一个池子出来。★ 路由优化器解的是眼前这笔交易的价格 —— 它对"你能不能按同样方式离开"没有任何看法。

两个值得养成的习惯:

在你现实中会用来出场的那个具体池子上查深度,不是在整条路由的汇总上查。

优先选那种"最大那一腿的池子深到能独自承受你全部出场"的路由。

真正会咬人的是出场那一腿

★总流动性,不如它变化得多快重要。★

// ★追踪储备的变化量,不只是水位。★
connection.onAccountChange(poolVault, (info, ctx) => {
  const now = info.data.readBigUInt64LE(64);
  history.push({ slot: ctx.slot, reserve: now });
}, "processed");

又深又稳的池子可以交易。而一个此刻很深、但过去几分钟已经流失掉很大一部分储备的池子,是一个有人正在离开的池子 —— 而单一时刻测出的深度分辨不了这两者。

对新代币来说这是更有信息量的信号,因为早期流动性往往既大又短暂。

把它变成一条规则

function maxTradeSize(reserves, maxImpactBps) {
  let lo = 0n, hi = reserves.in / 2n;
  while (hi - lo > PRECISION) {                 // ★在曲线上二分搜索★
    const mid = (lo + hi) / 2n;
    if (impactAt(mid, reserves.in, reserves.out, reserves.feeBps) * 10_000 > maxImpactBps) hi = mid;
    else lo = mid;
  }
  return lo;
}

★在冲击曲线上做二分搜索,给你一个由池子推导出来、而不是凭习惯挑出来的规模上限。★ 把冲击容忍度设一次,之后每个池子都会告诉你它自己的最大值。

然后取它和出场侧上限里更小的那个 —— 那才是真正该管住这笔交易的数字。

上链应该是什么水平

我们投递节点上的真实交易:中位确认 336 毫秒 —— 不到一个 slot。

★深度是在你读取的那个 slot 上测的;而你在之后才执行。★ 在一个薄池子上,几个 slot 的延迟就足够让别人的交易改变你能拿到什么 —— 这就是"保守定规模"和"快速落块"是从两个方向解决同一个问题的原因。

BoltTx 在这里的位置

我们负责提交。深度分析和仓位规模完全留在你的代码里。

提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易在落块之前、传输途中观察不到。你的池子读取继续用现在的端点 —— 两条路径互相独立。

你在本地签名。我们不托管资金、不代签、不修改交易内容,包括你算出来的规模和最小输出量。tip 带在交易里、从你自己的钱包链上支付,交易失败时跟着一起 revert —— 这是 Solana 原子交易的机制决定的。只有到达链上的交易才计费。

免费领 API key,没有月费:

const sender = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");

常见问题

怎么测量一个 Solana 池子的流动性深度? 对着池子的储备,在几个不同交易规模上算价格冲击。得到的曲线会显示冲击从哪里开始陡升 —— 那就是这个池子的可用规模。

为什么 TVL 不能衡量我能交易多少? 它把两侧储备加在一起,而你的交易受限于你要收到的那个代币的储备。头条数字很大的池子,可能恰好在你需要的那一侧很薄。

价格冲击怎么算? 拿你这个规模实际收到的输出,和同样储备下一笔无穷小交易能收到的输出比。差额就是你自己的规模造成的冲击。

我的出场为什么比进场难? 因为你的进场把储备推动了。买入让你将要卖进去的那一侧变薄,所以同样的名义规模,出来时面对的深度更差。

该按进场还是出场能力定仓位? 出场。取"进场上限"和"按进场后储备算出的出场上限"里更小的那个 —— 因为出不来是更贵的那种失败。

价格冲击随规模线性增长吗? 不是,在恒定乘积池上快于线性。规模翻倍,冲击不止翻倍 —— 这就是减小规模比人们以为的更有效的原因。

集中流动性有什么不同? 流动性放在价格区间里,而不是铺满整条曲线。在活跃区间内深度可以极好,在区间外几乎没有 —— 所以恒定乘积公式不适用。

怎么知道我在交易哪种池子? 拥有池子账户的那个程序会标明它。 套用任何深度公式之前先确认,因为不同池子设计的算术不一样。

聚合器解决了深度问题吗? 只解决一部分。它优化的是眼前这笔交易的价格,这可能意味着从多个池子进、从一个池子出。在你现实中会用来出场的那个池子上查深度。

价格冲击的上限设多少合理? 取决于你的策略,但有用的做法是从一个固定的冲击容忍度推导规模,而不是凭习惯挑一个规模。之后每个池子都会告诉你它自己的最大值。

给定冲击上限,怎么找到最大规模? 在零到"输入储备的一半"之间对冲击曲线做二分搜索。这产出的是一个由池子推导出来、而不是假设出来的规模上限。

深的池子就一定能放心交易吗? 不是。此刻的深度对一分钟后的深度什么都说明不了。 追踪储备随时间的变化,才能把稳定的池子和"人们正在离开的"区分开。

为什么新代币的流动性更要紧? 因为早期流动性往往既大又短暂。一个池子在上线时看起来很深、随后很快流失掉大部分储备 —— 单一时刻的测量揭示不了这一点。

深度测量能缓存吗? 不能。储备每笔交易都变,这正是深度是一个实时测量而不是一个属性的原因。要缓存的是池子的金库地址和费率。

把一笔交易拆开能减少冲击吗? 在同一个池子、同一个区块内不能 —— 每一部分执行时储备都在动。拆到真正不同的池子上确实有帮助,因为每个只吸收更小的一份。

一个我经常交易的池子该监控什么? 储备水位、它的变化速率,以及你的标准规模会产生多大冲击。第二项会在第三项变成问题之前提醒你。

延伸阅读

← 返回博客列表