新高新低指标(New Highs-New Lows,NH-NL)是衡量市场广度和趋势强度的核心技术指标,通过统计一定周期内创阶段新高/新低的标的数量,判断市场整体强弱、趋势持续性及反转信号,适用于股票、指数、行业板块等宽基市场分析,是量化策略中“择时+仓位管理”的关键工具。
一、核心原理:用“市场广度”反映趋势本质
1. 定义与核心逻辑
市场趋势的本质是“多数标的的一致性运动”:
- • 当大量标的同步创阶段新高,说明多头力量占优,上涨趋势具备广度和持续性;
- • 当大量标的同步创阶段新低,说明空头力量占优,下跌趋势未结束;
- • 若新高/新低数量背离(如指数创新高但新高标的数减少),预示趋势可能反转。
2. 核心指标公式
新高新低指标有3个核心维度,需结合使用:
| | |
| 统计周期内,收盘价创N日新高的标的数量(N常见20/50/60/120日,代表不同周期) | |
| | |
| | 整体市场情绪(正数=多头占优,负数=空头占优,绝对值越大趋势越强) |
| | 相对强弱(比值>3=极端多头,比值<0.3=极端空头) |
3. 关键参数说明
- • 短期:20/30日(反映短期市场情绪,适合短线择时);
- • 中期:60/90日(反映中期趋势,适合波段操作);
- • 长期:120/250日(反映长期趋势,适合仓位管理);
- • 统计范围:可选择全市场(如A股全个股)、行业板块(如半导体板块)、指数成分股(如沪深300成分股);
- • 新高/新低判定:当日收盘价>过去N个交易日收盘价→创N日新高;当日收盘价<过去N个交易日收盘价→创N日新低(等于不算)。
二、核心应用:趋势判断与反转信号
1. 基础应用:趋势强度判断
通过NH-NL差值/比率判断市场整体状态,指导仓位管理:
2. 进阶应用:背离信号(核心用法)
新高新低指标的最大价值是识别“指数与广度的背离”,预判趋势反转:
(1)顶背离(下跌预警)
- • 特征:指数创阶段新高,但同期新高数(NH)未同步新高,甚至减少;
- • 逻辑:指数上涨仅由少数权重股推动,多数标的未跟随,“赚指数不赚钱”,上涨趋势无广度;
- • 案例:沪深300创年内新高,但20日新高标的数从500只降至200只→后续指数回调概率80%+。
(2)底背离(上涨预警)
- • 特征:指数创阶段新低,但同期新低数(NL)未同步新低,甚至减少;
- • 逻辑:指数下跌仅由少数权重股拖累,多数标的已止跌,下跌趋势无广度;
- • 案例:创业板指创年内新低,但20日新低标的数从400只降至100只→后续指数反弹概率80%+。
3. 实战技巧:多周期共振
单一周期易失真,需结合短/中/长期新高新低指标:
- • 短(20日)+ 中(60日)+ 长(120日)NH-NL均为正→强多头趋势,持仓不动;
- • 短NH-NL为正,但中/长为负→反弹行情,快进快出;
- • 短NH-NL为负,但中/长为正→回调行情,可低吸。
4. 适用场景与注意事项
适用场景
- • 行业板块轮动(如筛选NH-NL持续为正的板块);
- • 量化策略仓位管理(根据NH-NL绝对值调整仓位);
注意事项
- • 避免单一个股分析:新高新低是“广度指标”,对单一个股无意义,仅适用于多标的市场;
- • 剔除异常标的:统计时需排除停牌、ST、新股(上市<N日),避免数据失真;
- • 结合成交量:NH-NL扩大+成交量放大,趋势更具持续性;NH-NL扩大但成交量萎缩,趋势易反转。
三、Python实战实现(基于A股全市场数据)
以下代码以“A股全个股20日新高新低”为例,实现指标计算、可视化及背离信号检测,适配量化回测场景。
1. 核心代码
import pandas as pd
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
# 初始化tushare(需注册获取token)
ts.set_token("你的tushare_token")
pro = ts.pro_api()
def get_stock_list():
"""获取A股全市场股票列表(剔除ST、停牌、新股)"""
# 获取基础股票列表
stock_basic = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,list_date')
# 剔除上市不足20日的新股
stock_basic['list_date'] = pd.to_datetime(stock_basic['list_date'])
stock_basic = stock_basic[stock_basic['list_date'] < pd.Timestamp.now() - pd.Timedelta(days=20)]
# 剔除ST股(可根据需要补充停牌筛选)
stock_basic = stock_basic[~stock_basic['name'].str.contains('ST', na=False)]
return stock_basic['ts_code'].tolist()
def calculate_nh_nl(stock_codes, start_date, end_date, period=20):
"""
计算指定周期内的新高新低指标
:param stock_codes: 股票代码列表
:param start_date: 开始日期(格式:YYYYMMDD)
:param end_date: 结束日期(格式:YYYYMMDD)
:param period: 新高/新低统计周期(默认20日)
:return: 每日NH、NL、NH-NL、NH/NL的DataFrame
"""
# 初始化结果字典
nh_nl_dict = {
'trade_date': [],
'nh': [], # 新高数
'nl': [], # 新低数
'nh_nl_diff': [], # 新高-新低
'nh_nl_ratio': [] # 新高/新低
}
# 获取全市场交易日列表
trade_cal = pro.trade_cal(exchange='SSE', start_date=start_date, end_date=end_date)
trade_dates = trade_cal[trade_cal['is_open'] == 1]['cal_date'].tolist()
# 遍历每个交易日,计算当日NH/NL
for trade_date in trade_dates:
print(f"计算{trade_date}新高新低...")
nh_count = 0
nl_count = 0
# 批量获取当日全市场股票收盘价(tushare单次最多取500只,需分批)
batch_size = 500
for i in range(0, len(stock_codes), batch_size):
batch_codes = stock_codes[i:i+batch_size]
# 获取该批次股票的period+1日收盘价(需历史数据判断新高/新低)
try:
df = pro.daily(
ts_code=','.join(batch_codes),
start_date=str(int(trade_date) - period*100), # 往前推period天(简化处理)
end_date=trade_date
)
if df.empty:
continue
# 按股票代码分组,计算每只股票的新高/新低
for ts_code, group in df.groupby('ts_code'):
# 按日期排序,确保数据连续
group = group.sort_values('trade_date')
if len(group) < period:
continue # 数据不足period天,跳过
# 取最新收盘价和过去period日收盘价
latest_close = group['close'].iloc[-1]
past_close = group['close'].iloc[:-1][-period:] # 过去period日收盘价
# 判断是否创period日新高
if latest_close > past_close.
max():
nh_count += 1
# 判断是否创period日新低
if latest_close < past_close.min():
nl_count += 1
except Exception as e:
print(f"批量获取数据失败:{e}")
continue
# 计算衍生指标
nh_nl_diff = nh_count - nl_count
nh_nl_ratio = nh_count / nl_count if nl_count != 0 else np.inf
# 存入结果
nh_nl_dict['trade_date'].append(trade_date)
nh_nl_dict['nh'].append(nh_count)
nh_nl_dict['nl'].append(nl_count)
nh_nl_dict['nh_nl_diff'].append(nh_nl_diff)
nh_nl_dict['nh_nl_ratio'].append(nh_nl_ratio)
# 转换为DataFrame并整理
nh_nl_df = pd.DataFrame(nh_nl_dict)
nh_nl_df['trade_date'] = pd.to_datetime(nh_nl_df['trade_date'])
nh_nl_df = nh_nl_df.sort_values('trade_date').reset_index(drop=True)
return nh_nl_df
def detect_divergence(nh_nl_df, index_df, period=20):
"""
检测指数与新高新低的背离信号
:param nh_nl_df: 新高新低指标DataFrame
:param index_df: 指数收盘价DataFrame(含trade_date、close)
:param period: 背离检测周期(默认20日)
:return: 含背离信号的DataFrame
"""
# 合并新高新低和指数数据
merge_df = pd.merge(nh_nl_df, index_df, on='trade_date', how='inner')
merge_df.rename(columns={'close': 'index_close'}, inplace=True)
# 计算指数和NH的20日最大值(用于判断背离)
merge_df['index_high_20'] = merge_df['index_close'].rolling(window=period).max()
merge_df['nh_high_20'] = merge_df['nh'].rolling(window=period).max()
# 顶背离:指数创新高,但NH未创新高
merge_df['top_divergence'] = (merge_df['index_close'] == merge_df['index_high_20']) & \
(merge_df['nh'] < merge_df['nh_high_20'])
# 底背离:指数创新低,但NL未创新低
merge_df['index_low_20'] = merge_df['index_close'].rolling(window=period).min()
merge_df['nl_low_20'] = merge_df['nl'].rolling(window=period).min()
merge_df['bottom_divergence'] = (merge_df['index_close'] == merge_df['index_low_20']) & \
(merge_df['nl'] < merge_df['nl_low_20'])
return merge_df
def plot_nh_nl(nh_nl_df, index_df=None):
"""可视化新高新低指标及指数背离"""
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True)
# 子图1:NH、NL、NH-NL差值
ax1.plot(nh_nl_df['trade_date'], nh_nl_df['nh'], color='red', label='20日新高数(NH)')
ax1.plot(nh_nl_df['trade_date'], nh_nl_df['nl'], color='green', label='20日新低数(NL)')
ax1.plot(nh_nl_df['trade_date'], nh_nl_df['nh_nl_diff'], color='blue', label='NH-NL差值', linestyle='--')
ax1.axhline(y=0, color='black', alpha=0.3, linestyle='-')
ax1.set_title('A股全市场20日新高新低指标')
ax1.set_ylabel('数量')
ax1.legend()
ax1.grid(alpha=0.3)
# 子图2:指数+背离信号(若传入指数数据)
if index_df is not None:
merge_df = pd.merge(nh_nl_df, index_df, on='trade_date', how='inner')
ax2.plot(merge_df['trade_date'], merge_df['close'], color='black', label='沪深300指数')
# 标记顶背离(红色三角)和底背离(绿色三角)
top_div = merge_df[merge_df['top_divergence'] == True]
bottom_div = merge_df[merge_df['bottom_divergence'] == True]
ax2.scatter(top_div['trade_date'], top_div['close'], color='red', marker='^', s=100, label='顶背离')
ax2.scatter(bottom_div['trade_date'], bottom_div['close'], color='green', marker='v', s=100, label='底背离')
ax2.set_ylabel('指数收盘价')
ax2.legend()
ax2.grid(alpha=0.3)
plt.xlabel('日期')
plt.tight_layout()
plt.show()
# --------------- 主函数调用 ---------------
if __name__ == "__main__":
# 1. 获取A股股票列表
stock_codes = get_stock_list()
print(f"有效股票数量:
{len(stock_codes)}")
# 2. 计算2024年1-6月的20日新高新低指标
nh_nl_df = calculate_nh_nl(
stock_codes=stock_codes,
start_date="20240101",
end_date="20240630",
period=20
)
# 3. 获取沪深300指数数据(用于背离检测)
index_df = pro.index_daily(ts_code='000300.SH', start_date='20240101', end_date='20240630')
index_df['trade_date'] = pd.to_datetime(index_df['trade_date'])
index_df = index_df.sort_values('trade_date').reset_index(drop=True)
# 4. 检测背离信号
merge_df = detect_divergence(nh_nl_df, index_df, period=20)
# 5. 可视化
plot_nh_nl(nh_nl_df, index_df)
# 6. 输出结果
print("新高新低指标(最后10日):")
print(nh_nl_df.tail(10)[['trade_date', 'nh', 'nl', 'nh_nl_diff', 'nh_nl_ratio']])
2. 代码说明
(1)数据获取层
- •
get_stock_list():筛选有效标的(剔除ST、新股、停牌股),保证统计广度; - •
pro.daily():批量获取股票/指数收盘价,适配tushare分批限制(单次500只)。
(2)指标计算层
- • 遍历每个交易日,统计全市场创20日新高/新低的标的数量;
- • 计算NH-NL差值(整体情绪)、NH/NL比率(相对强弱),处理NL=0时的除零异常。
(3)信号检测层
- •
detect_divergence():通过滚动窗口判断“指数新高但NH未新高(顶背离)”“指数新低但NL未新低(底背离)”; - • 顶/底背离是趋势反转的核心信号,量化策略中可作为“减仓/加仓”的触发条件。
(4)可视化层
- • 分两行展示“新高新低数量+指数走势”,直观标记背离信号,便于策略分析。
3. 轻量化实现(单标的/小样本)
若无需全市场数据,仅分析单指数/板块成分股,可简化代码:
def calculate_nh_nl_light(df, close_col='close', period=20):
"""
轻量化版:计算单DataFrame内的新高新低(如板块成分股)
:param df: 含code、trade_date、close的DataFrame
:return: 每日NH/NL
"""
# 按日期分组,计算每日新高/新低数
df['high_roll'] = df.groupby('code')[close_col].rolling(period).max().reset_index(0, drop=True)
df['low_roll'] = df.groupby('code')[close_col].rolling(period).min().reset_index(0, drop=True)
df['is_nh'] = df[close_col] > df['high_roll'].shift(1) # 创period日新高
df['is_nl'] = df[close_col] < df['low_roll'].shift(1) # 创period日新低
# 按日期统计数量
nh_nl = df.groupby('trade_date').agg(
nh=('is_nh', 'sum'),
nl=('is_nl', 'sum')
).reset_index()
nh_nl['nh_nl_diff'] = nh_nl['nh'] - nh_nl['nl']
nh_nl['nh_nl_ratio'] = nh_nl['nh'] / nh_nl['nl'].replace(0, np.inf)
return nh_nl
四、实战策略集成示例
新高新低指标可直接融入量化策略的择时逻辑,以下是“沪深300指数增强策略”的核心逻辑:
def strategy_nh_nl(merge_df):
"""
基于新高新低的择时策略
规则:
1. NH-NL>100 → 满仓(100%)
2. 0<NH-NL≤100 → 半仓(50%)
3. NH-NL≤0 → 空仓(0%)
4. 顶背离 → 强制减仓至20%
5. 底背离 → 强制加仓至80%
"""
merge_df['position'] = 0.0
# 基础仓位
merge_df.loc[merge_df['nh_nl_diff'] > 100, 'position'] = 1.0
merge_df.loc[(merge_df['nh_nl_diff'] > 0) & (merge_df['nh_nl_diff'] <= 100), 'position'] = 0.5
# 背离修正
merge_df.loc[merge_df['top_divergence'] == True, 'position'] = 0.2
merge_df.loc[merge_df['bottom_divergence'] == True, 'position'] = 0.8
# 计算策略收益
merge_df['index_return'] = merge_df['index_close'].pct_change()
merge_df['strategy_return'] = merge_df['index_return'] * merge_df['position'].shift(1)
merge_df['strategy_nav'] = (1 + merge_df['strategy_return']).cumprod()
# 输出策略绩效
print(f"策略累计收益:{(merge_df[
'strategy_nav'].iloc[-1]-1)*100:.2f}%")
print(f"指数累计收益:{(merge_df['index_close'].iloc[-1]/merge_df['index_close'].iloc[0]-1)*100:.2f}%")
return merge_df
# 调用策略
merge_df = strategy_nh_nl(merge_df)
五、关键优化与注意事项
1. 数据优化
- • 补充复权收盘价:使用前复权价格计算新高/新低,避免除权除息导致的虚假新高/新低;
- • 处理停牌数据:停牌标的按“最后收盘价”填充,避免统计遗漏。
2. 参数优化
- • 周期适配:短线策略用20日,中长线用60/120日;
- • 阈值校准:全市场NH-NL差值的阈值需根据市场规模调整(如A股全市场NH-NL>500为强多头)。
3. 策略优化
- • 结合成交量:NH-NL扩大+成交量放大,趋势确认;NH-NL扩大但成交量萎缩,趋势存疑;
- • 多指标共振:新高新低+均线(如指数站上60日线),提高信号胜率。
总结
新高新低指标的核心价值在于“脱离指数权重陷阱,反映真实市场广度”:
- • 原理上,通过“多数标的的一致性运动”判断趋势,比单一指数更贴近市场本质;
- • 应用上,是趋势强度判断、反转信号检测、仓位管理的核心工具;
- • 实现上,需注意数据广度(全市场/板块)、周期适配(短/中/长期)、背离检测(核心信号)。
对量化策略而言,新高新低指标是“择时体系”的基础组件,结合均线、成交量、情绪指标(如PSY)可大幅提升策略胜率,尤其适合宽基指数的趋势性交易和仓位管理。