基于 Python 股票时间序列分析与股价预测(线性回归 + ARIMA)
一、项目简介
本文利用Python对股票历史行情数据进行时间序列分析,完成数据可视化、描述性统计,并分别使用线性回归、ARIMA 时间序列模型实现股价预测,对比两个模型效果,同时基于 ARIMA 完成未来 10 个交易日股价预测。
使用工具:pandas、numpy、matplotlib、pyecharts、scikit-learn、statsmodels 数据集:股票 Excel 行情数据(包含日期、开盘价、收盘价、最高价、最低价、成交量、涨跌幅、5/10/20 日均线等字段)
二、环境准备
首先安装所需第三方库,本次使用 Anaconda 环境,执行如下命令:
python
运行
pip install pyecharts pandas numpy matplotlib scikit-learn statsmodels openpyxl
执行结果显示依赖库均已存在,无需重复安装。
三、数据导入与探索
3.1 导入依赖库并配置绘图环境
解决 Matplotlib 中文、负号显示异常问题:
python
运行
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 解决中文和负号显示问题
plt.rcParams["axes.unicode_minus"] = False
plt.rcParams["font.family"] = "SimHei"
3.2 读取数据与基础查看
读取 Excel 格式的股票数据,并查看前几行、数据整体信息、缺失值:
python
运行
# 读取股价数据
df = pd.read_excel("股价数据.xlsx")
# 查看前5行数据
print(df.head())
# 查看数据基本信息(字段、数据类型、样本量)
df.info()
# 统计各字段缺失值数量
print(df.isnull().sum())
数据说明: 数据集共611条样本、14个字段,包含日期、开盘价、收盘价、最高价、最低价、成交量、涨跌幅、5/10/20 日均线等,无缺失值,数据质量良好。
四、数据预处理
时间序列分析必须保证时间有序,同时精简分析字段:
python
运行
# 1. 将日期列转为时间格式
df['date'] = pd.to_datetime(df['date'])
# 2. 按时间升序排序
df.sort_values(by='date', inplace=True)
# 3. 保留核心分析字段
df = df[['date','open','close','high','low', 'volume','p_change','ma5','ma10','ma20']]
# 4. 重置行索引
df.reset_index(drop=True, inplace=True)
# 查看预处理后数据
print(df.head())
五、股票数据可视化分析
5.1 收盘价与 5/10/20 日均线走势
均线是股票技术分析核心指标,绘制收盘价与三条均线对比曲线:
python
运行
plt.figure(figsize=(14,6))
plt.plot(df['date'],df['close'],label='收盘价')
plt.plot(df['date'],df['ma5'], label='5日均线',linestyle='--')
plt.plot(df['date'],df['ma10'],label='10日均线',linestyle='--')
plt.plot(df['date'], df['ma20'], label='20日均线',linestyle='--')
plt.title('收盘价与均线走势')
plt.legend()
plt.grid(alpha=0.3)
plt.show()
分析结论: 收盘价整体围绕均线波动,短期均线(5 日、10 日)贴合股价变化,20 日均线趋势更平缓,能反映中长期股价走势。
5.2 股票 K 线图(pyecharts)
使用pyecharts绘制交互式 K 线图,直观展示每日开盘、收盘、最高、最低价格:
python
运行
from pyecharts.charts import Kline
from pyecharts import options as opts
import webbrowser, os
# 提取K线所需数据:开盘、收盘、最高、最低
y_data = df[['open','close','high','low']].values.tolist()
x_data = df['date'].tolist()
# 构建K线图
kline = (
Kline()
.add_xaxis(x_data)
.add_yaxis("股票K线", y_data)
.set_global_opts(
title_opts=opts.TitleOpts(title="股票行情K线图"),
toolbox_opts=opts.ToolboxOpts(is_show=True)
)
)
# 导出HTML并自动打开
html_name = "股票K线.html"
kline.render(html_name)
webbrowser.open("file://"+os.path.abspath(html_name))
运行后会在本地生成 HTML 文件,浏览器打开即可查看交互式 K 线图,支持缩放、数据查看、下载等功能。
5.3 成交量柱状图
成交量反映市场交易活跃度:
python
运行
plt.figure(figsize=(14,6))
plt.bar(df['date'],df['volume'])
plt.title('股票成交量柱状图')
plt.show()
5.4 涨跌幅分布直方图
统计单日涨跌幅分布,红色虚线为涨跌分界线:
python
运行
plt.figure(figsize=(14,6))
plt.hist(df['p_change'],bins=40)
plt.axvline(0, color='red',linestyle='--',label='涨跌分界线')
plt.legend()
plt.title('股票单日涨跌幅分布直方图')
plt.show()
分析结论: 股票单日涨跌幅主要集中在 0 值附近,小幅涨跌为常态,大幅涨跌样本数量较少。
六、构建数据集(特征工程 + 划分训练集 / 测试集)
时间序列采用滞后特征,使用前一日收盘价作为核心特征,结合开盘价、5 日均线预测当日收盘价:
python
运行
# 构造滞后1期特征:前一日收盘价
df["close_lag1"] = df["close"].shift(1)
# 去除因滞后产生的空值
df = df.dropna()
# 划分数据集:前80%训练集,后20%测试集
split = int(len(df)*0.8)
train = df.iloc[:split]
test = df.iloc[split:]
# 定义特征列与标签列
feature_cols = ["close_lag1", "open","ma5"]
X_train = train[feature_cols]
y_train = train["close"]
X_test = test[feature_cols]
y_test = test["close"]
print(f"训练集:{len(train)} 条 | 测试集:{len(test)} 条")
输出结果:训练集:488 条 | 测试集:122 条
七、模型一:线性回归股价预测
7.1 模型训练、预测与评估
使用平均绝对误差 (MAE)、均方根误差 (RMSE)、决定系数 (R²) 评估模型效果:
python
运行
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# 定义模型评估函数
def eval_model(y_true,y_pred):
mae = mean_absolute_error(y_true,y_pred)
rmse = np.sqrt(mean_squared_error(y_true,y_pred))
r2 = r2_score(y_true, y_pred)
print(f"MAE(平均绝对误差):{mae:.2f}")
print(f"RMSE(均方根误差):{rmse:.2f}")
print(f"R2(拟合度):{r2:.2f}")
# 训练线性回归模型
lr = LinearRegression()
lr.fit(X_train,y_train)
# 测试集预测
y_pred_lr = lr.predict(X_test)
# 模型评估
print("===== 线性回归模型评估 =====")
eval_model(y_test,y_pred_lr)
模型评估结果:
plaintext
===== 线性回归模型评估 =====
MAE(平均绝对误差):0.16
RMSE(均方根误差):0.21
R2(拟合度):0.98
7.2 线性回归预测结果可视化
python
运行
plt.figure(figsize=(14,6))
plt.plot(test["date"],y_test,label="真实收盘价",linewidth=2)
plt.plot(test["date"],y_pred_lr,label="线性回归预测",linestyle='--')
plt.title("线性回归模型 - 股价预测结果")
plt.xlabel("日期")
plt.ylabel("股价")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
八、模型二:ARIMA 时间序列预测
ARIMA 是经典时间序列模型,本次采用滚动一步预测方式(工业界常用时序预测方案):
python
运行
from statsmodels.tsa.arima.model import ARIMA
# 初始化历史数据与预测列表
history = list(train["close"])
predictions = []
# 滚动单步预测
for i in range(len(test)):
# 构建ARIMA模型,阶数 order=(5,1,1)
model = ARIMA(history,order=(5,1,1))
model_fit = model.fit()
# 预测下一日股价
yhat = model_fit.forecast()[0]
predictions.append(yhat)
# 将真实值加入历史数据,迭代更新模型
history.append(test["close"].iloc[i])
# 转为数组
y_pred_arima = np.array(predictions)
# 模型评估
print("\n===== ARIMA模型评估 =====")
eval_model(y_test, y_pred_arima)
模型评估结果:
plaintext
===== ARIMA模型评估 =====
MAE(平均绝对误差):0.18
RMSE(均方根误差):0.26
R2(拟合度):0.97
8.1 ARIMA 预测结果可视化
python
运行
plt.figure(figsize=(14,6))
plt.plot(test["date"],y_test,label="真实收盘价",linewidth=2)
plt.plot(test["date"],y_pred_arima,label="ARIMA预测",linestyle="--", color="orange")
plt.title("ARIMA模型 - 股价预测结果")
plt.xlabel("日期")
plt.ylabel("股价")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
九、双模型效果对比
将线性回归、ARIMA 预测结果与真实值同图对比,直观区分模型优劣:
python
运行
plt.figure(figsize=(15,6))
plt.plot(test["date"],y_test,label="真实收盘价",linewidth=2)
plt.plot(test["date"],y_pred_lr,label="线性回归",linestyle='--')
plt.plot(test["date"],y_pred_arima, label="ARIMA", linestyle="--", color="red")
plt.title("线性回归 VS ARIMA 预测效果对比")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
模型对比总结:
- 两个模型拟合度
R²均达到 0.97 以上,预测效果优秀; - 线性回归 MAE、RMSE 更小,误差略低于 ARIMA;
- ARIMA 为纯时序模型,仅依赖历史股价,线性回归融合了开盘价、均线等特征,在本次任务中表现更优。
十、基于 ARIMA 预测未来 10 个交易日股价
使用全量数据训练 ARIMA 模型,预测未来 10 日收盘价:
python
运行
# 全量收盘价序列
full_series = df["close"]
# 训练ARIMA模型
final_arima = ARIMA(full_series,order=(5,1,0)).fit()
# 预测未来10步
future_pred = final_arima.get_forecast(steps=10).predicted_mean
# 输出预测结果
print("\n===== 未来10个交易日预测收盘价 =====")
for i,val in enumerate(future_pred,1):
print(f"第{i}天预测股价:{val:.2f} 元")
预测输出结果:
plaintext
===== 未来10个交易日预测收盘价 =====
第1天预测股价:12.25 元
第2天预测股价:12.31 元
第3天预测股价:12.28 元
第4天预测股价:12.36 元
第5天预测股价:12.42 元
第6天预测股价:12.40 元
第7天预测股价:12.41 元
第8天预测股价:12.41 元
第9天预测股价:12.40 元
第10天预测股价:12.39 元
提示:代码运行出现的
ValueWarning为索引格式警告,不影响预测结果,可忽略。
十一、项目总结
- 数据层面:本次股票数据集完整无缺失,通过时间排序、特征构造完成时序数据预处理,满足建模要求;
- 可视化层面:完成均线、K 线、成交量、涨跌幅多维度可视化,清晰还原股票行情规律;
- 模型层面:
- 线性回归结合多特征,预测误差更小,适合该场景下的股价短期预测;
- ARIMA 经典时序模型,仅依靠历史序列也能实现高精度预测,通用性更强;
- 业务层面:模型可实现短期股价预测,但股票行情受政策、消息、市场情绪等多重因素影响,本结果仅为技术学习使用,不构成投资建议。
十二、完整源码汇总
python
运行
# ==================== 1. 导入库 + 环境配置 ====================
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pyecharts.charts import Kline
from pyecharts import options as opts
import webbrowser, os
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from statsmodels.tsa.arima.model import ARIMA
# 解决中文、负号显示问题
plt.rcParams["axes.unicode_minus"] = False
plt.rcParams["font.family"] = "SimHei"
# ==================== 2. 数据读取与探索 ====================
df = pd.read_excel("股价数据.xlsx")
print("数据前5行:")
print(df.head())
print("\n数据基本信息:")
df.info()
print("\n缺失值统计:")
print(df.isnull().sum())
# ==================== 3. 数据预处理 ====================
df['date'] = pd.to_datetime(df['date'])
df.sort_values(by='date', inplace=True)
df = df[['date','open','close','high','low', 'volume','p_change','ma5','ma10','ma20']]
df.reset_index(drop=True, inplace=True)
print("\n预处理后数据:")
print(df.head())
# ==================== 4. 数据可视化 ====================
# 4.1 收盘价与均线
plt.figure(figsize=(14,6))
plt.plot(df['date'],df['close'],label='收盘价')
plt.plot(df['date'],df['ma5'], label='5日均线',linestyle='--')
plt.plot(df['date'],df['ma10'],label='10日均线',linestyle='--')
plt.plot(df['date'], df['ma20'], label='20日均线',linestyle='--')
plt.title('收盘价与均线走势')
plt.legend()
plt.grid(alpha=0.3)
plt.show()
# 4.2 K线图
y_data = df[['open','close','high','low']].values.tolist()
x_data = df['date'].tolist()
kline = (
Kline()
.add_xaxis(x_data)
.add_yaxis("股票K线", y_data)
.set_global_opts(
title_opts=opts.TitleOpts(title="股票行情K线图"),
toolbox_opts=opts.ToolboxOpts(is_show=True)
)
)
html_name = "股票K线.html"
kline.render(html_name)
webbrowser.open("file://"+os.path.abspath(html_name))
# 4.3 成交量
plt.figure(figsize=(14,6))
plt.bar(df['date'],df['volume'])
plt.title('股票成交量柱状图')
plt.show()
# 4.4 涨跌幅分布
plt.figure(figsize=(14,6))
plt.hist(df['p_change'],bins=40)
plt.axvline(0, color='red',linestyle='--',label='涨跌分界线')
plt.legend()
plt.title('股票单日涨跌幅分布直方图')
plt.show()
# ==================== 5. 特征工程 + 数据集划分 ====================
df["close_lag1"] = df["close"].shift(1)
df = df.dropna()
split = int(len(df)*0.8)
train = df.iloc[:split]
test = df.iloc[split:]
feature_cols = ["close_lag1", "open","ma5"]
X_train = train[feature_cols]
y_train = train["close"]
X_test = test[feature_cols]
y_test = test["close"]
print(f"\n训练集:{len(train)} 条 | 测试集:{len(test)} 条")
# ==================== 6. 模型评估函数 ====================
def eval_model(y_true,y_pred):
mae = mean_absolute_error(y_true,y_pred)
rmse = np.sqrt(mean_squared_error(y_true,y_pred))
r2 = r2_score(y_true, y_pred)
print(f"MAE(平均绝对误差):{mae:.2f}")
print(f"RMSE(均方根误差):{rmse:.2f}")
print(f"R2(拟合度):{r2:.2f}")
# ==================== 7. 线性回归模型 ====================
lr = LinearRegression()
lr.fit(X_train,y_train)
y_pred_lr = lr.predict(X_test)
print("\n===== 线性回归模型评估 =====")
eval_model(y_test,y_pred_lr)
# 线性回归可视化
plt.figure(figsize=(14,6))
plt.plot(test["date"],y_test,label="真实收盘价",linewidth=2)
plt.plot(test["date"],y_pred_lr,label="线性回归预测",linestyle='--')
plt.title("线性回归模型 - 股价预测结果")
plt.xlabel("日期")
plt.ylabel("股价")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
# ==================== 8. ARIMA时间序列模型 ====================
history = list(train["close"])
predictions = []
for i in range(len(test)):
model = ARIMA(history,order=(5,1,1))
model_fit = model.fit()
yhat = model_fit.forecast()[0]
predictions.append(yhat)
history.append(test["close"].iloc[i])
y_pred_arima = np.array(predictions)
print("\n===== ARIMA模型评估 =====")
eval_model(y_test, y_pred_arima)
# ARIMA可视化
plt.figure(figsize=(14,6))
plt.plot(test["date"],y_test,label="真实收盘价",linewidth=2)
plt.plot(test["date"],y_pred_arima,label="ARIMA预测",linestyle="--", color="orange")
plt.title("ARIMA模型 - 股价预测结果")
plt.xlabel("日期")
plt.ylabel("股价")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
# ==================== 9. 双模型对比 ====================
plt.figure(figsize=(15,6))
plt.plot(test["date"],y_test,label="真实收盘价",linewidth=2)
plt.plot(test["date"],y_pred_lr,label="线性回归",linestyle='--')
plt.plot(test["date"],y_pred_arima, label="ARIMA", linestyle="--", color="red")
plt.title("线性回归 VS ARIMA 预测效果对比")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
# ==================== 10. 预测未来10个交易日 ====================
full_series = df["close"]
final_arima = ARIMA(full_series,order=(5,1,0)).fit()
future_pred = final_arima.get_forecast(steps=10).predicted_mean
print("\n===== 未来10个交易日预测收盘价 =====")
for i,val in enumerate(future_pred,1):
print(f"第{i}天预测股价:{val:.2f} 元")更多推荐
所有评论(0)