3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个非常实用的风控需求。在量化交易中,这种机制通常被称为大盘择时过滤器(Market Timing Filter)或红绿灯机制。
其核心逻辑是:在执行具体的个股买卖策略之前,先判断大盘指数(如沪深300)的走势。如果大盘处于下跌趋势(熊市),则强制空仓或禁止开新仓;只有当大盘处于上涨趋势(牛市)时,才允许策略正常运行。
下面是一个完整的 PTrade 策略代码示例。
import numpy as np
import pandas as pd
def initialize(context):
"""
初始化函数,设置策略参数
"""
# 1. 设置要操作的股票(这里以恒生电子为例)
g.security = '600570.SS'
set_universe(g.security)
# 2. 设置大盘风控参数
g.index_security = '000300.SS' # 风控基准:沪深300指数
g.ma_window = 60 # 均线周期:60日均线
g.market_status = 'BULL' # 初始市场状态:BULL(牛), BEAR(熊)
# 3. 设置回测参数(仅回测有效)
set_commission(commission_ratio=0.0003, min_commission=5.0)
set_slippage(slippage=0.002)
def before_trading_start(context, data):
"""
盘前处理函数:每天开盘前判断市场状态
"""
# 获取指数的历史行情数据
# 多取一些数据以确保均线计算准确,这里取 ma_window + 10 天
count = g.ma_window + 10
# 获取历史收盘价,不包含当天(include=False)
history_data = get_history(count, frequency='1d', field='close',
security_list=g.index_security, include=False)
# 检查数据是否获取成功
if history_data is None or len(history_data) < g.ma_window:
log.warning("历史数据不足,无法计算大盘均线,默认维持原状态")
return
# 提取收盘价序列
# 注意:get_history 返回的是 DataFrame,列名是股票代码或 'close'
# 如果是单只股票,直接取 'close' 列;如果是多只,列名是代码
# 这里我们明确指定了 security_list 为单个字符串,通常返回 DataFrame 的列包含该代码或 'close'
# 为了兼容性,我们使用 values 属性转为 numpy 数组处理
close_prices = history_data['close'].values
# 计算均线 (MA60)
ma_value = np.mean(close_prices[-g.ma_window:])
# 获取前一天的收盘价
current_index_price = close_prices[-1]
# 判断市场状态
if current_index_price < ma_value:
g.market_status = 'BEAR'
log.info("大盘风控:当前指数价格(%.2f) < %d日均线(%.2f),判定为熊市,停止开仓/清仓。" % (current_index_price, g.ma_window, ma_value))
else:
g.market_status = 'BULL'
log.info("大盘风控:当前指数价格(%.2f) > %d日均线(%.2f),判定为牛市,策略正常运行。" % (current_index_price, g.ma_window, ma_value))
def handle_data(context, data):
"""
盘中交易函数
"""
# ---------------------------------------------------
# 1. 熊市风控逻辑
# ---------------------------------------------------
if g.market_status == 'BEAR':
# 如果处于熊市,检查是否有持仓
position = get_position(g.security)
# 如果有持仓,则清仓卖出
if position.amount > 0:
log.info("熊市避险:卖出 %s" % g.security)
order_target(g.security, 0)
# 熊市直接返回,不执行后续的买入逻辑
return
# ---------------------------------------------------
# 2. 牛市正常策略逻辑 (示例:简单的买入持有)
# ---------------------------------------------------
# 下面是您的具体策略逻辑,这里仅作示例:
# 如果是牛市且没有持仓,则买入
position = get_position(g.security)
# 简单的示例逻辑:如果没持仓就买入
if position.amount == 0:
log.info("牛市环境:买入 %s" % g.security)
# 用所有现金买入
order_value(g.security, context.portfolio.cash)
def after_trading_end(context, data):
"""
盘后处理
"""
pass
before_trading_start 的作用:
handle_data)之前就已经确定了当天的风控基调。get_history 获取指数(000300.SS)的历史数据。注意 include=False,表示使用昨天收盘的数据来决定今天的操作,避免使用未来数据。均线计算:
numpy.mean() 计算过去60天的平均收盘价。current_index_price(昨日收盘价)与 ma_value(均线价格)。handle_data 中的拦截机制:
if g.market_status == 'BEAR': 的判断块。order_target(g.security, 0) 将目标股票仓位调整为0。return 语句。一旦进入熊市逻辑,代码直接返回,不再执行下方任何买入逻辑。这是实现“停止开仓”的关键。g.index_security 改为 '399006.SZ'(创业板指)。g.ma_window 改为 20(20日均线)。60 或改为 120(半年线)。order_target(g.security, 0) 修改为:# 获取当前持仓
current_amount = position.amount
# 目标是减半
target_amount = int(current_amount * 0.5 / 100) * 100
order_target(g.security, target_amount)
Q1: 为什么要在 before_trading_start 里计算均线,而不是在 handle_data 里?
A1: before_trading_start 每天只运行一次,适合处理全天通用的逻辑(如大盘判断)。handle_data 可能每分钟运行一次(分钟回测时),如果在那里频繁获取历史数据并计算均线,会严重拖慢回测速度,且逻辑上没有必要(因为日线级别的均线一天才变一次)。
Q2: get_history 获取指数数据时,需要考虑复权吗?
A2: 对于指数(如沪深300),通常不需要像个股那样考虑分红配股带来的复权问题,直接使用默认数据即可。如果是个股作为基准,建议加上 fq='pre'(前复权)。
Q3: 这个过滤器能避免所有亏损吗?
A3: 不能。均线策略有滞后性。当市场急跌时,价格可能已经跌了很多才会跌破均线触发止损。它的主要作用是避免在长期的单边下跌趋势中持有仓位,从而保护本金,而不是为了躲避每一次短期回调。