问题描述
解决方案
这是一个非常经典的量化交易思路,通常被称为**“市场状态识别”或“体制转换(Regime Switching)”**策略。
策略核心逻辑
该模型的核心在于识别当前市场是处于“趋势状态”还是“震荡状态”,从而选择不同的交易逻辑:
-
市场状态过滤器(Switching Mechanism):
- 使用 ADX (平均趋向指标) 来判断趋势强度。
- ADX > 25:判定为强趋势市场,启用动量策略。
- ADX <= 25:判定为震荡/弱趋势市场,启用反转策略。
-
动量策略(Momentum/Trend):
- 当市场处于强趋势时使用。
- 逻辑:双均线交叉。短期均线上穿长期均线买入,下穿卖出。
-
反转策略(Reversal/Mean Reversion):
- 当市场处于震荡时使用。
- 逻辑:RSI (相对强弱指标)。RSI < 30 超卖时买入(博反弹),RSI > 70 超买时卖出(博回调)。
PTrade 策略代码实现
以下是完整的策略代码。代码中使用了 talib 库来计算 ADX,这是 PTrade 平台支持的标准第三方库。
import numpy as np
import talib
import pandas as pd
def initialize(context):
"""
初始化函数,设置参数和股票池
"""
# 1. 设置要操作的股票(示例:恒生电子)
g.security = '600570.SS'
set_universe(g.security)
# 2. 策略参数设置
# ADX 参数
g.adx_period = 14 # ADX 计算周期
g.adx_threshold = 25 # 趋势/震荡的分界线
# 动量策略参数 (均线)
g.ma_short = 5 # 短期均线
g.ma_long = 20 # 长期均线
# 反转策略参数 (RSI)
g.rsi_period = 14 # RSI 周期
g.rsi_lower = 30 # 超卖阈值 (买入)
g.rsi_upper = 70 # 超买阈值 (卖出)
# 3. 设置手续费和滑点 (回测用)
set_commission(commission_ratio=0.0003, min_commission=5.0, type="STOCK")
set_slippage(slippage=0.002)
def handle_data(context, data):
"""
盘中运行函数,每日或每分钟执行
"""
security = g.security
# 1. 获取历史数据
# 我们需要足够的数据来计算 ADX 和 MA,这里取 60 天
# 注意:talib 需要 numpy.ndarray 类型的数据
hist_len = 60
h = get_history(hist_len, frequency='1d', field=['high', 'low', 'close'], security_list=security, fq='pre')
# 如果数据不足,直接返回
if h is None or len(h) < hist_len:
return
# 提取数据为 numpy 数组 (talib 需要 float 类型)
# 注意:get_history 返回的 DataFrame 列名是小写的
high_data = h['high'].values.astype(float)
low_data = h['low'].values.astype(float)
close_data = h['close'].values.astype(float)
# 2. 计算技术指标
# A. 计算 ADX (趋势强度)
# talib.ADX(high, low, close, timeperiod)
adx_array = talib.ADX(high_data, low_data, close_data, timeperiod=g.adx_period)
current_adx = adx_array[-1]
# B. 计算均线 (动量指标)
ma_short_array = talib.MA(close_data, timeperiod=g.ma_short, matype=0)
ma_long_array = talib.MA(close_data, timeperiod=g.ma_long, matype=0)
curr_ma_short = ma_short_array[-1]
curr_ma_long = ma_long_array[-1]
prev_ma_short = ma_short_array[-2]
prev_ma_long = ma_long_array[-2]
# C. 计算 RSI (反转指标)
# 也可以使用 PTrade 自带的 get_RSI,这里为了统一使用 talib
rsi_array = talib.RSI(close_data, timeperiod=g.rsi_period)
current_rsi = rsi_array[-1]
# 3. 获取当前持仓和资金
position = get_position(security).amount
cash = context.portfolio.cash
current_price = data[security]['close']
# 4. 策略逻辑核心:状态切换
# 打印当前状态日志
log.info("日期: %s | ADX: %.2f | RSI: %.2f | MA5: %.2f | MA20: %.2f" % (
context.blotter.current_dt, current_adx, current_rsi, curr_ma_short, curr_ma_long
))
if current_adx > g.adx_threshold:
# ---------------------------------------------------
# 场景 A: 强趋势市场 (ADX > 25) -> 使用动量策略 (均线)
# ---------------------------------------------------
log.info(">>> 市场状态:强趋势 (使用动量策略)")
# 金叉买入 (短期均线上穿长期均线)
if curr_ma_short > curr_ma_long and prev_ma_short <= prev_ma_long:
if position == 0:
log.info("动量信号:金叉买入")
order_value(security, cash)
# 死叉卖出 (短期均线下穿长期均线)
elif curr_ma_short < curr_ma_long and prev_ma_short >= prev_ma_long:
if position > 0:
log.info("动量信号:死叉卖出")
order_target(security, 0)
else:
# ---------------------------------------------------
# 场景 B: 震荡市场 (ADX <= 25) -> 使用反转策略 (RSI)
# ---------------------------------------------------
log.info(">>> 市场状态:震荡 (使用反转策略)")
# 超卖买入 (RSI < 30)
if current_rsi < g.rsi_lower:
if position == 0:
log.info("反转信号:RSI超卖买入")
order_value(security, cash)
# 超买卖出 (RSI > 70)
elif current_rsi > g.rsi_upper:
if position > 0:
log.info("反转信号:RSI超买卖出")
order_target(security, 0)
def after_trading_end(context, data):
"""
盘后处理
"""
pass
代码关键点解析
-
数据获取 (
get_history):- 我们获取了
high,low,close三种价格,因为计算 ADX 指标必须用到最高价和最低价,而不仅仅是收盘价。 fq='pre'表示使用前复权数据,这在回测中非常重要,可以避免分红派息导致的价格跳空影响指标计算。
- 我们获取了
-
指标计算 (
talib):- PTrade 原生支持
talib库,这是一个非常高效的金融技术指标计算库。 talib.ADX:计算趋势强度。talib.MA:计算移动平均线。talib.RSI:计算相对强弱指数。
- PTrade 原生支持
-
状态切换逻辑 (
if current_adx > g.adx_threshold):- 这是模型的“大脑”。它决定了当前时刻是听从“均线”的指挥,还是听从“RSI”的指挥。
- 为什么这样做? 均线策略在震荡市中会频繁止损(左右打脸),而 RSI 在单边大趋势中会过早卖出(踏空)或逆势抄底(接飞刀)。通过 ADX 将两者结合,可以互补短板。
-
交易执行:
order_value(security, cash):全仓买入(使用当前所有可用资金)。order_target(security, 0):清仓卖出。
如何优化此模型?
如果你想进一步改进这个策略,可以考虑以下方向:
- 增加止损:无论处于哪种模式,增加一个固定比例(如 -5%)的硬止损,防止反转策略在暴跌中持续抄底。
- 调整 ADX 阈值:25 是一个经验值,你可以根据回测结果调整为 20 或 30。
- 多因子确认:在反转模式下,除了 RSI 超卖,还可以增加布林带(Bollinger Bands)下轨支撑作为双重确认。
Q&A
Q1: 为什么选择 ADX 作为切换指标?
A1: ADX (Average Directional Index) 是专门设计用来衡量趋势强度而非方向的指标。它能很好地量化当前市场是“有趋势”还是“无趋势”,因此是动量与反转策略切换的最佳裁判。
Q2: 代码中的 talib 需要我自己安装吗?
A2: 不需要。PTrade 的云端环境(回测和实盘)已经预装了 talib 库,直接 import talib 即可使用。
Q3: 这个策略适合什么级别的频率?
A3: 该代码默认设置为日线级别 (frequency='1d')。如果需要用于分钟级别(如 15分钟或 60分钟),只需在回测设置中更改频率,并适当调整 g.adx_period 等参数即可。