🚀 AI 一键生成 joinquant 策略代码

JoinQuant 因子投资:如何基于盈利预期因子构建选股策略?

joinquant | 发布时间: 2025/12/16
以下内容由 EasyQuant 生成。

问题描述

怎样利用分析师盈利预测数据来编写一个选股策略?

解决方案

利用分析师盈利预测数据(Analyst Estimates)进行选股,通常是基于“一致预期”因子。在聚宽(JoinQuant)平台中,这些数据已经被封装在 jqfactor 因子库的 成长因子(Growth)盈利预期因子(Earnings Yield) 类别中。

最常用的因子是 short_term_predicted_earnings_growth(预期短期盈利增长率),它代表了分析师对未来一年净利润的一致预期增长率。

策略思路

  1. 股票池:沪深300指数成分股(流动性好,分析师覆盖率高)。
  2. 调仓频率:按月调仓(分析师研报通常随财报季或重大事件更新,日频调仓意义不大)。
  3. 选股逻辑
    • 过滤掉 ST 股、停牌股、涨跌停股。
    • 获取 short_term_predicted_earnings_growth(预期短期盈利增长率)因子值。
    • 获取 predicted_earnings_to_price_ratio(预期市盈率倒数/预期盈利收益率)因子值,用于估值保护。
    • 核心逻辑:选取 预期增长率最高 的前 50 只股票,再从中选取 预期估值最低(即预期盈利收益率最高) 的 10 只股票买入。这是一种“高增长+合理估值”的策略(GARP)。
  4. 交易方式:等权重买入。

策略代码实现

# -*- coding: utf-8 -*-
from jqdata import *
from jqfactor import get_factor_values

def initialize(context):
    # 1. 设置策略基准:沪深300
    set_benchmark('000300.XSHG')
    
    # 2. 开启真实价格模式(动态复权)
    set_option('use_real_price', True)
    
    # 3. 设置手续费:买入万三,卖出万三加千一印花税
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 4. 设置滑点
    set_slippage(PriceRelatedSlippage(0.002), type='stock')
    
    # 5. 设置全局变量
    g.stock_num = 10  # 持仓数量
    
    # 6. 设置定时运行:每月第一个交易日开盘进行调仓
    run_monthly(rebalance, 1, time='09:30')

def rebalance(context):
    """
    调仓主函数
    """
    # 获取当前日期
    date = context.previous_date
    
    # 1. 获取初始股票池:沪深300
    initial_stocks = get_index_stocks('000300.XSHG', date)
    
    # 2. 过滤掉 ST、停牌、退市股票
    check_out_list = filter_paused_and_st_stocks(initial_stocks, date)
    
    # 3. 获取分析师预测因子数据
    # short_term_predicted_earnings_growth: 预期短期盈利增长率 (越高越好)
    # predicted_earnings_to_price_ratio: 预期盈利市值比 (即预期PE的倒数,越高代表估值越低)
    factor_list = ['short_term_predicted_earnings_growth', 'predicted_earnings_to_price_ratio']
    
    factor_data = get_factor_values(
        securities=check_out_list, 
        factors=factor_list, 
        end_date=date, 
        count=1
    )
    
    # 提取因子值 DataFrame
    growth_series = factor_data['short_term_predicted_earnings_growth'].iloc[0]
    val_series = factor_data['predicted_earnings_to_price_ratio'].iloc[0]
    
    # 合并数据并去除空值(分析师未覆盖的股票会被剔除)
    df = pd.DataFrame({'growth': growth_series, 'val': val_series}).dropna()
    
    # 4. 选股逻辑
    # 第一步:按预期增长率降序排列,取前 50 名 (高增长池)
    top_growth_stocks = df.sort_values(by='growth', ascending=False).head(50).index.tolist()
    
    # 第二步:在增长率高的股票中,按预期盈利收益率降序排列(估值越低越好),取前 g.stock_num 只
    # 注意:这里是在 top_growth_stocks 范围内再次筛选
    final_df = df.loc[top_growth_stocks].sort_values(by='val', ascending=False).head(g.stock_num)
    target_list = final_df.index.tolist()
    
    # 5. 执行交易
    trade(context, target_list)

def trade(context, target_list):
    """
    交易执行函数
    """
    # 获取当前持仓
    current_holdings = list(context.portfolio.positions.keys())
    
    # 卖出不在目标列表中的股票
    for stock in current_holdings:
        if stock not in target_list:
            order_target_value(stock, 0)
            log.info(f"卖出 {stock}")
    
    # 买入目标列表中的股票
    if len(target_list) > 0:
        # 等权重分配资金
        value_per_stock = context.portfolio.total_value / len(target_list)
        for stock in target_list:
            order_target_value(stock, value_per_stock)
            log.info(f"买入/调仓 {stock}")

def filter_paused_and_st_stocks(stock_list, date):
    """
    过滤停牌、ST、涨跌停股票
    """
    current_data = get_current_data()
    return [stock for stock in stock_list 
            if not current_data[stock].paused 
            and not current_data[stock].is_st 
            and 'ST' not in current_data[stock].name 
            and '*' not in current_data[stock].name]

关键点解析

  1. 数据获取 (get_factor_values):

    • 我们使用了 jqfactor 库,这是获取分析师数据的最便捷方式。
    • short_term_predicted_earnings_growth: 这个因子聚合了分析师对未来一年净利润的一致预期。如果数值高,说明市场普遍看好其短期爆发力。
    • predicted_earnings_to_price_ratio: 这是预期市盈率(Forward PE)的倒数。使用倒数是因为在排序时,数值越大代表估值越便宜(性价比越高),便于处理。
  2. 双重筛选逻辑 (GARP 策略):

    • 单纯追求高增长可能会买入估值过高的股票(透支未来)。
    • 单纯追求低估值可能会买入“价值陷阱”(基本面恶化)。
    • 本策略先选“高预期增长”,再选“相对低估值”,试图在成长与安全边际之间寻找平衡。
  3. 缺失值处理 (dropna):

    • 分析师预测数据并不是覆盖所有股票的。对于没有分析师覆盖的股票,因子值会返回 NaN。代码中使用了 .dropna() 自动剔除这些股票,这本身也是一种质量筛选(通常只有受关注的优质股才有分析师覆盖)。

常见问题 Q&A

Q: 为什么使用 short_term 而不是 long_term 预测因子?
A: A股市场波动较大,分析师对未来1年的预测(Short Term)通常比未来3-5年(Long Term)的预测准确度相对更高,且股价对短期业绩预期的变化反应更敏感。

Q: 这个策略适合小盘股吗?
A: 不太适合。小盘股的分析师覆盖率较低,研报更新不及时,导致“一致预期”数据的滞后性严重。建议在沪深300或中证500等指数成分股中使用。

Q: 如何查看还有哪些分析师因子可用?
A: 可以使用 get_all_factors() 函数查看聚宽因子库。在 growth(成长)和 style(风格)分类下,你可以找到更多如 long_term_predicted_earnings_growthearnings_yield 等相关因子。