问题描述
解决方案
这是一个基于 心理线 (PSY, Psychological Line) 指标的 PTrade 量化交易策略。
策略原理
心理线 (PSY) 是一种建立在研究投资人心理趋向基础上,分析某段期间内多空双方力量对比,从而判断股价未来走势的技术指标。
- 计算公式:
$$PSY(N) = \frac{A}{N} \times 100$$- $N$:周期(通常为 12 天)。
- $A$:周期内上涨的天数(收盘价 > 前一日收盘价)。
- 交易逻辑(反转策略):
- 买入信号(超卖):当 PSY 值低于 25(或 20)时,表示市场过度悲观,属于超卖区域,可能出现反弹,执行买入。
- 卖出信号(超买):当 PSY 值高于 75(或 80)时,表示市场过度乐观,属于超买区域,可能出现回调,执行卖出。
PTrade 策略代码
import numpy as np
def initialize(context):
"""
策略初始化函数
"""
# 设置我们要操作的股票,这里以恒生电子为例
g.security = '600570.SS'
# 设置股票池
set_universe(g.security)
# --- 策略参数设置 ---
# PSY计算周期,通常为12
g.psy_period = 12
# 买入阈值(超卖线),通常为25
g.buy_threshold = 25
# 卖出阈值(超买线),通常为75
g.sell_threshold = 75
# 设置滑点和手续费(可选,回测更精准)
set_slippage(slippage=0.002)
set_commission(commission_ratio=0.0003, min_commission=5.0)
def handle_data(context, data):
"""
按周期运行的策略逻辑(日线级别)
"""
security = g.security
# 1. 获取历史数据
# 我们需要计算过去 N 天的涨跌情况,因此需要 N+1 个收盘价来计算 N 个差值
# count 设置为 g.psy_period + 1
hist_data = get_history(g.psy_period + 1, frequency='1d', field='close', security_list=security, fq='pre')
# 检查数据长度是否足够,不足则不计算
if len(hist_data) < g.psy_period + 1:
log.info("历史数据不足,跳过计算")
return
# 获取收盘价序列
close_prices = hist_data['close'].values
# 2. 计算 PSY 指标
# 计算每日涨跌:今日收盘 - 昨日收盘
# np.diff 会计算相邻元素的差值,结果长度为 len(close_prices) - 1
price_diff = np.diff(close_prices)
# 统计上涨天数 (差值 > 0 的天数)
up_days_count = np.sum(price_diff > 0)
# 计算 PSY 值
psy_value = (up_days_count / g.psy_period) * 100
# 打印日志方便调试
log.info("日期: %s, 标的: %s, PSY值: %.2f" % (context.blotter.current_dt, security, psy_value))
# 3. 获取当前持仓和资金
position = context.portfolio.positions[security].amount
available_cash = context.portfolio.cash
# 4. 交易逻辑判断
# 信号:PSY <= 买入阈值 (超卖,看涨) 且 当前无持仓
if psy_value <= g.buy_threshold and position == 0:
log.info("PSY值 (%.2f) 进入超卖区,触发买入信号" % psy_value)
# 全仓买入
order_value(security, available_cash)
# 信号:PSY >= 卖出阈值 (超买,看跌) 且 当前有持仓
elif psy_value >= g.sell_threshold and position > 0:
log.info("PSY值 (%.2f) 进入超买区,触发卖出信号" % psy_value)
# 清仓卖出
order_target(security, 0)
代码解析
-
初始化 (
initialize):- 定义了全局变量
g.psy_period(12天)、g.buy_threshold(25) 和g.sell_threshold(75)。 - 设置了单一标的
600570.SS。
- 定义了全局变量
-
数据获取 (
get_history):- PSY 需要判断过去 N 天是涨还是跌。
- 为了判断 N 天的涨跌,我们需要 N+1 天的收盘价数据(因为第1天的涨跌需要对比第0天的价格)。
- 使用
numpy.diff快速计算价格差分。
-
指标计算:
np.sum(price_diff > 0):统计上涨的天数。- 公式:
(上涨天数 / 周期) * 100。
-
交易执行:
- 买入:当 PSY 低于 25 时,认为市场恐慌过度,全仓买入。
- 卖出:当 PSY 高于 75 时,认为市场过热,清仓止盈。
注意事项
- Python 版本兼容性:代码使用了 Python 3.5 兼容的字符串格式化
%语法,避免了 f-string,确保在 PTrade 旧版本环境中也能运行。 - 数据复权:
get_history中使用了fq='pre'(前复权),这是技术指标计算的标准做法,防止分红派息导致的价格缺口影响涨跌判断。 - 风险提示:PSY 是一个震荡指标,在单边大牛市或大熊市中可能会出现“钝化”(即长期停留在超买或超卖区),建议结合均线(MA)或趋势指标一起使用以过滤假信号。
Q&A
Q1: 如何修改 PSY 的计算周期,例如改为 24 天?
A1: 在 initialize 函数中,将 g.psy_period = 12 修改为 g.psy_period = 24 即可。
Q2: 这个策略可以用于分钟级别回测吗?
A2: 可以。代码逻辑是通用的。只需在回测配置界面将频率设置为“分钟”,或者在 get_history 中将 frequency='1d' 改为 frequency='1m' (或其他分钟周期),并相应调整阈值,因为分钟级别的波动频率与日线不同。
Q3: 为什么使用 np.diff 而不是循环计算涨跌?
A3: numpy 是 Python 中用于科学计算的库,np.diff 属于向量化操作,比 Python 原生的 for 循环计算速度快得多,且代码更简洁,非常适合量化交易中的指标计算。