Mandate 阶段 - 详细展开

对应摘要版:../简介/Mandate阶段.md 第一次阅读建议先看:./英文术语表.md

1. 先说人话:Mandate 到底在干什么

Mandate 不是“开始研究”的同义词,也不是“先随便写点想法”。
它的真正作用是:把这条研究线的边界先锁死,再允许后续的人去做数据、信号、训练和验证。

横截面因子研究里,最常见的失败不是代码写错,而是研究边界在过程中不断漂移:

  • 看到结果不好,就换 universe
  • 看到相关性弱,就改 horizon
  • 看到某个时期效果差,就把切分窗口挪一挪
  • 看到某些资产拖后腿,就把它们悄悄排除

Mandate 的存在,就是为了把这些“研究过程中看起来很合理、事后看起来全是污染”的动作拦在最前面。

如果一句话概括:

Mandate 阶段只决定“研究合同”,不决定“研究胜负”。


2. Mandate 回答的核心问题

一篇合格的 Mandate,至少要能回答下面这几类问题。

2.1 研究对象是什么

  • 这是哪一类市场
  • 比较的是哪些资产
  • 研究的对象是相对排序,还是单资产方向

横截面因子研究里,研究对象通常不是“BTC 明天涨不涨”,而是:

在同一个时点,哪些资产相对更强,哪些相对更弱?

所以这里必须明确写出:

  • research_route = cross_sectional_factor
  • 比较口径是 date x asset
  • 后续评价的是排序能力,而不是单资产命中率

这里的 date x asset,意思不是“拿同一个资产跨时间去比”,而是把每一条研究样本定义为“某个日期上的某个资产”。

也就是说:

  • date 先固定同一个时点
  • asset 再在这个时点上横向比较不同资产
  • 比较发生在“同日不同资产”之间,而不是“同资产不同时点”之间

例如在某一天的 universe 里有 BTC、ETH、SOL、DOGE,那么研究问题不是“BTC 明天涨不涨”,而是“这一天里,谁相对更强,谁相对更弱”。

这就是为什么横截面因子后续要看的是:

  • 该因子能不能在每个 date 上把资产排出有信息量的顺序
  • 高分组和低分组之间,后续收益是否出现稳定分层

而不是去看:

  • 某一个资产的方向命中率
  • 某一个资产自己的时间序列预测是否准确

2.2 研究边界是什么

边界主要有四类:

  • Universe 边界
  • 时间边界
  • 数据边界
  • 执行表达边界

这四类边界,管的其实是四个不同层面的问题:

  • Universe 边界:你到底拿哪些资产来互相比。也就是“谁有资格进入这场横截面排名”。
  • 时间边界:你到底用哪一段历史做 train、哪一段做 test、哪一段做 backtest、哪一段留给 holdout。
  • 数据边界:你到底允许使用什么数据、这些数据按什么时间语义可见、怎样对齐到研究样本。
  • 执行表达边界:你的研究结果最终准备怎样被消费,是 long_only_ranklong_short_market_neutral,还是别的组合表达。

如果把话说得更直白一点:

  • Universe 边界不清,后面就会在结果差时偷偷换样本池。
  • 时间边界不清,后面就会在结果差时偷偷挪窗口。
  • 数据边界不清,后面就会在不知不觉中引入未来信息、错位对齐或替换数据源。
  • 执行表达边界不清,后面就会把原本不是同一个研究对象的结果硬说成“只是实现方式不同”。

只要这四类边界有一个写不清,后面就一定会出现“看结果再修边界”的冲动。

所以 Mandate 阶段不是在追求“先写得灵活一点”,而是在提前堵住四种最常见的研究漂移:

  • 换比较对象
  • 挪时间窗口
  • 改数据语义
  • 换信号消费方式

2.3 允许改什么,不允许改什么

Mandate 的价值不只在“写了什么”,还在“哪些东西一旦写下去就不能再偷偷改”。

比如:

  • Universe 口径改了,通常不是正常迭代,而是研究线变了
  • Holdout 时间窗改了,通常不是优化,而是污染最终验证
  • 交易表达从 long_only_rank 改成 long_short_market_neutral,不是小修,是研究对象变了

这些都应在 Mandate 里预先定义为:

  • 正常推进
  • 需要 review
  • 必须开 child lineage

3. Mandate 里真正该冻结的东西

下面逐项展开。

3.1 research_intent

这是“为什么研究这条线”的正式描述。

不要写成空话,比如:

  • “看看这个因子能不能赚钱”
  • “探索一下有没有 alpha”

这种写法没有约束力,也没有研究可复用性。

更好的写法应包含:

  • 市场观察
  • 假设机制
  • 预期受益对象
  • 可能失效的场景

例如:

research_intent:
  observation: "流动性改善阶段,小市值高换手资产更容易在横截面上跑赢"
  hypothesis: "20 日成交活跃度变化能解释后续 1d 排序差异"
  counter_hypothesis: "该现象只是 beta 或小市值暴露,并不构成独立 alpha"

这里的重点不是文采,而是后续能不能明确反驳自己。

3.2 research_route

这一项必须非常明确。

在横截面因子研究里,最容易发生的混乱是:

  • 文档标题说自己在做横截面
  • 内容却开始写单资产 hit rate、best horizon、择时命中率

所以 research_route 的作用,是把这条线从一开始就钉死。

对于这组文档,应明确为:

research_route: cross_sectional_factor

一旦这项写清,下游就不该再引入:

  • 单资产触发语义
  • 单资产开平仓胜率
  • 时序主线的 best_h 叙事

3.3 scope_contract

这项解决的是“研究范围到底有多大”。

需要写清:

  • 只做单因子,还是允许多因子确定性打分
  • 只研究某一市场,还是允许跨 venue
  • 是否允许长短组合
  • 是否允许 regime filter

这里的 venue,指的是具体交易场所或撮合场,比如:

  • binance_spot
  • binance_futures
  • bybit_perp
  • okx_spot

也就是说,venue 不是泛泛地说“加密市场”或“股票市场”,而是要明确到“在哪个交易场里取样、成交、计价和落地”。

所谓“只研究某一市场,还是允许跨 venue”,本质上是在问:

  • 这条研究线是不是只在一个固定 venue 的 universe 内做横截面排序
  • 还是允许把多个 venue 的资产池、行情或成交环境一起纳入研究

这件事必须提前说清,因为一旦从单 venue 变成跨 venue,很多东西都会一起变化:

  • 可交易资产集合变了
  • 流动性分布变了
  • 价格形成机制和微观结构变了
  • 手续费、滑点、资金费率或成交约束也可能变了

所以 “是否跨 venue” 不是数据细节,而是研究范围定义的一部分。

这里不要为了显得灵活而写成:

  • “后续可按结果决定”
  • “必要时扩大范围”

这类写法会让后面的每一步都失去治理约束。

3.4 target_market / universe 口径

这是 Mandate 里最关键的部分之一。

需要明确:

  • 市场:现货、合约、股票还是期货
  • 交易 venue:一个交易所还是多个
  • 入选口径:按市值、成交额、流动性还是其他
  • 排除口径:稳定币、杠杆代币、新上市资产、低价垃圾币等

这一项必须写到“别人按文档也能重建同一 universe”为止。

一个合格的 universe 定义,通常至少要包括:

target_market:
  market: crypto
  venue_scope:
    - binance_spot
  universe_rule: "Top 50 by 24h liquidity after exclusions"
  exclusions:
    - stablecoins
    - leveraged_tokens
    - listing_days < 30

为什么 universe 必须先冻结

因为横截面结果极度依赖比较对象。

你把样本池从 Top 50 改成 Top 200,看起来只是“覆盖更全面”,但实际上会同步改变:

  • 排序难度
  • 流动性结构
  • 横截面离散度
  • 因子覆盖度
  • 下游容量和成本

所以 Universe 变化不是小修,而是研究对象变化。

3.5 time_split

很多量化文档写到这里就开始模糊,这是大坑。

你必须明确:

  • Train 窗口
  • Test 窗口
  • Backtest 窗口
  • Holdout 窗口

并且要写成具体时间边界,而不是:

  • “训练期较长”
  • “后面留一些数据做验证”

因为只要时间边界不死,后面就一定会有人在结果差的时候想“再挪一点”。

建议写法:

time_split:
  train:
    start: "2021-01-01"
    end: "2023-12-31"
  test:
    start: "2024-01-01"
    end: "2024-06-30"
  backtest:
    start: "2024-07-01"
    end: "2024-09-30"
  holdout:
    start: "2024-10-01"
    end: "2024-12-31"

为什么 Holdout 必须在 Mandate 就定掉

因为如果 Holdout 不是一开始就定死,后面任何“延长一下”“换个更代表性的窗口”的动作,都会变成把最终验证窗口当成可消费资源。

3.6 data_contract

DataReady 阶段会把数据真正落成 panel,但 Mandate 阶段必须先写清数据合同。

至少应写出:

  • 数据源
  • bar size
  • 时区
  • 对齐语义
  • 数据发布时间语义

例如:

data_contract:
  bar_size: 1h
  timezone: UTC
  price_source: spot_klines
  liquidity_source: rolling_volume_24h
  signal_timestamp: close_time
  available_after: next_bar_open

这一步的重点是:

DataReady 以后只能实现这个合同,不能重新发明一个版本。

3.7 execution_contract

这一项不要求你在 Mandate 就把所有交易细节写死,但至少要冻结“研究打算如何被消费”。

比如:

  • long_short_market_neutral
  • long_only_rank

这两种表达差异非常大。

如果这一步不写,后面很容易出现这样的偷换:

  • 先用多空表达验证
  • 结果一般
  • 再换成长多
  • 然后说“本质上还是一个信号”

这会导致研究对象漂移。

3.8 kill_criteria

很多文档写到这里最敷衍,但其实这是 Mandate 的治理核心。

Kill criteria 要解决的是:

  • 什么情况说明这条线不值得继续
  • 什么情况说明不是结束,而是该开 child lineage

例如:

  • Universe 无法稳定重建
  • 数据来源无法回放
  • 交易表达与原始假设冲突
  • 关键定义轴必须被修改

只要这些条件成立,就不能把后续动作包装成“正常优化”。


4. Mandate 阶段最容易写错的地方

4.1 把统计门槛写进 Mandate

最常见错误就是把:

  • IC > 0.03
  • Sharpe > 1.0
  • 容量 > 500 万

写成 Mandate 的 formal gate。

这类内容看起来很“量化”,但其实是错位的。

原因不是这些指标没用,而是:

  • 这些指标要依赖下游证据才有资格判断
  • 一旦写在 Mandate,人就会倾向于倒推研究过程去满足它

正确做法是:

  • Mandate 只定义后面要检什么
  • 不在 Mandate 决定后面会不会通过

4.2 先试几个 horizon 再冻结一个

这是另一个非常高频的污染源。

如果你在 Mandate 阶段写:

for h in [1, 4, 24, 72]:
    ...

然后选一个“最好”的 horizon 写回合同,本质上就是把研究结果倒灌回研究授权。

正确思路是:

  • horizon 来自假设与执行表达
  • 不是来自先跑结果再回填

4.3 Universe 留口子

比如写:

  • “先用 Top 50,不行再扩到 Top 100”
  • “必要时调整样本池”

这种话等于没冻结。

一篇看起来完整但 universe 可随结果变动的 Mandate,实际上没有治理价值。

4.4 把横截面问题写成时间序列问题

错误表述包括:

  • “预测 BTC 明天方向”
  • “提高单币 hit rate”
  • “best horizon 是多少”

横截面研究关心的是同一时点的相对强弱排序。
只要正文老是滑回单资产叙事,后面所有阶段都会一起跑偏。


5. Mandate 的输出应该长什么样

最少应该有:

  • mandate.md
  • research_scope.md
  • research_route.yaml
  • time_split.json
  • stage_completion_certificate.yaml

这里最重要的不是文件名,而是文件是否真的能约束后续行为。

比如 research_route.yaml 必须是机器可读的,不应只在 Markdown 里口头说“这是横截面研究”。


6. Mandate 结束后,下游应该怎么消费

DataReady 的职责不是“再理解一遍 Mandate”,而是严格实现 Mandate

所以从 Mandate 到 DataReady,最关键的交接要求是:

  • 不能静默改 universe
  • 不能静默改时间边界
  • 不能静默改研究路线
  • 不能静默改执行表达

如果 DataReady 发现 Mandate 本身不可执行,正确动作应是:

  • 回到 Mandate 修正
  • 或者开新 lineage

而不是让 DataReady 悄悄替 Mandate 做决定。


7. 最后给一个判断标准

怎么判断一篇 Mandate 到底合不合格?

最简单的方法是问三遍:

  1. 我们在研究什么?
  2. 哪些东西后面不能改?
  3. 如果非改不可,是正常迭代还是另起一条线?

如果这三问答不清,Mandate 就还没写完。