问题描述
解决方案
威廉指标 (Williams %R) 超买超卖交易策略
威廉指标(Williams %R,简称 W%R)是一种动量指标,用于衡量市场的超买或超卖状态。它的取值范围在 0 到 -100 之间。
策略原理
-
指标计算:
$$ W%R = \frac{H_n - C}{H_n - L_n} \times -100 $$- $C$:当前收盘价
- $H_n$:过去 $N$ 个周期内的最高价
- $L_n$:过去 $N$ 个周期内的最低价
- $N$:通常取 14 或 28。
-
交易逻辑:
- 超卖区间(买入信号):当 W%R 低于 -80 时,表示市场处于超卖状态,价格可能反弹,执行买入。
- 超买区间(卖出信号):当 W%R 高于 -20 时,表示市场处于超买状态,价格可能回调,执行卖出。
PTrade 策略代码实现
由于 PTrade API 文档中未直接提供 get_WR 函数,我们需要利用 get_history 获取高、低、收数据后手动计算。
以下是完整的策略代码:
import numpy as np
def initialize(context):
"""
策略初始化函数
"""
# 1. 设置股票池 (这里以恒生电子为例,也可以设置为指数成分股)
g.security_list = ['600570.SS']
set_universe(g.security_list)
# 2. 设置威廉指标参数
g.N = 14 # 计算周期
g.buy_threshold = -80 # 超卖阈值 (买入)
g.sell_threshold = -20 # 超买阈值 (卖出)
# 3. 设置回测费用 (佣金万三,印花税千一)
set_commission(commission_ratio=0.0003, min_commission=5.0, type='STOCK')
# 设置滑点
set_slippage(slippage=0.002)
def handle_data(context, data):
"""
按周期运行的策略逻辑 (日线级别)
"""
# 遍历股票池中的每一只股票
for stock in g.security_list:
# 过滤停牌股票
if data[stock].is_open == 0:
continue
# 1. 获取历史数据
# 我们需要过去 N 天的数据来计算最高价和最低价
# include=True 表示包含当前这根K线的数据 (日线回测时为当日收盘数据)
hist = get_history(g.N, '1d', ['high', 'low', 'close'], stock, fq='pre', include=True)
# 如果数据长度不足 N 天,跳过计算
if len(hist) < g.N:
continue
# 2. 提取数据
highs = hist['high'].values
lows = hist['low'].values
close = hist['close'].values[-1] # 当前最新收盘价
# 3. 计算 N 日内的最高价和最低价
highest_high = np.max(highs)
lowest_low = np.min(lows)
# 4. 计算威廉指标 W%R
# 防止分母为0的情况 (即最高价等于最低价)
if highest_high == lowest_low:
wr = -50 # 给予一个中性值
else:
wr = (highest_high - close) / (highest_high - lowest_low) * -100
# 打印日志方便调试
# log.info("股票: %s, W%%R: %.2f" % (stock, wr))
# 5. 获取当前持仓
position = context.portfolio.positions[stock].amount
# 6. 交易逻辑
# 信号:W%R < -80 (超卖,买入)
if wr < g.buy_threshold:
if position == 0:
# 全仓买入 (这里简单示例使用所有现金买入一只,多只股票需分配资金)
cash = context.portfolio.cash
if cash > 0:
order_value(stock, cash)
log.info("买入信号触发: %s, W%%R: %.2f, 价格: %.2f" % (stock, wr, close))
# 信号:W%R > -20 (超买,卖出)
elif wr > g.sell_threshold:
if position > 0:
# 清仓卖出
order_target(stock, 0)
log.info("卖出信号触发: %s, W%%R: %.2f, 价格: %.2f" % (stock, wr, close))
代码解析
-
数据获取 (
get_history):- 我们使用
get_history(g.N, ...)获取过去 14 天的最高价、最低价和收盘价。 - 参数
include=True非常重要,确保计算时包含当天的最新价格,这样指标才是实时的。
- 我们使用
-
指标计算:
- 利用
numpy库的max和min函数快速找出周期内的极值。 - 严格按照 W%R 公式进行计算。增加了
highest_high == lowest_low的判断以防止除以零错误(虽然在日线级别很少见,但在分钟级别或极端行情下可能发生)。
- 利用
-
交易执行:
- 买入: 当
wr < -80且当前无持仓时,使用order_value用当前可用资金买入。 - 卖出: 当
wr > -20且当前有持仓时,使用order_target(stock, 0)将持仓调整为 0,即清仓。
- 买入: 当
优化建议
该策略是一个基础的均值回归策略,在实际应用中可以考虑以下优化方向:
- 结合趋势指标:W%R 在强趋势行情中容易出现“钝化”(例如长期停留在超买区但价格继续上涨)。可以结合移动平均线(MA)过滤信号,仅在上升趋势中执行超卖买入。
- 资金管理:代码中使用了全仓买入,建议根据股票池大小将资金等分(例如
cash / len(g.security_list))。 - 止损设置:虽然是超卖买入,但价格可能继续下跌,建议加入固定比例止损(如亏损 5% 止损)。
Q&A
Q: 为什么不直接调用 get_WR 接口?
A: PTrade 的官方 API 文档中并未提供直接计算威廉指标的接口(如 get_WR)。虽然部分第三方库(如 TA-Lib)可能支持,但为了保证策略在 PTrade 所有环境(回测、实盘)下的兼容性和稳定性,使用 numpy 基于基础行情数据手动计算是最可靠的方法。
Q: 这个策略适合什么频率?
A: 上述代码默认基于日线(handle_data 每日运行一次)。如果需要运行在分钟级别,只需在回测设置中将频率改为“分钟”,并将 get_history 的频率参数从 '1d' 改为 '1m' 即可。
Q: 如何避免 W%R 在 -100 或 0 附近频繁波动导致的频繁交易?
A: 可以修改开仓逻辑,不单纯判断“低于 -80”,而是判断“从下方上穿 -80”(即 昨日W%R < -80 且 今日W%R > -80),这样可以确认反弹趋势确立后再入场。