如何在Pandas中识别价格状态(用EMA斜率替代滞后ADX)
基于EMA斜率识别股票价格趋势状态
背景
已有包含26条股票开盘价(Open)、最高价(High)、最低价(Low)、收盘价(Close)的Pandas DataFrame,需新增Regime字段,用1、2、3分别标记非趋势状态、下跌趋势状态、上涨趋势状态。因ADX指标存在滞后性,改用EMA斜率实现更及时的趋势识别。
原始数据与DataFrame构建
代码实现
import pandas as pd import numpy as np ds = { 'Date': ['15/06/2025','16/06/2025','17/06/2025','18/06/2025','19/06/2025','20/06/2025','21/06/2025','22/06/2025','23/06/2025','24/06/2025','25/06/2025','26/06/2025','27/06/2025','28/06/2025','29/06/2025','30/06/2025','01/07/2025','02/07/2025','03/07/2025','04/07/2025','05/07/2025','06/07/2025','07/07/2025','08/07/2025','09/07/2025','10/07/2025'], 'Open': [310,320,305,312,312,324,315,321,320,300,289,276,274,273,265,249,200,189,175,161,171,179,200,240,285,286], 'High': [321,321,307,313,320,329,321,321,320,301,289,277,276,274,266,250,201,190,179,170,180,199,239,289,286,300], 'Low': [310,305,304,311,300,300,310,321,298,289,275,272,274,265,254,200,190,175,160,161,170,175,185,241,284,280], 'Close': [315,319,306,311,318,321,321,321,299,289,276,273,274,265,250,252,190,175,161,170,180,199,239,288,284,300], } df = pd.DataFrame(data=ds)
原始DataFrame输出
print(df) Date Open High Low Close 0 15/06/2025 310 321 310 315 1 16/06/2025 320 321 305 319 2 17/06/2025 305 307 304 306 3 18/06/2025 312 313 311 311 4 19/06/2025 312 320 300 318 5 20/06/2025 324 329 300 321 6 21/06/2025 315 321 310 321 7 22/06/2025 321 321 321 321 8 23/06/2025 320 320 298 299 9 24/06/2025 300 301 289 289 10 25/06/2025 289 289 275 276 11 26/06/2025 276 277 272 273 12 27/06/2025 274 276 274 274 13 28/06/2025 273 274 265 265 14 29/06/2025 265 266 254 250 15 30/06/2025 249 250 200 252 16 01/07/2025 200 201 190 190 17 02/07/2025 189 190 175 175 18 03/07/2025 175 179 160 161 19 04/07/2025 161 170 161 170 20 05/07/2025 171 180 170 180 21 06/07/2025 179 199 175 199 22 07/07/2025 200 239 185 239 23 08/07/2025 240 289 241 288 24 09/07/2025 285 286 284 284 25 10/07/2025 286 300 280 300
实现步骤
1. 标准化日期格式
先将Date列转换为datetime类型,确保时间序列的连续性:
df['Date'] = pd.to_datetime(df['Date'], format='%d/%m/%Y')
2. 计算收盘价的EMA
选择合适的EMA周期(这里用12日作为示例,可根据需求调整),Pandas的ewm方法直接支持EMA计算:
# 计算12日EMA ema_period = 12 df['EMA'] = df['Close'].ewm(span=ema_period, adjust=False).mean()
3. 计算EMA的斜率
EMA斜率代表EMA的变化速率,提供两种实现方式:
方式一:简单差分法(适合短周期快速计算)
因数据是连续交易日,时间间隔视为1,直接计算EMA的一阶差分:
df['EMA_Slope'] = df['EMA'].diff()
方式二:滚动窗口线性回归(更稳定,适合趋势判断)
用滚动窗口内的线性回归拟合斜率,能过滤短期噪音:
from sklearn.linear_model import LinearRegression def calculate_slope(series): x = np.arange(len(series)).reshape(-1, 1) y = series.values.reshape(-1, 1) model = LinearRegression().fit(x, y) return model.coef_[0][0] # 用5天滚动窗口计算斜率 window_size = 5 df['EMA_Slope'] = df['EMA'].rolling(window=window_size).apply(calculate_slope)
4. 设定阈值并标记趋势状态
根据斜率的正负和绝对值设定阈值,区分三种状态:
- 斜率绝对值小于阈值 → 非趋势(标记为1)
- 斜率为负且绝对值大于阈值 → 下跌趋势(标记为2)
- 斜率为正且大于阈值 → 上涨趋势(标记为3)
# 设定阈值(可根据数据波动调整,这里用2作为示例) slope_threshold = 2 df['Regime'] = np.where( abs(df['EMA_Slope']) < slope_threshold, 1, np.where( df['EMA_Slope'] > slope_threshold, 3, 2 ) ) # 处理滚动窗口产生的NaN值(前window_size-1行无斜率,暂标记为1) df['Regime'] = df['Regime'].fillna(1).astype(int)
最终结果示例
执行上述代码后,Regime字段已添加到DataFrame中,可通过打印查看核心列:
print(df[['Date', 'Close', 'EMA', 'EMA_Slope', 'Regime']])
关键说明
- EMA周期和斜率阈值需要根据股票的波动特性调整,建议通过回测优化参数
- 滚动窗口线性回归的窗口大小也会影响趋势识别的灵敏度,窗口越大越滞后但越稳定
- 若需要更精准的趋势判断,可结合多个周期EMA的斜率综合分析
内容的提问来源于stack exchange,提问作者Giampaolo Levorato
相关产品推荐
相关产品推荐

