基于scipy linregress的时间序列支撑阻力趋势线计算优化问题
股票时间序列支撑位、阻力位趋势线拟合优化方案
原代码异常原因定位
- 核心逻辑缺陷:原阻力位拟合不断删除低于回归线的样本、支撑位拟合不断删除高于回归线的样本,最终仅用剩余2个点生成趋势线。遇到ENZ、ARTL这类存在短期大幅跳涨/跳空走势的个股时,剩余2个点大概率是极端异常点,完全无法反映整体走势的支撑/阻力水平
- 噪声未过滤:未对收盘价做平滑处理,小盘股的短期异常波动会直接干扰拟合结果
- 绘图坐标轴适配问题:使用
twiny双X轴未做刻度对齐,容易出现趋势线和日期走势视觉错位的问题
优化后完整代码
import pandas as pd import pandas_datareader as pdr import yfinance as yf import numpy as np import matplotlib.pyplot as plt from scipy.stats import linregress import datetime as dt # 优化参数配置:可根据个股波动调整 SMOOTH_WINDOW = 3 # 收盘价平滑窗口 EXTREMUM_QUANTILE = 0.05 # 极值筛选分位数,值越小保留的极值点越少 for ticker in ['SNN','AAPL', 'ENZ', 'ARTL']: print('Processing ticker '+ str(ticker)) # 获取数据 df = pdr.get_data_yahoo(ticker, start = '2020-09-01', end = '2021-09-01', interval = "d") df['Ticker'] = ticker df_len = len(df) df['Number'] = np.arange(df_len)+1 # 收盘价滚动平滑,过滤短期噪声 df['smooth_close'] = df['Close'].rolling(SMOOTH_WINDOW, min_periods=1).mean() # 阻力位拟合:筛选顶部极值点后拟合,而非裁剪到只剩2个点 df_high = df.copy() # 先初步回归得到基础斜率,筛选高于回归线的前N%极值点 slope, intercept, r_value, p_value, std_err = linregress(x=df_high['Number'], y=df_high['smooth_close']) high_pct = df_high['smooth_close'].quantile(1-EXTREMUM_QUANTILE) df_high = df_high.loc[(df_high['smooth_close'] > slope * df_high['Number'] + intercept) & (df_high['smooth_close'] >= high_pct)] # 最终用筛选后的极值点拟合,避免仅用2个点的偏差 slope_resist, intercept_resist, _, _, _ = linregress(x=df_high['Number'], y=df_high['smooth_close']) df['resistance'] = slope_resist * df['Number'] + intercept_resist print( '\tresistance:\t' + ' slope: ' + str(round(slope_resist, 3)) + '\tintercept: '+ str(round(intercept_resist,3))) # 支撑位拟合:筛选底部极值点后拟合 df_low = df.copy() slope, intercept, r_value, p_value, std_err = linregress(x=df_low['Number'], y=df_low['smooth_close']) low_pct = df_low['smooth_close'].quantile(EXTREMUM_QUANTILE) df_low = df_low.loc[(df_low['smooth_close'] < slope * df_low['Number'] + intercept) & (df_low['smooth_close'] <= low_pct)] slope_support, intercept_support, _, _, _ = linregress(x=df_low['Number'], y=df_low['smooth_close']) df['support'] = slope_support * df['Number'] + intercept_support print( '\tsupport:\t' + ' slope: ' + str(round(slope_support,3)) +'\tintercept: '+ str(round(intercept_support,3))) # 修复绘图逻辑,统一X轴避免错位 fig, ax = plt.subplots(figsize=(8,5)) xdate = [x.date() for x in df.index] ax.plot(xdate, df.Close, label="收盘价", color='tab:green') ax.plot(xdate, df.resistance, label="阻力位", color='tab:red') ax.plot(xdate, df.support, label="支撑位", color='tab:blue') ax.set_xlabel('日期') ax.set_ylabel('价格') plt.title(label=ticker) plt.legend() plt.grid() plt.xticks(rotation=45) plt.tight_layout() plt.show()
优化效果说明
- 对于存在异常波动的ENZ、ARTL等个股,拟合的趋势线不再被极端值带偏,能反映区间内的整体支撑/阻力水平
- 平滑窗口和分位数参数可根据个股属性灵活调整,小盘股可适当调大窗口、提高分位数阈值,大盘股可降低参数值提升灵敏度
内容的提问来源于stack exchange,提问作者Vincent H
相关产品推荐
相关产品推荐

