为什么一定要把「真实成本」纳入回测?
回测不计成本,胜率和年化都可能是“纸面繁荣”。交易落地后会出现三类主要成本:
1)交易所收费(现货/合约费用、折扣与阶梯费率);
2)交易执行偏差(滑点/市场冲击);
3)衍生成本(永续合约资金费率、借贷利息、清算/强平相关费用等)。
这些变量具有“随时间与账户等级动态变化”的特征,必须以事件驱动地注入回测引擎,而非一个固定常数。
手续费:别把“0.1%”当常量
费率不是单一数,会受 VIP 等级、是否用平台币抵扣(如 BNB)与合约/现货品类影响。以 Binance 为例:官方公布的现货/保证金交易 VIP 阶梯表清楚列出不同级别的 maker/taker 费率区间,并且开启“用 BNB 支付”可享**现货 25%与合约 10%**的费率折扣(以官方政策为准)。
不同平台阶梯与折扣不一,甚至存在 maker 负费(回扣)的情况(高阶 VIP,或阶段性活动)。OKX 的官方学习文档与费率示例就提示高等级可能出现负 maker 费。
回测实现建议
- 维护一份“费率曲线”而非常数:按日期段 × 账户等级 × 是否用平台币支付进行查表;回测时依据交易时间戳选择当期费率。
- 模拟“以何种资产扣费”:有的平台允许选择以报价币扣费或获得币扣费,OKX 2025/09 起支持在现货/保证金中选择扣费币种,这会改变仓位现金流路径。
最低(最小额/步长)与精度:订单能不能下得去?
即便费率正确,如果你下单的数量/价格不符合交易所交易规则过滤器,现实中会被拒单。核心限制包括:
- LOT_SIZE / MARKET_LOT_SIZE:数量最小值、最大值与步长(stepSize);
- PRICE_FILTER:价格最小/最大与最小跳动(tickSize);
- MIN_NOTIONAL / NOTIONAL:最小名义金额。
Binance 的开发者文档与 US 站 API 文档均明示上述过滤器的结构与错误提示(如 “Filter failure: LOT_SIZE/PRICE_FILTER/MIN_NOTIONAL”)。
回测实现建议
- 下单前按当时的 symbol 过滤器做向下取整(价格按 tickSize、数量按 stepSize),并校验名义金额不低于最小值,否则模拟“未成交/被拒”。
- 交易所会不定期调整 tick/step 精度(影响可成交性与滑点)。把精度变更作为事件纳入回测(例如 Binance 公告频繁调整 tick/step)。
滑点与市场冲击:从“价差假设”到“订单簿重放”
定义与度量
滑点是期望成交价与实际成交价的差异,属于交易成本的一部分。一个标准的总体度量是Implementation Shortfall(执行偏离):以“决策价”到“执行价”的收益差衡量综合成本(含费用、时延、冲击与机会成本)。
建模层次
1)固定基点/价差模型:对流动性好的主流币对、且小额下单时可用,但易低估在快市与大单下的冲击。
2)盘口深度模型(L2 回放):用当刻的 order book 深度逐层吃单,得到逐笔成交均价与量加权滑点。Binance 提供 futures/spot 的 depth 与 bookTicker 接口用于构建本地簿与回放。
3)冲击函数模型:大量实证支持平方根冲击律(订单规模相对市场成交量的平方根缩放)。对比比特币与其他资产的研究均观察到类似规律,可用于“bar 级别回测”在缺失 L2 时的近似。
数据建议
- tick/成交数据与盘口:可用 Binance 公共数据存档(S3)获取历史 aggTrades/深度文件。
- 实时回放:结合
GET /fapi/v1/depth
与 WebSocket<symbol>@depth
维护本地簿,离线重放时按撮合规则逐笔撮合。
资金费率(永续合约):别把“每 8 小时”写死
永续合约通过资金费率使合约价向指数价锚定。通常按固定周期结算,常见是每 8 小时结算(00:00/08:00/16:00 UTC),但部分合约或时期会调整为每 4 小时等频率,交易所会公告与提供 API 查询历史与当期费率及结算时点。
Binance 官方近期也公告了结算频率与查询端点(Real-Time Funding、Funding Rate History、以及新增加的 GET /fapi/v1/fundingInfo
),用于确认cap/floor与interval等调整信息。回测时应以实际交易时刻所在区间的费率计入持仓现金流。
OKX 的帮助中心同样给出资金费率机制的定义与更新说明,跨所做多空或搬砖回测时应分别拉取各所历史费率再统一口径。
回测实现建议
- 以**持仓名义价值 × 资金费率 ×(结算周期/8h)**计费;遇到 4h 结算需按公告节奏分摊,不要用 8h 常数。
- 使用官方历史端点批量拉取并缓存:
/fapi/v1/fundingRate
(USDT 本位)与/dapi/v1/fundingRate
(币本位)。
借贷与杠杆成本、清算与 ADL:极端情形的“隐藏账单”
- 杠杆现货/保证金存在借贷利息,应按资金使用时长与阶梯年化折算到回测现金流。
- 清算/强平:实际撮合按**标记价(Mark Price)触发,不是最后成交价;不同所的标记价以指数价与基差移动平均构成。若策略在历史上会被强平,需按各所规则模拟清算流程与可能的费用/滑点,并考虑极端下的ADL(自动减仓)**风险。
DEX 特有成本:Gas、失败交易与滑点容差
在链上做量化或混合路由(CEX+DEX)时,还要把Gas 与 失败交易同样要付费的事实纳入模型;Uniswap 的滑点容差(一般 0.1%~5% 区间)也决定能否成交。
把这些成本“喂”进回测引擎:一份可落地的流程
1)数据对齐与基础设施
- 规则层:按交易时刻查询 symbol 的
PRICE_FILTER / LOT_SIZE / MIN_NOTIONAL
,对订单做精度与最小额校验。 - 费率层:按“日期段 × 账户等级 × 是否用平台币”生成费率时间表(例如 Binance 的 VIP 阶梯与 BNB 折扣);实际成交后根据taker/maker角色应用不同费率。
- 资金费率层:批量拉取历史 funding(USDT 与币本位),按合约的实际结算时点记账。
2)撮合/执行层的三档模型(由简到繁)
- Bar 内均价 + 固定滑点:对高流动性、短线频繁交易不建议长期使用。
- 成交量冲击(平方根):以“订单规模占当期成交量”的平方根函数估计冲击,系数用历史执行回放或文献范围校准。
- 订单簿回放(推荐):用 depth 快照 + 逐层吃单,得到加权平均成交价,再叠加手续费与资金费率,最接近真实执行。
3)度量与回放验证
- 用Implementation Shortfall做统一口径的“含费含滑点”绩效度量;分解为费用、延迟、冲击与机会成本,能快速发现模型低估的环节。
- 在费率/精度/资金费率变更的日期点做前后切片回放,检查收益断点是否来自规则变化而不是策略本身。
示例:单笔交易的全成本计算(思路)
1)下单前:按 tickSize/stepSize 调整委托价与数量,并验证 MIN_NOTIONAL;
2)撮合:
- 如果是市价单 → 按当刻 order book 逐层成交,得到成交均价(VWAP);
- 如果是限价挂单 → 若订单进入簿则计为 maker(可能获得更低费率或回扣),若立即成交则为 taker。
3)手续费:按当期 VIP 档位与是否用平台币扣减(现货 25%、合约 10% 示例,以公告为准);
4)合约仓位:跨越资金费率结算时点的持仓,按名义价值 × 当期费率计费;
5)统计 IS:以“下单决策时的参考价”为基准,汇总价差 + 费率 + 资金费率 + 借贷利息(若有)。
常见疏漏清单(Checklist)
- 忘记平台调整资金费率结算频率/费率上限下限(需基于公告与 API 动态更新)。
- 用了固定“0.1% 手续费”,忽略 VIP 与平台币折扣。
- DEX 回测只算价格不算 Gas,或忽略失败交易也要付费。
- 忽略 LOT_SIZE/MIN_NOTIONAL/PRICE_FILTER 导致“理论上能成交、现实下不去单”。
- 用“最后价”做风控,而实际强平按标记价触发,导致风控与资金费率记账错位。
FAQ(结构化摘要)
Q1:资金费率历史数据从哪儿取?
A:Binance 提供 GET /fapi/v1/fundingRate
(USDT 本位)与 GET /dapi/v1/fundingRate
(币本位);OKX 也提供相应历史端点。回测应以实际结算时间段逐条计入。
Q2:结算频率总是 8 小时吗?
A:不是。有的合约采用 4 小时;交易所会公告并在页面/API 同步。建模时请以公告与接口返回为准。
Q3:没有 L2 深度,滑点怎么估?
A:可用平方根冲击律按“订单规模/当期成交量”的平方根估计冲击,并以历史回放做系数校准。
Q4:为什么我的模拟下单量在真盘里会被拒?
A:多因LOT_SIZE/PRICE_FILTER/MIN_NOTIONAL不合规;请按交易所 exchangeInfo
的过滤器对价格与数量做精度处理与最小名义校验。