问题描述
解决方案
这是一个基于量价关系的经典交易策略。
策略逻辑说明
这个策略的核心思想是捕捉**“放量上涨”**的信号。在技术分析中,价格上涨配合成交量放大,通常意味着上涨趋势得到了资金的确认,后续上涨的概率较大。
具体规则如下:
- 买入条件(同时满足):
- 价格上涨:当日收盘价 > 昨日收盘价。
- 趋势向上:当日收盘价 > 5日均线(MA5),确保处于短期上升趋势。
- 放量:当日成交量 > 过去5日平均成交量的 2倍(即量比 > 2.0)。
- 卖出条件(满足任一):
- 止损:亏损超过 5%。
- 止盈:盈利超过 10%。
- 趋势破坏:收盘价跌破 5日均线。
PTrade 策略代码实现
以下代码可以直接复制到 PTrade 的策略编辑器中运行。代码兼容 Python 3.5 环境。
def initialize(context):
"""
初始化函数,设置股票池和全局变量
"""
# 设置要操作的股票,这里以 恒生电子(600570.SS) 为例
# 实际使用时可以替换为您关注的股票代码
g.security = '600570.SS'
set_universe(g.security)
# 策略参数设置
g.ma_days = 5 # 均线周期
g.vol_multiplier = 2.0 # 放量倍数(当日量 > 均量 * 倍数)
g.stop_loss = -0.05 # 止损比例 (-5%)
g.take_profit = 0.10 # 止盈比例 (+10%)
# 设置手续费(可选,回测时建议设置)
# set_commission(commission_ratio=0.0003, min_commission=5.0)
def handle_data(context, data):
"""
每日交易逻辑,日线级别回测会在 15:00 运行
"""
security = g.security
# 1. 获取历史数据
# 获取过去 g.ma_days + 1 天的数据,include=True 表示包含当天
# 我们需要当天的数据来判断是否放量和上涨,需要前几天的数据计算均值
count = g.ma_days + 1
hist = get_history(count, frequency='1d', field=['close', 'volume'], security_list=security, include=True)
# 如果数据不足(例如新股上市),则跳过
if len(hist['close']) < count:
return
# 2. 提取数据
# 当日收盘价
current_price = hist['close'][-1]
# 昨日收盘价
prev_close = hist['close'][-2]
# 当日成交量
current_vol = hist['volume'][-1]
# 计算过去5日的收盘价均值(包含今天)
ma5_price = hist['close'][-g.ma_days:].mean()
# 计算过去5日的成交量均值(不包含今天,用于对比今天的量是否异常放大)
# 取切片 [:-1] 拿到除今天外的前面数据,再取最后 g.ma_days 天
# 注意:如果 count 设置刚好,这里其实就是 hist['volume'][:-1]
avg_vol_past = hist['volume'][:-1].mean()
# 3. 获取当前持仓和成本
position_info = get_position(security)
current_amount = position_info.amount
avg_cost = position_info.cost_basis
# 4. 交易逻辑判断
# --- 卖出逻辑 ---
if current_amount > 0:
# 计算浮动盈亏比例
pnl_ratio = (current_price - avg_cost) / avg_cost
# 条件A: 止损
if pnl_ratio <= g.stop_loss:
order_target(security, 0)
log.info("触发止损卖出 %s,盈亏比例: %.2f%%" % (security, pnl_ratio * 100))
return # 卖出后结束本次循环
# 条件B: 止盈
elif pnl_ratio >= g.take_profit:
order_target(security, 0)
log.info("触发止盈卖出 %s,盈亏比例: %.2f%%" % (security, pnl_ratio * 100))
return
# 条件C: 趋势破坏(跌破均线)
elif current_price < ma5_price:
order_target(security, 0)
log.info("跌破MA5卖出 %s,当前价: %.2f, MA5: %.2f" % (security, current_price, ma5_price))
return
# --- 买入逻辑 ---
# 仅当空仓时买入(可根据需求修改为加仓)
if current_amount == 0:
# 条件1: 价格上涨 (今日收盘 > 昨日收盘)
cond_price_up = current_price > prev_close
# 条件2: 处于上升趋势 (今日收盘 > 5日均线)
cond_trend_up = current_price > ma5_price
# 条件3: 放量 (今日成交量 > 过去均量 * 倍数)
# 防止分母为0
if avg_vol_past > 0:
cond_vol_up = current_vol > (avg_vol_past * g.vol_multiplier)
else:
cond_vol_up = False
if cond_price_up and cond_trend_up and cond_vol_up:
# 全仓买入
cash = context.portfolio.cash
if cash > 0:
order_value(security, cash)
log.info("放量上涨买入 %s: 现价:%.2f, 现量:%.0f, 均量:%.0f" % (
security, current_price, current_vol, avg_vol_past))
代码关键点解析
-
get_history的使用:- 参数
include=True非常重要。在日线回测模式下,handle_data默认在收盘时(15:00)运行。我们需要当天的收盘价和成交量来判断是否“放量上涨”,所以必须包含当天数据。 count = g.ma_days + 1:我们需要取 N+1 天的数据,其中 N 天用于计算历史均值,第 N+1 天(也就是今天)用于和历史做比较。
- 参数
-
放量的定义:
- 代码中使用了
avg_vol_past = hist['volume'][:-1].mean()。这意味着我们是用今天的成交量,去对比过去5天(不含今天)的平均成交量。这是量比计算的一种常用方式,能更敏锐地捕捉突发资金入场。
- 代码中使用了
-
风险控制:
- 策略中加入了止盈(10%)和止损(-5%)逻辑。量价策略通常波动较大,严格的风控是必须的。
- 加入了均线(MA5)作为趋势过滤器,避免在下跌趋势的反弹中误买入。
-
兼容性:
- 代码避免了 Python 3.6+ 的 f-string 语法(如
f"{var}"),使用了%格式化字符串,确保在 PTrade 的 Python 3.5 环境中也能完美运行。
- 代码避免了 Python 3.6+ 的 f-string 语法(如
如何在 PTrade 中使用
- 打开 PTrade 客户端,进入“量化投研” -> “策略研究”。
- 新建一个策略文件。
- 将上述代码完整复制粘贴进去。
- 点击“回测”,设置回测时间段(例如最近一年),频率选择“日线”。
- 点击“运行回测”即可查看效果。
Q&A
Q: 如何修改放量的标准?
A: 修改 initialize 函数中的 g.vol_multiplier 参数。例如,将其改为 1.5 表示只要成交量是均量的 1.5 倍即触发买入。
Q: 这个策略可以用于多只股票吗?
A: 可以。目前的示例只设置了一只股票。如果要操作多只股票,可以在 initialize 中将 g.security 设置为一个列表(如 ['600570.SS', '000001.SZ']),然后在 handle_data 中使用 for 循环遍历这个列表,对每一只股票分别执行逻辑判断。
Q: 为什么有时候买入失败?
A: 常见原因包括:资金不足、股票停牌、或者股票已经涨停(无法买入)。PTrade 的日志(Log)中通常会有提示。