🚀 AI 一键生成 joinquant 策略代码

基于市现率(PCF Ratio)的价值投资策略:JoinQuant 实现指南

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

问题描述

请编写一个低市现率 PCF 的选股策略

解决方案

这是一个基于 低市现率 (Low PCF, Price-to-Cash Flow Ratio) 的选股策略。

策略逻辑说明

  1. 核心指标:市现率 (PCF) = 股价 / 每股现金流。
    • 市现率越低,通常意味着股票价格相对于其产生的现金流越便宜,具有较高的安全边际。
    • 注意:我们只选择 PCF > 0 的股票,因为负的市现率意味着公司现金流为负,通常代表经营风险。
  2. 股票池:全市场 A 股(剔除 ST 股、停牌股、次新股)。
  3. 选股逻辑
    • 获取全市场股票的市现率数据。
    • 剔除市现率为负的股票。
    • 按照市现率从小到大排序。
    • 选取排名前 20 只股票。
  4. 调仓频率:按月调仓(每月第一个交易日)。
  5. 资金分配:等权重买入。

策略代码

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

def initialize(context):
    """
    初始化函数,设定基准、手续费、滑点等
    """
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # 设定股票交易手续费:买入万分之三,卖出万分之三加千分之一印花税,最低5元
    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
    
    # 按月运行,每月第一个交易日开盘后运行
    run_monthly(rebalance, 1, time='09:30')

def rebalance(context):
    """
    调仓主函数
    """
    # 1. 获取待操作的股票池
    # 这里选取全市场股票,也可以换成 get_index_stocks('000300.XSHG') 等
    all_stocks = list(get_all_securities(['stock']).index)
    
    # 2. 过滤掉 ST、停牌、退市的股票
    current_data = get_current_data()
    valid_stocks = [
        stock for stock in all_stocks 
        if not current_data[stock].is_st 
        and not current_data[stock].paused 
        and '退' not in current_data[stock].name
    ]
    
    # 3. 过滤次新股(上市不满1年),保证财务数据稳定性
    # 获取当前时间
    curr_date = context.current_dt.date()
    valid_stocks = [
        stock for stock in valid_stocks 
        if (curr_date - get_security_info(stock).start_date).days > 365
    ]

    # 4. 查询财务数据:市现率 (pcf_ratio)
    # pcf_ratio 在 valuation 表中
    # 我们需要 pcf_ratio > 0 且越小越好
    q = query(
        valuation.code,
        valuation.pcf_ratio
    ).filter(
        valuation.code.in_(valid_stocks),
        valuation.pcf_ratio > 0  # 剔除现金流为负的公司
    ).order_by(
        valuation.pcf_ratio.asc() # 按市现率从小到大排序
    ).limit(g.stock_num) # 取前 N 只

    df = get_fundamentals(q)
    
    # 获取最终的目标股票列表
    target_list = list(df['code'])
    
    # 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("卖出 %s" % stock)
    
    # 买入目标列表中的股票
    # 资金分配:等权重分配
    # 注意:这里简单处理,每次调仓都重新平衡资金。
    # 如果资金不足,order_target_value 会自动调整为最大可买数量
    if len(target_list) > 0:
        # 将可用资金分配给要买入的股票(这里假设满仓操作)
        # 实际操作中,因为卖出有手续费和非即时到账(回测中即时),可用资金可能变化
        # 这里使用 total_value 来计算每只股票的目标持仓市值
        position_value = context.portfolio.total_value / len(target_list)
        
        for stock in target_list:
            order_target_value(stock, position_value)
            # log.info("买入/调整 %s 到目标市值 %.2f" % (stock, position_value))

def after_trading_end(context):
    """
    收盘后运行
    """
    log.info('今日交易结束,当前总资产: %.2f' % context.portfolio.total_value)

常见问题 (Q&A)

Q1: 为什么要过滤 pcf_ratio > 0
A: 市现率 (PCF) = 股价 / 每股现金流。如果 PCF 为负数,说明企业的经营现金流是负的,这意味着企业在通过经营活动“烧钱”而不是赚钱。虽然负值在数学上比正值“小”,但在投资逻辑中,负现金流通常代表高风险,因此我们只在正值范围内寻找低估值的股票。

Q2: 为什么选择全市场而不是沪深300?
A: 低估值策略(如低 PCF、低 PE)通常在中小盘股中表现出更强的 Alpha(超额收益)。如果限定在沪深300,可选范围较小,策略趋同性高。当然,如果您希望策略更稳健,可以将 get_all_securities 替换为 get_index_stocks('000300.XSHG')

Q3: 策略可以如何改进?
A:

  1. 多因子结合:单一的低 PCF 可能会选中“价值陷阱”(基本面在恶化的公司)。可以结合 ROE(净资产收益率)净利润增长率 来筛选既便宜又赚钱的公司。
  2. 行业中性化:不同行业的 PCF 天然不同(例如重资产行业和轻资产行业)。可以在每个行业内分别选低 PCF 的股票,以避免持仓过度集中在某些夕阳行业。
  3. 止损机制:加入个股止损(如跌幅超过 10% 卖出)或大盘风控(如大盘跌破 20 日均线空仓)。

Q4: 回测时需要注意什么?
A: 建议开启 set_option('use_real_price', True) 以使用真实价格复权,避免未来函数。同时,get_fundamentals 在回测中默认获取的是“已知”的历史数据(即 T 日只能获取 T-1 日及以前发布的财报),这已经自动避免了未来数据问题。