← 文档库
📄 文档learn/择时策略/策略研发标准化流程-从策略基类到GA优化到策略池.md
🌱 创建于2026-09-25 01:35
✏️ 修改于2026-09-25 01:35
👁️ 浏览量—
🔗 短链接/~learn/s/97a0ba8f
🔐 可见性

策略研发标准化流程:从策略基类到 GA 优化到策略池

> 📅 2026-09-21 建立 > > 本文把「写一个择时策略 → 遗传算法调参 → 入选策略池」这条完整链路固化成可复制的 SOP。 > 之前只有一份口语化的《策略研发sop.txt》(在 adata/aohan/learn/策略研发/),本文是对它的工程化落地版:每一步都对应到仓库里的真实文件、真实函数、真实契约。 > > 本文回答三个问题: > 1. 一个新策略从零到能跑,要碰哪几个文件?(第一章 · 链路总览) > 2. 参数是怎么被遗传算法"看见"并优化的?(第二章 · 参数体系) > 3. 每一步最容易踩什么坑?(第三章 · 避坑清单)

---

一、链路总览(七步)

① 写策略类 ──→ ② 暴露参数 ──→ ③ 声明参数边界 ──→ ④ 未来函数自检(写的时候就防)
   Strategy.py      __init__        params_grid.py
     ↓
⑤ GA 参数优化 ──→ ⑥ 未来函数终检(必检!) ──→ ⑦ 入选策略池
   ParamsOptGeneticDevelop        future-function          timing_config.py
   + params_deep_seek.py          check
步骤 做什么 落点文件
------------------------
① 继承 Strategy,实现 generate_signals() 产出 position adata/aohan/quant/backtest/strategy/<新策略>.py
② __init__ 暴露可优化参数 + 同名实例属性 同上
③ 写参数搜索空间(GA 的基因边界) backtest/strategy/params_grid/params_grid.py
④ 编码时就避开未来函数(指标全部用历史数据) 策略类内部
⑤ 配置策略类 + 参数边界 + 目标品种,跑遗传算法 backtest/deep_learning/params_deep_seek.py
⑥ 高收益策略必须手工 shift 1 天复检 手工 + 可视化
⑦ 用最优参数实例注册到策略池 chart_test/timing_config.py

> ⚠️ ④ 和 ⑥ 不是同一件事:④ 是写的时候就按"只能用历史数据"的规矩写;⑥ 是跑出来之后再用"手工 shift 一天,看收益是否大幅缩水"做终检。两者都做,缺一不可。

---

二、Step 1:继承策略基类

基类文件:Strategy.py · class Strategy(L25)

2.1 唯一强制接口

你只需要实现一个方法:

def generate_signals(self):
    # 算出 self.df['position']:全仓=1,空仓=0
    ...
    self.generate_trade_signals_based_on_positions(delay_days=self.signal_offset)

基类把职责收得很窄,其余全部由框架代劳。完整流程入口是 init_and_generate_signal:

_preprocess_missing_columns()   # 补 open/high/low/volume 缺失列
   → initialize_positions()      # 初始化仓位列
   → calculate_daily_return()    # 算日收益
   → generate_signals()          # ★ 你实现的那一个

2.2 position 与 signal 的语义(务必搞清)

> 🚩 这条直接决定未来函数怎么防:因为 position 是"当天开盘"用的,所以生成 position 的所有指标,只能用到当天开盘之前已知的数据(严格说 ≤ 前一日收盘)。详见 Step 4。

2.3 两个收益口径

方法 含仓位管理 速度 谁在用
-------------------------------
calculate_returns_quick ❌ 不含 快 GA 优化器、快速验证
calculate_returns ✅ 含 慢 最终评估

指标统一入口是 evaluate_strategy_metrics(verbose, quick),结果写进 self.last_calculated_results(一个 dict,值多为带 % 的字符串)。

---

三、Step 2:暴露可优化参数

没有任何声明式 schema,参数就是子类 __init__ 的显式形参,并且必须存成同名实例属性:

def __init__(self, df=None, rsi_period=32, rsi_buy=45, rsi_sell=60, signal_offset=0):
    super().__init__(df)
    self.rsi_period = rsi_period    # 形参 → 同名属性,缺一不可
    self.rsi_buy = rsi_buy
    self.rsi_sell = rsi_sell
    self.signal_offset = signal_offset

为什么必须是同名属性? 两个地方靠这个约定吃饭:

> 💡 约定速成:形参名 = 属性名 = params_grid 里的 key 名 = GA 基因名。四者一旦不一致,链路就断。

---

四、Step 3:声明参数边界(params_grid.py)

文件:params_grid.py,全部是模块级 dict,一个策略一个,命名 XxxStrategy_params。

参数边界支持 4 种形态,由 create_toolbox 统一解释:

形态 含义 示例
------------------
单元素 list 固定值(不参与搜索) 'k_type': [3]
全 int 二元 tuple 整数均匀区间 randint 'rsi_period': (4, 80)
全 float 二元 tuple 浮点均匀区间 uniform 'threshold': (0.03, 0.12)
多元素 list 枚举 choice(字符串参数靠这个) 'priority': ['buy', 'sell']
RSIStrategy_params = {
    'rsi_period': (4, 80),
    'rsi_buy': (10, 80),
    'rsi_sell': (10, 80),
}

> params_grid.py 里还有个 _calculate_total_combinations(),会把 tuple 区间按小数位数推断步长,折算成组合总数,用于"搜索空间过大时提前终止"。

---

五、Step 4:编码时避开未来函数

核心规矩:生成 position 时,只能用"当天开盘前已知"的数据。

错误写法 正确写法
--------------------
close.pct_change().rolling(5) 先 .shift(1) 再 rolling
df['high'].rolling(5).max() df['high'].shift(1).rolling(5).max()
close > sar(当日 sar) close.shift(1) > sar.shift(1)

---

六、Step 5:遗传算法参数优化

6.1 优化器本体

文件:ParamsOptGeneticDevelop.py · class ParamsOptGeneticDevelop(基于 deap)

ParamsOptGeneticDevelop(
    df_list,                 # 目标品种的数据列表(每个品种一个 df)
    strategy_class,          # 策略类
    param_bounds,            # 来自 params_grid.py 的 dict
    weight_rules,            # 多目标权重规则(阶段式)
    result_dir, codes,
    enable_short=False,
)

6.2 入口与"目标品种"

入口:params_deep_seek.py · ParamsDeepSeek.test_opt_mult_market_params(L99)→ optimize_strategy_for_codes(L353)

strategy_configs = [
    {"strategy_class": FourMovingAveragesStrategy, "param_bounds": FourMovingAveragesStrategy_params},
    # ... 一个策略一条
]
for config in strategy_configs:
    for codes in codes_list:          # ← 目标品种
        self.optimize_strategy_for_codes(codes=codes, strategy_class=..., param_bounds=..., ...)

阶段式多目标权重(写在 optimize_strategy_for_codes 内):

weight_rules = {'type': 'stage', 'rules': [
    (0, 0,          (3.0, 12.0, 2.0,  -8.0,   0)),      # 高年化
    (1, 1,          (1.0,  4.0, 2.0,  -4.0,  -1.0)),    # 高凯利低回撤
    (2, 2,          (3.0, 12.0, 1.0,  -5.0,  -20.0)),   # 高收益+超低资金占用
    (3, float('inf'),(2.0, 8.0, 1.0, -20.0,  -1.0)),    # 高收益低回撤
]}

数据准备:fetch_data(code, start, end, k_type=1) → DataProcessor(df).process_all()。

> 🚩 大坑:目标品种是硬编码的 codes_list,而且这个变量在同一个函数里被反复重赋值十余次(L243~L302)。因为是顺序覆盖,文本上最后一次赋值生效。改目标品种就改这最后一处,别改中间的。详见 附录 B。

6.3 运行成本参考(来自源码注释)

---

七、Step 6:未来函数终检(必检)

跑出结果后,尤其当收益漂亮得不像话时,做这两件事:

> 原则:不检未来函数,参数优化就是白做。

---

八、Step 7:入选策略池

注册表本体:allocation_utils.py · class StrategyAllocationManager 池内容(真正写死的地方):timing_config.py · build_allocation_manager()

allocation_manager = build_allocation_manager()
strategy = RSIStrategy(rsi_period=32, rsi_buy=45, rsi_sell=60)
allocation_manager.add_strategy_to_board(strategy, ['399102', '399300'], weight=2)

---

附录 A:新策略落地清单(照着抄)

□ 1. 新建 adata/aohan/quant/backtest/strategy/<XxxStrategy>.py
       class XxxStrategy(Strategy):
           def __init__(self, df=None, <param1>=..., signal_offset=0):
               super().__init__(df)
               self.<param1> = <param1>
               self.signal_offset = signal_offset
           def generate_signals(self):
               ...  # 只用历史数据,产出 self.df['position']
               self.generate_trade_signals_based_on_positions(delay_days=self.signal_offset)

□ 2. 在 params_grid.py 加 XxxStrategy_params = {...}(四形态选其一)
□ 3. 在 params_deep_seek.py:
       - import 策略类 与 param
       - strategy_configs 里加一条
       - 确认 codes_list(目标品种,改最后一处赋值)
□ 4. 先小样本手动跑一次 evaluate_strategy_metrics(quick=True) 看有没有交易
□ 5. 未来函数自检(Step 4)→ 跑 GA → 未来函数终检(Step 6)
□ 6. 用最优参数实例 add_strategy_to_board 注册进 timing_config.py

---

附录 B:目标品种 codes_list 的坑

params_deep_seek.py 的 test_opt_mult_market_params 里,codes_list 被反复赋值,最后生效的是文本上最后一次那处。当前源码最后一次生效的是医药四指数:

codes_list = [
    ["000933.SH"],   # 中证医药卫生指数
    ['399139.SZ'],   # 深证医药卫生指数
    ['000913.SH'],   # 沪深300医药卫生指数
    ['931152.CSI'],  # 中证创新药产业指数
]

想换品种,直接改这最后一处,不要改中间的(改了不生效,会让你怀疑人生)。

---

附录 C:避坑清单

---

📚 相关阅读

▶ 📜 变更历史(1 次提交)

d20f8c82026-09-25 01:35feat: 通用「策略板块」组件 + 对数价格策略板块
🍃quant 知识库
by 伊甸园和枫

🔊 播放设置

勾选音源后调节配比,总和自动归一化
已选 0 个 · 合计:0%
合成中...
00:00/00:00
1.0x

分享图片

生成中...