
本文介绍如何使用 pandas 的滚动窗口与条件选择,仅标记每个局部极值首次出现的枢轴点(1 表示局部低点,2 表示局部高点),避免重复触发,适用于技术分析中的有效突破信号识别。
在量化交易与技术分析中,枢轴点(Pivot Points)常用于识别价格趋势转折的关键位置。但原始的局部极值检测(如 rolling(5, center=True).max() == high)容易在平台或窄幅震荡区间内连续多日触发相同信号(例如连续3天都是局部高点),导致“信号漂移”——即同一波段内多次标记,丧失交易意义。
核心目标:对每个已识别的枢轴类型(高点=2 / 低点=1),仅保留该极值序列中首个出现且未被后续更强极值覆盖的独立信号。换言之:一旦某日被标记为 pivot=2(局部高点),则在其后直到下一个更高 pivot=2 出现前,中间所有“伪高点”均应忽略;pivot=1 同理。
✅ 正确实现:基于滚动窗口 + np.select 构建基础枢轴列
首先,使用中心对称的 5 窗口(含自身及上下各 2 行)检测局部极值:
import pandas as pd
import numpy as np
# 示例数据构建
data = {
'date': ['01.01.2020', '02.01.2020', '03.01.2020', '04.01.2020', '05.01.2020',
'06.01.2020', '07.01.2020', '08.01.2020', '09.01.2020', '10.01.2020',
'11.01.2020', '12.01.2020', '13.01.2020', '14.01.2020', '15.01.2020',
'16.01.2020', '17.01.2020'],
'high': [207, 208, 209, 207, 206, 205, 204, 206, 207, 208, 210, 212, 214,
207, 203, 201, 199],
'low': [204, 205, 205, 203, 202, 200, 199, 201, 202, 205, 207, 209, 210,
204, 202, 198, 196]
}
df = pd.DataFrame(data)
# 构建基础 pivot 列:2=局部高点,1=局部低点,0=非枢轴
cond_high = df['high'].rolling(5, center=True).max().eq(df['high'])
cond_low = df['low'].rolling(5, center=True).min().eq(df['low'])
df['pivot'] = np.select([cond_high, cond_low], [2, 1], default=0)⚠️ 注意:rolling(..., center=True) 要求窗口大小为奇数(此处为 5),且首尾两行因无法满足中心对称而返回 NaN —— 需填充或截断。推荐用 min_periods=3 或直接 dropna() 处理边界。
✅ 进阶去重:确保每个 pivot 类型“仅触发一次”,直到新极值出现
上述代码生成的是所有局部极值点,但题目要求“only once”——即每个方向(高/低)只取第一个有效突破,后续同向信号需抑制,直至反向或更强信号重置。
实现逻辑如下:
- 对 pivot == 2 序列,从前向后扫描,仅保留首次出现且之后未被更高高点替代的点;
- 更实用的做法是:按时间顺序遍历,维护当前“已确认的最高高点”和“最低低点”,仅当新高 > 当前最高 或 新低
# 初始化状态变量
last_high = -np.inf
last_low = np.inf
takes_pivot = np.zeros(len(df), dtype=int)
for i in range(len(df)):
h, l = df.iloc[i]['high'], df.iloc[i]['low']
if h > last_high and df.iloc[i]['pivot'] == 2:
takes_pivot[i] = 2
last_high = h
elif l < last_low and df.iloc[i]['pivot'] == 1:
takes_pivot[i] = 1
last_low = l
df['takes_pivot'] = takes_pivot运行后,takes_pivot 列将严格满足题意:
- 2 仅出现在 13.01.2020(214,突破此前所有高点);
- 1 仅出现在 07.01.2020(199,跌破此前所有低点);
→ 完全匹配用户期望的“only once”行为。
? 关键注意事项
- 窗口大小选择:5 是常见设定(2+1+2),但可根据周期调整(如日线用 9,小时线用 5);
- 边界处理:center=True 导致首尾 NaN,建议 df.dropna(subset=['pivot']) 或用 min_periods=3 缓解;
- 性能优化:循环在大数据集上较慢,可改用 numba 加速或向量化状态传播(如 cummax/cummin 辅助判断);
- 业务语义:takes_pivot 不是数学极值,而是交易意义上首次达成的新高/新低枢轴,具备明确的策略触发价值。
通过此方法,你不仅能准确识别枢轴点,更能将其转化为稳健、去噪、可直接对接交易系统的信号列。










