← 返回《初阶量化交易:策略编写及系统搭建》
📑 查看全课大纲(第 22 / 38 节)

实现低PE股票池(2)

约 12 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

实现低PE股票池(二):调仓周期设计与集合变动追踪

小象实战讲义 · 初阶量化交易:策略编写及系统搭建 本讲义仅用于编程与量化方法教学,不构成任何投资建议。

在量化投资中,选股模型不仅需要在静态截面上挑选优质标的,更必须在动态时间序列上制定科学的定期调仓机制(Rebalancing Frequency)。股票的市盈率估值随着每日收盘价波动而实时微调,若每日都根据全市场最新 PE 重新洗牌股票池,不仅会引发高额的印花税、佣金与冲击成本摩擦,更会破坏持仓的稳定性。

本讲深入剖析调仓周期设为 7 个交易日的工程权衡哲学;详细讲解新调入(Incoming)、被调出(Outgoing)与留存(Retained)三大股票集合的动态运算与持久化方案;并建立股票池等权持有基准收益率的追踪流水线。


💡 核心导读

  • 为什么选择 7 个交易日作为调仓周期?
    • 高频调仓的摩擦弊端:每日调仓会导致股票池频繁微调,持股周期极短,产生的印花税(单边千分之一)与交易佣金会迅速蚕食掉策略全部 Alpha 超额收益;
    • 低频调仓的滞后弊端:若按季度(60 交易日)调仓,标的股价大幅上涨导致估值严重偏离时无法及时止盈退出;
    • 7 日平衡点:约 1.5 周的调仓周期,既给予低估值因子充分的价值回归时间窗口,又将换手率与交易成本控制在极低水平。
  • 股票池定期调仓的三大集合变动运算
    • 设上一期股票池标的集合为 St1S_{t-1},当期最新筛选出的股票池标的集合为 StS_t
    • 新调入集合(Incoming=StSt1\text{Incoming} = S_t - S_{t-1}:新进入估值优势区间的标的,进入择时买入监控队列
    • 被调出集合(Outgoing=St1St\text{Outgoing} = S_{t-1} - S_t:估值过高或基本面劣化的标的,若当前持有则触发无条件清仓卖出(强制离场)
    • 留存集合(Retained=StSt1\text{Retained} = S_t \cap S_{t-1}:估值依然具备优势的标的,维持现有持仓状态,继续受均线择时规则保护
  • 股票池持久化与基准收益率基准
    • 将每期调仓结果按 {'date': date, 'codes': [code1, code2, ...]} 格式落库至 MongoDB stock_pool 集合;
    • 计算每期股票池等权平均收益率,作为衡量后续择时策略能否击败纯选股基准的对比基准。
┌─────────────────────────────────────────────────────────────┐
│                    股票池定期调仓集合变动模型                   │
└──────────────────────────────┬──────────────────────────────┘

       上期股票池 (S_t-1)              当期股票池 (S_t)
       ┌──────────────────┐          ┌──────────────────┐
       │   被调出 Outgoing │  交集    │   新调入 Incoming │
       │ (S_t-1 - S_t)    │ Retained │ (S_t - S_t-1)    │
       │  ► 触发清仓卖出  │ (S_t-1∩S)│  ► 触发择时监控  │
       └──────────────────┴──────────┴──────────────────┘

一、动态股票池状态转移与交易路由

在量化交易引擎中,股票池不仅是一个静态名单,更是驱动交易路由状态机的核心动力:

┌────────────────────────────────────────────────────────────────┐
│                    调仓日状态转移与执行规则                      │
├────────────────────────────────────────────────────────────────┤
│ 1. 扫描 Outgoing 集合: 若账户持有该标的,立即发出 Sell 订单清仓 │
│ 2. 扫描 Incoming 集合: 将标的加入候选监控列表,等待均线金叉触发 │
│ 3. 扫描 Retained 集合: 标的保留在持仓中,若均线死叉则执行止损   │
└────────────────────────────────────────────────────────────────┘

二、MongoDB stock_pool 数据集合结构设计

在数据库中,每期调仓记录作为独立文档存储:

{
  "date": "2023-01-16",
  "codes": ["600000", "600002", "600005", "..."],
  "count": 100
}

通过在 date 字段建立唯一索引,回测引擎可秒级查询任意回测交易日所属的当期有效股票池。


📝 动手练一练

下面我们用 Python 编写一段 股票池定期调仓与集合变动动态追踪脚本,模拟连续多个调仓周期中新调入与被调出标的的自动识别:

import pandas as pd
import numpy as np

def track_stock_pool_rebalance(pool_history_dict, rebalance_dates):
    """
    股票池定期调仓与集合动态追踪引擎
    计算各个调仓周期: 新进集合 (Incoming)、调出集合 (Outgoing)、留存集合 (Retained)
    """
    records = []
    prev_set = set()
    
    for r_date in rebalance_dates:
        curr_list = pool_history_dict[r_date]
        curr_set = set(curr_list)
        
        # 集合运算
        incoming = curr_set - prev_set
        outgoing = prev_set - curr_set
        retained = curr_set & prev_set
        
        records.append({
            'rebalance_date': r_date,
            'pool_size': len(curr_list),
            'incoming_count': len(incoming),
            'outgoing_count': len(outgoing),
            'retained_count': len(retained),
            'incoming_codes': list(incoming),
            'outgoing_codes': list(outgoing)
        })
        prev_set = curr_set
        
    return pd.DataFrame(records)

def test_rebalance_run():
    dates = ["2023-01-05", "2023-01-16", "2023-01-27"]
    
    # 模拟 3 期各 4 只股票的低 PE 股票池变动
    pool_data = {
        "2023-01-05": ['600000', '600001', '600002', '600003'],
        "2023-01-16": ['600000', '600002', '600004', '600005'],  # 600001/600003 调出, 600004/600005 调入
        "2023-01-27": ['600000', '600004', '600005', '600006']   # 600002 调出, 600006 调入
    }
    
    res = track_stock_pool_rebalance(pool_data, dates)
    print("================ 股票池定期调仓集合追踪报告 ================")
    print(res[['rebalance_date', 'pool_size', 'incoming_count', 'outgoing_count', 'retained_count']])
    print("------------------------------------------------------------")
    for _, row in res.iterrows():
        print(f"[{row['rebalance_date']}] 新调入买入候选: {row['incoming_codes']} | 调出强平清仓: {row['outgoing_codes']}")
    print("============================================================")

if __name__ == "__main__":
    test_rebalance_run()

运行结果输出:

================ 股票池定期调仓集合追踪报告 ================
  rebalance_date  pool_size  incoming_count  outgoing_count  retained_count
0     2023-01-05          4               4               0               0
1     2023-01-16          4               2               2               2
2     2023-01-27          4               1               1               3
------------------------------------------------------------
[2023-01-05] 新调入买入候选: ['600003', '600001', '600002', '600000'] | 调出强平清仓: []
[2023-01-16] 新调入买入候选: ['600004', '600005'] | 调出强平清仓: ['600003', '600001']
[2023-01-27] 新调入买入候选: ['600006'] | 调出强平清仓: ['600002']
============================================================

本章小结

  1. 确立 7 日调仓纪律:兼顾价值因子的动态有效性与交易摩擦成本的最优控制。
  2. 掌握三元集合状态机:新调入标的纳入监控、被调出标的强制离场、留存标的继续持有并由均线择时护航。
  3. 完成选股层与执行层的解耦:股票池提供候选资产集,择时模块负责买卖触发,回测引擎负责订单撮合。

📋 行动清单 (Action Checklist)

  • 运行本讲调仓追踪代码,观察多期交集与差集的运算结果;
  • 验证调出标的清仓逻辑:确保持仓股一旦跌出前 100 名能在调仓日被准确卖出;
  • 预习下一讲,将 10 日均线择时信号接入股票池,开始搭建完整的回测撮合引擎。

— 小象教研组

配套学习资源与课件
  • 第3章课件:上手搭建最简单的交易系统(PDF · 3.8MB)
    下载
  • 开发环境准备:软件安装指导(PDF · 0.9MB · 建议第3章动手实践前先通读)
    下载
  • 量化入门推荐书单(PDF · 318KB)
    下载
🎁 免费学习资源

领取《小象 11GB VIP 课件资料包与大厂真题手册》

包含全套实战 Jupyter 源码、清洗后数据集、大厂高频面试真题与专属学员答疑交流群。

  • 完整 Python / 数据分析 Jupyter 实战源码
  • 大厂真实业务数据集与练习题
  • 微信扫码添加课程顾问,免费获取网盘下载链接
微信二维码:扫码添加课程顾问微信扫码添加顾问