---
name: hl-quant
description: 当用户要用启发式学习优化量化交易策略时使用：诊断策略弱点、提出有经济含义的假设、只修改策略文件、用固定回测评分器评估，并按严格门槛决定是否接受。触发词包括“启发式探索”“启发式学习”“HL循环”“策略调参”“回测改进”“优化策略分数”“heuristic exploration”“heuristic learning”。
---

# hl-quant：量化策略的启发式探索 Skill

用固定回测评分器反复评估策略候选：先诊断弱点，再提出一个有经济含义的假设，只改策略文件，最后用同一评分口径决定是否接受。

本 skill 是「启发式探索」通用框架在**量化交易**领域的实例化。完整框架（核心启发、固定评估器范式、模块结构）见随 skill 分发的 [`references/framework.md`](references/framework.md)；要把同一方法迁移到性能/转化率/错误率等其它可度量问题，也从该文档入手。下文示例里的指标名、阈值、参数均为**教学用的虚构值**，不代表任何真实生产策略。

## 什么时候使用

- 策略表现弱：Sortino / Sharpe 偏低、回撤偏大、胜率偏低。
- 需要提高策略 score、收益或风险调整后表现。
- 需要解释策略为什么失败，而不是只知道分数低。
- 需要判断某个参数变化是真改进，还是过拟合。

## 什么时候不要用

- 已有固定参数空间，只需要纯数值搜索：优先用 Optuna / grid search。
- 策略逻辑已经冻结，只是在做部署。
- 还没有固定评估器：先建立评估器，再做 HL。

## 核心循环

```
Probe → Diagnose → Propose → Patch → Evaluate → Replay → Decide → Compress
```

1. **Probe**：运行固定评估器，记录基线 score、关键指标和归因材料。
2. **Diagnose**：解释弱点来自哪里，例如入场太早、离场太快、震荡中反复打脸、仓位过度集中、 regime 错配。
3. **Propose**：每轮只提一个有经济含义的假设，例如“放慢均线可以过滤噪声并持有趋势更久”，而不是“MA=17 分数最高”。
4. **Patch**：只修改策略文件。不要改评估器、数据源、成本模型或评分公式。
5. **Evaluate**：用同一评估器重跑，比较 score 和关键指标。
6. **Replay**：检查 golden case、实盘约束和已知失败场景没有退化。
7. **Decide**：通过严格门槛才接受，否则拒绝并回到 Diagnose。
8. **Compress**：删除无触发、负贡献、重复或解释不清的规则，保持策略小而可解释。

## 固定评估器与唯一可编辑程序

候选策略必须走同一条评估管线：

```
同一数据 → 同一回测引擎 → 同一成本模型 → 同一评分公式 → 一个 score
```

只有策略文件可以改。如果为了提分去改评估器，候选之间就不再可比，整个循环会变成自欺。

如果认证失败、数据缺口、引擎崩溃或缓存不可读，停止并报告阻塞。不要靠修改基线绕过问题。

## Score 口径

本仓库示例采用 **Sortino ratio（索提诺比率）** 作为主 score：

```
score = Sortino
```

Sortino 只惩罚下行波动，比 Sharpe 更贴近“上行波动不是坏事、下行波动才是风险”的交易直觉。Sharpe、收益、回撤、胜率和交易笔数仍是验收门槛的一部分。

## 接受门槛

候选必须同时满足以下条件。右列是**开箱即用的默认容忍带**，在 run 开始前写入 run_config 即可，避免临场心证：

| 指标 | 默认门槛 |
| --- | --- |
| **score / Sortino** | 严格高于基线（硬门槛，不容忍） |
| 总收益 | ≥ 基线 − 5% |
| 年化收益 | ≥ 基线 − 5% |
| Sharpe | ≥ 基线 − 5% |
| 最大回撤 | ≤ 基线 + 2pp |
| 胜率 | ≥ 基线 − 3pp |
| 交易笔数 | ≥ 50（见下） |

唯一需要你先确认的是**交易笔数下限**：它和回测区间长度、股票池大小强相关。默认给 50，覆盖多数全市场年级别回测；如果你的区间明显更短、股票池更小（笔数天然偏少），或反过来很大，按规模上调/下调后固定下来，不要直接套 50。其余几项的 5% / 2pp / 3pp 容忍带可直接沿用。

score 变高但关键指标塌掉，拒绝。一次幸运交易带来的高 Sortino，不等于策略真的变好。

## 反过拟合纪律

### 每个改动必须有经济含义

| 改动 | 经济解释 | 结论 |
| --- | --- | --- |
| 均线从 5/10 放慢到 10/20 | 过滤日间噪声，持有趋势更久 | 可接受 |
| 增加价格高于 60 日均线的趋势过滤 | 避免下行趋势里逆势买入 | 可接受 |
| 某震荡指标死区 31-32 | 为什么偏偏是 31-32，没有市场解释 | 拒绝 |
| 止损从 10% 调到 8.2% | 8.2% 太精确，像曲线拟合 | 拒绝 |

### 禁止连续变量上的窄坑过滤

不要在 RSI、量比、资金流、动量等连续变量上挖 1-2 个点宽的死区（下面用一个虚构的振荡指标 `OSC` 举例）：

```python
# 过拟合：为什么是 43-44，而不是 42-43？
ENTRY_OSC_DEAD_ZONE_MIN = 43.0
ENTRY_OSC_DEAD_ZONE_MAX = 44.0
```

更稳健的变化应该是有经济含义的连续区间，或规则级开关：

```python
# 可解释：OSC 40-50 表示一段有市场含义的“中性犹豫区”
ENTRY_OSC_DEAD_ZONE_MIN = 40.0
ENTRY_OSC_DEAD_ZONE_MAX = 50.0
```

### 禁止未来函数

- 入场信号只能使用已完成的 bar。
- 成交必须发生在下一根 bar 的开盘，或另一个预先固定的成交规则。
- 不要用同日未完成的 high / low / amplitude 过滤当日入场。

### 保证样本量

高 score 但只有 1-2 笔交易，通常是运气，不是稳健 edge。交易次数太少时，即使 score 更高，也不能直接接受。

### 训练 / 验证纪律

如果有股票池或时间切分：

- **训练集**：可以用于搜索。
- **验证集**：每轮都可以评估，但不能用来指导搜索方向；可以用验证退化来拒绝过拟合，不能为了最大化验证分数去调参。
- **最终 holdout**：候选冻结后只跑一次。holdout 输给基线，不部署。

泛化健康度参考：

| 指标 | 健康 | 警告 | 拒绝 |
| --- | --- | --- | --- |
| train score - validation score | < 0.5 | 0.5 ~ 1.0 | > 1.0 |
| train return / validation return | 0.7 ~ 1.3 | 0.5 ~ 0.7 或 1.3 ~ 2.0 | < 0.5 或 > 2.0 |

进入拒绝区的候选，即使训练集硬门槛通过，也视为过拟合。

## 改进泛化的做法

1. **优先结构变化，而不是精调阈值**：增加 regime filter 往往比把阈值从 7.2% 调到 7.8% 更可泛化。
2. **每轮只动一个变量**：否则失败无法归因，成功也难复现。
3. **接受后压缩**：删掉不触发、重叠、解释不清的规则。
4. **回放 golden case**：真实失败场景要固定成回归样例，防止实盘约束退化。
5. **不要追最高分**：停在多指标都变好且交易笔数健康的候选，而不是追 1-2 笔交易堆出来的最高 score。

## 示例流程

```bash
# 1. Probe：跑基线
python backtest.py
# score 0.7545, return +5.46%, Sharpe 0.698, Sortino 0.755, drawdown 7.10%, 12 trades

# 2. Diagnose
# 5/10 均线太灵敏，震荡中频繁翻转，只吃到同期指数涨幅的一小段。

# 3. Propose
# 放慢到 10/20：过滤噪声，持有趋势更久。这是灵敏度的连续调整，不是窄坑。

# 4. Patch
# 修改 strategy.py：SHORT_WINDOW=10, LONG_WINDOW=20

# 5. Evaluate
python backtest.py
# score 2.0615, return +17.33%, Sharpe 1.839, Sortino 2.062, drawdown 6.71%, 7 trades

# 6. Decide
# score 严格提高，关键指标不退化，交易笔数仍可接受 → ACCEPT

# 7. Compress
# 没有冗余规则，策略保持最小。
```

## 常见错误

| 错误 | 修正 |
| --- | --- |
| 为了提分修改评估器 | 只改策略文件 |
| 在连续变量上加窄死区 | 使用有经济含义的连续区间或规则开关 |
| 只看 score 接受候选 | score 是主指标，但关键指标也要过门槛 |
| 用验证集调参 | 验证集只用于检查泛化，不用于指导搜索 |
| 高 score 但只有 1-2 笔交易 | 要求足够样本量 |
| 入场信号使用未来信息 | 只用已完成 bar |
| 不断叠规则不压缩 | 接受后删除无效和重叠规则 |
| 一轮改多个变量 | 每轮一个假设，保证归因清楚 |
| 忽略 train / validation 分化 | 分化过大即拒绝 |
