交易的真实成本:四笔费用、Gas 假设与到手的价格
一笔买入有四段费用,spread 从不写进收据;gas 早已不是 2021 年的 gas;USDT 还有 6 位和 18 位两个版本。把真实成本算清楚。
一句话结论:你为一次加密交易付的钱,从来不是"手续费"三个字能概括的。至少分了四段:入金、交易(或点差)、货币换算、提现;其中 spread 从不作为一行费用出现在收据上,它只是让你被报的价格悄悄变差。另一半成本在链上:2026 年 gas 常低于 1 gwei,很多代码还活在 2021 年,硬编码 40 gwei 不只是贵,而且算错了。本文编译 2026 年 10 月的四篇材料:四段费用模型与 Python 实现(@nutshellcrypto,10-08)、EIP-1559 时代的 Gas 假设更新清单(@swapcoreexchange,10-14)、DEX 与 CEX 报价为何不同(@katie_hargreaves,10-08)、以及精度如何变成真金白银的损耗(@polycratia,10-10)。
披露:@nutshellcrypto 的文章文末推广自家手续费指南与 DCA 计算器(费率取自 2026-10-08 的公开费率页,spread 与网络费为假设值);@swapcoreexchange 的作者任职于 SwapCore(非托管即时兑换,文内披露);@katie_hargreaves 的文章推广 Dexlyn 产品(机制讲解为主);@polycratia 为开源库 cryptomoney 作者。数字均按"方法与口径"引用。
一、一笔买入的四段费用
如果你用"交易手续费"对比过两家交易所,那你比较的很可能是错的数字。一笔买入的成本最多经过四个阶段:
- 入金(Funding):银行转账通常免费;信用卡、PayPal 可能收几个百分点。
- 交易(Trading):在订单簿/"专业版"界面按 maker / taker 付费;点"一键买入"的简单界面,通常付一笔便利费加上已隐含在报价里的 spread。
- 换算(Conversion):用法币 A 买入以法币 B 计价的交易对,可能多一笔外汇费。
- 提现(Withdrawal):把币提到自己的钱包要付网络费,有时再加平台处理费。
spread 是最隐蔽的一项:它是买价与卖价之间的差,永远不会以"费用"为名列在你的收据上——它只是让你拿到的报价差一点点。
maker 与 taker,一段话
如果你的订单立刻和挂单成交,你是 taker,付较高的费率;如果你的订单挂在盘口等别人来成交,你是 maker,付较低的费率。市价单永远是 taker;挂在当前价格之外的限价单通常是 maker;部分成交的限价单可能两头都付——立即成交的部分按 taker,剩下的按 maker。
用 Python 把四段费用加起来
思路和我们在工程里处理任何成本一样:每个组成部分都建模,然后求和。
def all_in_cost(amount, trade_fee_pct=0.0, spread_pct=0.0,
funding_fee_pct=0.0, fx_fee_pct=0.0, network_fee=0.0):
"""近似估算:买入 `amount` 金额的加密资产并提现的总成本。
百分比以小数给出。"""
funding = amount * funding_fee_pct
trading = amount * trade_fee_pct
spread = amount * spread_pct
fx = amount * fx_fee_pct
total = funding + trading + spread + fx + network_fee
return round(total, 2), round(total / amount * 100, 2)
# 示例输入;请以你自己交易所的当前费率表为准
scenarios = {
"专业版限价单(maker)": dict(trade_fee_pct=0.004),
"专业版市价单(taker)": dict(trade_fee_pct=0.008),
"一键买入 + spread": dict(trade_fee_pct=0.01, spread_pct=0.005),
"taker + 借记卡入金": dict(trade_fee_pct=0.012, funding_fee_pct=0.0349),
}
for name, fees in scenarios.items():
cost, pct = all_in_cost(1_000, network_fee=2.0, **fees)
print(f"{name:24} ${cost:>6} ({pct}%)")
按 2026-10-08 公开费率页整理的场景(spread 0.5% 与网络费 $2 为假设值),$1,000 买入并提现的差异是这样的:
| 场景 | 计算 | 总成本 | 占比 |
|---|---|---|---|
| 专业版限价单(maker 0.4%) | 4 + 2 | $6.00 | 0.60% |
| 专业版市价单(taker 0.8%) | 8 + 2 | $10.00 | 1.00% |
| 一键买入(1% + spread 0.5%) | 10 + 5 + 2 | $17.00 | 1.70% |
| taker 1.2% + 借记卡入金 3.49% | 12 + 34.9 + 2 | $48.90 | 4.89% |
结论很清楚:你按哪个按钮、用什么方式入金,可能比选哪家交易所更重要。 一次买入能藏住小差异,每周定投会把每一段费用重复一遍,卖出时再收一次——这也是为什么在自动化之前,先用自己的参数把 all-in 成本跑一遍。
省钱的动作并不复杂:银行转账而不是卡(ACH / Interac);用专业版限价单拿 maker 费率;合并提现(网络费按笔收,一次大额优于多次小额);避免没必要的货币换算;先小额试转、确认网络;并且把费用记录下来——报税时它们影响成本基础。
二、Gas 假设更新:2026 年不是 2021 年
第二半成本在链上,而且很多代码还活在过去。@swapcoreexchange 的判断很直接:2026 年标准 gas 大多远低于 1 gwei,ERC-20 转账只要几美分。这不只让旧代码"贵",还让一部分旧代码错。
1. 停止硬编码 gas 价格与最低入金额
如果你的代码库还有这样的常量,它可能已经高估了两个数量级:
# 写于另一个费率时代
PAYOUT_GAS_PRICE_GWEI = 40
MIN_DEPOSIT_USDT = 50
伦敦升级后费率是 EIP-1559:协议设定的 base fee + 优先费(tip),两者都应该在出款的当时从实时数据估算:
def estimate_fees(w3, priority_percentile=50):
hist = w3.eth.fee_history(10, "latest", [priority_percentile])
base_fee = hist["baseFeePerGas"][-1] # 下一个区块的 base fee
tips = [r[0] for r in hist["reward"]]
tip = sorted(tips)[len(tips) // 2]
# 给 base fee 留几个区块的上涨空间
max_fee = base_fee * 2 + tip
return {"maxFeePerGas": max_fee, "maxPriorityFeePerGas": tip}
最低入金额也应该由实时估算推导,而不是固定值:按 40 gwei 设定的门槛会把今天本可盈利服务的用户挡在门外;按 0.2 gwei 设定的门槛,下次网络拥堵时又会算错。每次都重新算。
2. 检测内部交易,而不只是顶层转账
最简单的入账监听只看:每块里 to 是充值地址、value 非零的交易。这会漏掉从智能合约钱包发出的 ETH——比如 Safe 多签通过合约调用执行转账,链上表现为内部调用,不是一笔发到你地址的顶层交易,天真的监听器永远看不到。
两种可靠做法:
- 追踪调用:用
debug_traceTransaction或trace_block(取决于服务商),在任何调用深度上寻找发往你地址的 value 转移; - 盯余额:每个区块轮询充值地址的
eth_getBalance,把任何增长与已知交易对账——更粗,但不会漏。
Pectra 升级引入的 EIP-7702 允许普通账户委托给合约代码,随着采用率上升,"通过合约执行到达的 ETH 充值"会变成常态;ERC-20 没有这个问题,因为不管谁调用代币合约,都会发出 Transfer 事件——监听日志即可。
3. 用最终性(finality),而不只是确认数
以太坊每 12 秒一个块;PoS 还多了一个信号:最终性。一个区块在两个 epoch 后(约 13 分钟)被 finalize,回滚它需要销毁大量质押 ETH。很多服务还在用 PoW 时代留下的确认数习惯,其实可以直接用 JSON-RPC 的区块标签:
latest = w3.eth.get_block("latest")
safe = w3.eth.get_block("safe") # 已被证明,极不可能重组
finalized = w3.eth.get_block("finalized") # 经济上最终
def is_credited(deposit_block, amount_usd):
threshold = safe if amount_usd < 10_000 else finalized
return deposit_block <= threshold["number"]
safe 通常落后链头几分钟,finalized 约 13 分钟。按金额分层:小额用户拿到快速入账,大额存款等经济最终性。
4. 把 L2 上的 ETH 当另一种资产
主网、Arbitrum、Base、Optimism 上的 ETH 用完全相同的 0x 地址:你为主网生成的充值地址,在每条 L2 上都是字节级合法的。用户会把 L2 的 ETH 发到主网充值地址,而主网监听器看不到。两个动作:
- 把所有地方(报价、订单、UI)的资产建模成
(chain_id, asset)对,"Base 上的 ETH"永远不要和"主网 ETH"混为一谈; - 可选地在主要 L2 上也监听你的充值地址——不自动入账,但可以主动标记给客服,而不是等用户几天后报告"资金丢失"。
5. 提醒"油箱见底"问题
用户把全部 ETH 换成 USDT,就会得到一堆无法转走的代币——因为没有 ETH 付 gas。如果你的界面提供 ETH 的"Max"按钮,考虑默认留一小笔储备、允许显式覆盖:
const RESERVE_WEI = estimateReserve(liveFees, 5); // 约 5 次未来转账
const maxSendable = balance - gasForThisTx - RESERVE_WEI;
在当前费率下储备很小,但重点是它不为零。
6. 充值流程永远不要请求授权
充值地址模型就是一笔普通转账,不需要 approve、不需要 permit、不需要签名。如果这是你的产品形态,就把这句话明确写进 UI 文案:用户已经学到"正规的兑换有时会要授权"(基于 router 的 DEX 兑换确实会),你明确说"我们永远不会",就等于给用户一个识别仿冒站的方法。
Gas 时代的核验清单:费率用 EIP-1559 实时估算、最低额每次报价推导、入账检测覆盖内部交易、按 safe / finalized 分层入账、资产按 chain ID 建键、Max 默认留 gas 储备、充值流程绝不要求授权或签名。
三、为什么 DEX 的报价和 CEX 不一样
这也是"到手价格"的一部分。同一个代币,CEX 显示 $100、DEX 显示 $101——不是谁错了,是两个市场的价格形成机制不同。
- CEX 来自订单:买单 $100、$99.80、$99.50,卖单 $100.20、$100.50、$101,价格由买卖盘和成交推动;
- DEX 来自流动性:经典恒定乘积 AMM 满足
x × y = k,一笔交易取出一种资产、放入另一种,储备比例改变,下一笔的价格随之改变。DEX 里没有"一个固定价格",报价取决于池子、当前储备和这笔交易的大小。
套利会把价格拉回去,但不需要也不应该分秒相等:手续费、交易规模、网络状况、流动性都会留下差价。
流动性深度决定"这一笔会不会把价格打飞":两个池子都标价约 $100,第一个有数百万美元流动性,第二个只有一点点——一笔 $20,000 的交易在第一个池子几乎不动价格,在第二个池子会显著移动。所以只看显示的代币价格,不足以知道你会拿到什么价格。 Chainlink 的文档也警告:低流动性、低交易量的资产更难定价,也更暴露于价格操纵。
集中流动性又改变了图景:LP 可以把资金放在选定价格区间内,价格在区间内时仓位参与交易并赚手续费,价格离开区间后仓位失活。两个 DEX 的 TVL 相近,在某个具体价格上的交易条件可能完全不同。此外还有第三个价格:预言机参考价(例如 Chainlink 聚合多数据源)——于是你同时能看到 DEX 池价、CEX 价、预言机价,它们不一样是正常的。
大额兑换前该看的清单:你实际会收到多少、池子可用流动性、预期滑点、交易手续费,以及"交易在等待确认期间市场是否已经移动"。跨链交易还要考虑两条链的流动性状态不同。
四、精度也是成本:6 位和 18 位的 USDT
最后一个容易被忽视的成本来源:同一个符号在不同部署里的精度不同。USDT 在有些链上是 6 位小数,在另一些部署里是 18 位。以符号为键的金额类型,会把 6 位余额和 18 位余额直接相加而不报错,然后返回一个错了 10¹² 倍、但符号完全正确的数字。
这不是"舍入误差",是两种方向都昂贵的 bug:
- 用 6 位精度读一个 18 位的整数,金额放大 10¹² 倍——余额检查通过、付款发出,金库先于用户发现问题;
- 用 18 位精度读 6 位整数,同一笔金额塌缩成尘埃、在屏幕上四舍五入为零,客服一小时内就会发现问题。
两边运算都是精确的,错的只是代码假设的小数位数。所以 cryptomoney 这类库把资产定义为 (symbol, decimals) 对,等号比较两个字段,任何二元运算前先 _require_same_asset;6 位和 18 位的 USDT 混合相加会抛出 CurrencyMismatch——报错信息读起来很怪:"USDT 和 USDT 是不同的资产",但这正是工程师需要在堆栈里看到的那句话:符号相同,精度不同。
两个顺带的工程结论:
- 注册表按部署划分:一个全局 registry 结构上装不下同一符号的两个定义。把 registry 当部署配置、在每个把文本/整数变成金额的调用点显式传入;
register遇到冲突要响亮地失败,而不是合并出一个平均值; - 跨精度是换算,要写明余数:18 位转 6 位会产生一段没有着落的
dust(例如 901234567890 个基本单位)——把它当一笔有名字的账:向下取整归平台、下次转账补上,或直接拒绝低于目标精度的金额。把它埋进总额里,就再也分不清是坏汇率还是漏收的费用了。
一句话:符号给人看,数对(symbol, decimals)给算术用。
五、把真实成本放在一张表里
算清一笔交易
- [ ] 列出四段费用:入金 / 交易或 spread / 换算 / 提现;
- [ ] 市价单按 taker 算、限价单按 maker 算,部分成交要做好两头费率;
- [ ] 用实时 EIP-1559 数据估 gas,不引用任何硬编码 gwei;
- [ ] 把滑点与池子深度也算进去——报价不是成交价;
- [ ] 检查精度:资产按
(chain_id, symbol, decimals)建键,跨精度换算的 dust 要有归宿; - [ ] 记录数据来源与抓取时间(费率页、gas、池子储备),方便复盘。
校验一笔入账 / 出款
- [ ] 内部交易(Safe、合约钱包、EIP-7702)是否被监听?
- [ ] 入账用的是
safe/finalized分层,而不是"约等于几次确认"? - [ ] L2 误充主网地址能否被主动发现并转客服?
- [ ] 提现后用户是否还有 gas 余额?"Max"是否留了储备?
- [ ] 充值流程有没有多余地要求 approval / permit / 签名?
从今天开始
- [ ] 垃圾数据不如没有数据:把上面每个数字连同来源和时间写进一行日志;
- [ ] 定期复跑——gas 会变、费率会变、spread 会变,2021 年的假设今天可能错两个数量级。
相关工具:需要发币/多链部署用 TokenDIY;检查并撤销不需要的授权用 Revoke(仅 http)。
相关阅读
- 稳定币支付现实核查 2026:数字落地的真实进度
- 链上核验清单:收据、钱包与授权,别只看表面
- AI 回测的"未来函数":为什么回测很美、实盘就崩
- 专题:量化交易 / 策略与市场结构完全指南
- 专题:交易平台 / DEX / 永续与预测市场完全指南
本系列另两篇(三站相关系列 · 成本体检)
- Agent 成本体检:92,413 个 token 的启动账(AI 应用的账:上下文预算、30 token 毁掉缓存、压缩只值 2%)
- CAD 工作站别为 64 核买单:配置实测与 AI 3D 账单(3D 工作流的账:单核优先、20–35 倍内存规则、一次 AI 建模会话 $22.13)
来源
- @nutshellcrypto, Your Crypto Buy Has Four Fees, Not One: Modeling the All-In Cost in Python(dev.to,2026-10-08;费率取自公开费率页 2026-10-08,spread 0.5% 与网络费 $2 为假设值;AI 辅助撰写):https://dev.to/nutshellcrypto/your-crypto-buy-has-four-fees-not-one-modeling-the-all-in-cost-in-python-2mn2
- @swapcoreexchange, Your Ethereum Gas Assumptions Are Probably From 2021(dev.to,2026-10-14;作者任职于 SwapCore,文内披露):https://dev.to/swapcoreexchange/your-ethereum-gas-assumptions-are-probably-from-2021-d64
- @katie_hargreaves, Why DEX Prices Can Differ From Centralized Exchange Prices(dev.to,2026-10-08;内容推广 Dexlyn,机制讲解为主):https://dev.to/katie_hargreaves/why-dex-prices-can-differ-from-centralized-exchange-prices-427b
- @polycratia, A ticker is not an asset: USDT at six decimals and USDT at eighteen(dev.to,2026-10-10;作者为开源库
cryptomoney作者):https://dev.to/polycratia/a-ticker-is-not-an-asset-usdt-at-six-decimals-and-usdt-at-eighteen-30n2
本文由 DefiPlot 翻译整理,转载请注明出处。
免责声明
本文仅用于学习与研究目的,不构成任何投资建议、财务建议或交易建议。文中费率、gas 与流动性数据来自原文作者引用的公开页面及其自述口径(部分为假设值),未做独立复测;相关作者与所在机构均带有各自立场(已在文内披露)。加密资产与链上交互存在合约漏洞、精度错误、网络拥堵、滑点与钓鱼风险;请勿使用无法承受损失的资金参与,任何操作前请自行核对官方费率页与链上数据并 DYOR(Do Your Own Research)。
本文属于「量化交易」专题。查看专题全部文章 →
免责声明:本站资源仅用于学习目的,也不应被视为投资建议,读者在采取任何行动之前应自行研究并对自己的决定承担全部责任。