股票回测费用对账:最低佣金、卖出税费与滑点分别落在哪
股票交易费用对账应从实际执行数量和模拟使用的成交价开始。先由成交价计算名义金额,再应用佣金和方向性收费,最后逐笔桥接现金。如果滑点已经改变成交价格,就不能再把同一滑点金额作为现金费用扣一次,否则账面净损益会被重复压低。
本指南交付一笔完整往返,包含最低佣金、虚构卖出方向收费和不利价格滑点。所有费率都是教学假设,不是现行券商报价,也不是历史税制。样例只核对会计机制;CoTrading费用核账功能仍为规划或待核实,没有运行真实账户和实际交易引擎。
先冻结费用模型再计算
记录市场、账户币种、费用模型版本、生效期、佣金比例、每单最低佣金、方向收费、滑点定义和舍入规则。参考代码中的默认值不能证明今天适用。研究真实交易时,需要对照券商自己的费表及相应市场带日期的规则,不凭一段代码推断完整制度。
每个参数写明单位。千分之一作为乘数是零点零零一,最低一个货币单位是金额,不是比例。小订单可能由最低值主导,大订单由比例主导。还要区分每张订单最低、每日最低与每笔撮合最低;拆单在不同约定下会改变费用,不能把它们统称成同一种最低佣金。
样例初始资金二千,交易一百股。佣金取一与成交名义金额乘零点零零零三中的较大者;卖出方向收费为卖出名义金额乘零点零零一。滑点对买卖都不利,幅度为原参考价的千分之一,买价提高、卖价降低。为让算术透明,计算保留完整十进制精度,展示前不默认为某券商的分位舍入。
买入从含滑点价格开始
原买入参考价十,乘一点零零一后,执行价为十点零一零。一百股名义金额一千零一,比例佣金零点三零零三,小于最低一,因此收一。买入侧不收本例的卖出方向费。方向判定应在费用计算中明确,不能统一对所有名义金额加同样税率。
买入现金变化为负一千零一再减一,合计负一千零二。剩余现金九百九十八,持仓一百股。流水分列参考价、执行价、数量、名义金额、各收费和现金净变化。若只保留一个“总成本”,重复滑点或混用买卖方向会很难定位,也无法独立复算。
参考买入金额一千与执行金额一千零一之间的差一,就是入场滑点,已经包含在一千零二的现金流出中。分析时单独记录这一个单位很有帮助,但不能再从现金扣一,否则账户就不再与自己声明的执行价一致。记录成本归因和实际扣款是两件事。
卖出与方向性收费
原卖出参考价十点五,乘零点九九九,执行价十点四八九五,一百股名义金额一千零四十八点九五。比例佣金仍小于一,因此最低一再次触发。虚构卖出方向费为一点零四八九五,基数使用含滑点卖出名义金额,而不是买入金额或原参考收入。
卖出净现金流入为一千零四十八点九五减一再减一点零四八九五,等于一千零四十六点九零一零五。加原有九百九十八,得到二千零四十四点九零一零五。持仓归零,终值等于现金,往返净盈利四十四点九零一零五,所有差额都有对应来源。
不能因为公式方便,就把这项卖出费复制到其他市场或日期。A股、港股和美股需要各自真实假设;美股某模型佣金为零,也不意味着价差、滑点及其他所有成本都零。本文不声明任何市场的现行费用水平,也不把教学数值写成外部事实。
用第二条独立利润桥复核
若按原参考价,不计任何成本,价格盈利为五十,即一百乘十点五与十的差。入场滑点一;退出滑点为原卖出一千零五十与执行收入一千零四十八点九五的差一点零五。滑点合计二点零五,两个方向都对账户不利,没有把卖出滑点符号反过来。
五十减二点零五、减两次佣金二、再减卖出费一点零四八九五,等于四十四点九零一零五,与现金桥完全一致。这项检查从参考价利润开始,与从实际现金流开始的方法不同,因此不是简单复述同一个汇总数字,而是解释毛收益到净收益的全部差额。
反例是在已经净化的四十四点九零一零五上再次减滑点二点零五,得到四十二点八五一零五。错误原因是执行买卖价已经吸收了滑点。另一个边界把买入数量扩大到一万股,此时比例佣金超过最低值;模型必须支持这个分支,不能因小额样例每次收一,就把所有订单佣金硬编码为一。
十进制算术也要有规则
Python十进制官方文档说明了十进制表示以及可选精度和舍入。本例从字符串构造金额,避免无意引入二进制浮点近似。采用十进制并不会自动决定经济上正确的舍入方式,仍需声明每项费用何时、按什么粒度取整,以及累计顺序。
完整标准库文件是docs/growth/research/round-5/examples/R600.py,核心现金公式如下:
commission = max(minimum, notional * commission_rate)
sell_charge = notional * sell_rate if side == 'sell' else Decimal(0)
cash_delta = (-notional - commission if side == 'buy'
else notional - commission - sell_charge)
运行python docs/growth/research/round-5/examples/R600.py。断言核对两个执行价、最低佣金、买入侧方向费为零、卖出费金额、精确期末现金、两条利润桥相等,以及重复滑点会产生错误结果。同时执行大订单比例佣金分支,保证不是只测试最低收费这一条路径。
对真实引擎逐字段核账
真实运行把每笔成交连接到费用模型版本,独立重算名义金额。查看引擎价格是否已含滑点、佣金是正金额还是带方向符号、税费是否单列。名为费用的字段可能已经包含多个分项,没核对定义前不能把这些分项再加一次。字段名相似不意味着口径相同。
Investor.gov订单参考可帮助区分观察参考价与实际执行条件。若引擎提供未成交和部分成交数量,也要保留。未成交目标不应凭空产生按已成交名义金额计算的费用,除非所选模型明确另有订单层收费,并且该收费已有规则依据。
完成条件有三项:初始现金加全部带符号现金变化等于期末现金;期末股数等于期初加执行买入减执行卖出;已实现损益与参考价独立利润桥一致。差额未解释前,先不比较策略收益。费表、撮合约定、拆单或舍入变化后另存版本重跑,保留旧配置与结果,才能把成本变化和信号变化分别归因。
账户中若还有未平仓股,期末现金不等于账户权益,必须另外估值,不能拿这笔已清仓例子的等式直接推广。跨币种现金更需要转换规则。本例有意限定同币种、完整一买一卖,先把最小账闭合;扩展复杂性时逐条增加工件与验收,而不是用一个看似精确的小数替代缺失模型。
下载完整示例,保存为R600.py,使用Python 3.12及以上运行python R600.py。仅使用标准库与合成数据。
明确舍入发生在哪一层
本例保留小于分的金额,是为了暴露公式。真实收费可能每个分项各自取整,也可能每笔总费取整,或汇总后才舍入。名义费率完全相同,现金仍可能不同。所选约定应与费用模型版本放在一起,独立利润桥也使用相同规则。界面只显示两位小数不等于账本已按两位计算,展示格式不能代替经济舍入政策。
遇到差额,依次比较原参考金额、实际执行金额、未舍入分项、最终收费和带符号现金。只发生于舍入的差异,与费率错误或重复滑点分别归类。不要反向调佣金比例,让某一张账单恰好一致。先取得真实计算约定,再重跑小额最低值与大额比例值两条路径,保存原差异及修正输出。
若同一订单分成多个成交,还要核对最低佣金是逐成交应用,还是最后按整张订单合计。本文样例一单一次完成,没有实现该扩展。只有补齐订单编号、成交编号和聚合约定,才可把它用于拆单账。保留这些边界,可以避免把一笔往返准确到很多小数的结果,误说成对所有实际收费方式都已验证。
收费前先验证成交请求。side只能精确为buy或sell,大写BUY直接拒绝,不能误作卖出。数量必须为正整数,布尔值不算整数数量;原价格必须能转换成有限正十进制数。零或负数量、小数数量、零或负价格、非数值、无穷及错误价格文本均抛出ValueError,下载文件逐项断言。零股目标不是成交,不能经过这个函数收取最低佣金。
常见问题
这些费率能用于我的券商吗?
不能直接使用,它们是虚构教学参数。真实研究需要适用日期的实际费表。
滑点一定是额外费用吗?
本例通过执行价体现滑点,单列只是归因,实际只扣一次。