小木最近在复盘高频做市项目,把近 140 项实验记录重新梳理了一遍。其中有一项修改特别值得回头审视:让减仓单保留原有的队列位置,同时不改动加仓规则。结果退出确实更顺畅了,残余库存也减少了,可账户收益反而下降了。
另一项实验刚好相反。我们把相关持仓纳入共同的风险限额,原本担心成交会因此变少,结果成交次数反而增加了。
这些对照实验逼着我们重新追问:之前的判断到底漏掉了什么?队列位置有价值,减少库存也有意义,但在拿到这些好处的同时,成交条件和后续持仓结构可能已经悄悄变了。
1|保住队列,收益反而可能下降
成交偏少的时候,第一反应通常是参与范围太窄,或者所选市场不够活跃。扩大报价覆盖之后,成交确实上来了,收益却没有稳定改善。逐市场排查也发现,活跃度并不对应更好的经济结果;拿过去的做市收益去挑市场,同样没能稳定奏效。
除了市场选择,我们也检查了订单处理方式。减仓单既然已经排了一段时间,继续保留队列位置,看起来比撤单重挂更划算。
这个想法本身没错。但保住队列的同时,也保住了一个旧价格。行情一旦变化,这个价格未必还适合当前持仓;等它真正成交时,市场状态和账户的减仓需求可能已经对不上了。
延长入场订单的存活时间(TTL)给了更鲜明的结果。按当时严格的穿价(Strict trade-through)回放口径,完整往返从 323 次增加到 859 次,全策略亏损却扩大到原来的三倍多。
旧买价可能等来正常的卖出需求,也可能等到市场整体下移之后,对手才愿意接单。逆向选择在这里表现为成交条件的改变,而不只是一项事后扣除的成本。当然,这组实验的总收益差额还混着持仓与再入场的影响,不能全算到新增成交头上。
提前撤单也没有自动解决问题。盘口支撑恶化的信号,撤掉了一堆原本有报价价值的单子;另一些信号虽然能识别风险,却留不下足够的撤单时间。
等待与撤单,本质上都在选择对手流。我们很难只改善成交效率,而不改变最终参与的那批交易。
2|库存退出得更快,亏损却可能扩大
多轮回放之后我们发现,成交价相对内部估计的公允价确实有优势,execution edge 为正。但未闭合库存在持有期间产生了不利的估值变化——负 carry,把这部分优势直接抵消了。
损失主要发生在持有期间,那缩短持仓时间,看起来比继续折腾入场更直接。固定时间后的 Taker 减仓确实减少了残余库存,也提高了周转速度,但完整策略收益反而恶化了。
“平仓兑现了浮亏”并不能解释这个结果。亏损不会因为不卖就自动消失。真正要比的是退出与继续持有两条路:主动退出要付多少执行成本,继续持有会经历怎样的价格变化,以及退出释放出来的额度,接下来又会拿去做什么新交易。
这组实验里,原有库存退出去之后,新成交又形成了新的不利库存。我们缩短了一批持仓的停留时间,却加快了重新承担相似风险的过程。
不过,另一套广覆盖报价策略上,动态 Taker 救援又明显收窄了亏损,只是还没到盈利。两组策略的入场和退出设置不同,不能直接比绝对收益,也不能凭一次结果就断定所有主动退出都有用或者都没用。
双路被动退出也曾有效减少损失。两条替代路线通过 OCO(one-cancels-other)协调,一条成交后取消或削减另一条,增加了完成往返的机会。剩下的问题是收益仍然为负,循环集中在少数时段,而不是退出改进本身毫无作用。
3|收紧风险,成交反而可能增加
每个合约只拿少量仓位,看着很克制。但不同的交易代码背后,可能指向同一个经济结果。逐合约限额都没有突破,账户仍有可能在重复累积同一种敞口。
于是我们把这些持仓放进共同的风险预算里。在一组开发样本中,相关风险受到约束后,收益改善了,成交笔数还从 119 次增加到 172 次。
这个结果不能简单理解成从原来的成交清单里删掉几笔。少接受某些相关交易,会改变后续的库存占用,也会改变哪些时点还有额度可用。限制的是某一时刻的暴露,最后统计的却是一段时间内不断发生的交易。
但这也不意味着限额越紧越好。在进一步压缩总持仓的实验中,持有期损失虽然减少了,部分时间稳定性指标也变好了,总收益却降了,未闭合库存的占比反而上升。把同样的风控逻辑放进另一份历史样本,原来的收益改善也没能复现。
所以风险管理不只是调一个阈值。哪些仓位应该放在一起计量,额度该留给哪些交易,为了降低暴露愿意放弃多少收益,这些都得分开判断。
更准确地描述敞口,并没有替我们决定最合适的预算。降低风险本身可以有价值,但这份保护的成本也该算清楚。
4|成交价格更好,持仓损益却可能更差
直接拒单会放弃机会,那对风险较高的订单挂远一点,让对手多付点补偿,是不是更合适?
在一组报价外移实验中,execution 项确实提高了,carry 却进一步恶化,最终收益还是不如基线。更低的买价看似提供了保护,可它可能只有在更强的卖压下才会成交。
我们以为只是在原来的交易上多收一点价差,实际接到的已经是另一组交易。
另一组 risk charge 测试几乎没收到预期的补偿:最低档位已经让目标合约组基本不成交了,继续提高补偿要求也没带来不同结果。我们算出了自己愿意承担风险的价格,市场并不接受。实际效果更接近直接停止参与这些交易。
结构相对价值(relative value)也遇到类似的落差。合约关系有依据,纸面价差存在,对冲腿有深度,Maker 入场腿却几乎不成交。另一个针对即时对冲的检查,即便采用零延迟的乐观设定,也没有拿到完整交易。
有利的报价与可获得的收益之间,还隔着实际成交这一层。对于这类机会,首先要确认整笔交易能不能完成,再去讨论执行速度能带来多少增量价值。
5|以 Maker 成交,利润却未必来自价差
我们还试过改变库存形成的顺序。
在一类具有固定组合兑付关系的合约上,先持有完整抵押组合,再以 Maker 卖出其中一腿,随后买回或通过替代路线恢复组合。这样就不必先在市场上买入单边库存,看起来绕开了原来的风险来源。
但第一腿卖出之后,完整组合就被拆开了。先买后跌的风险,变成了先卖后无法以合适价格补回的风险。
开发样本上的结果一度很好。但从当时的损益归因看,主要贡献来自有利的 carry;换到另一份历史样本,相同交易方案却没能复现这份收益。进一步让资本在闭环后重新投入,循环增加了,也反复形成了新的单边敞口。
资本重新可用,解决的是能否继续交易的问题,并没有回答下一轮值不值得做。
这也逼着我们重新审视利润的来源。Maker 身份只说明了成交方式,没说利润从哪里来。一段有利的库存变化,可能让策略暂时显得比报价机制本身更有效。
另有一轮总收益为正的回放,在剔除最大贡献市场或最大贡献小时后,就转成亏损了。这当然不意味着可以事后挑出最赚钱的市场来交易,但确实提醒我们:看起来不错的总收益,可能主要由少数行情片段撑起来。
交易收益与额外激励也必须分开。即使按当时乐观假设计入奖励,总收益提高了,盈利时段仍未达到原先要求。补贴改善了总账,却没有让那套策略的收益变得足够稳定。
价差、库存变化和激励,都能进入同一张收益表,但它们不是同一种收入。如果分不清来源,就很容易把有利的持仓方向当成报价能力,或者把补贴带来的收益当成交易机制的进步。
6|模型预测更准,账户反而更难闭环
订单之间的关系,促使我们尝试改变决策方式。原先各市场分别生成订单再逐笔筛选,后来我们把相关合约放在一起比较,看哪些订单值得同时保留。除了单笔预期收益,还要考虑它们怎样共用额度、能否相互配对。
在这类组合研究中,最初使用的模型概率预测表现不理想。改进之后,模型的排序指标和衡量概率预测误差的 Brier score 都达到了当时设定的要求。但接入完整策略后,收益仍低于基线,残余库存占比还上升了。
拆开损益发现,持有期损失确实减少了,但策略也放弃了更多有利成交,连有助于完成配对的订单都被一起过滤掉了。
判断未来价格会怎么走,与判断一张订单该不该做,并不是同一个问题。一笔交易单独看可能不值得接受,放进已有持仓里,却可能帮助完成对冲或恢复组合。删掉它,省下了眼前的交易成本,也可能让另一部分库存失去退出机会。
早期统一毒性惩罚就曾误伤减少库存的 SELL。同一个信号,用于 risk-add 和 risk-reduce 订单时,不能默认具备相同的经济意义。
另一组实验中,按独立候选估算的收益原本为正;放进完整策略回放(Planner-complete replay)后却转为亏损。候选各自可取,不代表可以按原计划同时执行:第一笔成交触发限额后,第二笔可能被取消,原本设想的配对就完不成了。
因此,模型需要回答的,不只是这笔交易单独看是否有利,还包括接受或拒绝它,会给已有持仓和后续交易带来什么变化。
7|模型和规则变了,交易路径却可能不变
我们也尝试根据当前策略在回放中经历的状态重新训练模型,也就是 on-policy 重训。结果模型确实变了,成交与收益却逐项相同。预测分数有所变化,最终的报价和下单选择却没有动。
类似情况也会出现在报价和风控上。报价参数调整了,最终价格仍可能落在同一个价位;新规则要拦住的订单,可能早已被现有风险限额排除在外。
所以评价一次更新是否有效,先要看它到底改变了哪些订单。如果实际交易没有变化,就不能仅凭这次结果断言某种定价或风控思路本身无效。
对于确实改变动作的方案,我们在固定历史行情与执行假设下,采用同状态分叉(same-state fork):从相同的现金、库存、活动订单和行情位置出发,只改变一项决策,再让两种方案分别运行下去。
在一次执行模型对照中,我们曾怀疑最终收益较差的那条回放路径,是不是一开始就多接了坏单。于是只比较分叉前账户状态相同的首次额外成交。结果发现,这些成交的五秒 markout——也就是成交后五秒的短期价值变化——合计反而是正的。
这不能保证这些交易最终有利,但不支持把较差的最终结果直接归因于首次成交的短期逆选。短期表现尚可,损失仍可能在之后的持仓与交易中逐渐形成。
也正因如此,反事实不能简化成事后删除亏损单。第一笔交易不再发生,后面的余额、库存和报价就必须重新算。当两种方案已经持有不同库存时,再把后续成交强行一一对应,也分不清差异到底来自哪里。
Oracle test 允许事后使用未来信息,可以帮助检查既定约束下有没有值得选择的交易。但事后能挑出来,不代表当时就能识别。观察窗口结束时仍未闭合的交易,也不能一律按零收益处理;如果只看已经完成的往返,就可能把最难退出的库存排除在评价之外。
当看到某个指标变好之后,还得继续往下看:为了这份改善,账户放弃了哪些机会,接受了哪些成交,最后又留下了什么持仓?
我们改动的可能只是一张订单,承担后果的却是一个会继续交易的账户。这些实验记录,在云栈社区上也有不少同行的复盘与讨论,值得翻一翻别人的拆解思路。