问题描述
解决方案
这是一个基于 ARBR(人气意愿指标) 的量化交易策略。
策略原理
ARBR 指标由 AR(人气指标)和 BR(意愿指标)两个指标构成,主要用于分析市场买卖气势的强弱。
- AR (人气指标):以当天开盘价为基础,比较最高价、最低价与开盘价的关系,反映市场买卖人气的强弱。
- 公式:$AR = \frac{\sum (H - O)}{\sum (O - L)} \times 100$
- BR (意愿指标):以昨日收盘价为基础,比较当天最高价、最低价与昨日收盘价的关系,反映市场买卖意愿的强弱。
- 公式:$BR = \frac{\sum \max(0, H - PC)}{\sum \max(0, PC - L)} \times 100$
策略逻辑
本策略采用经典的超买超卖反转逻辑:
- 买入信号:当市场处于极度低迷(超卖)状态时买入。
- 条件:BR < 40。这通常意味着空方力量衰竭,股价可能见底反弹。
- 卖出信号:当市场处于极度亢奋(超买)状态时卖出。
- 条件:AR > 180。这通常意味着多方力量过度消耗,股价可能见顶回落。
- 标的:以平安银行(000001.XSHE)为例。
- 周期:日线级别,参数 N=26。
策略代码
# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
def initialize(context):
"""
初始化函数,设定基准、标的、参数等
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定要操作的股票,这里以平安银行为例
g.security = '000001.XSHE'
# ARBR指标的计算周期,通常为26
g.N = 26
# 设定买入和卖出的阈值
g.buy_threshold_br = 40 # BR低于此值视为超卖,买入
g.sell_threshold_ar = 180 # AR高于此值视为超买,卖出
# 每天开盘时运行策略
run_daily(market_open, time='09:30')
def market_open(context):
"""
每日开盘运行的逻辑
"""
security = g.security
# 1. 获取历史数据
# 计算BR需要用到前一日收盘价,所以获取 N + 1 天的数据
# fields: high(最高价), low(最低价), open(开盘价), close(收盘价)
h_data = attribute_history(security, g.N + 1, '1d', ['high', 'low', 'open', 'close'])
# 如果数据长度不足(例如刚上市),则跳过
if len(h_data) < g.N + 1:
return
# 2. 数据预处理
# 提取最近 N 天的数据用于计算 AR
high_n = h_data['high'][-g.N:]
low_n = h_data['low'][-g.N:]
open_n = h_data['open'][-g.N:]
# 提取用于计算 BR 的数据
# 当天最高价、最低价(最近 N 天)
high_curr = h_data['high'][-g.N:].values
low_curr = h_data['low'][-g.N:].values
# 前一日收盘价(取前 N+1 天数据中的前 N 个,即对应每一天的"昨日收盘")
close_prev = h_data['close'][:-1].values
# 3. 计算 AR 指标
# AR = Sum(High - Open) / Sum(Open - Low) * 100
ar_numerator = (high_n - open_n).sum()
ar_denominator = (open_n - low_n).sum()
# 防止分母为0
if ar_denominator == 0:
ar = 100 # 默认值
else:
ar = (ar_numerator / ar_denominator) * 100
# 4. 计算 BR 指标
# BR = Sum(MAX(0, High - PrevClose)) / Sum(MAX(0, PrevClose - Low)) * 100
# 计算分子序列:今天最高价 - 昨天收盘价,取大于0的部分
br_up_part = np.maximum(0, high_curr - close_prev)
# 计算分母序列:昨天收盘价 - 今天最低价,取大于0的部分
br_down_part = np.maximum(0, close_prev - low_curr)
br_numerator = br_up_part.sum()
br_denominator = br_down_part.sum()
# 防止分母为0
if br_denominator == 0:
br = 100 # 默认值
else:
br = (br_numerator / br_denominator) * 100
# 记录指标值到日志,方便回测查看
# log.info("Date: %s, AR: %.2f, BR: %.2f" % (context.current_dt.date(), ar, br))
# 5. 交易逻辑
curr_position = context.portfolio.positions[security].closeable_amount
available_cash = context.portfolio.available_cash
# 信号1:买入逻辑 (BR < 40,市场人气极度低迷,意愿指标超卖)
if br < g.buy_threshold_br:
if available_cash > 0:
# 全仓买入
order_value(security, available_cash)
log.info("买入信号触发 (BR=%.2f < %.2f): 买入 %s" % (br, g.buy_threshold_br, security))
# 信号2:卖出逻辑 (AR > 180,市场人气过热,人气指标超买)
elif ar > g.sell_threshold_ar:
if curr_position > 0:
# 清仓卖出
order_target(security, 0)
log.info("卖出信号触发 (AR=%.2f > %.2f): 卖出 %s" % (ar, g.sell_threshold_ar, security))
Q&A
Q1: 为什么计算 BR 指标时需要获取 N+1 天的数据?
A1: BR 指标的公式中包含 $(H - PC)$ 和 $(PC - L)$,其中 $PC$ 代表昨日收盘价。为了计算最近 N 天的 BR 值,我们需要这 N 天每一天对应的“前一天收盘价”,因此总共需要向前多取一天的数据。
Q2: AR 和 BR 的阈值(40 和 180)是如何确定的?
A2: 这是 ARBR 指标的经典经验值。通常认为 BR < 50 为低价区,AR > 150-180 为高价区。在实际量化中,这些参数可以通过 JoinQuant 的参数优化功能进行回测调整,以适应不同的个股波动特性。
Q3: 这个策略可以用于多只股票吗?
A3: 可以。目前的示例代码为了清晰仅针对单只股票。若要操作多只股票,可以将 g.security 改为一个股票列表,并在 market_open 函数中使用 for 循环遍历列表,对每一只股票分别计算 ARBR 并执行交易逻辑。
Q4: 为什么代码中要判断分母是否为0?
A4: 在极端行情下(例如连续一字涨停或跌停,或者长时间停牌后复牌),开盘价可能等于最低价(导致 AR 分母为0),或者昨日收盘价等于今日最低价(导致 BR 分母为0)。为了防止程序抛出 ZeroDivisionError 导致策略中断,必须进行分母非零检查。