问题描述
解决方案
在PTrade策略中加入基于宏观指标(通常使用大盘指数如沪深300、上证指数作为宏观代理)的市场风险开关,是一种非常有效的风控手段。这通常被称为“大盘择时”或“红绿灯机制”。
其核心逻辑是:在每日开盘前或盘中,计算大盘指数的指标(如均线、RSI、MACD等),如果大盘走势恶化,则停止买入或清仓。
以下是具体的实现步骤和完整的代码示例。
实现思路
- 选择宏观标的:通常选择
000300.SS(沪深300) 或000001.SS(上证指数) 代表市场整体环境。 - 定义风险指标:例如,当大盘指数跌破20日均线时,视为高风险(Risk On);站上20日均线时,视为低风险(Risk Off)。
- 设置全局开关:在
g全局对象中设置一个变量(如g.market_risk_on)。 - 交易逻辑控制:
- 在
handle_data中,如果风险开关开启(高风险),则禁止开仓,并可选择是否清仓。 - 如果风险开关关闭(低风险),则执行正常的选股和买入逻辑。
- 在
完整策略代码示例
以下代码展示了一个完整的策略:当沪深300指数跌破20日均线时,触发风控开关,清空持仓并停止买入;反之则正常交易(示例中简单买入恒生电子)。
def initialize(context):
"""
初始化函数
"""
# 1. 设定要操作的股票(策略标的)
g.security = '600570.SS'
set_universe(g.security)
# 2. 设定宏观参考指标(这里使用沪深300指数)
g.macro_index = '000300.SS'
# 3. 定义风险开关标识 (True表示有风险/禁止买入,False表示安全/允许交易)
g.is_risk_mode = False
# 4. 设定均线周期
g.ma_window = 20
log.info("策略初始化完成,宏观风控标的:%s" % g.macro_index)
def before_trading_start(context, data):
"""
盘前处理:计算宏观指标,更新风控开关状态
"""
# 获取大盘指数的历史数据(取过去 N+1 天,以确保能算出 N 日均线)
# 注意:get_history 返回的是 DataFrame
hist_data = get_history(g.ma_window + 5, '1d', 'close', g.macro_index, fq=None, include=False)
if len(hist_data) < g.ma_window:
log.warning("历史数据不足,无法计算大盘均线,默认无风险")
g.is_risk_mode = False
return
# 计算大盘的 N 日均线
# 使用切片取最近的 N 天数据计算均值
close_prices = hist_data['close'].values
ma_value = close_prices[-g.ma_window:].mean()
# 获取大盘昨日收盘价
current_index_price = close_prices[-1]
# --- 核心风控逻辑 ---
# 如果 大盘价格 < 均线,判定为市场风险高
if current_index_price < ma_value:
g.is_risk_mode = True
log.info("【风控触发】大盘指数(%.2f) 低于 %d日均线(%.2f),今日暂停买入并执行清仓。" % (current_index_price, g.ma_window, ma_value))
else:
g.is_risk_mode = False
log.info("【市场安全】大盘指数(%.2f) 高于 %d日均线(%.2f),策略正常运行。" % (current_index_price, g.ma_window, ma_value))
def handle_data(context, data):
"""
盘中交易逻辑
"""
# 1. 检查风控开关
if g.is_risk_mode:
# --- 高风险模式下的操作 ---
# 遍历当前所有持仓
positions = context.portfolio.positions
for stock in positions:
# 如果有持仓,全部卖出 (清仓)
if positions[stock].amount > 0:
order_target(stock, 0)
log.info("风控模式:卖出清仓 %s" % stock)
# 直接返回,不执行后续的买入逻辑
return
# 2. --- 正常交易模式下的操作 (示例逻辑) ---
# 获取标的股票
target_stock = g.security
# 简单的示例策略:如果没持仓就买入
if get_position(target_stock).amount == 0:
# 全仓买入
order_value(target_stock, context.portfolio.cash)
log.info("市场环境安全,买入标的:%s" % target_stock)
def after_trading_end(context, data):
"""
盘后处理
"""
pass
代码详解
-
initialize:- 定义了
g.macro_index为沪深300指数。 - 初始化
g.is_risk_mode变量,用于在不同函数间传递风控状态。
- 定义了
-
before_trading_start:- 为什么在这里计算? 盘前计算效率最高,且大盘的日线级别趋势在开盘前就已经确定(基于昨日收盘价)。
- 使用
get_history获取大盘指数的收盘价序列。 - 计算移动平均线(MA),并与最新价格对比。
- 更新
g.is_risk_mode的状态。
-
handle_data:- 优先级最高:进入函数的第一步就是检查
if g.is_risk_mode:。 - 清仓逻辑:如果处于风险模式,循环遍历
context.portfolio.positions,使用order_target(stock, 0)卖出所有持仓。 - 阻断逻辑:清仓后使用
return语句,强制结束当次handle_data的执行,防止后续的买入代码被执行。
- 优先级最高:进入函数的第一步就是检查
进阶扩展:其他宏观指标
除了简单的均线,您还可以使用其他指标来定义“宏观风险”:
-
大盘成交量萎缩:
# 获取成交量数据 vol = get_history(10, '1d', 'volume', g.macro_index)['volume'] # 如果今日成交量小于过去10天均量的80%,视为流动性风险 if vol[-1] < vol.mean() * 0.8: g.is_risk_mode = True -
RSI 指标过热/过冷:
# 使用内置 get_RSI 函数 # 注意:get_RSI 需要传入 close 数组 close_data = get_history(30, '1d', 'close', g.macro_index)['close'].values rsi_val = get_RSI(close_data, n=14)[-1] # 如果 RSI < 30 (超卖) 或 RSI > 80 (超买风险),可触发特定风控 if rsi_val < 30: # 熊市不抄底逻辑 g.is_risk_mode = True -
多指数共振:
同时判断上证指数(000001.SS)和创业板指(399006.SZ),只有当两者同时处于均线之上时才交易,否则空仓。
常见问题 (Q&A)
Q: 为什么使用 000300.SS 而不是具体的宏观经济数据(如GDP)?
A: PTrade 是量化交易终端,主要提供高频和低频的行情数据。GDP、CPI 等宏观经济数据通常更新频率低(月度/季度),且在 API 中不直接作为标准行情提供。使用大盘指数(Index)的技术形态是衡量市场情绪最直接、最实时的“宏观”代理指标。
Q: 可以在盘中实时监控大盘风险吗?
A: 可以。将 before_trading_start 中的计算逻辑移动到 handle_data 中,并将频率设置为分钟级回测。但要注意,分钟级别的频繁止损可能会造成较高的交易成本(“左右挨打”),通常建议大盘风控以日线级别为准。
Q: 这种风控会影响回测速度吗?
A: 几乎不会。计算一个指数的均线是非常快速的数学运算,相比于策略本身的逻辑,其开销可以忽略不计。