周二早上,我们的原油期货趋势策略持有即将到期的合约多头。信号完全按照我们的要求运行:只要趋势保持向上,就继续持仓。回测显示小幅盈利。模拟账户里却出现了一笔卖出、一笔买入下一份合约的订单、两笔费用,而且买入价比图表暗示的更差。
信号本身没有发生什么神秘变化。回测把“期货价格”当成了一个连续不变的东西。账户却必须在两个不同的订单簿里,以两个不同的价格平掉一份合约,再开仓另一份。
我们在复核一个跨越移仓窗口的头寸时发现了这个差异。策略使用流动性较高的近月合约,并持有至日历规则将头寸转到下个月。这个规则本身合理,围绕它的核算却不合理。
我们的连续合约序列经过后复权处理,以消除合约之间的跳空。这让图表适合用来计算趋势,但也意味着序列里不存在一个真实价格,能让我们平掉旧合约或买入新合约。回测用复权后的价位计算移仓期间的交易盈亏,之后又仿佛头寸只是自然延续了一样继续交易。
连续合约序列可以作为衡量信号的有用工具,但用来代表成交价就不真实了。移仓包含两笔交易,合约间的价差属于市场结构的一部分,不能当作无关紧要的跳空抹平。
我们用两份挂牌合约重建了这次交易。旧月份合约的买价为 79.98,卖价为 80.00。下个月份合约的买价为 80.42,卖价为 80.45。我们的多头头寸必须按旧合约买价卖出,再按新合约卖价买入。按这两个可执行方向计算,日历价差为 44 美分。
这并不意味着我们必然亏损 44 美分。两份合约对应不同的交割月份,价差反映了持有成本、仓储、融资和供应预期。头寸在移仓后仍会承受新合约价格走势的影响;它并非要把报价价差全额当作费用支付。但策略必须实现旧合约的退出,并建立新合约的入场。如果收益计算把这两个价格当成同一种工具来比较,就可能凭空制造或抹去 P&L。
我们在回放中分开处理了这两项工作。连续合约序列仍用于生成趋势信号。标记价格和订单则使用具体合约的价格。移仓时,模拟器先为即将到期的月份发送平仓单,再为下个月份发送开仓单,分别计入各合约的费用,并采用各自订单簿的价差和可用深度。
第一次回放仍与模拟账户不一致。我们把两笔成交都同步到了同一时间戳,但订单其实是依次提交的。旧月份合约的平仓单成交时,下一份合约的卖价已经变动,买单到达订单簿时价格已不同。交易场所支持时,可以把移仓作为日历价差交易执行;也可以拆成两条交易腿,中间会有一段风险敞口。这是两种不同的执行假设。我们用模拟账户的实际下单顺序进行比较。
之后我们发现,日历规则本身也没有定义清楚。“到期前 5 天移仓”听起来很明确,但遇上假日导致交易日历变化、流动性提前转移,或合约采用不同到期惯例时,规则就不再那么简单了。我们在一处按日历日计数,另一处却按交易日计数。模拟策略在最后交易日前第 5 个交易日移仓;回测则在最后交易日前 5 个日历日移仓。
移仓日期是一项交易规则。应明确写出交易所日历和触发事件,否则回测与账户迟早会对持有哪份合约产生分歧。
我们修改了移仓指令,明确指定合约的最后交易日,按其所属交易所的日历计算交易日,并要求移仓前通过最低流动性检查。这个阈值是实用的保护措施,并不保证成交价会更好:成交量和未平仓量都无法准确反映我们的订单实际能否成交。我们记录了选定的合约和两条交易腿的价格,让每次回放都能说明这次交接的过程。
完成这些修改后,策略的历史收益率下降了。差异并非来自某一次巨额移仓亏损,而是由许多细小修正累积而成:使用正确的合约、正确的交易日、为两条交易腿计入实际费用,以及按照各自订单簿的价格计算平仓和开仓。有一次流动性较差的移仓尤其昂贵,所以我们保留了它的记录,没有把它平均掉。
如果你的期货回测使用连续合约图表,不妨问问这张图表能承担什么作用。它可以为信号提供一致的输入,却不能代替一份你本可以实际买入或卖出的合约。对于每个跨越到期日的头寸,研究记录都应注明旧合约、新合约、移仓触发条件、执行模型,以及由此产生的两笔现金流。
那笔周二的交易并非一次多么复杂的失败。它只是在图表上看起来盈利,却跳过了账户必须改变持仓的那个时刻。把这一刻补回回放后,结果没那么好看,却实用得多。
← 全部文章


