1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| import pandas as pd import numpy as np import matplotlib.pyplot as plt import statsmodels.api as sm from scipy.stats import linregress from sklearn.preprocessing import MinMaxScaler
plt.style.use('seaborn-v0_8') #指定字体,防止中文出现乱码,windows系统指定为‘SimHei’ plt.rcParams['font.sans-serif'] = ['SimHei'] #这行代码让中文的负号“-”可以正常显示 plt.rcParams["axes.unicode_minus"]=False
# 1. 创建示例数据(含异常值,替换为你的实际数据) df = pd.read_excel('../数据/2-1.xlsx') df = df[['日期', '收盘']] df.columns = ['date','Close'] df['date'] = pd.to_datetime(df['date']) df.set_index('date', inplace = True)
# 2. 修正趋势检测函数 def detect_trends(series): # 方法1:移动平均交叉策略(使用20日均线) ma20 = series.rolling(20).mean() # 20日移动平均线[1,3](@ref) # 方法2:线性回归趋势线 X = np.arange(len(series)) slope, intercept, r_value, p_value, std_err = linregress(X, series) # 方法3:时间序列分解(使用20日周期) decomposition = sm.tsa.seasonal_decompose(series, period=20) # 20日周期分解[9](@ref) trend_component = decomposition.trend.dropna() # 方法4:20日滚动斜率(修复索引错误) rolling_slopes = [] for i in range(len(series)): # 取20日窗口 start_idx = max(0, i - 19) # 确保有20个数据点 window = series.iloc[start_idx:i+1] if len(window) > 1: # 至少有2个点才能计算斜率 x = np.arange(len(window)) slope_value = linregress(x, window.values).slope rolling_slopes.append(slope_value) else: rolling_slopes.append(np.nan) # 不足2个点则填充NaN return ma20, slope, trend_component, rolling_slopes
# 3. 使用修正后的函数 ma20, overall_slope, trend_comp, rolling_slopes = detect_trends(df['Close'])
# 4. 可视化设计 plt.figure(figsize=(16, 12)) ax1 = plt.subplot(3, 1, 1)
# 原始价格与20日均线 df['Close'].plot(ax=ax1, label='收盘价', alpha=0.6) ma20.plot(ax=ax1, label='20日均线', color='orange', lw=2)
# 标注价格在均线上方的区域 ax1.fill_between(df.index, df['Close'], ma20, where=(df['Close'] > ma20), color='red', alpha=0.3, label='价格高于均线')
# 趋势分解可视化 ax2 = plt.subplot(3, 1, 2, sharex=ax1) trend_comp.plot(ax=ax2, color='green', label='趋势成分') ax2.fill_between(trend_comp.index, trend_comp, where=(trend_comp.pct_change() > 0), color='red', alpha=0.3, label='上涨区间')
# 20日滚动斜率可视化 ax3 = plt.subplot(3, 1, 3, sharex=ax1) slope_series = pd.Series(rolling_slopes, index=df.index) slope_series.plot(ax=ax3, color='blue', label='20日滚动斜率') ax3.axhline(0, color='black', ls='--') ax3.fill_between(df.index, rolling_slopes, 0, where=(np.array(rolling_slopes) > 0), color='red', alpha=0.3, label='上升趋势')
# 添加趋势强度标记 strong_trend_points = slope_series[slope_series > slope_series.quantile(0.9)] ax3.scatter(strong_trend_points.index, strong_trend_points.values, color='darkred', s=50, label='强上升趋势')
# 图表美化 ax1.set_title(f'整体趋势方向:{"上涨" if overall_slope > 0 else "下跌"} (斜率:{overall_slope:.4f})', fontsize=14) ax1.set_ylabel('价格', fontsize=12) ax1.legend(loc='upper left') ax1.grid(alpha=0.3)
ax2.set_title('趋势成分分解 (20日周期)', fontsize=14) ax2.set_ylabel('趋势值', fontsize=12) ax2.legend(loc='upper left') ax2.grid(alpha=0.3)
ax3.set_title('20日滚动斜率分析', fontsize=14) ax3.set_ylabel('斜率', fontsize=12) ax3.set_xlabel('日期', fontsize=12) ax3.legend(loc='upper left') ax3.grid(alpha=0.3)
plt.tight_layout() plt.show()
# 5. 输出趋势分析结果 print(f"整体趋势斜率: {overall_slope:.6f} (正值表示上涨趋势)") print(f"强上升趋势出现次数: {len(strong_trend_points)}次") print("主要强上升趋势时间段:") for i in range(0, len(strong_trend_points), 5): print(f"{strong_trend_points.index[i].strftime('%Y-%m-%d')} - 斜率: {strong_trend_points.iloc[i]:.4f}")
|