Py学习  »  Python

新高新低指标(NH-NL):原理、应用与Python实现

量化OK投研交流 • 1 周前 • 53 次点击  


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

新高新低指标(New Highs-New Lows,NH-NL)是衡量市场广度和趋势强度的核心技术指标,通过统计一定周期内创阶段新高/新低的标的数量,判断市场整体强弱、趋势持续性及反转信号,适用于股票、指数、行业板块等宽基市场分析,是量化策略中“择时+仓位管理”的关键工具。

一、核心原理:用“市场广度”反映趋势本质

1. 定义与核心逻辑

市场趋势的本质是“多数标的的一致性运动”:

  • • 当大量标的同步创阶段新高,说明多头力量占优,上涨趋势具备广度和持续性;
  • • 当大量标的同步创阶段新低,说明空头力量占优,下跌趋势未结束;
  • • 若新高/新低数量背离(如指数创新高但新高标的数减少),预示趋势可能反转。

2. 核心指标公式

新高新低指标有3个核心维度,需结合使用:

指标名称
计算公式
含义
新高数(NH)
统计周期内,收盘价创N日新高的标的数量(N常见20/50/60/120日,代表不同周期)
反映多头力量的广度(数量越多,多头越强)
新低数(NL)
统计周期内,收盘价创N日新低的标的数量
反映空头力量的广度(数量越多,空头越强)
新高新低差(NH-NL)
新高数 - 新低数
整体市场情绪(正数=多头占优,负数=空头占优,绝对值越大趋势越强)
新高新低比率(NH/NL)
新高数 / 新低数(NL=0时取极大值)
相对强弱(比值>3=极端多头,比值<0.3=极端空头)

3. 关键参数说明

  • • 统计周期N
    • • 短期:20/30日(反映短期市场情绪,适合短线择时);
    • • 中期:60/90日(反映中期趋势,适合波段操作);
    • • 长期:120/250日(反映长期趋势,适合仓位管理);
  • • 统计范围:可选择全市场(如A股全个股)、行业板块(如半导体板块)、指数成分股(如沪深300成分股);
  • • 新高/新低判定:当日收盘价>过去N个交易日收盘价→创N日新高;当日收盘价<过去N个交易日收盘价→创N日新低(等于不算)。

二、核心应用:趋势判断与反转信号

1. 基础应用:趋势强度判断

通过NH-NL差值/比率判断市场整体状态,指导仓位管理:

指标特征
市场状态
操作建议
NH-NL>0且持续扩大
多头趋势确认
重仓(如80%-100%),持有趋势型标的
NH-NL>0但逐步收窄
多头趋势衰减
减仓(如50%-60%),警惕反转
NH-NL<0且绝对值扩大
空头趋势确认
轻仓/空仓(0%-20%),避免抄底
NH-NL<0但绝对值收窄
空头趋势衰减
小仓位试错(20%-30%),等待反转信号
NH=NL(差值≈0)
震荡市
中性仓位(40%-50%),波段交易为主

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. 适用场景与注意事项

适用场景

  • • 宽基指数择时(如沪深300、创业板指);
  • • 行业板块轮动(如筛选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(0len(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(21, figsize=(1410), 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)可大幅提升策略胜率,尤其适合宽基指数的趋势性交易和仓位管理。


 


 

 

 

 


风险提示:本文仅作为知识分享,模拟测试不构成任何投资建议,不作投资依据。
图片



END
图片
如需 策略源文件→扫一扫成为星球会员
图片
如需系统学习Python→购买成为量化高手

 

 

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/189924