问题描述
解决方案
这是一个基于PTrade API编写的策略示例。该策略的核心功能是获取指定行业的所有成分股,剔除ST和停牌股票后,按照等权重(即每只股票买入金额相同)进行买入调仓。
策略说明
- 行业选择:在
initialize中设置行业代码(例如:J66000.XBHS代表货币金融服务/银行业)。 - 股票筛选:在
before_trading_start中获取该行业所有股票,并使用filter_stock_by_status自动过滤掉ST、停牌和退市的股票。 - 交易逻辑:在
handle_data中,计算当前账户总资产,除以股票数量得到每只股票的目标持仓金额,使用order_target_value进行调仓。
PTrade 策略代码
def initialize(context):
"""
初始化函数,设置策略参数
"""
# 1. 设置要交易的行业代码
# 示例使用 'J66000.XBHS' (货币金融服务/银行),您可以根据文档附录修改为其他行业
g.industry_code = 'J66000.XBHS'
# 2. 设置回测费用(可选,实盘会自动忽略)
# 设置佣金为万分之三,最低5元
set_commission(commission_ratio=0.0003, min_commission=5.0, type="STOCK")
# 3. 打印日志
log.info("策略初始化完成,目标行业: %s" % g.industry_code)
def before_trading_start(context, data):
"""
盘前处理函数,每日开盘前运行
"""
# 1. 获取指定行业的成分股列表
# 注意:get_industry_stocks 获取的是当下的数据,回测中存在未来函数风险,但在实盘中是准确的
industry_stocks = get_industry_stocks(g.industry_code)
if not industry_stocks:
log.warning("未获取到行业 %s 的成分股,请检查代码是否正确" % g.industry_code)
g.security = []
return
# 2. 过滤掉 ST、停牌、退市的股票
# filter_stock_by_status 是PTrade内置的高效过滤函数
# 过滤类型: "ST" (ST股), "HALT" (停牌), "DELISTING" (退市)
g.security = filter_stock_by_status(industry_stocks, ["ST", "HALT", "DELISTING"])
# 3. 设置股票池
if len(g.security) > 0:
set_universe(g.security)
log.info("今日待交易股票数量: %d" % len(g.security))
else:
log.warning("过滤后无可用股票")
def handle_data(context, data):
"""
盘中运行函数,执行交易逻辑
"""
# 如果没有股票池,直接返回
if not g.security:
return
# 获取当前股票池数量
stock_count = len(g.security)
# 获取账户总资产 (现金 + 持仓市值)
total_value = context.portfolio.portfolio_value
# 计算每只股票的目标持仓金额 (等权重分配)
# 注意:如果资金过少或股票过多,可能导致每只股票分配金额不足一手(100股)
target_value_per_stock = total_value / stock_count
# 遍历股票池进行交易
for stock in g.security:
# 检查该股票是否有行情数据(避免数据缺失导致报错)
if stock in data:
# 使用 order_target_value 调整持仓到目标金额
# 如果持有金额少于目标,会买入;如果多于目标,会卖出;如果没有,会建仓
order_target_value(stock, target_value_per_stock)
# 简单的日志记录 (可选,为了减少日志量,建议仅在特定条件下打印)
# log.info("调整 %s 持仓至 %.2f 元" % (stock, target_value_per_stock))
def after_trading_end(context, data):
"""
盘后处理函数
"""
log.info("今日交易结束,当前总资产: %.2f" % context.portfolio.portfolio_value)
如何修改行业代码
您可以参考 PTrade 文档中的《附录:行业列表》修改 g.industry_code。以下是一些常用行业代码示例:
- J66000.XBHS: 货币金融服务(银行)
- J67000.XBHS: 资本市场服务(证券)
- C27000.XBHS: 医药制造业
- I65000.XBHS: 软件和信息技术服务业
- C39000.XBHS: 计算机、通信和其他电子设备制造业
- A01000.XBHS: 农业
注意事项
- 资金量与股票数量:如果选择的行业包含几百只股票(如制造业),而您的资金量较小,计算出的单只股票买入金额可能不足一手(100股),导致无法成交。建议选择细分行业或增加资金。
- 未来函数:
get_industry_stocks获取的是当前最新的成分股。在回测几年前的数据时,它返回的仍然是今天的成分股列表(即包含当时未上市或不属于该行业的股票),这属于“未来函数”。但在实盘交易中,这是完全正确的用法。 - Python版本:代码已遵循 Python 3.5 兼容性规范(未使用 f-string)。
Q&A
Q: 如何限制最大持仓股票数量?
A: 可以在 before_trading_start 中对 g.security 列表进行切片。例如,只取前10只股票:g.security = g.security[:10]。
Q: 为什么回测时 get_industry_stocks 获取的股票和历史不一样?
A: PTrade 的 get_industry_stocks 返回的是当前时刻的行业成分股,不包含历史变动信息。如果在回测中需要严格的历史成分股,通常需要使用指数成分股接口 get_index_stocks(如沪深300行业指数)并指定 date 参数。
Q: 如何将交易频率改为每周调仓一次?
A: 可以在 handle_data 中增加日期判断,或者使用 run_daily 配合计数器逻辑,甚至直接在 initialize 中使用 run_weekly(如果平台支持)来控制调仓频率。最简单的方法是在 handle_data 开头判断 context.blotter.current_dt 是否为周五。