🚀 AI 一键生成 joinquant 策略代码

JoinQuant 价值因子:盈利收益率(Earnings Yield)策略构建

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

问题描述

如何基于盈利收益率因子构建选股策略?

解决方案

基于盈利收益率(Earnings Yield, EY)的选股策略是一种经典的价值投资策略。

策略原理

盈利收益率是市盈率(P/E Ratio)的倒数,即:
$$ EY = \frac{E}{P} = \frac{1}{PE} $$

  • 逻辑:盈利收益率越高,意味着投资者以同样的价格买入了更多的盈利能力,或者以更低的价格买入了同样的盈利。这通常代表股票被低估(Value)。
  • 操作:在股票池中,选取市盈率(PE)最低(且大于0)的股票,即等同于选取盈利收益率最高的股票。

策略设计

  1. 股票池:沪深300指数成分股(确保流动性和基本面相对稳定)。
  2. 选股因子:市盈率(PE_TTM)。
  3. 过滤条件
    • 剔除停牌、ST股票。
    • 剔除上市不满60天的次新股。
    • 剔除市盈率为负(亏损)的股票。
  4. 排序与买入:按市盈率从小到大排序(即盈利收益率从大到小),选取排名前20只股票。
  5. 调仓频率:按月调仓(每月第一个交易日)。
  6. 仓位管理:等权重买入。

策略代码实现

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

def initialize(context):
    """
    初始化函数,设定基准、手续费、滑点等
    """
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # 设定股票类每笔交易时的手续费
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 设定全局变量
    g.stock_num = 20  # 持仓股票数量
    g.index_security = '000300.XSHG' # 股票池:沪深300
    
    # 设定按月运行,每月第一个交易日开盘后运行
    run_monthly(rebalance, monthday=1, time='09:30')

def rebalance(context):
    """
    调仓主函数
    """
    # 1. 获取股票池(沪深300)
    target_list = get_index_stocks(g.index_security, date=context.previous_date)
    
    # 2. 过滤掉 停牌、ST、退市、次新股
    target_list = filter_basic_stock(context, target_list)
    
    # 3. 获取财务数据并进行排序(核心逻辑:低PE = 高盈利收益率)
    # 查询 PE_TTM (市盈率-动态)
    q = query(
        valuation.code,
        valuation.pe_ratio
    ).filter(
        valuation.code.in_(target_list),
        valuation.pe_ratio > 0  # 剔除亏损股(PE为负)
    ).order_by(
        valuation.pe_ratio.asc() # 按PE从小到大排序,即盈利收益率从大到小
    )
    
    # 获取查询结果
    df = get_fundamentals(q, date=context.previous_date)
    
    # 取前 N 只股票
    if len(df) > 0:
        buy_list = list(df['code'][:g.stock_num])
    else:
        buy_list = []
    
    # 4. 执行交易
    trade(context, buy_list)

def filter_basic_stock(context, stock_list):
    """
    基础过滤器:剔除停牌、ST、次新股
    """
    current_data = get_current_data()
    final_list = []
    for stock in stock_list:
        # 剔除停牌和已退市
        if current_data[stock].paused:
            continue
        # 剔除ST
        if current_data[stock].is_st:
            continue
        # 剔除上市不满60天的次新股
        info = get_security_info(stock)
        if not info or (context.current_dt.date() - info.start_date).days < 60:
            continue
        
        final_list.append(stock)
    return final_list

def trade(context, buy_list):
    """
    交易执行函数
    """
    # 获取当前持仓
    current_holdings = list(context.portfolio.positions.keys())
    
    # 1. 卖出不在买入列表中的股票
    for stock in current_holdings:
        if stock not in buy_list:
            order_target_value(stock, 0)
            # log.info(f"卖出 {stock}")
    
    # 如果没有需要买入的股票,直接返回
    if len(buy_list) == 0:
        return

    # 2. 买入/调仓
    # 计算每只股票的目标资金(等权重)
    # 注意:这里使用 total_value 可能会导致满仓波动,实际操作中可预留少量现金
    position_per_stk = context.portfolio.total_value / len(buy_list)
    
    for stock in buy_list:
        order_target_value(stock, position_per_stk)
        # log.info(f"买入/调整 {stock} 到目标市值 {position_per_stk}")

代码逻辑解析

  1. 选股逻辑 (rebalance 函数):

    • 我们使用了 get_fundamentals 查询 valuation 表。
    • valuation.pe_ratio 代表市盈率(TTM)。
    • valuation.pe_ratio > 0:盈利收益率策略通常只针对盈利企业,因此剔除亏损股。
    • order_by(valuation.pe_ratio.asc()):将市盈率从小到大排序。数学上,$\frac{1}{PE}$ 最大等同于 $PE$ 最小(在 $PE>0$ 的前提下)。
  2. 过滤逻辑 (filter_basic_stock 函数):

    • 使用 get_current_data() 获取当天的停牌和ST状态,避免买入无法交易或风险过大的股票。
    • 使用 get_security_info() 获取上市日期,剔除次新股,因为次新股的估值通常不稳定。
  3. 交易逻辑 (trade 函数):

    • 先卖出:遍历当前持仓,如果持仓股票不在新的选股列表中,则清仓。
    • 后买入:计算每只股票应分配的资金(总资产 / 选股数量),使用 order_target_value 自动调整仓位。如果已持有该股且价格上涨,会自动卖出部分以保持权重;如果未持有,则买入。

常见问题 Q&A

Q: 为什么不直接使用 1/PE 计算盈利收益率?
A: 在数据库查询层面,直接对 PE 进行升序排序(Ascending)的效果与计算 1/PE 后进行降序排序(Descending)的效果是完全一样的,且直接查询 PE 效率更高,代码更简洁。

Q: 这个策略适合什么市场环境?
A: 盈利收益率策略属于典型的价值策略。它通常在市场震荡期或熊市末期表现较好,因为这类股票具有较高的安全边际。在疯狂的牛市或成长股主导的行情中,该策略可能会跑输大盘。

Q: 如何改进这个策略?
A:

  1. 结合成长性:低 PE 可能意味着公司陷入困境(价值陷阱)。可以结合 PEG 指标(市盈率相对盈利增长比率)或 ROE(净资产收益率)来筛选既便宜又赚钱的公司。
  2. 行业中性化:不同行业的 PE 水平差异很大(如银行股 PE 常年很低,科技股 PE 很高)。可以在每个行业内分别选取低 PE 股票,避免持仓过于集中在银行地产板块。
  3. 格雷厄姆选股:结合 PB(市净率),例如要求 $PE \times PB < 22.5$。