选股策略自进化研究 Skill
0. 使用边界
本 Skill 用于学习研究、研报复刻、量化工作流搭建、回测审计和策略机制验证。它不是投资建议工具,不提供买卖指令,不推荐个股,不承诺收益,不保证未来表现。
必须在所有对外输出、报告、图文和结论中保留:
> 仅供学习研究,不构成投资建议。回测、样本声明和策略代码不代表未来收益,策略结果、数据来源和实盘风险需自行验证。
任何 Agent 使用本 Skill 时,必须遵守以下硬规则:
- 不把回测收益、Sharpe、胜率、回撤写成未来收益承诺。
- 不生成“买入、卖出、持有、止盈、止损、跟随策略”等交易建议。
- 不使用测试集结果筛选、调参、回写策略。
- 不在结果不完整时声称“完全复现”或“等同原研报代码”。
- 不隐藏数据缺口、字段代理、样本偏差、失败版本和过拟合信号。
- 不暴露 API token、账号、密钥、私密路径、非公开数据源凭证。
1. Skill 目标
把一个选股策略想法或一篇公开研报,转成一套可运行、可审计、可迭代的研究系统:
策略协议
-> 数据契约
-> 因子与组合配置
-> 三段样本回测
-> 候选版本演化
-> 验证集保留
-> 测试集人工复核
-> 版本账本和研究报告
本 Skill 的重点不是“找到一个最好看的历史结果”,而是让 Agent 按研究纪律回答:
- 原始策略逻辑是什么?
- 哪些内容来自公开披露,哪些是本地 clean-room 近似?
- 数据源覆盖是否足够?
- 训练集、验证集、测试集是否隔离?
- 每个版本到底改了什么?
- 验证集改善是否带来测试集外推折损?
- 结果是否可复跑、可回滚、可追溯?
2. 触发场景
当用户提出以下任一任务时,使用本 Skill:
- “复刻某篇选股策略研报。”
- “把这个选股逻辑做成 Skill。”
- “让 AI 自动迭代选股策略。”
- “检查这个回测是不是真的有效。”
- “用训练集/验证集/测试集做策略演化。”
- “根据 Tushare / AmazingData / 本地行情数据跑选股回测。”
- “生成策略版本账本、测试集复核和研究报告。”
- “把华泰选股自进化 Skill 的机制迁移到我的数据源。”
如果用户只想要市场观点、荐股、交易建议或收益承诺,不使用本 Skill;改为提示风险边界。
3. 默认研究对象
默认策略类型是“稳健低波价值优选”:
- 股票池:A 股可交易股票或用户指定股票池。
- 基础过滤:流动性、市值、上市时长、ROE、估值数据可用。
- 价值基础池:盈利收益率相对盈利能力具有吸引力,避免只买低质量低估值。
- 三组评分:
leader、dividend_defense、growth_stability。 - 组合构建:月度调仓,目标 30 只,缓冲 45 只,单票上限 5%,按下行波动倒数定权。
- 迭代方式:只允许小步修改参数、权重、持仓数、缓冲池、单票上限、调仓频率和可解释因子组合。
可以迁移到其他策略,但必须保留:
- 策略协议。
- 数据契约。
- 样本隔离。
- 版本留痕。
- 测试集只复核。
4. 默认样本切分
若用户没有指定样本区间,默认使用以下区间:
| 样本 | 区间 | 权限 | 作用 |
|---|---|---|---|
| 训练集 | 2014-12-31 至 2018-12-28 | 策略生成和初筛 | 建立基础逻辑,观察长期稳定性 |
| 验证集 | 2018-12-28 至 2020-12-31 | 版本保留判断 | 比较候选版本,决定是否保留 |
| 测试集 | 2020-12-31 至 2026-04-30 | 最终人工复核 | 只做外推风险检查,不参与自动选择 |
如果数据源不能覆盖默认区间:
- 先报告可用区间。
- 重新提出可执行的三段切分。
- 明确样本变更对可比性的影响。
- 不得继续沿用默认指标口径。
5. 数据契约
Agent 必须先检查数据是否满足最小契约,再写回测或调参代码。
5.1 行情数据 market
最小字段:
| 字段 | 含义 | 要求 |
|---|---|---|
trade_date |
交易日 | YYYY-MM-DD 或 YYYYMMDD,可排序 |
symbol |
股票代码 | 唯一标识 |
open |
开盘价 | 可选,但建议保留 |
high |
最高价 | 可选 |
low |
最低价 | 可选 |
close |
收盘价 | 必需 |
pre_close |
前收盘 | 可选 |
volume |
成交量 | 可选 |
amount |
成交额 | 流动性过滤需要 |
adj_factor |
复权因子 | 若没有,必须说明收益计算口径 |
5.2 估值与交易基础数据 daily_basic
最小字段:
| 字段 | 含义 | 用途 |
|---|---|---|
trade_date |
日期 | 对齐调仓日 |
symbol |
股票代码 | 连接行情 |
pe_ttm |
市盈率 TTM | 计算盈利收益率 |
pb |
市净率 | 估值/稳定性因子 |
total_mv 或 circ_mv |
总市值/流通市值 | 市值过滤 |
turnover_rate |
换手率 | 可选 |
dv_ttm 或 dividend_yield |
股息率 | 股息防御因子,可代理 |
5.3 财务数据 financial
最小字段:
| 字段 | 含义 | 用途 |
|---|---|---|
ann_date 或 end_date |
公告日/报告期 | 必须避免未来函数 |
symbol |
股票代码 | 连接 |
roe |
ROE | 价值残差锚点 |
roic |
ROIC | leader 因子 |
revenue 或 or_yoy |
收入规模/收入增速 | leader 代理 |
debt_to_assets |
资产负债率 | 质量过滤 |
netprofit_yoy |
净利润增速 | 成长稳定因子 |
ocf_to_profit |
经营现金流/利润 | 股息防御因子 |
5.4 股票元数据 stock_basic
最小字段:
| 字段 | 含义 | 用途 |
|---|---|---|
symbol |
股票代码 | 连接 |
name |
股票名称 | ST 过滤 |
list_date |
上市日期 | 上市时长过滤 |
industry |
行业 | 行业内标准化/残差 |
exchange |
交易所 | 可选 |
5.5 基准数据 benchmark
建议字段:
trade_dateclosereturn
如果没有基准,不得计算超额收益,只能计算绝对收益和风险指标。
6. 数据源适配
本 Skill 可使用任意数据源,包括 Tushare、AmazingData、本地 JSONL/CSV/Parquet、数据库、研究平台导出文件。
Agent 必须先完成字段映射表:
| 目标字段 | 数据源字段 | 覆盖区间 | 缺失率 | 是否代理 | 备注 |
|---|---|---:|---:|---|---|
| close | close | 2014-12-31~2026-04-30 | 0.1% | 否 | 后复权 |
| pe_ttm | pe_ttm | ... | ... | 否 | 用于 EP |
| roic | roic | ... | ... | 是/否 | 若代理需说明 |
若字段缺失:
- 判断是否可以用代理字段。
- 标注代理口径。
- 评估对结果的影响。
- 写入
known_gaps。
绝不能在缺字段时让 Agent 默默脑补。
7. 策略配置模板
Agent 应把策略参数写入配置,而不是散落在代码或对话中。
skill:
name: stock_selection_evolution
version: v0.0.0
result_root: result/skills
data:
market_snapshot: data/canonical/daily_bar.jsonl
financial_snapshot: data/canonical/fina_indicator.jsonl
daily_basic_snapshot: data/canonical/daily_basic.jsonl
stock_basic_snapshot: data/canonical/stock_basic.jsonl
feature_cache: data/canonical/feature_cache.jsonl
benchmark_snapshot: data/canonical/benchmark.jsonl
reference_claims: reference_claims.json
sample_splits:
train:
start: "2014-12-31"
end: "2018-12-28"
permission: strategy_generation_and_first_screening
valid:
start: "2018-12-28"
end: "2020-12-31"
permission: version_retention_decision
test:
start: "2020-12-31"
end: "2026-04-30"
permission: final_holdout_review_only
strategy:
profile: stable_lowvol_value_selection
hold_n: 30
buffer_n: 45
rebalance_days: 21
cost_bps_per_side: 30
min_history_days: 120
min_listed_days: 250
vol_lookback_days: 250
adv_lookback_days: 20
dividend_lookback_days: 504
dividend_min_observations: 60
min_adv_20d: 20000
adv_quantile_floor: 0.40
market_cap_quantile_floor: 0.35
fallback_adv_quantile_floor: 0.20
value_residual_keep_quantile: 0.33
fallback_value_residual_keep_quantile: 0.50
min_roe: 0.0
max_pe_ttm: 40.0
max_weight_per_stock: 0.05
weighting: inverse_downside_volatility
industry_grouping: true
factor_groups:
leader:
weight: 0.333333
factors:
- {name: roic, direction: 1}
- {name: revenue_scale, direction: 1}
- {name: debt_to_assets, direction: -1}
dividend_defense:
weight: 0.333333
factors:
- {name: earnings_yield, direction: 1}
- {name: dividend_yield_2y_avg, direction: 1}
- {name: dividend_price_ratio_2y_avg, direction: 1}
- {name: ocf_to_profit, direction: 1}
- {name: downside_volatility, direction: -1}
growth_stability:
weight: 0.333334
factors:
- {name: netprofit_yoy, direction: 1}
- {name: profit_growth_stability, direction: 1}
- {name: volatility, direction: -1}
- {name: pb, direction: -1}
evaluation:
primary_metric: sharpe
annualization_days: 252
test_used_once: true
8. 因子计算规则
8.1 基础衍生因子
使用以下默认近似:
| 因子 | 默认计算 | 注意事项 |
|---|---|---|
earnings_yield |
1 / pe_ttm |
PE 小于等于 0 或缺失时剔除或置空 |
value_residual |
盈利收益率对 ROE 做横截面回归残差 | 优先行业内做,数据不足时全市场做 |
revenue_scale |
log(revenue) 或可得收入规模字段 |
不能用未来公告 |
dividend_yield_2y_avg |
最近两年股息率均值 | 无股息字段时标注代理 |
dividend_price_ratio_2y_avg |
两年股利价格比均值 | 需说明口径 |
volatility |
过去 vol_lookback_days 日收益波动 |
年化与否要标注 |
downside_volatility |
过去窗口负收益标准差 | 用于权重 |
profit_growth_stability |
净利润增速时间序列稳定性 | 可用负波动或稳定性分数代理 |
8.2 标准化规则
默认处理:
- 每个调仓日单独计算横截面因子。
- 能行业内标准化时,优先行业内 z-score 或 rank。
- 行业内样本太少时,回退全市场标准化。
- 极值应 winsorize 或用 rank,避免少数异常值主导。
- 所有因子方向必须明确:越大越好为
1,越小越好为-1。
8.3 价值基础池
默认规则:
- 先过滤可交易、上市时长、流动性、市值、ROE、PE 等基础条件。
- 计算
earnings_yield。 - 用
earnings_yield ~ roe做横截面回归,取残差。 - 保留残差最有利的一部分,如 top 1/3。
- 若候选数量不足,使用 fallback 阈值,但必须写入报告。
9. 组合构建规则
默认组合规则:
- 每个调仓日生成得分排名。
- 目标持仓数
hold_n = 30。 - 缓冲池
buffer_n = 45,用于降低换手。 - 单票权重上限
max_weight_per_stock = 5%。 - 权重按下行波动倒数分配。
- 若权重超上限,截断后再归一。
- 交易成本按
cost_bps_per_side进入回测。 - 不做择时,不做仓位择时,不因为市场环境临时空仓,除非策略协议明确允许。
10. 运行流程
Step 1:创建研究目录
推荐结构:
stock_selection_evolution/
SKILL.md
skill_config.yaml
reference_claims.json
data/
raw/
canonical/
scripts/
build_snapshot.py
build_feature_cache.py
strategy_gen.py
strategy_backtest.py
evolve_versions.py
result/
skills/
{skill_name}/
v0.0.0/
evolution/
如果系统只能上传一个 Markdown Skill,则 Agent 应在首次运行时根据本 Skill 自动创建上述目录和文件。
Step 2:建立公开声明文件
把研报或用户描述中的结构化事实写入 reference_claims.json:
{
"source_note": "Structured facts from public report or user-provided strategy description. No verbatim copyrighted report text.",
"sample_splits": {
"train": {"start": "2014-12-31", "end": "2018-12-28"},
"valid": {"start": "2018-12-28", "end": "2020-12-31"},
"test": {"start": "2020-12-31", "end": "2026-04-30"}
},
"initial_strategy": {
"name": "稳健低波价值优选",
"portfolio": {
"rebalance": "monthly",
"target_holdings": 30,
"buffer_pool": 45,
"max_weight_per_stock": 0.05,
"weighting": "inverse downside volatility"
}
},
"reported_metrics": []
}
不要复制大段原文,不要搬运非公开代码。
Step 3:构建 canonical 数据
把不同数据源统一成标准格式:
daily_bar.jsonl
daily_basic.jsonl
fina_indicator.jsonl
stock_basic.jsonl
benchmark.jsonl
每个 JSONL 一行一个对象,日期和代码字段必须可连接。
构建后生成 data_inventory.md:
| 数据表 | 行数 | 股票数 | 起始日期 | 结束日期 | 缺失说明 |
|---|---:|---:|---|---|---|
| market | 1662810 | 700 | 2014-12-31 | 2026-04-30 | ... |
Step 4:生成特征缓存
若样本较大,必须优先构建调仓日特征缓存:
feature_cache.jsonl
feature_cache.meta.json
feature_cache.meta.json 至少包含:
- 数据文件路径。
- 数据文件 hash 或修改时间。
- 策略参数中影响原始特征的字段。
- 调仓日数量。
- 特征行数。
- 覆盖股票数。
- 构建耗时。
如果缓存签名不匹配,Agent 必须回退现场计算,不得复用旧缓存。
Step 5:跑基线版本
基线版本固定为 v0.0.0。
输出目录:
result/skills/{skill_name}/v0.0.0/
skill_config.yaml
reference_claims.json
metrics.json
metrics_table.csv
validation_report.json
equity_curve_train.csv
equity_curve_valid.csv
equity_curve_test.csv
positions_train.jsonl
positions_valid.jsonl
positions_test.jsonl
报告必须包含:
- 样本覆盖。
- 有效起止日期。
- 交易日数量。
- 年化收益。
- 年化波动。
- Sharpe。
- 最大回撤。
- Calmar。
- 年化换手。
- 月/季/半年/年度胜率,如可计算。
- 已知缺口。
Step 6:生成候选版本
候选版本只允许做小步变更。
推荐候选类别:
| 类别 | 可变参数 | 不允许 |
|---|---|---|
| 价值池 | value_residual_keep_quantile、max_pe_ttm |
改写策略目标 |
| 流动性 | adv_quantile_floor、min_adv_20d |
事后剔除亏损股票 |
| 市值 | market_cap_quantile_floor |
事后按收益筛行业 |
| 因子权重 | 三组因子权重 | 加入测试集表现因子 |
| 组合 | hold_n、buffer_n、max_weight_per_stock |
用测试集决定持仓数 |
| 风险窗口 | vol_lookback_days |
用未来波动 |
| 调仓 | rebalance_days |
按测试集择时 |
候选版本示例:
- version: v0.1.4
note: lower PE ceiling
set:
strategy.max_pe_ttm: 30.0
- version: v0.2.20
note: moderate liquidity and market-cap floors
set:
strategy.adv_quantile_floor: 0.35
strategy.market_cap_quantile_floor: 0.30
- version: v0.2.27
note: report-aligned final candidate
set:
strategy.value_residual_keep_quantile: 0.25
strategy.adv_quantile_floor: 0.40
strategy.market_cap_quantile_floor: 0.35
weight.leader: 0.35
weight.dividend_defense: 0.40
weight.growth_stability: 0.25
Step 7:验证集保留
版本保留规则:
- 每个候选都先跑验证集。
- 主指标默认
valid_sharpe。 - 只有验证集 Sharpe 创新高且逻辑未漂移,才进入 retained versions。
- 测试集可以随候选计算用于最终研究记录,但绝不能参与自动选择。
- 每个候选都进入版本账本,失败版本也保留。
账本字段:
| iteration | version | retained | mutation | valid_sharpe | test_sharpe | valid_return | test_return | max_drawdown | note |
|---:|---|---|---|---:|---:|---:|---:|---:|---|
Step 8:测试集人工复核
测试集复核必须回答:
- 验证集冠军在测试集是否同步改善?
- 测试集收益是否来自少数年份?
- 最大回撤是否恶化?
- 换手是否显著上升?
- 是否出现“验证集越高,测试集越差”的过拟合信号?
- 是否应拒绝最后验证集冠军,改保留更稳版本?
测试集复核结论必须人工化表达,不得让程序自动用测试集选择版本。
可用模板:
## 测试集人工复核
- 验证集冠军:vX.X.X
- 测试集表现较稳版本:vY.Y.Y
- 是否接受验证集冠军:接受 / 拒绝 / 暂缓
- 理由:
- ...
- 风险:
- ...
- 后续动作:
- ...
11. 指标计算口径
默认口径:
| 指标 | 计算 |
|---|---|
| 日收益 | equity_t / equity_{t-1} - 1 |
| 年化收益 | equity_end / equity_start 按交易日年化 |
| 年化波动 | 日收益标准差 × sqrt(252) |
| Sharpe | 年化收益 / 年化波动,若有无风险收益则扣除 |
| 最大回撤 | 净值相对历史高点最大跌幅 |
| Calmar | 年化收益 / 最大回撤 |
| 年化换手 | 单期换手按年化折算 |
| 胜率 | 月/季/半年/年度收益为正的比例 |
输出指标时必须同时写:
- 样本区间。
- 交易日数量。
- 年化天数。
- 是否同一窗口比较。
- 是否包含交易成本。
- 是否使用完整股票池。
12. 版本目录规范
不得覆盖旧版本。
每个版本目录必须包含:
v0.2.20/
skill_config.yaml
candidate_config.json
validation_report.json
metrics.json
metrics_table.csv
equity_curve_train.csv
equity_curve_valid.csv
equity_curve_test.csv
positions_train.jsonl
positions_valid.jsonl
positions_test.jsonl
figures/
演化目录必须包含:
evolution/run_YYYYMMDD_HHMMSS/
evolution_ledger.json
retained_versions.json
evolution_summary.csv
evolution_sharpe.svg
v0.0.0/candidate_config.json
v0.0.0/candidate_result.json
...
13. 验证报告模板
每次运行必须输出 validation_report.md 或 validation_report.json。
Markdown 模板:
# 选股策略自进化验证报告
## 1. 运行摘要
- Skill 名称:
- 版本:
- 运行时间:
- 数据源:
- 股票池:
- 样本区间:
- 是否使用交易成本:
- 是否使用特征缓存:
## 2. 数据覆盖
| 数据 | 行数 | 股票数 | 起始 | 结束 | 缺口 |
|---|---:|---:|---|---|---|
## 3. 样本结果
| 样本 | 起始 | 结束 | 交易日 | 年化 | Sharpe | 最大回撤 | 年化换手 |
|---|---|---|---:|---:|---:|---:|---:|
## 4. 版本演化
| 版本 | 保留 | 验证 Sharpe | 测试 Sharpe | 变更 | 备注 |
|---|---|---:|---:|---|---|
## 5. 测试集人工复核
- 验证集冠军:
- 测试集复核较稳版本:
- 是否接受:
- 理由:
## 6. 已知缺口
- 股票池:
- 因子口径:
- 交易约束:
- 行业分类:
- 数据缺失:
## 7. 风险提示
仅供学习研究,不构成投资建议。回测、样本声明和策略代码不代表未来收益,策略结果、数据来源和实盘风险需自行验证。
14. 复刻研报前的 10 项审计
任何 Agent 在声称“复刻”前,必须逐项回答:
| 项目 | 状态 | 说明 |
|---|---|---|
| 1. 样本区间是否完整 | 已对齐 / 代理 / 未复刻 | |
| 2. 训练/验证/测试是否隔离 | 已对齐 / 代理 / 未复刻 | |
| 3. 股票池是否一致 | 已对齐 / 代理 / 未复刻 | |
| 4. 因子口径是否一致 | 已对齐 / 代理 / 未复刻 | |
| 5. 是否有未来函数 | 已排除 / 有风险 / 未检查 | |
| 6. 是否处理 ST/停牌/涨跌停 | 已处理 / 代理 / 未处理 | |
| 7. 是否包含交易成本 | 已包含 / 代理 / 未包含 | |
| 8. 是否有版本账本 | 已生成 / 部分 / 未生成 | |
| 9. 测试集是否只复核 | 是 / 有污染 / 未说明 | |
| 10. 失败结果是否也保留 | 是 / 部分 / 否 |
如果任一项为“未复刻、未检查、有污染”,不得输出“完美复刻”。
15. 当前 clean-room 参考结果
以下结果来自一个本地 clean-room pilot,用于说明工作流,不代表未来收益。
测试集区间:2020-12-31 至 2026-04-30,约 1289 个交易日。
| 版本 | 选择状态 | 验证 Sharpe | 测试年化 | 测试 Sharpe | 最大回撤 | 年化换手 |
|---|---|---|---|---|---|---|
| 500只基线 v0.0.0 | 原中证500代理 | 0.853 | 7.32% | 0.458 | 30.50% | 3.58 |
| 700只基线 v0.0.0 | 全A代理基线 | 0.954 | 11.26% | 0.677 | 17.95% | 3.25 |
| 700只 v0.2.20 | 验证集保留且测试复核较好 | 1.098 | 13.05% | 0.753 | 24.24% | 2.91 |
| 700只 v0.2.27 | 最后验证集冠军 | 1.168 | 7.01% | 0.458 | 30.87% | 2.99 |
重要结论:
- 股票池是第一关键变量。500只代理池测试 Sharpe 0.458;700只全A代理池测试 Sharpe 0.677,接近公开披露初始版本约 0.69。
- 验证集也会骗人。v0.2.27 是最后验证集冠军,但测试集弱于 v0.2.20。
- 自进化不是一路把指标调高,而是持续暴露样本适配和过拟合风险。
16. 输出格式
当用户要求“运行 Skill”时,Agent 最终必须输出:
# 运行结果
## 已完成
- 数据契约检查:
- 基线回测:
- 候选版本:
- 验证集保留:
- 测试集复核:
- 报告位置:
## 核心指标
| 版本 | 验证 Sharpe | 测试年化 | 测试 Sharpe | 最大回撤 | 年化换手 |
|---|---:|---:|---:|---:|---:|
## 结论
...
## 已知风险
...
## 下一步
...
风险提示:仅供学习研究,不构成投资建议。回测、样本声明和策略代码不代表未来收益,策略结果、数据来源和实盘风险需自行验证。
17. 失败处理
如果运行失败,Agent 不得只说“失败”。必须给出:
- 失败步骤。
- 错误信息摘要。
- 已确认的输入文件。
- 缺失字段或缺失数据。
- 是否影响样本隔离。
- 最小可执行下一步。
失败输出模板:
# 运行失败报告
## 失败步骤
...
## 直接原因
...
## 已验证事实
- ...
## 不应继续的原因
- ...
## 最小下一步
...
18. Agent 行为纪律
- 先读配置和数据契约,再写代码。
- 先跑基线,再跑候选版本。
- 先解释数据边界,再解释收益指标。
- 不为了让结果好看而隐藏失败。
- 不把测试集反馈写回参数。
- 不覆盖旧结果。
- 不在没有数据支持时编造图表或指标。
- 不输出任何密钥、token、私密 ID。
- 对所有代理字段、近似口径、缺失处理做显式标注。
- 输出结论必须先讲研究边界,再讲指标。
19. 推荐用户指令
用户可以这样调用:
请使用“选股策略自进化研究 Skill”,基于我提供的 Tushare/AmazingData 数据,复刻这篇研报的公开机制。先做数据契约检查,再跑基线,最后做验证集保留和测试集人工复核。不要给投资建议。
或:
请把我的这个选股逻辑写成自进化 Skill。要求训练集、验证集、测试集隔离,每个候选版本落盘,测试集只做最终复核,并输出版本账本和风险报告。
20. 首次运行询问清单
当用户没有提供足够信息时,Agent 不要直接写代码。先用最少问题补齐关键输入。
20.1 必问问题
我需要先确认 6 件事,才能按自进化 Skill 跑得可靠:
1. 数据源是什么?Tushare、AmazingData、本地文件、数据库,还是研究平台导出?
2. 股票池是什么?全A、沪深300、中证500、中证1000、行业池,还是用户自定义列表?
3. 样本区间是否沿用默认切分:训练 2014-12-31~2018-12-28,验证 2018-12-28~2020-12-31,测试 2020-12-31~2026-04-30?
4. 是否允许我创建本地项目目录并写入配置、缓存、回测结果和报告?
5. 策略来源是什么?公开研报、用户自定义逻辑,还是已有代码?
6. 你希望本轮只做基线复刻,还是同时跑候选版本演化?
20.2 可默认假设
如果用户没有回答,Agent 可以采用以下默认值,但必须写入报告:
| 项目 | 默认值 | 报告说明 |
|---|---|---|
| 股票池 | 用户数据中可交易 A 股 | 若非完整历史全A,标注为代理池 |
| 调仓频率 | 21 个交易日近似月度 | 写明近似 |
| 目标持仓 | 30 只 | 来自默认协议 |
| 缓冲池 | 45 只 | 来自默认协议 |
| 单票上限 | 5% | 来自默认协议 |
| 交易成本 | 单边 30 bps | 若用户未指定,标注默认假设 |
| 年化天数 | 252 | 写入指标口径 |
| 无风险收益 | 0 | 若未提供无风险利率 |
20.3 不可默认假设
以下内容不能默认脑补:
- API token、数据库密码、私有数据下载权限。
- 完整历史全A股票池。
- ST、停牌、涨跌停处理已经完成。
- 行业分类是历史点位行业。
- 财务公告日已经正确避免未来函数。
- 研报内部原始代码和候选生成逻辑。
21. 跨 Agent 安装说明
本 Skill 是 Markdown-only 版本,适合上传到 Red Skill、Workbuddy、Hermes、OpenClaw、Codex 或其他支持 Markdown Skill 的 Agent 系统。
21.1 Red Skill / Workbuddy
安装方式:
- 创建新 Skill。
- Skill 名称填:
选股策略自进化研究 Skill。 - 简介填入本文件 frontmatter 的
description或red_skill_description.md的“一句话描述”。 - Skill 正文粘贴本 Markdown 全文。
- 如果平台支持默认 Prompt,填入“默认调用 Prompt”。
- 如果平台支持标签,使用:
量化研究、AI投研、回测审计、策略自进化、Auto Research。
使用注意:
- Red Skill 若不允许执行代码,则让 Agent 先生成项目文件和运行说明,再由用户在本地执行。
- Red Skill 若可调用文件工具,则必须按本文目录规范创建文件,不要只输出解释。
- Red Skill 若接入数据源连接器,先做字段映射,不要直接跑策略。
21.2 Hermes / OpenClaw
安装方式:
- 将本文件保存为
SKILL.md。 - 放入技能目录,例如
skills/stock-selection-evolution-research/SKILL.md。 - 如果系统支持技能索引,重启或刷新技能索引。
- 首次调用时,让 Agent 在工作区创建项目目录。
推荐目录:
stock-selection-evolution-research/
SKILL.md
projects/
{project_name}/
skill_config.yaml
reference_claims.json
data/
scripts/
result/
reports/
21.3 Codex / Claude Code
安装方式:
- 将本文件保存为
~/.codex/skills/stock-selection-evolution-research/SKILL.md或 Claude Skill 目录。 - 确认 YAML frontmatter 只有
name和description。 - 调用时明确说“使用 stock-selection-evolution-research”。
- 对涉及数据写入、长时间回测、外部 API 拉取的动作,先确认数据源和权限。
21.4 只有聊天能力、不能写文件的 Agent
如果 Agent 不能写文件或执行代码,应输出:
- 字段映射表。
skill_config.yaml文本。reference_claims.json文本。- Python 脚本草稿。
- 运行命令。
- 报告模板。
并明确说明:当前未实际运行,结果只是可执行方案。
22. 从零创建项目的执行蓝图
当用户允许写文件时,Agent 必须创建以下最小可运行项目。
22.1 目录结构
{project_root}/
README.md # 简短说明,可选
SKILL.md # 本 Skill 或项目专用协议
skill_config.yaml # 策略配置
reference_claims.json # 公开声明/用户策略描述结构化记录
data_contract_report.md # 数据契约审计
data/
raw/ # 原始数据,可选
canonical/
daily_bar.jsonl
daily_basic.jsonl
fina_indicator.jsonl
stock_basic.jsonl
benchmark.jsonl
feature_cache.jsonl
feature_cache.meta.json
scripts/
normalize_data.py
build_feature_cache.py
strategy_gen.py
strategy_backtest.py
evolve_versions.py
report_results.py
result/
skills/
{skill_name}/
v0.0.0/
evolution/
reports/
validation_report.md
test_review.md
如果用户要求严格单文件项目,至少创建:
skill_config.yaml
reference_claims.json
run_stock_selection_evolution.py
validation_report.md
22.2 文件创建顺序
reference_claims.jsonskill_config.yamldata_contract_report.md- canonical 数据文件
build_feature_cache.pystrategy_backtest.pyevolve_versions.pyvalidation_report.md
不要先写演化脚本再补数据契约。
22.3 最小运行命令约定
Agent 生成脚本时,应尽量支持以下命令:
python scripts/normalize_data.py --config skill_config.yaml
python scripts/build_feature_cache.py --config skill_config.yaml
python scripts/strategy_backtest.py --config skill_config.yaml
python scripts/evolve_versions.py --config skill_config.yaml
python scripts/report_results.py --config skill_config.yaml
如果系统只有一个脚本:
python run_stock_selection_evolution.py --config skill_config.yaml --mode all
python run_stock_selection_evolution.py --config skill_config.yaml --mode baseline
python run_stock_selection_evolution.py --config skill_config.yaml --mode evolve
python run_stock_selection_evolution.py --config skill_config.yaml --mode report
23. 数据源适配细则
23.1 Tushare 适配
常用接口映射:
| 目标表 | Tushare 接口 | 关键字段 | 备注 |
|---|---|---|---|
| 行情 | daily / pro_bar |
trade_date, ts_code, open, high, low, close, vol, amount |
注意复权 |
| 估值 | daily_basic |
pe_ttm, pb, total_mv, circ_mv, dv_ttm |
需按交易日对齐 |
| 财务 | fina_indicator |
roe, roic, debt_to_assets, netprofit_yoy, ocf_to_profit |
必须使用公告可得日期 |
| 股票元数据 | stock_basic |
ts_code, name, list_date, industry, exchange |
当前行业不等于历史行业 |
| 指数 | index_daily |
trade_date, close |
用于基准 |
Tushare 风险:
fina_indicator若按报告期直接 merge,容易未来函数。stock_basic行业通常是当前行业,不是历史行业。daily_basic缺失会影响 PE/PB/股息率。- 当前上市股票列表会产生幸存者偏差,完整复刻必须用历史可交易股票池。
23.2 AmazingData 适配
Agent 应先让用户或连接器提供 AmazingData 字段说明。不要假设字段名完全等于 Tushare。
适配步骤:
- 读取表列表和字段字典。
- 建立字段映射表。
- 抽样 5 个交易日检查日期、代码、复权、停牌、ST、行业字段。
- 检查财务数据是否有公告日。
- 将数据导出为 canonical JSONL 或直接在代码中映射。
字段映射模板:
| canonical | AmazingData 字段 | 类型 | 缺失率 | 转换规则 | 风险 |
|---|---|---|---:|---|---|
| symbol | ... | string | ... | 转成统一股票代码 | |
| trade_date | ... | date | ... | YYYY-MM-DD | |
| close | ... | float | ... | 复权/不复权说明 | |
| pe_ttm | ... | float | ... | 小于等于0置空 | |
| ann_date | ... | date | ... | 用于防未来函数 | |
23.3 本地文件适配
支持 CSV、JSONL、Parquet。
Agent 必须检查:
- 编码是否正确。
- 日期字段是否可解析。
- 股票代码是否统一。
- 是否有重复行。
- 是否有同一股票同一日期多条记录。
- 是否有明显异常值,如价格小于等于 0、市值小于等于 0。
- 是否有缺失严重的核心字段。
本地文件读取后统一输出为 canonical JSONL,除非用户明确要求保留原格式。
23.4 数据库适配
如果用户提供数据库连接,Agent 不得在报告中输出连接串、密码、token。
执行步骤:
- 只读取 schema 和样本行。
- 写字段映射表。
- 用只读查询导出需要字段。
- 不修改源表。
- 对大表使用分区日期增量拉取。
24. 未来函数防线
未来函数是本 Skill 的最高优先级风险之一。
24.1 财务数据可得性
财务数据必须按可得日期使用:
- 优先使用公告日
ann_date。 - 如果只有报告期
end_date,必须加保守滞后,例如 90 天或 120 天。 - 调仓日只能使用
ann_date <= rebalance_date的最新财务数据。 - 不允许把 2020 年年报数据用于 2020 年年报公告前的调仓。
伪代码:
def latest_financial_asof(financial_rows, symbol, rebalance_date):
rows = [
r for r in financial_rows[symbol]
if r["ann_date"] <= rebalance_date
]
return max(rows, key=lambda r: r["ann_date"]) if rows else None
如果没有公告日:
available_date = report_end_date + conservative_lag_days
并在报告中写:
> 财务数据缺少公告日,使用报告期后 120 天作为可得日近似。该处理可能改变因子时点,需要后续用公告日精确复核。
24.2 股票池未来函数
禁止使用“当前成分股”回推历史,除非明确标注为代理池。
股票池优先级:
- 历史全A可交易股票池。
- 历史指数成分或行业成分。
- 用户指定静态股票池。
- 当前股票池回推历史,只能作为代理实验。
报告必须写:
股票池口径:完整历史全A / 历史成分 / 静态代理池 / 当前成分回推
幸存者偏差风险:低 / 中 / 高
24.3 调仓时点
调仓时使用的信息必须满足:
- 行情:只能使用调仓日前一交易日或更早数据。
- 财务:只能使用调仓日已公告数据。
- 估值:只能使用调仓日前一交易日或当日收盘后可得数据;若假设次日开盘交易,必须写清。
- ST/停牌/涨跌停:必须用调仓日可得状态。
24.4 指标污染
以下行为视为测试集污染:
- 用测试集 Sharpe 决定参数。
- 看到测试集后改变候选版本顺序。
- 用测试集年度收益决定是否保留版本。
- 删除测试集差的候选版本。
- 把测试集表现写入下一轮候选生成提示词。
25. 回测引擎实现蓝图
25.1 核心伪代码
load_config()
load_canonical_data()
validate_data_contract()
build_or_load_feature_cache()
for split in ["train", "valid", "test"]:
dates = get_rebalance_dates(split.start, split.end)
portfolio = {}
equity = 1.0
for date in dates:
tradable_universe = filter_universe(date)
factor_panel = get_features_asof(date, tradable_universe)
scores = score_stocks(factor_panel, config.factor_groups)
target = construct_portfolio(scores, previous_portfolio=portfolio)
turnover = calculate_turnover(portfolio, target)
cost = turnover * cost_bps_per_side / 10000
period_return = calculate_forward_return(target, date, next_rebalance_date)
equity = equity * (1 + period_return - cost)
save_positions(date, target, scores, turnover)
portfolio = target
save_equity_curve(split)
calculate_metrics(split)
25.2 股票过滤顺序
推荐顺序:
- 是否上市满
min_listed_days。 - 是否有足够历史行情
min_history_days。 - 是否停牌。
- 是否 ST 或退市风险。
- 是否涨跌停导致不可买入/卖出。
- 成交额是否达到绝对阈值。
- 成交额是否达到横截面分位阈值。
- 市值是否达到分位阈值。
- ROE 是否为正。
- PE/PB/财务字段是否可用。
如果某项无法处理,写入 known_gaps,不要假装已处理。
25.3 打分流程
def score_stocks(panel, factor_groups):
group_scores = {}
for group_name, group in factor_groups.items():
factor_scores = []
for factor in group["factors"]:
values = panel[factor["name"]]
values = winsorize(values)
ranks = cross_sectional_rank(values)
if factor["direction"] == -1:
ranks = 1 - ranks
factor_scores.append(ranks)
group_scores[group_name] = average(factor_scores)
total_score = sum(group.weight * group_scores[name] for name, group in factor_groups.items())
return total_score.sort(descending=True)
25.4 缓冲池逻辑
缓冲池用于降低换手:
top_hold = ranked[:hold_n]
buffer = ranked[:buffer_n]
kept_old = [s for s in old_holdings if s in buffer]
new_needed = hold_n - len(kept_old)
new_names = [s for s in top_hold if s not in kept_old][:new_needed]
target = kept_old + new_names
如果 buffer_n < hold_n,配置无效。
25.5 权重计算
默认使用下行波动倒数:
risk = downside_volatility(symbol)
raw_weight = 1 / max(risk, epsilon)
weights = normalize(raw_weight)
weights = cap_at_max_weight(weights, max_weight_per_stock)
weights = renormalize(weights)
如果风险数据缺失:
- 优先使用普通波动率。
- 再回退等权。
- 必须写入报告。
26. 特征缓存设计
当股票数超过 300、调仓日超过 50,建议使用特征缓存。
26.1 缓存文件
feature_cache.jsonl 每行:
{
"rebalance_date": "2020-12-31",
"symbol": "000001.SZ",
"industry": "银行",
"listed_days": 7300,
"adv_20d": 123456789.0,
"market_cap": 12345678900.0,
"roe": 0.12,
"roic": 0.10,
"earnings_yield": 0.08,
"pb": 0.9,
"volatility": 0.22,
"downside_volatility": 0.15,
"dividend_yield_2y_avg": 0.03,
"netprofit_yoy": 0.10,
"profit_growth_stability": 0.75
}
feature_cache.meta.json:
{
"signature": "hash of data paths and feature-affecting parameters",
"created_at": "YYYY-MM-DD HH:MM:SS",
"rebalance_date_count": 133,
"row_count": 77064,
"symbol_count": 700,
"build_seconds": 104.4,
"inputs": {
"market_snapshot": "...",
"financial_snapshot": "...",
"daily_basic_snapshot": "...",
"stock_basic_snapshot": "..."
},
"feature_params": {
"min_history_days": 120,
"min_listed_days": 250,
"vol_lookback_days": 250,
"adv_lookback_days": 20,
"dividend_lookback_days": 504
}
}
26.2 缓存使用规则
- 缓存签名一致,允许使用。
- 缓存签名不一致,必须重建或现场计算。
- 因子权重变化不需要重建原始特征缓存。
- 影响原始特征的窗口参数变化,需要重建或回退现场计算。
- 缓存缺少某调仓日,不得静默跳过,必须报错或回退。
27. 候选版本库
Agent 可以从下列候选库开始,但必须根据用户策略和数据质量裁剪。
27.1 基线
- version: v0.0.0
note: base stable low-vol value profile
set: {}
27.2 价值基础池
- version: v0.1.1
note: looser value residual pool
set: {strategy.value_residual_keep_quantile: 0.25}
- version: v0.1.2
note: mildly looser value residual pool
set: {strategy.value_residual_keep_quantile: 0.28}
- version: v0.1.3
note: stricter value residual pool
set: {strategy.value_residual_keep_quantile: 0.40}
- version: v0.1.4
note: lower PE ceiling
set: {strategy.max_pe_ttm: 30.0}
- version: v0.1.5
note: higher PE ceiling
set: {strategy.max_pe_ttm: 50.0}
27.3 流动性和市值
- version: v0.1.6
note: lower market-cap floor
set: {strategy.market_cap_quantile_floor: 0.25}
- version: v0.1.7
note: higher market-cap floor
set: {strategy.market_cap_quantile_floor: 0.45}
- version: v0.1.8
note: lower liquidity floor
set: {strategy.adv_quantile_floor: 0.30}
- version: v0.1.9
note: higher liquidity floor
set: {strategy.adv_quantile_floor: 0.50}
27.4 因子权重
- version: v0.2.1
note: mild dividend-defense tilt
set: {weight.leader: 0.30, weight.dividend_defense: 0.40, weight.growth_stability: 0.30}
- version: v0.2.2
note: strong dividend-defense tilt
set: {weight.leader: 0.25, weight.dividend_defense: 0.50, weight.growth_stability: 0.25}
- version: v0.2.3
note: quality leader tilt
set: {weight.leader: 0.45, weight.dividend_defense: 0.30, weight.growth_stability: 0.25}
- version: v0.2.4
note: growth stability tilt
set: {weight.leader: 0.25, weight.dividend_defense: 0.30, weight.growth_stability: 0.45}
27.5 组合约束
- version: v0.2.5
note: lower single-name cap
set: {strategy.max_weight_per_stock: 0.04}
- version: v0.2.6
note: higher single-name cap
set: {strategy.max_weight_per_stock: 0.06}
- version: v0.2.7
note: more concentrated portfolio
set: {strategy.hold_n: 25, strategy.buffer_n: 40}
- version: v0.2.8
note: more diversified portfolio
set: {strategy.hold_n: 35, strategy.buffer_n: 50}
- version: v0.2.9
note: wider retention buffer
set: {strategy.buffer_n: 60}
27.6 调仓和风险窗口
- version: v0.2.10
note: lower turnover cadence
set: {strategy.rebalance_days: 42}
- version: v0.2.11
note: higher turnover cadence
set: {strategy.rebalance_days: 10}
- version: v0.2.12
note: shorter risk lookback
set: {strategy.vol_lookback_days: 120}
- version: v0.2.13
note: longer risk lookback
set: {strategy.vol_lookback_days: 500}
27.7 组合候选
- version: v0.2.20
note: moderate liquidity and market-cap floors
set:
strategy.adv_quantile_floor: 0.35
strategy.market_cap_quantile_floor: 0.30
- version: v0.2.24
note: lower drawdown retention profile
set:
strategy.buffer_n: 60
strategy.max_weight_per_stock: 0.04
strategy.rebalance_days: 21
- version: v0.2.27
note: public-report-aligned final candidate
set:
strategy.hold_n: 30
strategy.buffer_n: 45
strategy.max_weight_per_stock: 0.05
strategy.value_residual_keep_quantile: 0.25
strategy.adv_quantile_floor: 0.40
strategy.market_cap_quantile_floor: 0.35
weight.leader: 0.35
weight.dividend_defense: 0.40
weight.growth_stability: 0.25
27.8 候选库使用纪律
- 不需要一次跑完所有候选。数据慢时先跑 5-8 个代表候选。
- 每轮候选应围绕一个主题,避免同时改太多变量。
- 如果候选改变了原始特征窗口,确认特征缓存是否需要重建。
- 如果连续多轮验证集提升但测试集下降,停止追加候选,先做过拟合复盘。
28. 验收测试
Agent 在交付前必须完成以下检查。
28.1 文件验收
- [ ] `skill_config.yaml` 存在。
- [ ] `reference_claims.json` 存在。
- [ ] canonical 数据文件存在。
- [ ] `result/skills/{skill_name}/v0.0.0/` 存在。
- [ ] `validation_report` 存在。
- [ ] 若跑演化,`evolution_summary.csv` 存在。
- [ ] 未覆盖旧版本目录。
28.2 数据验收
- [ ] 行情覆盖训练、验证、测试三段。
- [ ] 财务数据有公告日或保守滞后处理。
- [ ] 股票代码格式统一。
- [ ] 调仓日特征行数合理。
- [ ] 股票池口径已说明。
- [ ] 缺失字段和代理字段已列出。
28.3 逻辑验收
- [ ] 基线版本先于候选版本运行。
- [ ] 验证集用于保留版本。
- [ ] 测试集未用于自动选择。
- [ ] 候选版本变更可解释。
- [ ] 失败版本也保留在账本。
- [ ] 输出包含已知缺口。
28.4 指标验收
- [ ] 每个指标写明样本区间。
- [ ] 年化天数写明。
- [ ] 是否含交易成本写明。
- [ ] 最大回撤和换手没有被省略。
- [ ] 没有只展示最优版本。
- [ ] 没有把回测写成未来收益。
29. 常见失败和恢复
29.1 数据覆盖不足
症状:
- 训练集或验证集无数据。
- 某段样本只有很少交易日。
- 股票数太少。
处理:
- 输出实际覆盖区间。
- 建议新的样本切分。
- 降级为 smoke test。
- 不输出策略有效性结论。
29.2 财务字段未来函数风险
症状:
- 财务表只有报告期,没有公告日。
- 因子在报告期结束日立即可用。
处理:
- 加 90-120 天保守滞后。
- 把该处理写入 known gaps。
- 重新跑基线。
- 比较滞后前后差异。
29.3 股票池偏差
症状:
- 用当前上市股票回推历史。
- 指数成分不是历史成分。
- 剔除了退市股票。
处理:
- 标注为代理池。
- 不与研报指标做强对齐。
- 优先补历史股票池。
- 先比较 500/700/全A 不同股票池的基线差异。
29.4 版本过拟合
症状:
- 验证集 Sharpe 连续提升。
- 测试集 Sharpe 下降。
- 最大回撤恶化。
- 收益集中在少数年份。
处理:
- 停止追加候选。
- 输出验证集保留路径。
- 输出测试集人工复核。
- 允许拒绝验证集冠军。
- 回到更早、更稳的版本。
29.5 运行太慢
症状:
- 每个候选都重新构造全量特征。
- 全A池循环耗时过长。
处理:
- 建调仓日特征缓存。
- 缓存不受因子权重影响的原始特征。
- 将组合收益矩阵预计算。
- 先跑代表候选,再扩展候选库。
29.6 字段口径不一致
症状:
- ROIC、股息率、收入规模、行业分类与研报定义不一致。
处理:
- 标注代理口径。
- 输出“公开披露 / 本地实现 / 差异影响”三列表。
- 不声称完全复现。
- 优先补影响最大的字段。
30. 交付物清单
完整运行后,Agent 应交付:
1. data_contract_report.md
2. skill_config.yaml
3. reference_claims.json
4. baseline validation_report.md/json
5. metrics_table.csv
6. equity_curve_train.csv
7. equity_curve_valid.csv
8. equity_curve_test.csv
9. positions_train.jsonl
10. positions_valid.jsonl
11. positions_test.jsonl
12. evolution_ledger.json
13. retained_versions.json
14. evolution_summary.csv
15. test_review.md
16. known_gaps.md
17. next_iteration_plan.md
如果某项未生成,报告中必须解释原因。
31. 对外表达规范
用于小红书、公众号、报告摘要或对外分享时,必须采用研究表达:
推荐说法:
- “本地 clean-room 复刻实验。”
- “测试集回测样本结果。”
- “验证集冠军在测试集出现外推折损。”
- “该版本只用于研究复核。”
- “股票池和因子口径仍需继续对齐。”
禁止说法:
- “这个策略可以赚钱。”
- “未来大概率跑赢。”
- “直接跟这个策略。”
- “稳赚/保本/无风险。”
- “完美复现华泰内部代码。”
对外摘要模板:
这是一套选股策略自进化研究 Skill,用于把公开研报或已有选股逻辑拆成可配置、可回测、可版本留痕的 AI 投研流程。它强调训练集、验证集、测试集隔离:训练集用于生成,验证集用于保留,测试集只做最终人工复核。所有结果仅供学习研究,不构成投资建议。
32. 更严格的质量门
如果用户要求“尽可能接近原研报”,Agent 必须提高质量门:
- 股票池必须从代理池升级到历史全A。
- ST、停牌、涨跌停必须逐日处理。
- 行业分类必须尽量使用历史行业。
- 财务字段必须使用公告日。
- 股息因子必须用真实分红数据,而不是只用估值表代理。
- ROIC、收入规模、利润稳定性必须逐项校验。
- 交易成本、调仓价格、复权口径必须写清。
- 输出与公开披露指标的差异归因。
差异归因模板:
| 差异来源 | 当前实现 | 原研报/目标口径 | 可能影响 | 优先级 |
|---|---|---|---|---|
| 股票池 | 700只代理池 | 完整历史全A | 高 | P0 |
| 行业分类 | 当前行业 | 历史行业 | 中 | P1 |
| 股息因子 | daily_basic近似 | 分红明细两年均值 | 中 | P1 |
| 涨跌停 | 未精确处理 | 逐日可交易约束 | 高 | P0 |
33. 二次迭代路线
当第一版跑通后,按以下顺序升级,不要直接继续调参:
- 补全历史股票池。
- 补公告日财务数据。
- 补 ST、停牌、涨跌停。
- 补历史行业分类。
- 补真实分红明细。
- 补组合收益矩阵缓存。
- 再扩展候选版本。
- 最后才讨论是否增加新因子。
每次升级只改一个大类,并输出升级前后基线对照。
34. 高质量回答示例
当用户问“这个策略现在怎么样”时,Agent 应回答:
当前只能说研究复刻进展,不能说策略可用。
已完成:
- 三段样本隔离。
- 基线回测。
- 候选版本演化。
- 验证集保留。
- 测试集人工复核。
关键发现:
- 股票池从 500 只代理扩到 700 只代理后,测试 Sharpe 从 0.458 提升到 0.677,说明数据边界是第一变量。
- v0.2.20 测试 Sharpe 0.753,但 v0.2.27 虽是验证集冠军,测试 Sharpe 回落到 0.458,说明继续追验证集会有外推折损。
不能下的结论:
- 不能说未来会有效。
- 不能说已经完全复现原研报。
- 不能给任何交易建议。
下一步:
- 补完整历史全A。
- 补 ST/停牌/涨跌停。
- 补历史行业和真实分红口径。
35. 最小可执行 Prompt 库
35.1 数据契约检查
使用选股策略自进化研究 Skill。请先读取我提供的数据文件,只做数据契约检查和字段映射,不要跑回测。输出字段覆盖、缺失率、样本区间、股票数、未来函数风险和最小补数建议。
35.2 基线复刻
使用选股策略自进化研究 Skill。基于当前数据创建 v0.0.0 基线版本,按训练/验证/测试三段回测,保存配置、持仓、净值、指标和验证报告。不要生成候选版本。
35.3 候选演化
使用选股策略自进化研究 Skill。在已有 v0.0.0 基线基础上,围绕价值池、流动性、市值、因子权重、持仓和调仓频率生成候选版本。只用验证集决定保留,测试集只做最终复核。
35.4 测试集复核
使用选股策略自进化研究 Skill。请读取演化账本,比较验证集冠军和测试集表现较稳版本,输出测试集人工复核意见。不要用测试集重新选择参数。
35.5 对外内容改写
使用选股策略自进化研究 Skill。请把这次复刻结果改写成研究型对外摘要,必须保留风险提示,不要提供投资建议,不要把回测结果写成未来收益。
36. Agent 执行协议
Agent 使用本 Skill 时必须按阶段工作,每个阶段都要有明确产物和退出条件。
36.1 Phase A:研究对象锁定
目标:确认要复刻的不是“收益率数字”,而是一套策略研究机制。
必须产出:
## 研究对象锁定
| 项目 | 内容 |
|---|---|
| 目标研究 | |
| 目标市场 | |
| 股票池 | |
| 样本切分 | |
| 调仓频率 | |
| 持仓数量 | |
| 交易成本 | |
| 因子组 | |
| 组合约束 | |
| 已知缺口 | |
退出条件:
- 用户提供了数据源或明确说明暂时只做框架设计。
- Agent 明确了复刻层级:概念复刻、代理池复刻、历史全A复刻、接近原研报复刻。
- Agent 写明哪些结论不能直接从公开材料得到。
36.2 Phase B:数据契约审计
目标:先判断数据能不能支撑研究,而不是先写回测。
必须产出:
## 数据契约审计
| 数据表 | 是否存在 | 日期范围 | 股票数 | 关键字段覆盖 | 主要风险 |
|---|---:|---:|---:|---|---|
| market | | | | | |
| daily_basic | | | | | |
| financial | | | | | |
| dividend | | | | | |
| stock_basic | | | | | |
| industry | | | | | |
| trade_status | | | | | |
| benchmark | | | | | |
退出条件:
- 能解释为什么训练集是否覆盖 2014-12-31 至 2018-12-28。
- 能解释是否覆盖验证集 2018-12-28 至 2020-12-31。
- 能解释是否覆盖测试集 2020-12-31 至 2026-04-30。
- 能识别财务字段是否有公告日。
- 能识别股票池是否存在幸存者偏差。
36.3 Phase C:基线先行
目标:先建立一个可解释、可重复的 v0.0.0,不得直接做多版本搜索。
必须产出:
configs/v0.0.0.yamlresults/v0.0.0/holdings.csvresults/v0.0.0/nav.csvresults/v0.0.0/metrics_by_split.csvresults/v0.0.0/yearly_returns.csvresults/v0.0.0/run_manifest.json
退出条件:
- 基线结果可复跑。
- 每个指标都有日期区间、交易日数量和年化口径。
- Agent 能说明基线为什么是基线,而不是调参后的版本。
36.4 Phase D:候选版本演化
目标:只在明确协议内做小步变化。
候选版本只能来自以下变化类型:
| 类型 | 示例 | 是否允许一次多改 |
|---|---|---|
| 股票池 | 500 代理池升级到 700 代理池、代理池升级到历史全A | 否 |
| 价值池阈值 | PE/PB/股息率/ROE/ROIC 阈值 | 否 |
| 因子权重 | 价值、防御、成长、质量、风险权重 | 否 |
| 组合约束 | 持仓数量、缓冲池、单票上限、行业上限 | 否 |
| 调仓机制 | 调仓频率、调仓日、延迟交易 | 否 |
| 风控参数 | 波动率窗口、下行波动率、流动性过滤 | 否 |
每个候选版本必须写明:
## 候选版本说明
| 字段 | 内容 |
|---|---|
| version | |
| parent_version | |
| changed_one_thing | |
| reason | |
| expected_effect | |
| validation_result | |
| test_review_status | not_opened / reviewed_once / rejected |
| keep_or_reject | |
36.5 Phase E:测试集人工复核
目标:测试集只用于“最后一次人工审计”,不用于挑参数。
Agent 必须在报告中声明:
测试集使用声明:
本轮测试集结果只用于最终复核,不用于新增候选版本、调参、排序或重新选择冠军版本。如果测试集暴露出风险,只允许回到研究问题和数据口径层面做下一轮独立实验设计。
37. 代码实现接口标准
当 Agent 有文件写入和代码运行能力时,必须优先生成模块化实现。已有项目可映射到现有文件名,但接口语义应保持一致。
37.1 推荐文件结构
stock_evolution_research/
README.md
configs/
base.yaml
v0.0.0.yaml
data/
raw/
canonical/
cache/
src/
io_contract.py
data_quality.py
universe.py
factors.py
portfolio.py
backtest.py
evolution.py
metrics.py
report.py
plots.py
tests/
test_no_future.py
test_schema.py
test_backtest_math.py
test_portfolio_constraints.py
results/
version_ledger.csv
skills/
stock_selection_evolution/
v0.0.0/
37.2 io_contract.py
必须负责把不同数据源映射为 canonical schema。
推荐接口:
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
import pandas as pd
@dataclass(frozen=True)
class DataCoverage:
table: str
start_date: str
end_date: str
row_count: int
symbol_count: int
missing_required_fields: list[str]
risk_flags: list[str]
def load_market(path_or_conn: str) -> pd.DataFrame:
"""Return market rows with date, symbol, open, high, low, close, adj_factor, volume, amount."""
def load_daily_basic(path_or_conn: str) -> pd.DataFrame:
"""Return valuation and trading rows with date, symbol, pe_ttm, pb, ps_ttm, dv_ttm, total_mv, circ_mv, turnover_rate."""
def load_financial(path_or_conn: str) -> pd.DataFrame:
"""Return financial rows with ann_date, end_date, symbol, revenue, net_profit, roe, roic and balance sheet fields."""
def audit_coverage(tables: dict[str, pd.DataFrame], required: dict[str, list[str]]) -> list[DataCoverage]:
"""Return table-level coverage and risk flags before any backtest is allowed."""
37.3 data_quality.py
必须先于回测运行。
推荐接口:
def assert_unique_key(df, keys: list[str], table_name: str) -> None:
"""Fail if duplicate canonical keys exist."""
def assert_date_range(df, date_col: str, start: str, end: str, table_name: str) -> None:
"""Fail if required sample window is not covered."""
def detect_price_anomalies(market) -> pd.DataFrame:
"""Return rows with negative price, zero close, impossible high/low, or extreme split-like jumps."""
def detect_future_financial_join(financial, feature_dates) -> pd.DataFrame:
"""Return rows where feature date uses financial data before ann_date is available."""
def write_data_contract_report(audit_rows, output_path: str) -> None:
"""Write a Markdown report that explains coverage and blocking risks."""
37.4 universe.py
必须逐日生成股票池,不能只用期末股票列表。
推荐接口:
def build_tradable_universe(
trade_date: str,
stock_basic,
market,
trade_status=None,
st_status=None,
index_membership=None,
min_listed_days: int = 250,
) -> pd.DataFrame:
"""Return symbols tradable on trade_date after listing age, ST, suspension and limit filters."""
def build_proxy_universe(
trade_date: str,
stock_basic,
market,
size: int,
industry_col: str | None = None,
) -> pd.DataFrame:
"""Return a deterministic proxy pool when full historical all-A is unavailable."""
37.5 factors.py
必须只使用当时已可获得的数据。
推荐接口:
def asof_financial_snapshot(financial, trade_date: str, min_lag_days: int = 0):
"""Return latest financial records with ann_date <= trade_date."""
def compute_base_features(trade_date: str, canonical: dict, config: dict) -> pd.DataFrame:
"""Return one row per symbol with value, quality, growth, dividend, liquidity and risk features."""
def winsorize_by_date(df, cols, lower=0.01, upper=0.99):
"""Clip cross-sectional outliers at each rebalance date."""
def zscore_by_date(df, cols):
"""Standardize cross-sectional features at each rebalance date."""
def score_candidates(features, factor_weights: dict) -> pd.DataFrame:
"""Return score and factor contribution columns."""
37.6 portfolio.py
必须把选股和权重分开,便于审计。
推荐接口:
def select_with_buffer(
scored,
previous_holdings,
hold_n: int,
buffer_n: int,
) -> pd.DataFrame:
"""Keep existing names inside buffer first, then fill by rank."""
def assign_weights(
selected,
scheme: str,
max_weight: float,
risk_col: str | None = None,
) -> pd.DataFrame:
"""Return symbol and target_weight."""
def validate_portfolio_constraints(weights, max_weight: float, hold_n: int) -> list[str]:
"""Return violations, not silent fixes."""
37.7 backtest.py
必须明确调仓日、成交价格、费用、持仓收益计算。
推荐接口:
def generate_rebalance_dates(calendar, start: str, end: str, rebalance_days: int) -> list[str]:
"""Return deterministic rebalance dates from trading calendar."""
def run_backtest(config: dict, canonical: dict, features_cache=None) -> dict:
"""Return nav, holdings, trades, metrics_by_split and manifest."""
def compute_daily_portfolio_returns(holdings, market_returns, cost_bps: float) -> pd.DataFrame:
"""Return daily portfolio returns after turnover cost."""
37.8 evolution.py
必须生成候选版本账本。
推荐接口:
def generate_candidate_configs(base_config: dict, candidate_library: list[dict]) -> list[dict]:
"""Return candidate configs where each candidate changes one controlled dimension."""
def evaluate_on_validation(results: list[dict], selection_rule: dict) -> pd.DataFrame:
"""Rank candidates by validation split only."""
def freeze_test_review(version_id: str, reviewer_note: str) -> None:
"""Record one-time test review without allowing further automatic tuning."""
37.9 report.py 和 plots.py
必须生成可复核报告和图表数据,而不是只生成图片。
推荐接口:
def write_version_ledger(results, output_csv: str) -> None:
"""Write every version, parent, changed field, validation metrics and review status."""
def write_validation_report(context: dict, output_md: str) -> None:
"""Write conclusion-first Markdown report with known gaps and next steps."""
def export_chart_data(results, output_dir: str) -> None:
"""Write CSV files behind every chart so charts can be audited."""
38. Canonical 数据样例
Agent 应把所有数据源统一为以下样例形态。字段可以更多,但必需字段不能少。
38.1 market.jsonl
{"date":"2021-01-04","symbol":"000001.SZ","open":18.70,"high":19.10,"low":18.40,"close":18.90,"pre_close":18.34,"adj_factor":112.31,"volume":155000000,"amount":2920000000.0}
必需检查:
date + symbol唯一。high >= max(open, close, low)。low <= min(open, close, high)。close > 0。volume >= 0。- 复权方式在报告中声明。
38.2 daily_basic.jsonl
{"date":"2021-01-04","symbol":"000001.SZ","pe_ttm":10.5,"pb":1.08,"ps_ttm":2.31,"dv_ttm":2.15,"total_mv":360000000000.0,"circ_mv":310000000000.0,"turnover_rate":0.87}
必需检查:
- 极端负值是否合理。
- 市值单位是否统一。
- 股息率单位是百分比还是小数。
pe_ttm为负时是否参与价值排序。
38.3 financial.jsonl
{"ann_date":"2021-04-20","end_date":"2020-12-31","symbol":"000001.SZ","revenue":153542000000.0,"net_profit":28928000000.0,"roe":0.103,"roic":0.078,"total_assets":4468514000000.0,"total_liab":4101582000000.0}
必需检查:
- 任何特征日只能使用
ann_date <= trade_date的记录。 - 如果只有
end_date没有ann_date,必须降级可信等级。 - 同一
symbol + end_date多条记录时,优先使用最新修订还是首次公告必须声明。
38.4 dividend.jsonl
{"ann_date":"2021-05-10","ex_date":"2021-06-18","symbol":"000001.SZ","cash_dividend":0.18,"base_shares":19405900000}
必需检查:
- 股息用公告日、除权日还是实施日必须声明。
- 近两年股息率必须基于当时可见信息。
- 不能用未来分红填充过去特征。
38.5 stock_basic.jsonl
{"symbol":"000001.SZ","name":"示例银行","list_date":"1991-04-03","delist_date":null,"exchange":"SZSE","board":"main"}
必需检查:
- 退市股票是否保留。
- 股票名称变更不应导致代码断裂。
- 上市不足
min_listed_days的股票是否剔除。
38.6 trade_status.jsonl
{"date":"2021-01-04","symbol":"000001.SZ","is_trade":true,"is_st":false,"is_suspended":false,"is_limit_up":false,"is_limit_down":false}
必需检查:
- 无法买入涨停、无法卖出跌停的处理方式。
- 停牌日收益是否沿用上一可交易价格。
- ST 状态是否使用历史状态,而不是当前状态。
39. 数据质量检查清单
回测前必须运行以下检查,并将结果写入 data_contract_report.md。
| 检查项 | 失败条件 | 处理方式 |
|---|---|---|
| 日期覆盖 | 任一样本段缺少交易日 | 停止回测,报告缺口 |
| 主键唯一 | date + symbol 重复 |
停止回测,输出重复样例 |
| 股票数异常 | 样本内股票数突变无法解释 | 标注股票池风险 |
| 价格异常 | 负价格、零价格、高低价冲突 | 停止或剔除并说明 |
| 复权口径 | 前复权/后复权/不复权混用 | 停止回测 |
| 财务可得性 | ann_date > trade_date 被使用 |
停止回测 |
| 股票池幸存者 | 只用期末成分或当前全A | 降级为代理复刻 |
| ST/停牌缺失 | 无法逐日过滤 | 标注为高优先级缺口 |
| 行业历史缺失 | 只用当前行业 | 标注行业偏差 |
| 分红缺失 | 股息因子无法真实计算 | 使用代理因子并标注 |
数据质量报告模板:
# 数据契约检查报告
## 结论
当前数据可信等级:高 / 中 / 低 / 阻塞
## 样本覆盖
| 样本段 | 目标区间 | 实际区间 | 交易日数 | 是否通过 |
|---|---|---|---:|---|
| 训练集 | 2014-12-31 至 2018-12-28 | | | |
| 验证集 | 2018-12-28 至 2020-12-31 | | | |
| 测试集 | 2020-12-31 至 2026-04-30 | | | |
## 字段覆盖
| 表 | 必需字段缺失 | 可选字段缺失 | 风险 |
|---|---|---|---|
## 阻塞项
## 可降级项
## 最小补数建议
40. 单元测试样例
如果 Agent 能写代码,至少生成以下测试。测试不必覆盖全部业务,但必须覆盖未来函数和组合约束。
40.1 Schema 测试
def test_market_unique_date_symbol(market):
duplicated = market.duplicated(["date", "symbol"]).sum()
assert duplicated == 0
def test_market_price_sanity(market):
assert (market["close"] > 0).all()
assert (market["high"] >= market[["open", "close", "low"]].max(axis=1)).all()
assert (market["low"] <= market[["open", "close", "high"]].min(axis=1)).all()
40.2 财务未来函数测试
def test_financial_asof_no_future(financial_features):
assert (financial_features["ann_date"] <= financial_features["feature_date"]).all()
40.3 样本隔离测试
def test_split_boundaries(metrics_by_split):
assert set(metrics_by_split["split"]) == {"train", "valid", "test"}
assert metrics_by_split.loc[metrics_by_split["split"] == "train", "end_date"].max() <= "2018-12-28"
assert metrics_by_split.loc[metrics_by_split["split"] == "valid", "start_date"].min() >= "2018-12-28"
assert metrics_by_split.loc[metrics_by_split["split"] == "test", "start_date"].min() >= "2020-12-31"
40.4 权重约束测试
def test_portfolio_weight_sum(holdings):
sums = holdings.groupby("date")["weight"].sum()
assert ((sums - 1.0).abs() < 1e-6).all()
def test_portfolio_max_weight(holdings, max_weight=0.05):
assert (holdings["weight"] <= max_weight + 1e-9).all()
40.5 测试集污染测试
def test_version_ledger_no_test_selection(version_ledger):
selected = version_ledger[version_ledger["selected_for_final_review"] == True]
assert not selected["selection_reason"].str.contains("test", case=False, na=False).any()
41. 回测计算细节
Agent 必须把回测口径写成可审计规则。
41.1 调仓日
推荐规则:
- 使用交易日历生成调仓日。
- 第一个调仓日为样本起点之后第一个可交易日。
- 每隔
rebalance_days个交易日调仓一次。 - 调仓信号使用调仓日前一交易日收盘后可见数据。
- 成交价格默认使用下一交易日开盘价;若无开盘价则降级为下一交易日收盘价并声明。
41.2 收益计算
日收益推荐:
portfolio_return_t = sum(weight_{i,t-1} * stock_return_{i,t}) - turnover_cost_t
换手成本推荐:
turnover_t = 0.5 * sum(abs(target_weight_i - current_weight_i))
turnover_cost_t = turnover_t * cost_bps_per_side / 10000
若使用买卖双边成本,可写为:
turnover_cost_t = sum(abs(target_weight_i - current_weight_i)) * cost_bps_per_side / 10000
必须在报告中声明采用哪一种。
41.3 停牌、涨跌停和无法成交
推荐降级顺序:
- 有逐日状态:按真实可交易状态处理。
- 无逐日状态但有成交量:成交量为 0 视为不可交易候选。
- 无状态也无成交量:标注为高风险回测,只能做概念验证。
涨跌停处理:
- 涨停日不允许新买入。
- 跌停日不允许卖出。
- 已持仓无法卖出时,保留至下一可卖日。
- 无法买入时,现金保留或按剩余标的重新归一,必须声明。
41.4 年化口径
默认:
annual_return = (ending_nav / beginning_nav) ** (252 / trading_days) - 1
annual_vol = daily_return_std * sqrt(252)
sharpe = annual_return / annual_vol
max_drawdown = min(nav / cumulative_max(nav) - 1)
calmar = annual_return / abs(max_drawdown)
如果样本不足 60 个交易日,不输出年化结论,只输出区间收益。
42. 华泰式图表复刻规格
为了让研究报告有可读性,Agent 应输出图表数据和图表,而不是只输出表格。图表可借鉴公开研报的表达结构,但必须标注本地数据和 clean-room 口径。
42.1 策略自进化框架图
用途:解释协议层、版本层、隔离层和策略核心。
图表规格:
chart_id: figure_strategy_framework
type: framework_diagram
required_elements:
- protocol_layer
- version_layer
- isolation_layer
- strategy_core
- factor_branch
- rule_branch
- risk_branch
- train_valid_test_boxes
message: 策略自进化不是随机调参,而是协议约束下的版本化实验。
42.2 样本切分表
用途:解释训练集、验证集、测试集的权限。
图表规格:
chart_id: figure_sample_split
type: table
columns:
- sample
- date_range
- permission
- use_case
rows:
- ["训练集", "2014-12-31 至 2018-12-28", "可用于生成和初筛", "建立基础逻辑,观察长期稳定性"]
- ["验证集", "2018-12-28 至 2020-12-31", "可用于版本保留判断", "比较迭代版本,决定是否回滚"]
- ["测试集", "2020-12-31 至 2026-04-30", "不进入自动调参闭环", "最终人工复核,评估真实外推风险"]
message: 样本权限比收益数字更重要。
42.3 Skill 组成表
用途:让读者知道这个 Skill 包含哪些文件和职责。
图表规格:
chart_id: figure_skill_components
type: table
columns:
- component
- file_or_directory
- function
- design_note
rows:
- ["策略说明", "SKILL.md", "描述策略逻辑和禁止事项", "把策略内核写成协议"]
- ["参数配置", "skill_config.yaml", "定义股票池、因子组、权重和调仓参数", "参数入参,不默认改代码"]
- ["组合生成", "strategy_gen.py", "生成持仓、得分和调仓", "不在每轮实验中改公共工具"]
- ["分段回测", "strategy_backtest.py", "训练/验证/测试分别回测", "测试集只做最终复核"]
- ["版本目录", "results/skills/{skill_name}/v{version}/", "保存配置和结果", "避免覆盖旧结果"]
message: 好的 Skill 首先是可复核的工程结构。
42.4 复用比率趋势图
用途:展示每次迭代相对上一版本的持仓变化。
数据字段:
version, split, reuse_ratio, changed_names, total_holdings, version_label
图表规则:
- x 轴为版本顺序。
- y 轴为复用比率。
- 训练、验证、测试三条线分色。
- 每个点标注版本号。
- 如果某一版本复用比率大幅下降,报告中解释原因。
message:
复用比率越高,说明版本更像小步迭代;复用比率骤降通常意味着策略逻辑发生漂移。
42.5 测试集净值曲线
用途:展示保留版本在测试集的表现分化。
数据字段:
date, version, nav, split
图表规则:
- x 轴为日期。
- y 轴为净值。
- 初始版本、验证集冠军、测试集较稳版本必须突出显示。
- 其他版本可用浅色线。
- 图例写清版本号和迭代次数。
- 标注样本区间和交易成本。
42.6 业绩指标热力图
用途:快速识别每个版本的强弱项。
数据字段:
version, annual_return, annual_vol, sharpe, max_drawdown, calmar, win_rate_monthly, win_rate_quarterly, win_rate_half_year, win_rate_yearly
图表规则:
- 行为版本。
- 列为指标。
- 收益、Sharpe、Calmar 越高越好。
- 波动、回撤越低越好,颜色方向需反转。
- 表下注明交易成本、样本区间、数据源。
42.7 版本贡献拆解图
用途:解释哪些迭代贡献了测试集复用比率或净值改善。
数据字段:
version, parent_version, metric_before, metric_after, delta, changed_field, note
图表规则:
- 左侧可画小型版本路径。
- 右侧画瀑布图或阶梯图。
- 每个变化点标注“改了什么”和“指标变化”。
- 不要只标收益,必须同时标风险指标变化。
43. 年度收益和归因模板
用户询问“每年收益怎么样”时,Agent 应输出年度表,而不是只说总收益。
年度收益表:
| 年份 | 策略收益 | 基准收益 | 超额收益 | 最大回撤 | 换手率 | 主要解释 |
|---:|---:|---:|---:|---:|---:|---|
| 2021 | | | | | | |
| 2022 | | | | | | |
| 2023 | | | | | | |
| 2024 | | | | | | |
| 2025 | | | | | | |
| 2026YTD | | | | | | |
归因说明必须包含:
- 该年市场环境。
- 策略暴露是否偏价值、低波、股息、质量或小市值。
- 是否有单一年份贡献过高。
- 是否存在回撤集中期。
- 是否与版本迭代有关。
如果没有基准数据,只能输出策略年度收益,并说明缺少基准导致无法计算超额收益。
44. 人工复核评分表
测试集复核不能只看收益。Agent 应输出评分表。
| 维度 | 权重 | 评分 | 证据 | 结论 |
|---|---:|---:|---|---|
| 数据完整性 | 20% | | | |
| 无未来函数 | 20% | | | |
| 样本隔离 | 15% | | | |
| 测试集稳健性 | 15% | | | |
| 回撤可接受性 | 10% | | | |
| 换手和成本敏感性 | 10% | | | |
| 与研报机制一致性 | 10% | | | |
评分解释:
- 90 分以上:可作为高可信研究复刻,但仍不是投资建议。
- 75-89 分:可作为阶段性复刻,需要补数据和做压力测试。
- 60-74 分:只能作为概念验证。
- 60 分以下:不应继续讨论策略有效性,应先修数据或协议。
45. 差异归因协议
当本地结果和目标研报差异明显时,Agent 不得直接继续调参,而要先做差异归因。
差异归因优先级:
- 样本区间是否一致。
- 股票池是否一致。
- 数据源和复权口径是否一致。
- 财务公告日是否一致。
- ST、停牌、涨跌停是否处理一致。
- 因子定义是否一致。
- 因子标准化和缺失值处理是否一致。
- 调仓日和成交价格是否一致。
- 交易成本是否一致。
- 组合约束是否一致。
输出模板:
## 与目标结果差异归因
| 排名 | 差异来源 | 当前实现 | 目标口径 | 证据 | 影响方向 | 下一步 |
|---:|---|---|---|---|---|---|
| 1 | 股票池 | | | | | |
| 2 | 财务可得性 | | | | | |
| 3 | 可交易约束 | | | | | |
只有当差异归因表完成后,才能进入下一轮迭代。
46. 完成定义
只有同时满足以下条件,Agent 才能说“本轮 Skill 运行完成”:
- 数据契约报告已生成。
- 样本切分已写入配置。
- 基线版本已独立保存。
- 候选版本账本已生成。
- 验证集保留规则已执行。
- 测试集没有被用于自动调参。
- 年度收益、关键指标、净值、持仓、换手至少有一种可审计输出。
- 图表背后的 CSV 数据已保存。
- 已知缺口已写入
known_gaps.md。 - 最小下一步已写入
next_iteration_plan.md。
如果其中任一项缺失,Agent 必须说“本轮未完成”,并说明缺失项和最小补救动作。