问题描述
解决方案
这是一个基于 ARBR(人气意愿指标) 的 PTrade 量化策略代码。
策略说明
ARBR 指标由 AR(人气指标)和 BR(意愿指标)两个指标构成,主要用于分析市场买卖气势的强弱。
- AR (人气指标):以当天开盘价为基础,比较最高价、最低价与开盘价的关系,反映市场的人气。
- BR (意愿指标):以昨日收盘价为基础,比较最高价、最低价与昨日收盘价的关系,反映市场的买卖意愿。
交易逻辑(本策略示例):
- 买入信号(金叉):当 AR 向上突破 BR(即 AR > BR 且 昨日AR < 昨日BR),且 AR < 100(处于非过热区间),视为人气回升,执行买入。
- 卖出信号(死叉或过热):当 AR 向下跌破 BR(即 AR < BR 且 昨日AR > 昨日BR),或者 AR > 150(市场过热),执行卖出止盈/止损。
策略代码
import numpy as np
import pandas as pd
def initialize(context):
"""
初始化函数,设置策略参数和股票池
"""
# 设定基准(这里以沪深300为例)
set_benchmark('000300.SS')
# 设置要操作的股票池(示例:平安银行)
# 实际使用时建议在 before_trading_start 中通过 get_index_stocks 获取指数成分股
g.security = ['000001.SZ', '600519.SS']
set_universe(g.security)
# ARBR 计算周期,通常为 26
g.N = 26
# 设置手续费(股票:万分之三,最低5元)
set_commission(commission_ratio=0.0003, min_commission=5.0, type='STOCK')
# 设置滑点
set_slippage(slippage=0.002)
log.info("ARBR 策略初始化完成")
def before_trading_start(context, data):
"""
盘前处理,每天开盘前运行一次
"""
# 可以在这里动态更新股票池,例如获取沪深300成分股
# g.security = get_index_stocks('000300.SS')
# set_universe(g.security)
pass
def calculate_arbr(security, n, current_dt):
"""
计算 AR 和 BR 指标
:param security: 股票代码
:param n: 计算周期
:param current_dt: 当前时间
:return: (ar, br, prev_ar, prev_br) 或者 None
"""
# 获取历史数据,需要 N+1 天的数据来计算前一天的 ARBR 用于判断交叉
# 字段需要:high, low, open, close, preclose
# 注意:BR计算需要昨收价,PTrade日线数据包含 preclose 字段
count = n + 2 # 多取2天,保证能计算出当天的ARBR和昨天的ARBR
df = get_history(count, frequency='1d', field=['high', 'low', 'open', 'preclose'], security_list=security, include=True)
if df is None or len(df) < count:
return None
# 提取数据列
high = df['high'].values
low = df['low'].values
open_p = df['open'].values
pre_close = df['preclose'].values
# --- 计算 AR ---
# AR = Sum(High - Open) / Sum(Open - Low) * 100
ho = high - open_p
ol = open_p - low
# --- 计算 BR ---
# BR = Sum(High - PreClose) / Sum(PreClose - Low) * 100
hc = high - pre_close
cl = pre_close - low
# 计算当天的 AR, BR (使用最近 N 天的数据)
# 切片范围 [-n:] 表示最后 N 天
ar_sum_ho = np.sum(ho[-n:])
ar_sum_ol = np.sum(ol[-n:])
br_sum_hc = np.sum(hc[-n:])
br_sum_cl = np.sum(cl[-n:])
# 避免除以零
if ar_sum_ol == 0: ar_sum_ol = 0.0001
if br_sum_cl == 0: br_sum_cl = 0.0001
ar = (ar_sum_ho / ar_sum_ol) * 100
br = (br_sum_hc / br_sum_cl) * 100
# 计算昨天的 AR, BR (使用倒数第2天到倒数第 N+1 天的数据)
# 切片范围 [-(n+1):-1]
prev_ar_sum_ho = np.sum(ho[-(n+1):-1])
prev_ar_sum_ol = np.sum(ol[-(n+1):-1])
prev_br_sum_hc = np.sum(hc[-(n+1):-1])
prev_br_sum_cl = np.sum(cl[-(n+1):-1])
if prev_ar_sum_ol == 0: prev_ar_sum_ol = 0.0001
if prev_br_sum_cl == 0: prev_br_sum_cl = 0.0001
prev_ar = (prev_ar_sum_ho / prev_ar_sum_ol) * 100
prev_br = (prev_br_sum_hc / prev_br_sum_cl) * 100
return ar, br, prev_ar, prev_br
def handle_data(context, data):
"""
盘中运行函数,处理交易逻辑
"""
# 获取当前持仓
positions = context.portfolio.positions
# 获取当前可用资金
cash = context.portfolio.cash
# 平均分配资金(简单示例)
if len(g.security) > 0:
per_stock_cash = cash / len(g.security)
else:
per_stock_cash = 0
for stock in g.security:
# 过滤停牌或无数据的股票
if stock not in data or np.isnan(data[stock]['close']):
continue
# 计算指标
result = calculate_arbr(stock, g.N, context.blotter.current_dt)
if result is None:
continue
ar, br, prev_ar, prev_br = result
# 获取当前持仓数量
curr_position = positions[stock].amount
# --- 交易逻辑 ---
# 1. 买入信号:
# AR 向上金叉 BR (今天 AR > BR 且 昨天 AR < BR)
# 且 AR 处于相对低位 (例如 < 100),防止高位接盘
buy_signal = (ar > br) and (prev_ar < prev_br) and (ar < 100)
# 2. 卖出信号:
# AR 向下死叉 BR (今天 AR < BR 且 昨天 AR > BR)
# 或者 AR 过高 (例如 > 150) 视为超买
sell_signal = ((ar < br) and (prev_ar > prev_br)) or (ar > 150)
# 执行交易
if buy_signal and curr_position == 0:
# 有信号且无持仓,买入
log.info("股票: %s 触发买入信号, AR: %.2f, BR: %.2f" % (stock, ar, br))
# 使用 order_value 按金额下单
order_value(stock, per_stock_cash)
elif sell_signal and curr_position > 0:
# 有信号且有持仓,卖出
log.info("股票: %s 触发卖出信号, AR: %.2f, BR: %.2f" % (stock, ar, br))
# 卖出全部持仓
order_target(stock, 0)
代码解析
-
initialize:- 设置了基准指数、股票池、手续费和滑点。
- 定义了全局变量
g.N = 26,这是 ARBR 指标的标准计算周期。
-
calculate_arbr:- 这是核心计算函数。它通过
get_history获取过去N+2天的数据。 - AR 公式: $\frac{\sum(High - Open)}{\sum(Open - Low)} \times 100$
- BR 公式: $\frac{\sum(High - PreClose)}{\sum(PreClose - Low)} \times 100$
- 函数不仅计算了当天的 AR/BR,还计算了前一天的 AR/BR,用于判断“金叉”和“死叉”。
- 加入了分母为 0 的保护机制,防止程序报错。
- 这是核心计算函数。它通过
-
handle_data:- 遍历股票池中的每一只股票。
- 买入逻辑: 当 AR 从下向上穿过 BR(金叉),并且 AR 小于 100(说明人气刚开始回暖,尚未过热)时,全仓买入该股分配的资金。
- 卖出逻辑: 当 AR 从上向下穿过 BR(死叉),或者 AR 绝对值超过 150(视为情绪过热,风险较大)时,清仓卖出。
注意事项
- 数据频率: 该策略默认使用日线级别 (
frequency='1d')。ARBR 指标在日线级别上效果最稳定。 - 股票池: 示例代码中仅手动设置了两只股票。在实盘或回测中,建议在
before_trading_start中使用get_index_stocks动态获取指数成分股(如沪深300)。 - Python 版本: 代码兼容 Python 3.5 及以上版本(PTrade 环境通常支持)。
Q&A
Q1: 为什么 ARBR 指标有时候会失效?
A1: ARBR 是情绪类指标,在震荡市中表现较好。在单边暴涨或暴跌的极端行情中,指标可能会出现钝化(长期停留在高位或低位),导致过早卖出或过早抄底。建议结合均线(MA)或 MACD 等趋势指标一起使用。
Q2: 如何调整策略的灵敏度?
A2: 可以调整 g.N 的值。减小 g.N(如改为 14)会使指标更敏感,交易更频繁,但假信号也会增加;增大 g.N(如改为 42)会使指标更平滑,适合捕捉中长期趋势。
Q3: 代码中的 include=True 是什么意思?
A3: 在 get_history 中,include=True 表示获取的数据包含当前回测时间点的数据。在日线回测中,这通常意味着包含当天的收盘价(回测引擎会在收盘后撮合)。如果是盘中实时交易,获取到的可能是截至当前分钟的日线数据快照。