问题描述
解决方案
这是一个基于 PSY(Psychological Line,心理线) 指标的量化交易策略。
策略原理
PSY 指标主要用于研究投资者对股市涨跌产生心理波动的轨迹。它通过计算一段时间内股价上涨天数占总天数的比例,来判断市场的超买或超卖状态。
- 计算公式:$PSY(N) = \frac{N \text{日内上涨天数}}{N} \times 100$
- 参数设置:通常 $N$ 取 12 或 24。
- 交易逻辑:
- 买入信号:当 PSY 值低于 25(或 20),表示市场处于超卖区域,投资者恐慌过度,可能出现反弹,执行买入。
- 卖出信号:当 PSY 值高于 75(或 80),表示市场处于超买区域,投资者情绪过热,可能出现回调,执行卖出。
策略代码实现
以下代码可在聚宽(JoinQuant)的回测环境中直接运行。
# -*- coding: utf-8 -*-
import jqdata
import numpy as np
def initialize(context):
"""
初始化函数,设定基准、手续费、滑点及策略参数
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 设置交易手续费:买入万三,卖出万三加千一印花税
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 设定要操作的标的(这里以平安银行为例,也可以换成ETF如 510300.XSHG)
g.security = '000001.XSHE'
# PSY指标参数 N
g.N = 12
# 策略阈值
g.buy_threshold = 25 # PSY低于此值买入
g.sell_threshold = 75 # PSY高于此值卖出
# 每天开盘时运行策略
run_daily(market_open, time='open')
def market_open(context):
"""
每日交易逻辑
"""
security = g.security
# 获取过去 N+1 天的收盘价数据
# 我们需要 N 个变动数据,所以需要 N+1 个历史收盘价来计算涨跌
hist_data = attribute_history(security, g.N + 1, '1d', ['close'], skip_paused=True)
# 如果数据不足(例如刚上市),则不操作
if len(hist_data) < g.N + 1:
return
close_prices = hist_data['close'].values
# 计算上涨天数
up_days = 0
for i in range(1, len(close_prices)):
# 如果今天的收盘价 > 昨天的收盘价,记为上涨
if close_prices[i] > close_prices[i-1]:
up_days += 1
# 计算 PSY 值
psy_value = (up_days / g.N) * 100
# 获取当前账户资金和持仓情况
curr_position = context.portfolio.positions[security].closeable_amount
available_cash = context.portfolio.available_cash
# 打印日志方便调试
# log.info(f"日期: {context.current_dt.date()}, PSY值: {psy_value:.2f}")
# 交易逻辑
# 1. 超卖区域(PSY <= 25)且空仓 -> 全仓买入
if psy_value <= g.buy_threshold and curr_position == 0:
log.info(f"PSY值为 {psy_value:.2f},进入超卖区,执行买入")
order_value(security, available_cash)
# 2. 超买区域(PSY >= 75)且持仓 -> 清仓卖出
elif psy_value >= g.sell_threshold and curr_position > 0:
log.info(f"PSY值为 {psy_value:.2f},进入超买区,执行卖出")
order_target(security, 0)
# 记录曲线,方便在回测图中查看 PSY 走势
record(PSY=psy_value)
代码解析
- 数据获取:使用
attribute_history获取过去N+1天的收盘价。为什么是N+1?因为判断第 1 天是否上涨需要参考第 0 天的价格。 - 指标计算:通过循环对比每日收盘价与前一日收盘价,统计上涨天数,进而算出 PSY 值。
- 交易执行:
- 当 PSY $\le$ 25 时,认为市场情绪悲观,属于反向操作的买入时机。
- 当 PSY $\ge$ 75 时,认为市场情绪亢奋,属于获利了结的卖出时机。
- 可视化:使用
record(PSY=psy_value)可以在回测结果页面的图表中看到 PSY 指标的历史走势,便于分析策略有效性。
Q&A
Q: PSY 指标适合什么类型的市场?
A: PSY 指标在震荡市中表现较好,能够捕捉到情绪的极值点进行高抛低吸。在单边的大牛市或大熊市中,PSY 可能会出现“钝化”现象(例如在大牛市中 PSY 长期高于 75),导致过早卖出或过早抄底,因此建议结合均线(MA)或趋势指标(MACD)一起使用。
Q: 如何调整策略的灵敏度?
A: 可以通过调整参数 g.N 来改变灵敏度。
- 减小
g.N(如改为 6):指标更敏感,交易信号更频繁,但假信号也更多。 - 增大
g.N(如改为 24):指标更平滑,信号更稳定,但可能滞后。
Q: 代码中的 attribute_history 包含当天的数据吗?
A: 在 run_daily 设置为 time='open'(开盘运行)时,attribute_history 获取的是截止到昨天收盘的历史数据,不包含今天的数据。这是为了避免“未来函数”,即在开盘时我们只能利用昨天及以前的数据来做决策。
Q: 这个策略可以用于多只股票吗?
A: 上述代码是针对单只股票设计的。如果要用于多只股票,需要将 g.security 改为股票列表,并在 market_open 函数中增加一个 for 循环遍历股票列表,分别计算每只股票的 PSY 并独立下单。