问题描述
解决方案
这是一个基于 低市现率 (Low PCF, Price-to-Cash Flow Ratio) 的选股策略。
策略逻辑说明
- 核心指标:市现率 (PCF) = 股价 / 每股现金流。
- 市现率越低,通常意味着股票价格相对于其产生的现金流越便宜,具有较高的安全边际。
- 注意:我们只选择 PCF > 0 的股票,因为负的市现率意味着公司现金流为负,通常代表经营风险。
- 股票池:全市场 A 股(剔除 ST 股、停牌股、次新股)。
- 选股逻辑:
- 获取全市场股票的市现率数据。
- 剔除市现率为负的股票。
- 按照市现率从小到大排序。
- 选取排名前 20 只股票。
- 调仓频率:按月调仓(每月第一个交易日)。
- 资金分配:等权重买入。
策略代码
# -*- 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:
- 多因子结合:单一的低 PCF 可能会选中“价值陷阱”(基本面在恶化的公司)。可以结合 ROE(净资产收益率) 或 净利润增长率 来筛选既便宜又赚钱的公司。
- 行业中性化:不同行业的 PCF 天然不同(例如重资产行业和轻资产行业)。可以在每个行业内分别选低 PCF 的股票,以避免持仓过度集中在某些夕阳行业。
- 止损机制:加入个股止损(如跌幅超过 10% 卖出)或大盘风控(如大盘跌破 20 日均线空仓)。
Q4: 回测时需要注意什么?
A: 建议开启 set_option('use_real_price', True) 以使用真实价格复权,避免未来函数。同时,get_fundamentals 在回测中默认获取的是“已知”的历史数据(即 T 日只能获取 T-1 日及以前发布的财报),这已经自动避免了未来数据问题。