把一句人话改成可判定的规则:需求改写的三个硬指标

1 阅读8分钟

把一句人话改成可判定的规则:需求改写的三个硬指标

本文是《Python 量化回测系统工程》系列的第 4 篇。第 3 篇讲的是"目标能不能成立",这一篇讲更靠前的一步:当目标过关之后,怎么把一句人话,变成一段能编码、能判定真假的规则。

一、"我觉得这样能赚"为什么不能用

先看一句最常见的需求:

"放量突破就买,跌破均线就卖。"

这句话在人脑里是有画面的,但交给程序,它一个字都执行不了。哪里不行?三处:

  1. "放量"是多少? 是比昨天大、比前五日均值大、还是比同一时段的历史分位大?三种定义会选出三批完全不同的信号。
  2. "突破"用什么价确认? 盘中触及算不算?必须收盘站上去算不算?差一个条件,胜率能差十几个点。
  3. "跌破均线"是哪条均线、什么周期、按什么价计算? 均线本身还牵扯复权方式和样本起点。

这不是"描述得不够细"的问题,而是这三句话都无法判定真假。无法判定,就无法回测;无法回测,就只能靠感觉;靠感觉的东西,复盘时也没有对错可言——因为你连"当时该不该买"都定义不出来。

所以需求改写不是文档工作,它是把一个不可证伪的想法,变成可证伪的假设。这是整个流程里唯一不可跳过的一步。

二、三个硬指标:可判定、有边界、可计量

我把它固化成三个检查项。任何一条不满足,这条规则就不许进回测。

指标一:可判定(能写成布尔表达式)

把规则写成一句话之后,问自己:这句话能不能改写成 if (条件) then (动作),并且条件和动作都是程序里存在的量?

能,才叫可判定。检验方法很土但有效——把规则念给一个不懂交易的人听,让他按字面意思写成代码,看写出来的和你想的是不是同一件事。

举个真实的改写过程:

原始说法不可判定的原因改写后
放量突破"放量"无定义当日成交量 > 过去 N 日同时段成交量均值 × k(k、N 写进参数表,不写进正文)
强势上涨"强势"是形容词当日收盘价 > 当日开盘价,且当日涨幅 > 过去 M 日涨幅的 P 分位
缩量回调无量化口径当日成交量 < 过去 N 日均量的 q 倍,且当日跌幅在 [0, r] 区间内
走势健康不可判定直接删除,这类需求无法编码

注意右列我没有写任何具体数字。参数值属于策略构造,正文里一个都不能出现——这是这类文章的红线,也是我给自己定的规矩。改写交付的是"结构",不是"答案"。

指标二:有边界(触发条件 + 失效条件,成对出现)

只写"什么情况下买",不写"什么情况下这笔判断作废",是最常见的缺陷。

失效条件至少要覆盖三类:

  • 时间失效:信号产生后 N 根 K 线内没有成交/没有走出预期,算失效。没有这一条,回测里会出现"挂了三天才成交"的幽灵单。
  • 结构失效:原本支撑这个判断的结构被破坏(比如突破后重新跌回区间内),判断立即作废,而不是等止损。
  • 数据失效:行情缺失、合约换月、涨跌停无法成交——这些情况下这条信号必须跳过,不能按"假设成交"处理。

失效条件写不出来的规则,通常意味着这个想法本身没想清楚"我错在哪"。写不出失效条件的信号,本质上是一个信仰。

指标三:可计量(口径唯一,且写在需求单上)

这是最容易被忽略、又最容易让回测失真的一个。同一个信号,口径不同,结果能差出量级。

必须固定的口径至少有六项:

  1. 价格类型:收盘价、结算价、还是盘口某一档?期货里用收盘价和结算价做信号,结果不一样。
  2. 复权方式:前复权、后复权、不复权。跨除权日回测时,不复权会凭空造出一个跳空信号。
  3. 主力合约换月规则:按持仓量切换、按固定日期切换、还是不切换?不处理换月,跨月持仓的盈亏是假的。
  4. 成交假设:下一根开盘成交、当根收盘成交、还是限价挂单?这是回测乐观程度的最大来源之一。
  5. 手续费与滑点:费率、最小变动价位、滑点跳数,必须显式写进配置,不能默认 0。
  6. 样本起止与剔除规则:哪段时间、剔除哪些异常交易日、停牌怎么算。

这六项不齐,回测跑出来的数字只能叫"某个假设下的数字",不能叫"这个策略的收益"。我在系列第 1 篇里那组 6.24% / 2.72% / 2.49% 的对照,就是只动"滑点"一个口径得到的——六个口径全放开,误差可以大到让结论反转。

三、四类必须当场拒掉的需求

有了上面三个指标,拒绝就有依据了。以下四类,我现在的做法是当场说不行,而不是"我先试试":

  1. 形容词型:健康、强势、活跃、有资金介入。无法编码,改写成本高于重做。
  2. 未来信息型:"收盘前十分钟判断今天会不会收阳"——用到了当根未完成的数据,回测里必然高估,实盘必然做不到。
  3. 无失效条件型:只有入场,没有退出逻辑,也没有作废条件。这类需求跑出来的回测曲线通常很漂亮,但无法执行。
  4. 口径不可定型:连用哪个价、算哪段时间都定不下来。这不是需求,这是还没想。

拒绝不是不服务,拒绝是把"三个月后发现做不到",换成"三分钟说清楚为什么做不到"。

四、工程上怎么落地:需求单 + 校验器

口头约定靠不住,得让它变成流程里的一个强制环节。我的做法是两张表 + 一个脚本:

表一:规则条目表。 每条规则一行,字段固定:

id | 方向 | 触发条件(布尔表达式) | 失效条件 | 计量口径(6 项) | 参数名(不含值) | 验收判据

表二:口径登记表。 全局口径写一次,所有规则引用同一份,避免不同规则悄悄用了不同的价格类型。

脚本:规则校验器。 在跑回测之前先扫一遍规则表,只要出现下列任一情况就终止流程:

  • 触发条件里出现未登记的参数名(说明有隐含假设没写出来)
  • 失效条件为空
  • 口径字段有缺失(尤其是复权方式和成交假设)
  • 条件表达式里出现未来函数(shift(-n)、当根未收盘数据)

这四条检查加起来不到一百行代码,但能拦住绝大部分"跑到一半发现没法解释"的作业。能被机器拦住的错误,不要靠人记住。

五、和第 3 篇连起来看

第 3 篇解决的是"这个目标现实吗",这一篇解决的是"这个想法能编码吗"。两关都过了,才轮到回测引擎去跑。

目标体检(第3篇)   → 目标是否可行        → 不可行直接拒
        ↓
需求改写(这篇)     → 想法是否可判定      → 不可判定直接拒
        ↓
回测引擎(第1篇)    → 带摩擦、可复现地跑
        ↓
验证协议(第2篇)    → 结论脆不脆

注意这条链上每一环的输出都是下一步的输入,而且每一环都有终止权限。一条永远不会终止的流水线,产出的不是结论,是加班记录。

六、一句话总结

能被判定的想法才叫策略,其余的叫愿望。 需求改写的价值不在于写得漂亮,而在于它逼你在写第一行代码之前,就把"我什么时候算错"说清楚。

下一篇预告

《回测引擎的六个必填口径:为什么你的收益曲线和别人算的不一样》——把上面那六项口径逐个展开,讲每一项写错会发生什么,以及怎么在代码里做成不可跳过的必填项。


本文只讨论工程方法与数据口径,不涉及任何具体策略、指标与参数,不构成任何投资建议。文中出现的所有收益数字均来自公开演示作业,并附带样本区间、滑点假设与最大回撤。