问题描述
解决方案
这是一个基于 低市净率(Low P/B Ratio) 的选股策略。
策略逻辑
- 选股范围:沪深300指数成分股(确保流动性和基本面相对稳定)。
- 核心因子:市净率(PB)。市净率越低,通常意味着股票估值越低,安全边际可能越高。
- 过滤条件:剔除ST股、停牌股、退市股。
- 调仓频率:按月调仓(每月第一个交易日)。
- 持仓方式:选取PB最低的10只股票,等权重买入。
策略代码实现
import pandas as pd
import numpy as np
def initialize(context):
"""
初始化函数,设置策略参数
"""
# 1. 设置基准为沪深300
set_benchmark('000300.SS')
# 2. 设置股票池为沪深300
g.index_security = '000300.SS'
# 3. 设置持仓数量
g.stock_num = 10
# 4. 设置交易费率(可选,根据实际情况调整)
set_commission(commission_ratio=0.0003, min_commission=5.0, type="STOCK")
# 5. 每日检查是否需要调仓
run_daily(context, rebalance_check, time='09:30')
def rebalance_check(context):
"""
每日运行,判断是否为月初,如果是则执行调仓逻辑
"""
# 获取当前日期
current_date = context.blotter.current_dt.date()
# 获取上一个交易日
prev_day = get_trading_day(-1)
# 如果当前月份与上一个交易日月份不同,说明是月初,执行调仓
if current_date.month != prev_day.month:
log.info("今日为月初,开始执行低市净率选股调仓...")
trade_logic(context)
def trade_logic(context):
"""
核心交易逻辑:选股与买卖
"""
# 1. 获取沪深300成分股
security_list = get_index_stocks(g.index_security)
# 2. 过滤ST、停牌、退市股票
# filter_stock_by_status 是PTrade内置便捷函数
# 默认过滤 ["ST", "HALT", "DELISTING"]
valid_stocks = filter_stock_by_status(security_list)
if not valid_stocks:
log.warning("没有符合条件的股票")
return
# 3. 获取基本面数据:市净率 (pb)
# 注意:get_fundamentals 在回测中默认取当前回测日期的数据
# 为了避免未来函数,通常数据是截止到前一天的,PTrade底层已做处理
df = get_fundamentals(
security=valid_stocks,
table='valuation',
fields=['pb'],
date=None # 默认为当前回测日期
)
# 数据清洗:去除空值
df = df.dropna()
# 4. 排序与筛选
# 按市净率从小到大排序
df = df.sort_values(by='pb', ascending=True)
# 取前 N 只股票
target_list = df.index[:g.stock_num].tolist()
log.info("本月目标持仓股票: %s" % target_list)
# 5. 执行交易
# 获取当前持仓
current_positions = list(context.portfolio.positions.keys())
# 卖出不在目标列表中的股票
for stock in current_positions:
if stock not in target_list:
# 检查是否停牌,停牌无法卖出(虽然下单会失败,但最好判断一下)
# 这里简单直接下单,依靠系统拒单机制,或者使用 get_stock_status 判断
order_target_value(stock, 0)
# 买入目标列表中的股票
# 采用等权重分配资金
if len(target_list) > 0:
# 计算每只股票的目标市值 = 总资产 / 股票数量
target_value = context.portfolio.portfolio_value / len(target_list)
for stock in target_list:
order_target_value(stock, target_value)
def handle_data(context, data):
"""
盘中运行函数,本策略主要逻辑在 run_daily 中,此处留空
"""
pass
代码关键点解析
get_fundamentals: 这是获取财务数据的核心函数。我们查询valuation表中的pb(市净率)字段。filter_stock_by_status: 这是一个非常实用的辅助函数,用于一键过滤掉ST股、停牌股和退市股,防止买入风险过大或无法交易的标的。order_target_value: 使用目标市值下单。这个函数会自动计算需要买入或卖出的股数,使该股票的持仓市值达到指定金额。这对于等权重策略非常方便。- 调仓时间控制: 通过比较
current_date.month和prev_day.month来判断是否是该月的第一个交易日。
策略风险提示
- 低估值陷阱:市净率极低的公司可能面临经营困难、资产减值或破产风险(即所谓的“烟蒂股”)。
- 行业偏差:低PB策略容易集中在银行、钢铁、煤炭等传统行业,缺乏成长性,可能导致策略在成长股行情中跑输大盘。
- 流动性风险:虽然选择了沪深300,但在极端市场下,部分股票仍可能缺乏流动性。
常见问题 (Q&A)
Q: 如何修改选股范围为中证500?
A: 将 initialize 函数中的 g.index_security = '000300.SS' 修改为 g.index_security = '000905.XBHS' (注意PTrade中指数后缀可能不同,通常中证500是 000905.SS 或 000905.XBHS,具体请参考PTrade文档附录)。
Q: 如何增加过滤条件,例如剔除亏损股?
A: 在 get_fundamentals 中增加查询 income_statement 表的 net_profit 字段,然后在获取数据后,筛选 net_profit > 0 的股票。
Q: 为什么回测时要用 order_target_value 而不是 order?
A: order_target_value 是调仓神器。比如你持有股票A市值5万,目标是10万,它会自动买入5万;如果目标是0,它会自动卖出所有。这比手动计算买卖数量要简单且准确得多。