在量化交易领域,波动率是描述市场价格波动剧烈程度的核心指标。高波动率往往伴随市场不确定性的攀升,而低波动率则可能标志着市场进入平稳阶段。不同的波动率指标在各异的市场环境中发挥着独特作用,助力交易者依据市场变化制定精准决策。
本文将系统解读六种主流波动率指标 —— 平均真实波幅(ATR)、布林带(Bollinger Bands)、唐奇安通道(Donchian Channels)、凯尔特纳通道(Keltner Channels)、Chaikin 波动率以及相对波动率指数(RVI)。
平均真实波幅(ATR)
平均真实波幅(ATR)通过对价格波动范围的均值计算来量化市场波动性。ATR 数值较高时,意味着市场波动性增强,这种情况通常出现在市场的高点、低点或突破阶段。
在实际交易中,ATR 的应用场景十分广泛:
- 止损设置:交易者常将止损订单置于当前价格下方 1.5 倍 ATR 的位置,以此适应市场的波动性。
- 趋势强度判断:在趋势跟随策略中,ATR 的上升可能意味着趋势强度的增强。
import qstock as qs
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 计算平均真实波幅(ATR)的函数
def calculate_atr(data, window=14):
# 计算每个周期的三种波动范围
high_low = data['high'] - data['low']
high_close = abs(data['high'] - data['close'].shift())
low_close = abs(data['low'] - data['close'].shift())
# 合并三种范围,取最大值作为真实波幅
ranges = pd.concat([high_low, high_close, low_close], axis=1)
true_range = ranges.max(axis=1)
# 计算ATR:真实波幅的移动平均值
atr = true_range.rolling(window=window).mean()
return atr
# 获取数据
data = qs.get_data('bnb', start="2021-01-01", end="2024-12-16")
# 计算ATR及其移动平均
data['ATR'] = calculate_atr(data)
data['ATR_MA'] = data['ATR'].rolling(window=14).mean() # ATR的14天移动平均
# 定义买入和卖出信号
buy_signal = (data['ATR'] > data['ATR_MA']) & (data['ATR'].shift(1) <= data['ATR_MA'].shift(1))
sell_signal = (data['ATR'] < data['ATR_MA']) & (data['ATR'].shift(1) >= data['ATR_MA'].shift(1))
# 绘图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(18, 8), sharex=True) # 共享x轴
# 绘制带有ATR基础买入和卖出信号的股票价格图
ax1.plot(data['close'], label='收盘价', alpha=0.5)
ax1.scatter(data.index[buy_signal], data['close'][buy_signal], label='买入信号(ATR)', marker='^', color='red', alpha=1)
ax1.scatter(data.index[sell_signal], data['close'][sell_signal], label='卖出信号(ATR)', marker='v', color='green', alpha=1)
for idx in data.index[buy_signal]:
ax1.axvline(x=idx, color='green', linestyle='--', alpha=0.5)
for idx in data.index[sell_signal]:
ax1.axvline(x=idx, color='red', linestyle='--', alpha=0.5)
ax1.set_title('价格与基于ATR的信号', size=18)
ax1.set_ylabel('价格', size=14)
ax1.legend()
# 绘制ATR子图,显示买入和卖出信号
ax2.plot(data['ATR'], label='平均真实波幅(ATR)', color='purple')
ax2.plot(data['ATR_MA'], label='14天ATR移动平均', color='orange', alpha=0.6)
ax2.scatter(data.index[buy_signal], data['ATR'][buy_signal], label='买入信号(ATR)', marker='^', color='red')
ax2.scatter(data.index[sell_signal], data['ATR'][sell_signal], label='卖出信号(ATR)', marker='v', color='green')
for idx in data.index[buy_signal]:
ax2.axvline(x=idx, color='green', linestyle='--', alpha=0.5)
for idx in data.index[sell_signal]:
ax2.axvline(x=idx, color='red', linestyle='--', alpha=0.5)
ax2.set_title('平均真实波幅(ATR)与信号', size=18)
ax2.set_ylabel('ATR', size=14)
ax2.legend()
plt.tight_layout()
plt.show()布林带(Bollinger Bands)
布林带能为交易者提供关于市场波动性和超买 / 超卖状态的深度洞察,在均值回归策略中应用广泛 —— 当价格触及下轨时买入,触及上轨时卖出。
布林带的构成包括:
- 中轨(MB):过去 n 期的简单移动平均(SMA),即 MB = SMA (n)。
- 上轨(UB):中轨上方 k 个标准差(SD),即 UB = MB + (k × SD)。
- 下轨(LB):中轨下方 k 个标准差(SD),即 LB = MB - (k × SD)。
布林带的带宽变化是判断波动性的重要依据:带宽扩大表明市场波动性增加,带宽收窄则意味着波动性减弱。
布林带的一大特色是能根据市场条件动态调整,这一点与 ATR 类似,都可用于衡量波动性。但布林带通过将价格与近期价格区间的相对位置可视化,增加了趋势分析的维度。当布林带收缩(即 “挤压” 状态)时,往往预示着价格将出现大幅波动,表明市场正处于整理阶段,即将迎来突破。
def calculate_bollinger_bands(data, window=20, num_of_std=2):
rolling_mean = data['close'].rolling(window=window).mean()
rolling_std = data['close'].rolling(window=window).std()
upper_band = rolling_mean + (rolling_std * num_of_std)
lower_band = rolling_mean - (rolling_std * num_of_std)
return rolling_mean, upper_band, lower_band
# 获取数据(参考前文示例)
# 计算布林带
data['Middle Band'], data['Upper Band'], data['Lower Band'] = calculate_bollinger_bands(data)
# 定义买入和卖出信号
buy_signal = (data['close'] < data['Lower Band']) & (data['close'].shift(1) >= data['Lower Band'].shift(1))
sell_signal = (data['close'] > data['Upper Band']) & (data['close'].shift(1) <= data['Upper Band'].shift(1))
# 绘制图形
fig, ax = plt.subplots(figsize=(20, 8))
# 绘制价格与布林带及买卖信号
ax.plot(data['close'], label='收盘价', alpha=1, linewidth=2)
ax.plot(data['Middle Band'], label='中轨(20日SMA)', color='blue', alpha=0.5)
ax.plot(data['Upper Band'], label='上轨(2倍标准差)', color='green', alpha=0.5)
ax.plot(data['Lower Band'], label='下轨(2倍标准差)', color='red', alpha=0.5)
ax.fill_between(data.index, data['Lower Band'], data['Upper Band'], color='grey', alpha=0.1)
ax.scatter(data.index[buy_signal], data['close'][buy_signal], label='买入信号', marker='^', color='red', alpha=1)
ax.scatter(data.index[sell_signal], data['close'][sell_signal], label='卖出信号', marker='v', color='green', alpha=1)
ax.set_title('价格与布林带', size=18)
ax.set_ylabel('价格', size=14)
ax.legend()
plt.tight_layout()
plt.show()唐奇安通道(Donchian Channels)
唐奇安通道以历史最高价和最低价为基础,为交易者提供市场范围和波动性的视角。通道的宽度直接反映波动性:通道越宽,表明市场波动越大。
唐奇安通道在突破策略中备受青睐:
- 当价格突破上轨时,生成买入信号;
- 当价格跌破下轨时,生成卖出信号。
同时,唐奇安通道也有助于趋势跟踪,当价格持续位于通道的上方或下方时,表明存在较强的趋势。
唐奇安通道的构成:
- 上轨(UB):过去 n 期的最高价;
- 下轨(LB):过去 n 期的最低价。
唐奇安通道的一个精妙之处在于其在市场整理阶段的有效性。当通道收窄时,意味着波动性降低,资产正处于整理状态。这对寻找突破机会的交易者来说至关重要,因为整理阶段之后往往会出现价格的大幅波动。
def calculate_donchian_channel(data, window=20):
data['Upper'] = data['high'].rolling(window=window).max()
data['Lower'] = data['low'].rolling(window=window).min()
data['Middle'] = (data['Upper'] + data['Lower']) / 2
return data
def generate_signals(data):
# 当收盘价突破上轨时买入
buy_signals = (data['close'] < data['Lower'].shift(1)) & (data['close'].shift(1) >= data['Lower'].shift(1))
# 当收盘价跌破下轨时卖出
sell_signals = (data['close'] > data['Upper'].shift(1)) & (data['close'].shift(1) <= data['Upper'].shift(1))
return buy_signals, sell_signals
# 获取数据(参考第一个示例)
# 计算唐奇安通道(20日周期)
window = 60
data = calculate_donchian_channel(data, window)
# 生成买入和卖出信号
buy_signals, sell_signals = generate_signals(data)
data['Buy Signals'] = buy_signals
data['Sell Signals'] = sell_signals
# 绘制图形
fig, ax = plt.subplots(figsize=(20, 8))
# 绘制价格与唐奇安通道及买卖信号
ax.plot(data['close'], label='收盘价', alpha=0.5)
ax.plot(data['Upper'], label='上轨唐奇安通道', linestyle='--', alpha=0.4)
ax.plot(data['Lower'], label='下轨唐奇安通道', linestyle='--', alpha=0.4)
ax.plot(data['Middle'], label='中轨唐奇安通道', linestyle='--', alpha=0.4)
ax.fill_between(data.index, data['Lower'], data['Upper'], color='grey', alpha=0.1)
# 标记买入和卖出信号
ax.scatter(data.index[data['Buy Signals']], data['close'][data['Buy Signals']], label='买入信号', marker='^', color='red', alpha=1)
ax.scatter(data.index[data['Sell Signals']], data['close'][data['Sell Signals']], label='卖出信号', marker='v', color='green', alpha=1)
ax.set_title('价格与唐奇安通道', size=18)
ax.set_ylabel('价格', size=14)
ax.legend()
plt.tight_layout()
plt.show()凯尔特纳通道(Keltner Channels)
凯尔特纳通道利用平均真实范围(ATR)来设定通道边界,使其对市场波动性的突发变化极为敏感。在波动剧烈的市场中,通道会扩大;而在平静的市场中,通道则会收缩。
与布林带类似,凯尔特纳通道可应用于均值回归和突破策略。常见的用法是:当价格从通道外部回归并收于通道内部时买入,这通常意味着可能出现价格反转。
凯尔特纳通道的构成:
- 中线(ML):最近 n 周期的指数移动平均(EMA),即 ML = EMA (n)。
- 上轨(UB):中线加上 k 倍的平均真实范围(ATR),即 UB = ML + (k × ATR)。
- 下轨(LB):中线减去 k 倍的 ATR,即 LB = ML - (k × ATR)。
凯尔特纳通道通过将波动性概念与指数移动平均(EMA)相结合,为市场趋势分析提供了独特视角。尽管凯尔特纳通道和布林带在表示波动性方面看似相似,但凯尔特纳通道使用 ATR 设置通道边界。由于 ATR 关注价格的极端变动,因此能提供更稳定的波动性测量结果。这使得凯尔特纳通道在区分正常价格波动和重大价格变动方面特别有效,能为交易者提供基于波动性和价格趋势的明确信号,而非仅仅是基于移动平均线的价格行为信号。
# 计算凯尔特纳通道
def calculate_keltner_channel(data, ema_period=20, atr_period=10, multiplier=2):
# 计算指数移动平均(EMA)
data['EMA'] = data['close'].ewm(span=ema_period, adjust=False).mean()
# 计算平均真实范围(ATR)
high_low = data['high'] - data['low']
high_close = abs(data['high'] - data['close'].shift())
low_close = abs(data['low'] - data['close'].shift())
ranges = pd.concat([high_low, high_close, low_close], axis=1)
true_range = ranges.max(axis=1)
data['ATR'] = true_range.rolling(window=atr_period).mean()
# 计算上轨和下轨
data['Upper'] = data['EMA'] + (data['ATR'] * multiplier)
data['Lower'] = data['EMA'] - (data['ATR'] * multiplier)
return data
# 生成买入和卖出信号
def generate_signals(data):
# 当收盘价高于上轨时买入
buy_signals = (data['close'] < data['Lower'])
# 当收盘价低于下轨时卖出
sell_signals = (data['close'] > data['Upper'])
return buy_signals, sell_signals
# 获取数据(参考示例1)
# 计算凯尔特纳通道
data = calculate_keltner_channel(data)
# 生成买入和卖出信号
data['Buy Signals'], data['Sell Signals'] = generate_signals(data)
# 绘图展示
fig, ax = plt.subplots(figsize=(20, 8))
# 绘制价格及凯尔特纳通道和买卖信号
ax.plot(data['close'], label='收盘价', alpha=0.5)
ax.plot(data['EMA'], label='EMA(20期)', color='blue', alpha=0.6)
ax.plot(data['Upper'], label='上轨凯尔特纳通道', linestyle='--', alpha=0.4)
ax.plot(data['Lower'], label='下轨凯尔特纳通道', linestyle='--', alpha=0.4)
ax.fill_between(data.index, data['Lower'], data['Upper'], color='grey', alpha=0.1)
ax.scatter(data.index[data['Buy Signals']], data['close'][data['Buy Signals']], label='买入信号', marker='^', color='red', alpha=1)
ax.scatter(data.index[data['Sell Signals']], data['close'][data['Sell Signals']], label='卖出信号', marker='v', color='green', alpha=1)
ax.set_title('价格与凯尔特纳通道', size=18)
ax.set_ylabel('价格', size=14)
ax.legend()
plt.tight_layout()
plt.show()Chaikin 波动率(Chaikin Volatility)
Chaikin 波动率指标通过高低价之间的范围来衡量市场波动性。该指标数值上升,表明市场波动性增强,这可能预示着市场顶部或底部的形成。
Chaikin 波动率指标主要用于识别市场波动性的极端情况。交易者会关注波动性较低的时期(可能是市场整合期),随后若出现波动性激增,则可能意味着市场即将突破。
Chaikin 波动率的计算方式是:先计算一定时期 n 内高低价差的指数移动平均(EMA),再计算该 EMA 与前一时期相比的百分比变化。
这一指标能深入反映市场情绪的变化,通常在价格变动之前就预示着市场情绪的转变。虽然多数交易者认为高波动性与市场顶部相关、低波动性与市场底部相关,但波动性变化的时机和方向能提供识别潜在反转或趋势持续的细微信号。例如,波动性逐渐降低后突然上升,可能表明市场压力在积聚,预示着重大价格突破即将到来。这一模式说明,交易者不仅可以用 Chaikin 波动率评估当前市场波动状态,还能借此预测未来市场动态,使其成为一种战略性的交易决策前置工具。
import qstock as qs
import pandas as pd
import matplotlib.pyplot as plt
# 计算Chaikin波动率的函数
def calculate_chaikin_volatility(data, ema_period=10, change_period=10):
# 计算每日高低价差值
data['HL_Spread'] = data['high'] - data['low']
# 计算高低价差值的指数移动平均(EMA)
data['EMA_HL_Spread'] = data['HL_Spread'].ewm(span=ema_period, adjust=False).mean()
# 计算Chaikin波动率:EMA的百分比变化
data['Chaikin_Volatility'] = (data['EMA_HL_Spread'] - data['EMA_HL_Spread'].shift(change_period)) \
/ data['EMA_HL_Spread'].shift(change_period) * 100
return data
# 获取价格
data = qs.get_data('bnb', start="2021-01-01", end="2024-12-16")
# 计算Chaikin波动率
data = calculate_chaikin_volatility(data, ema_period=10, change_period=10)
# 创建图形
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(20, 10), gridspec_kw={'height_ratios': [2, 1]})
# 绘制股票价格图表
ax1.plot(data['close'], label='收盘价', color='blue', linewidth=2)
ax1.set_title('价格走势', fontsize=18)
ax1.set_ylabel('价格', fontsize=14)
ax1.grid(True, alpha=0.3)
ax1.legend(fontsize=12)
# 绘制Chaikin波动率图表
ax2.plot(data['Chaikin_Volatility'], label='Chaikin波动率', color='orange', linewidth=2)
ax2.axhline(y=0, color='gray', linestyle='--', alpha=0.5) # 零轴参考线
ax2.set_title('Chaikin波动率指标', fontsize=18)
ax2.set_ylabel('波动率 (%)', fontsize=14)
ax2.set_xlabel('日期', fontsize=14)
ax2.grid(True, alpha=0.3)
ax2.legend(fontsize=12)
# 调整布局并显示
plt.tight_layout()
plt.show()相对波动指数(RVI)
相对波动指数(RVI)是衡量市场波动性的指标,其计算逻辑与相对强弱指数(RSI)类似,但RVI采用标准差而非价格变动幅度。该指标可帮助识别市场顶部和底部的潜在区域。
在实际应用中,RVI常与RSI、MACD等其他指标结合使用以确认信号。例如:
- 当RVI值高于60等较高阈值时,可能表明市场过热;
- 当RVI值低于40等较低阈值时,可能表明市场过冷。
RVI的计算过程是:先计算特定时期内高价和低价的标准差,再将高价标准差与高低价标准差之和的比值标准化到0-100范围内。其信号线为RVI的4期简单移动平均(SMA),用于生成买卖信号。
RVI的独特价值在于它关注波动性的方向而非单纯的幅度。与其他基于价格极值发出超买或超卖信号的指标不同,RVI能反映当前市场波动性相对于近期历史是上升还是下降。这在波动剧烈的市场中尤为实用,因为传统基于价格的指标可能会产生相互矛盾的信号。
def calculate_rvi(data, period=14):
# 计算给定周期内的高低价的标准差
data['StdDev_High'] = data['high'].rolling(window=period).std()
data['StdDev_Low'] = data['low'].rolling(window=period).std()
# 计算RVI
data['RVI'] = 100 * (data['StdDev_High'] / (data['StdDev_High'] + data['StdDev_Low']))
# 计算RVI的4周期简单移动平均作为信号线
data['RVI_Signal'] = data['RVI'].rolling(window=4).mean()
return data
def generate_signals(data, signal_threshold=5):
# 当RVI上穿信号线且超过一定阈值时买入
data['Buy Signals'] = ((data['RVI'] > data['RVI_Signal']) &
(data['RVI'] - data['RVI_Signal'] > signal_threshold) &
(data['RVI'].shift(1) <= data['RVI_Signal'].shift(1)))
# 当RVI下穿信号线且超过一定阈值时卖出
data['Sell Signals'] = ((data['RVI'] < data['RVI_Signal']) &
(data['RVI_Signal'] - data['RVI'] > signal_threshold) &
(data['RVI'].shift(1) >= data['RVI_Signal'].shift(1)))
return data
# 计算RVI
data = calculate_rvi(data)
# 生成买入和卖出信号,并调整阈值以控制信号强度
signal_threshold = 1
data = generate_signals(data, signal_threshold)
# 绘图展示
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(20, 10), gridspec_kw={'height_ratios': [2, 1]})
# 价格图表及买卖信号
ax1.plot(data['close'], label='收盘价', color='blue')
ax1.scatter(data.index[data['Buy Signals']], data['close'][data['Buy Signals']], label='买入信号', marker='^', color='green', alpha=1)
ax1.scatter(data.index[data['Sell Signals']], data['close'][data['Sell Signals']], label='卖出信号', marker='v', color='red', alpha=1)
ax1.set_title('价格及RVI信号', size=18)
ax1.set_ylabel('价格', size=14)
ax1.legend()
# RVI及信号线图表
ax2.plot(data['RVI'], label='RVI', color='green')
ax2.plot(data['RVI_Signal'], label='信号线', color='red', linestyle='--')
ax2.scatter(data.index[data['Buy Signals']], data['RVI'][data['Buy Signals']], label='买入信号', marker='^', color='blue', alpha=1)
ax2.scatter(data.index[data['Sell Signals']], data['RVI'][data['Sell Signals']], label='卖出信号', marker='v', color='orange', alpha=1)
ax2.set_title('相对波动指数(RVI)及信号', size=18)
ax2.set_ylabel('RVI', size=14)
ax2.legend()
plt.tight_layout()
plt.show()技术指标的实际应用远不止识别买卖信号。从平均真实波动范围(ATR)到相对波动指数(RVI),每种指标都从不同角度解读市场行为,帮助交易者深化对市场动态的理解。波动率技术指标的实际应用主要体现在以下方面:
- 与振荡器的确认:将趋势跟踪指标与 RVI 等振荡器结合,可确认市场状况。例如,RVI 值高于 60 可能表明市场超买,但若同时布林带收缩,可能意味着潜在的波动性挤压和即将到来的价格突破。
- 识别反转机会:布林带、Keltner 通道等指标在识别潜在反转机会时作用显著。比如,价格突破布林带上轨可能表明市场过度延伸,暗示可能回调;价格突破 Keltner 通道后重新回到通道内,可能意味着均值回归机会。
- 利用突破机会:唐奇安通道和 ATR 在突破策略中效果显著。价格突破上轨可能是突破信号,而 ATR 上升表明波动性增强,可确认价格移动的强度。
- 动态止损单:利用 ATR 设置止损单,有助于实现经波动性调整后的风险管理。将止损设为当前 ATR 的倍数,可根据市场条件变化调整,高波动时避免不必要损失,正常波动时防止过早退出。
- 动态头寸大小:在确定头寸大小时应用 ATR 等波动性度量,能显著增强风险管理。根据当前波动性调整头寸大小,可确保每次交易的风险一致,不受市场状况影响。
- 市场情绪与指标极值:技术指标还能反映市场情绪。例如,RVI 值极高或极低可能不仅表明超买或超卖,还可能反映极端情绪,这种情绪可能引发市场重大反转。
- 指标背离:价格行为与 RVI、布林带宽度等指标的背离,可能为潜在趋势变化提供早期预警。比如,价格创新高但指标未确认并创新高,可能表明动能减弱,存在反转可能。
波动率技术指标的局限性主要包括:
- 滞后信号:多数技术指标基于过去的价格数据,存在固有延迟,可能导致入场或退出信号滞后,错失机会或增加风险。
- 假设重复性:技术分析假设市场行为会重复,但这一假设并非总能成立,尤其在面对前所未有的事件或市场基本面变化时。
- 市场噪音:在震荡或高度波动的市场中,指标易产生虚假信号。例如,RVI 等振荡器可能在超买和超卖水平间波动,却无明确趋势,导致信号混乱。
- 单一指标不足:任何单一指标都无法完整呈现市场状况,单独使用可能误解信号。
- 市场心理:技术指标无法考量人类情绪或市场情绪的突变,而这些因素可能极大影响价格波动。新闻、经济事件或投资者情绪变化可能迅速使技术信号失效。


