问题描述
解决方案
这是一个非常实用的量化交易需求。要在 PTrade 中实现同时交易多只股票并根据市场情况调整仓位,我们需要设计一个策略框架,包含以下核心逻辑:
- 股票池设置:定义一组要监控和交易的股票。
- 大盘风控(市场环境判断):引入一个基准指数(如沪深300),当指数走弱时降低仓位或空仓,走强时进行交易。
- 个股择时:对股票池中的每一只股票进行独立的技术分析(如均线判断)。
- 动态调仓:根据符合买入条件的股票数量,动态分配资金(例如等权重分配)。
下面是一个完整的 PTrade 策略代码示例。该策略实现了**“大盘择时 + 个股均线轮动 + 资金等权分配”**的逻辑。
策略逻辑说明
- 核心思想:顺势而为,大盘不好不操作,个股走坏就剔除。
- 大盘判断:使用沪深300指数(000300.SS)作为风向标。如果指数低于20日均线,视为市场环境恶劣,清仓防守。
- 个股判断:如果大盘环境安全,则检查股票池中的股票。如果个股价格高于其20日均线,则买入/持有;否则卖出。
- 仓位管理:将当前总资产平分给所有符合买入条件的股票(等权重模型)。
PTrade 策略代码
# 导入必要的库 (PTrade环境中通常不需要显式导入numpy/pandas,但为了代码提示可写)
import numpy as np
def initialize(context):
"""
初始化函数,设置股票池、参数和全局变量
"""
# 1. 设定要操作的股票池 (示例:几只科技和消费龙头)
g.security_list = ['600519.SS', '000858.SZ', '601318.SS', '600036.SS', '002594.SZ']
# 2. 设定大盘基准指数 (沪深300)
g.benchmark_index = '000300.SS'
# 3. 设定均线周期参数
g.ma_days = 20
# 4. 设置股票池 (必须步骤)
set_universe(g.security_list)
# 5. 开启日志输出
log.info("策略初始化完成,股票池数量: %d" % len(g.security_list))
def handle_data(context, data):
"""
核心交易逻辑,按频率运行 (日线或分钟线)
"""
# ---------------------------------------------------
# 第一步:大盘风控 (市场环境判断)
# ---------------------------------------------------
# 获取大盘指数过去 N+1 天的历史收盘价
index_hist = get_history(g.ma_days + 1, '1d', 'close', g.benchmark_index)
# 如果数据不足,直接返回
if len(index_hist) < g.ma_days + 1:
return
# 计算大盘均线 (不包含当天的前N天均值)
index_close = index_hist['close'].values
index_ma = index_close[:-1].mean()
index_current_price = index_close[-1]
# 判断大盘趋势
market_is_safe = index_current_price > index_ma
if not market_is_safe:
log.info("大盘跌破%d日均线,市场环境恶劣,执行清仓风控。" % g.ma_days)
# 遍历持仓,全部卖出
for stock in context.portfolio.positions.keys():
order_target(stock, 0)
return # 结束本周期运行
# ---------------------------------------------------
# 第二步:个股筛选 (选出符合条件的股票)
# ---------------------------------------------------
buy_list = []
for stock in g.security_list:
# 获取个股历史数据
# 注意:这里为了演示清晰,在循环中获取数据。
# 实际生产中,若股票池很大,建议一次性获取所有股票数据以提高效率。
stock_hist = get_history(g.ma_days + 1, '1d', 'close', stock)
if len(stock_hist) < g.ma_days + 1:
continue
stock_close = stock_hist['close'].values
stock_ma = stock_close[:-1].mean()
stock_current_price = stock_close[-1]
# 如果个股价格在均线之上,加入待买入列表
if stock_current_price > stock_ma:
buy_list.append(stock)
# ---------------------------------------------------
# 第三步:资金分配与调仓 (根据市场情况调整仓位)
# ---------------------------------------------------
# 如果没有符合条件的股票,清仓
if len(buy_list) == 0:
log.info("没有符合条件的个股,清仓观望。")
for stock in context.portfolio.positions.keys():
order_target(stock, 0)
return
# 计算每只股票的目标市值
# 逻辑:总资产 / 待买入股票数量 = 每只股票应持有的金额
total_value = context.portfolio.portfolio_value
target_value_per_stock = total_value / len(buy_list)
log.info("符合买入条件的股票: %s, 每只目标市值: %.2f" % (str(buy_list), target_value_per_stock))
# 开始调仓
# 1. 先卖出:不在buy_list中的持仓股票需要卖出
for stock in context.portfolio.positions.keys():
if stock not in buy_list:
order_target(stock, 0)
log.info("卖出不符合条件的股票: %s" % stock)
# 2. 后买入/调整:在buy_list中的股票,调整到目标市值
for stock in buy_list:
# order_target_value 会自动计算买入或卖出的数量,使持仓市值达到 target_value_per_stock
# 如果资金不足,系统会自动调整为最大可买数量
order_target_value(stock, target_value_per_stock)
代码关键点解析
-
get_history的使用:- 我们获取
g.ma_days + 1天的数据,是为了计算截止到昨天的均线,并与今天的最新价(或收盘价)进行对比。 index_close[:-1].mean()计算的是前N天的均值,避免包含当前未收盘的价格导致信号闪烁(如果是回测,这代表昨日均线)。
- 我们获取
-
order_target_value的优势:- 这是多股策略中最核心的函数。
- 你不需要计算“我要买多少股”,只需要告诉系统“我希望这只股票现在的持仓市值是多少”。
- 如果当前持仓为0,它会买入;如果当前持仓市值低于目标,它会加仓;如果高于目标(例如股价大涨导致仓位过重),它会减仓。这自动实现了再平衡(Rebalancing)。
-
风险控制:
- 代码优先判断大盘指数。这是一种典型的“自上而下”的策略思路,能有效避免在系统性风险(股灾)来临时频繁止损。
-
兼容性:
- 代码避免了 Python 3.6+ 的 f-string 语法(如
f"text {var}"),改用%格式化,确保在 PTrade 旧版本 Python 环境中也能稳定运行。
- 代码避免了 Python 3.6+ 的 f-string 语法(如
如何在 PTrade 中运行
- 新建一个策略,选择“股票”类型。
- 将上述代码复制到代码编辑区。
- 在右侧设置回测参数(如资金 1,000,000,频率选择“日线”)。
- 点击“运行回测”即可看到多只股票同时交易的效果。
Q&A: 常见问题解答
Q: 如何修改股票池?
A: 在 initialize 函数中,修改 g.security_list 列表即可。你可以填入任意你关注的股票代码(注意后缀 .SS 代表上交所,.SZ 代表深交所)。
Q: 为什么使用 order_target_value 而不是 order?
A: order 只是简单的按数量买卖。在多股策略中,股价各不相同(有的10元,有的100元),如果按固定数量买入,会导致高价股占用过多资金。order_target_value 可以按金额等权分配,是管理多只股票仓位的最佳工具。
Q: 这个策略可以用于分钟级别回测吗?
A: 可以。如果在回测设置中选择“分钟”频率,handle_data 会每分钟执行一次。但要注意,均线计算逻辑可能需要调整(例如获取分钟级别的历史数据),且高频交易会增加手续费成本。对于均线策略,通常建议在日线级别运行。
Q: 如何增加止损逻辑?
A: 可以在 handle_data 的循环中加入判断。例如,获取持仓成本 context.portfolio.positions[stock].cost_basis,如果 (current_price - cost_basis) / cost_basis < -0.10 (亏损10%),则强制执行 order_target(stock, 0)。