/~learn/s/569c1da9
> 📅 2026-10-24 建立 > > 本文是实验场的操作手册:目录怎么放、评测怎么跑、什么样的策略才允许推给你、出问题怎么止损。 > 上位文档:00-AI策略实验场总纲
---
一轮实验算"有效",必须同时满足:
verdict.md,失败的候选也写清淘汰原因月度标准:至少 1 个候选进入人工复核。连续两个月为 0,说明方向或评测台有问题,暂停并复盘。
---
cron / 手动
│
▼
Trae CLI Agent(独立 worktree,只读评测台)
│ ① 读上轮结论 + 去重指纹
│ ② 提 3–5 个假设
│ ③ 写 candidates/<fp>/strategy.py
│ ④ 调 harness 跑 IS → OOS → 冻结集
▼
固定评测台 experiments/harness/
├─ data.py 只读数据 + 复权 + 防未来函数
├─ timing.py 择时回测(复用 Strategy 体系)
├─ cross.py 截面回测(复用 selection/backtest)
├─ costs.py 手续费 / 滑点 / 冲击成本
├─ metrics.py 夏普 / 索提诺 / 卡玛 / 回撤 / 换手 / 分年
├─ splits.py IS / OOS / 冻结集
└─ score.py 试验计数 + 折价夏普 + 晋升闸门
│
▼
experiments/runs/<日期-序号>/ → result.json + verdict.md
│
▼
lab.db 排行榜 → 超阈值才推送 → 你 review → 合并进生产策略池
---
experiments/
├── harness/ # 固定评测台(人类所有,Agent 只读)
│ ├── config.yaml # 阈值、成本、样本区间(唯一可调参入口)
│ ├── data.py
│ ├── timing.py
│ ├── cross.py
│ ├── costs.py
│ ├── metrics.py
│ ├── splits.py
│ └── score.py
├── candidates/
│ └── <fingerprint>/
│ ├── strategy.py # 必须实现 build_strategy()
│ ├── params.json # 参数与搜索范围
│ └── note.md # 一句话逻辑 + 经济学解释 + 预期失效条件
├── runs/
│ └── 2026-10-25-001/
│ ├── hypothesis.md
│ ├── result.json
│ ├── equity.csv
│ └── verdict.md
├── lab.db # 试验次数 / 结果 / 排行榜
└── README.md
候选唯一入口:strategy.py 必须暴露统一接口,评测台只认这个接口,避免 Agent 自带回测逻辑。
def build_strategy(params: dict):
"""返回 (strategy 实例, 元信息 dict)。不得自行加载数据、不得写库。"""
result.json 固定字段(缺字段视为无效):
{
"fingerprint": "sha1...",
"kind": "timing", // timing | cross
"hypothesis": "...",
"params": {},
"is": {"sharpe": 0.0, "annual": 0.0, "mdd": 0.0, "turnover": 0.0},
"oos": {"sharpe": 0.0, "annual": 0.0, "mdd": 0.0, "turnover": 0.0},
"frozen": "PASS", // PASS | FAIL | BAND_A/B/C(只给结论,不给明细)
"baseline_oos": {"sharpe": 0.0, "annual": 0.0, "mdd": 0.0},
"robustness": {"neighbor": "PASS", "cross_symbol": "2/3", "yearly_win": 0.7},
"n_trials": 1234,
"verdict": "candidate" // candidate | rejected | abandoned
}
---
DataCacheManager.load(code, start, end, k_type=1, future_days=1),与线上 /lab 完全同源。selection/backtest/cross_section_backtester.py 与 factor_ic_analysis.py 的数据口径。quant.db 任何表;需要缓存写 experiments/cache/。rejected。
择时复用现有 Strategy 体系,不重写引擎:
s = build_strategy(params)
df = DataCacheManager.load(code, start, end, k_type=1, future_days=1)
s.init_and_generate_signal(df)
s.calculate_returns_quick() # 产出 strategy_return
s.calculate_max_drawdown()
s.calculate_number_of_trades()
截面复用 selection/backtest/ 现成的分组回测与 IC 分析,不另起一套。
| 项 | 取值 | 说明 |
| --- | --- | --- |
| 手续费 | 万 0.5(买+卖) | 与线上口径一致 |
| 滑点 | 1–3 bp(按品种流动性) | 不得设为 0 |
| 冲击成本 | 大额/低流动性品种加 5 bp | 截面调仓尤其要算 |
| 调仓频率 | 按策略声明 | 换手必须计入指标 |
成本口径只能比线上更严,不能更松。 成本参数写在 harness/config.yaml,Agent 不可改。
优先复用 timing_overlay/backtest/metrics.py 的纯函数(sharpe / sortino / calmar / compute_drawdown),保证与现有研究口径一致。
必算:夏普、索提诺、卡玛、年化、最大回撤、换手率、交易次数、分年收益、最差连续 12 个月。
| 区间 | 用途 | Agent 可见性 |
| --- | --- | --- |
| 2015–2022 | IS(样本内) | 可见完整指标 |
| 2023–2024 | OOS(样本外) | 可见完整指标 |
| 2025-01-01 至今 | 冻结集 | 只返回 PASS / FAIL / BAND,不给明细 |
冻结集由一条稳定的代码路径读取,路径与参数写死在 harness 内;候选代码不得直接读原始数据。
你说过"次数不是问题,之前一个策略能跑几万次遗传算法"。次数确实不设上限,但必须记录并按次数折价:
lab.db.trials,累计 n_trials 是全局的,不是单轮的。这样既保留你"海量搜索"的优势,又不会让排行榜被噪声刷屏。
---
| 步骤 | 动作 | 产物 |
| --- | --- | --- |
| 1 | 读 lab.db 最近 20 条结果 + 去重指纹 | 本轮不重复清单 |
| 2 | 提 3–5 个假设(逻辑 + 经济学解释 + 预期失效条件) | hypothesis.md |
| 3 | 为每个假设写 candidates/<fp>/strategy.py | 代码 |
| 4 | 静态自检:无未来函数、无写库、无网络请求 | 自检日志 |
| 5 | 跑 IS | result.json.is |
| 6 | 通过 IS → 跑 OOS;通过 OOS → 跑冻结集 | result.json.oos/frozen |
| 7 | 写结论(为什么留 / 为什么杀) | verdict.md |
| 8 | 更新 lab.db 与排行榜,满足条件才推送 | 排行榜行 |
Agent 提示词硬约束(写进每轮 prompt):
experiments/candidates/ 与 experiments/runs/ 下文件experiments/harness/ 只读;不得修改任何阈值quant.dbabandoned---
| 条件 | 要求 |
| --- | --- |
| 未来函数检测 | 三项全通过 |
| 冻结集 | PASS |
| 样本外长度 | ≥ 2 年 |
| 参数邻域 | 通过 |
| 与现有策略相关性 | < 0.8 |
| 试验计数 | 已记录,折价夏普达标 |
| 条件 | 要求 |
| --- | --- |
| OOS 夏普 | ≥ 同标的基线 + 0.3 |
| OOS 年化 | ≥ 基线 |
| OOS 最大回撤 | 不差于基线 2 个百分点 |
| 跨品种 | 3 个品种中 ≥ 2 个正超额 |
| 换手 | ≤ 基线 1.5 倍 |
| 条件 | 要求 |
| --- | --- |
| OOS RankIC | ≥ 现有因子 + 0.01 |
| ICIR | ≥ 现有因子 |
| 分组单调性 | 通过 |
| 多头组合 OOS 年化超额 | ≥ 现有 + 2%/年 |
| 换手 | 不显著高于现有调仓频率 |
阈值全部写在 harness/config.yaml,调整阈值 = 修改晋升标准,必须由你决定。
---
| 项 | V0(手动) | V1(定时) |
| --- | --- | --- |
| 触发 | trae-cli run 手动 | 每日 02:00 cron |
| 每轮候选数 | 1–2 | 3–5 |
| 单候选超时 | 10 分钟 | 10 分钟 |
| 单轮预算 | 不限(人工盯) | token / 金额硬上限 |
| 连续无产出 | — | 连续 3 轮 0 候选 → 自动降为每周 + 推送提示 |
| 失败重试 | Agent 自修 2 次 | 同左,之后 abandoned |
禁止静默失败:任何异常必须 logging.error 并写入 runs/<id>/verdict.md,不允许 except: pass。
---
实验场依赖 Trae CLI 跑 Agent,当前状态:
| 项 | 状态 |
| --- | --- |
| CLI 安装 | ✅ workspace/trae-agent/.venv/bin/trae-cli |
| 独立 venv | ✅ Python 3.12 |
| 模型 provider 配置 | ✅ 已配置:模型 deepseek-flash(DeepSeek 官方 API),凭证在 workspace/trae-agent/.env |
| 冒烟测试 | ✅ 2026-10-24 通过:Agent 2 步内调用 bash 工具落盘文件并正常收尾 |
| MCP / 工具白名单 | 暂不启用 MCP(只用 bash / 编辑器 / 思维链 / task_done) |
关于"provider":不是 priority,指的是 trae_config.yaml 里的 model_providers(模型提供方)。Trae CLI 本身不带模型,必须填一个 provider 的 API key 才能跑。
关键实现细节(勿随意改):trae-agent 内置的 provider 类型只有 openai / anthropic / azure / ollama / openrouter / doubao / google,没有 deepseek。DeepSeek 走 OpenAI 兼容协议,因此配置里用 provider: doubao(对应 OpenAICompatibleClient,走 chat/completions)+ base_url: https://api.deepseek.com/v1。不能用 provider: openai,因为那条路径调的是 OpenAI 的 /responses 接口,DeepSeek 不支持。
凭证管理:API key 只写在 workspace/trae-agent/.env 的 DOUBAO_API_KEY(变量名由 provider 字段大写推导),trae_config.yaml 里只留占位符。两个文件都已被 .gitignore 忽略,禁止提交。
冒烟命令(注意:task 是位置参数,配置文件用 --config-file):
cd workspace/trae-agent
env -u PYTHONHOME -u PYTHONPATH .venv/bin/trae-cli run \
--config-file trae_config.yaml \
--working-dir /tmp/trae_smoke \
--max-steps 8 --console-type simple \
"在当前目录创建 hello.txt,内容写 ok,然后用 task_done 结束。"
能正常返回 Success: Yes,才进入 V0。
---
| 阶段 | 内容 | 验收 |
| --- | --- | --- |
| V0 | 建目录 + 固定评测台(择时)+ 手动跑 1 轮 | 你自己复核 1 个候选,结果可复现 |
| V1 | cron 每日一轮 + 排行榜 + 阈值推送 | 连续 7 天无人值守运行,失败可追溯 |
| V2 | 接入截面策略 + GA/参数搜索 | 截面候选能跑通 IC / 分组回测 |
| V3 | 多 Agent 分工(假设 / 实现 / 审查)+ 组合层 | 审查 Agent 能拦下未来函数与过拟合 |
---
experiments/harness/ 权限为只读(Agent 不可写)lab.db 独立于 quant.db