简介:这份资源面向希望用人工智能技术研究股票市场的开发者与量化初学者,围绕机器学习算法对股票价格的分析与预测展开,帮助读者理解从数据到模型的完整技术链路。压缩包共2个文件,以1个Python脚本和1个txt说明为主,整体约45KB,脚本承载核心建模逻辑,文本文件用于补充依赖或运行说明,结构精简便于快速上手。目前已有442人学习下载,具备一定的参考热度。内容覆盖数据预处理、特征工程、模型选择、训练验证、评估调优及持续监控等环节,涉及缺失值处理、标准化、技术指标构建,以及线性回归、随机森林、LSTM等模型的适用场景,并讨论了市场不可预测因素带来的局限。读者可据此获得一套可复用的股票预测实践思路与代码骨架,用于课程设计、项目练手或量化研究入门。
1. 拆开这个股票预测包:一份能跑通的机器学习落地样本
很多人第一次接触金融时序预测,都是被"用 LSTM 预测股价"这类标题吸引进来的,结果下载了一堆代码,跑起来要么报错,要么预测出来的曲线跟真实价格差了十万八千里。这个基于机器学习算法的股票市场股票价格的分析及预测.zip属于少见的"结构干净、入口明确"的样本——主程序app_fn.py加上一份requirements.txt,没有多余的目录嵌套,拿到手就能顺着入口往下读。它解决的核心问题不是"教你炒股",而是把一条完整的机器学习流水线摊开给你看:从历史行情数据的读取、缺失值处理、技术指标特征构造,到模型训练、验证、评估,最后落到一个可交互的预测入口。适合两类人:一类是刚学完机器学习理论、想找一个真实场景练手的从业者;另一类是手里有行情数据、想快速搭一个基线模型做对比的分析师。它不承诺预测精度,但能让你在半小时内把整条链路跑通,知道每个环节该动哪个参数。
2. 环境与数据入口:从 requirements.txt 到第一份可用数据集
2.1 依赖清单怎么读,版本冲突怎么绕
拿到压缩包先别急着python app_fn.py,第一步是看requirements.txt。这类金融时序项目常见的依赖组合是pandas、numpy、scikit-learn,如果涉及深度学习还会带tensorflow或torch。我一般会先建一个干净的虚拟环境,避免和系统里已有的包打架:
# 创建独立环境,Python 版本建议 3.9 或 3.10 python -m venv venv_stock # 激活环境(Windows 用 venv_stock\Scripts\activate) source venv_stock/bin/activate # 安装依赖,加 -i 指定国内镜像会快很多 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这里有个血泪经验:requirements.txt里如果只写了包名没锁版本,pip会拉最新版,而scikit-learn在 1.0 之后对某些 API 做了调整,老代码里的sklearn.cross_validation这类导入会直接报ModuleNotFoundError。遇到这种情况,不要急着改代码逻辑,先确认是不是版本问题。常见做法是手动降级到项目写作时的主流版本,比如pip install scikit-learn==0.24.2。参数上,-i只是换源,不影响包内容;真正影响兼容性的是版本号,所以如果项目里带了requirements.txt且写了==,尽量别改。
2.2 数据从哪来,字段怎么对齐
股票预测项目最容易被忽略的一步是数据源。这个包本身不一定附带完整历史数据,常见做法是让使用者自己准备一份 CSV,字段至少包含Date、Open、High、Low、Close、Volume。我一般会从公开的行情接口拉一份日线数据,存成 UTF-8 编码的 CSV,避免中文路径和 BOM 头带来的读取玄学。读取时用pandas指定parse_dates,把日期列直接转成时间索引:
import pandas as pd # 读取行情数据,指定日期列解析为 datetime df = pd.read_csv("stock_data.csv", parse_dates=["Date"], index_col="Date") # 按日期升序排列,时序模型对顺序敏感 df = df.sort_index() # 检查缺失情况,打印每列空值数量 print(df.isnull().sum()) # 查看前五行,确认字段名和数值范围 print(df.head())逻辑说明:parse_dates把字符串日期转成Timestamp,后续做移动平均和差分时才能按时间对齐;sort_index是必须的,很多公开数据是倒序的,不排直接算指标会得到反向结果。参数上,index_col="Date"让日期成为索引,这样df["Close"].rolling(5).mean()才会按交易日滚动而不是按行号。如果数据里有停牌导致的缺失,isnull().sum()会告诉你哪一列缺得多,缺超过 30% 的列建议直接弃用,别硬填。
2.3 预处理流水线:缺失值、异常值和标准化
数据预处理这块,项目正文里提到了缺失值处理、异常值检测、标准化和归一化。落到代码上,我一般按这个顺序走:先处理缺失,再处理异常,最后做缩放。缺失值用前向填充ffill比较符合金融时序的直觉,因为价格在停牌期间保持不变;异常值用分位数截断,把超过 99.5% 分位或低于 0.5% 分位的值拉回边界;标准化用MinMaxScaler把价格压到 0 到 1 之间,这对神经网络尤其重要。
from sklearn.preprocessing import MinMaxScaler # 前向填充缺失值,再用 0 兜底 df = df.ffill().fillna(0) # 对收盘价做分位数截断,处理极端异常值 low, high = df["Close"].quantile([0.005, 0.995]) df["Close"] = df["Close"].clip(low, high) # 只对价格列做 MinMax 缩放 scaler = MinMaxScaler() df["Close_scaled"] = scaler.fit_transform(df[["Close"]])这里要注意:fit_transform只能用在训练集上,测试集必须用训练集拟合出来的scaler做transform,否则就是数据泄露,模型在验证集上表现虚高,上线就翻车。参数上,quantile([0.005, 0.995])的两个分位点可以根据数据长度调整,数据少于 500 条时建议放宽到 0.01 和 0.99,避免把真实波动误杀。
3. 特征工程与模型选型:把 MACD、RSI 和 LSTM 串起来
3.1 技术指标特征怎么算、怎么拼
项目正文里明确提到了 MACD、RSI 这类技术指标,这是股票预测区别于普通回归任务的关键。MACD 由快慢两条指数移动平均线之差构成,RSI 衡量近期涨跌力量对比。我一般会把它们和原始价格、成交量拼成一张特征表,再喂给模型。计算时用pandas的ewm做指数加权,比rolling更贴近券商软件的口径:
# 计算 MACD:12 日快线减 26 日慢线 ema_fast = df["Close"].ewm(span=12, adjust=False).mean() ema_slow = df["Close"].ewm(span=26, adjust=False).mean() df["MACD"] = ema_fast - ema_slow # 计算 RSI:14 日涨跌幅均值比 delta = df["Close"].diff() gain = delta.clip(lower=0).rolling(14).mean() loss = (-delta.clip(upper=0)).rolling(14).mean() df["RSI"] = 100 - (100 / (1 + gain / loss)) # 构造价格变动率和成交量变化 df["Return"] = df["Close"].pct_change() df["Vol_change"] = df["Volume"].pct_change() # 去掉指标计算产生的空值 df = df.dropna()逻辑说明:ewm(span=12, adjust=False)里的adjust=False让加权方式与常见行情软件一致,不加这个参数前期数值会偏。RSI分母里loss可能为 0,实际跑的时候加一个极小值1e-9更稳。pct_change()算的是日收益率,比绝对价格更能反映波动。拼完特征后,建议用df.corr()看一眼各列和收盘价的相关性,相关性低于 0.05 的特征可以考虑剔除,减少噪声。
3.2 模型选型:线性回归、随机森林还是 LSTM
项目正文列了线性回归、决策树、随机森林、SVM、LSTM、GRU 等一堆模型,但实际落地时不是越多越好。我的经验是:如果只有几百到几千条日线数据,随机森林和梯度提升树往往比 LSTM 更稳,因为 LSTM 参数量大,小数据上极易过拟合;如果数据量上万条且做了滑窗构造,LSTM 才有发挥空间。这个包里app_fn.py的入口设计,通常允许你通过参数切换模型。以随机森林为例:
from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split # 特征列和目标列 features = ["Open", "High", "Low", "Volume", "MACD", "RSI", "Return"] X = df[features].values y = df["Close_scaled"].values # 按时间顺序切分,不能随机打乱 split = int(len(X) * 0.8) X_train, X_test = X[:split], X[split:] y_train, y_test = y[:split], y[split:] # 训练随机森林,树的数量先给 100 model = RandomForestRegressor(n_estimators=100, random_state=42) model.fit(X_train, y_train)关键点:时序数据切分必须按时间先后,train_test_split默认的随机打乱会把未来信息泄露到训练集,这是新手最常踩的坑。参数上,n_estimators从 100 起步,数据量大可以加到 300;random_state固定住,保证每次跑结果可复现。如果换成 LSTM,输入要改成三维张量(样本数, 时间步, 特征数),常见做法是用 30 天滑窗构造序列,这里不展开,但思路一样:先保证切分正确,再谈调参。
3.3 训练与验证:k 折交叉验证在时序上的正确用法
项目正文提到 k 折交叉验证防止过拟合,但普通KFold在时序上会打乱顺序,正确做法是用TimeSeriesSplit。它保证每一折的训练集都在测试集之前,模拟真实预测场景:
from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_squared_error import numpy as np tscv = TimeSeriesSplit(n_splits=5) scores = [] for train_idx, val_idx in tscv.split(X): X_tr, X_val = X[train_idx], X[val_idx] y_tr, y_val = y[train_idx], y[val_idx] model.fit(X_tr, y_tr) pred = model.predict(X_val) rmse = np.sqrt(mean_squared_error(y_val, pred)) scores.append(rmse) print("各折 RMSE:", scores, "均值:", np.mean(scores))逻辑说明:TimeSeriesSplit的n_splits=5表示切 5 折,每折训练集逐步扩大。mean_squared_error算完开根号得到 RMSE,单位跟缩放后的价格一致。如果各折 RMSE 波动特别大,说明模型对时间段敏感,常见原因是某段行情波动剧烈,可以考虑加入波动率特征或对数据做分段标准化。参数上,n_splits不要超过数据量的十分之一,否则每折样本太少,评估结果没有参考价值。
4. 避坑与排查:股价预测里最容易翻车的五件事
4.1 现象:预测曲线是一条直线,几乎不动
原因:模型学到的是缩放后的均值,特征里没有足够的变化信息,或者目标列被错误地做了全局标准化导致信息丢失。解决:检查Close_scaled的方差,如果接近 0,说明缩放前数据本身波动就小;换用StandardScaler或直接对收益率建模,而不是对绝对价格建模。
4.2 现象:训练集 RMSE 极低,测试集 RMSE 高得离谱
原因:典型过拟合,常见于 LSTM 或深层树模型在小数据上训练。解决:减少模型复杂度,随机森林把max_depth限制在 5 到 10,LSTM 把隐藏层单元降到 32 并加Dropout;同时检查是否误用了未来数据做特征。
4.3 现象:app_fn.py运行报KeyError或ValueError
原因:CSV 字段名和代码里写的不一致,比如代码要Close而数据里是close或收盘价。解决:统一列名,用df.columns = [c.strip().title() for c in df.columns]做清洗;日期格式不一致也会导致parse_dates失败,先print(df.head())确认。
4.4 现象:模型每次跑出来的结果都不一样
原因:没有固定随机种子,RandomForest、train_test_split、神经网络初始化都带随机性。解决:在代码开头统一设置np.random.seed(42)和random_state=42,深度学习框架还要设torch.manual_seed或tf.random.set_seed。
4.5 现象:预测值全部偏向一个方向,涨跌判断完全失效
原因:金融时序本身信噪比极低,模型可能只学到了滞后一期的价格。解决:不要只看 RMSE,加入方向准确率指标,即预测涨跌方向与真实方向一致的比例;如果方向准确率接近 50%,说明模型没有预测能力,需要重新审视特征工程,而不是继续调参。
5. 进阶技巧:用滚动预测和方向准确率验证模型真实能力
5.1 滚动预测:模拟真实交易场景
一次性预测未来 30 天和每天滚动预测下一步,是两回事。真实场景里你只能拿到今天之前的数据,预测明天,然后等明天收盘后再更新。我一般会写一个滚动循环,每次只用当前及之前的数据训练或更新模型,预测下一天,再滑动窗口:
# 滚动预测:每次预测下一天,然后纳入真实值 window = 60 # 用过去 60 天数据 preds = [] for i in range(window, len(X)): X_train = X[i-window:i] y_train = y[i-window:i] model.fit(X_train, y_train) pred = model.predict(X[i:i+1]) preds.append(pred[0]) # 把预测值反缩放回原始价格 preds_real = scaler.inverse_transform(np.array(preds).reshape(-1, 1))逻辑说明:window=60表示每次只用最近 60 个交易日,这样模型能适应市场风格切换。inverse_transform把缩放后的预测值还原成真实价格,方便和实际收盘价对比。这个循环跑起来比一次性训练慢,但得到的评估结果更接近实盘。参数上,window太小模型不稳,太大反应迟钝,60 到 120 是常见区间。
5.2 方向准确率:比 RMSE 更贴近交易决策
RMSE 低不代表能赚钱,因为价格绝对值的微小误差在交易里可能毫无意义。我习惯再算一个方向准确率,看模型预测的涨跌方向对不对:
# 计算方向准确率 actual_diff = np.diff(y_test) pred_diff = np.diff(preds_real.flatten()) direction_correct = (np.sign(actual_diff) == np.sign(pred_diff)).mean() print("方向准确率:", direction_correct)如果方向准确率稳定在 55% 以上,说明模型有一定参考价值;如果只有 50% 左右,那和抛硬币没区别,别拿它做交易依据。这个指标也是区分"看起来能跑"和"真的有用"的分水岭。
5.3 模型更新节奏与监控习惯
股票市场是动态的,今天有效的特征下个月可能就失效了。我的习惯是每季度用最新数据重新训练一次,同时监控两个信号:一是滚动 RMSE 是否持续上升,二是方向准确率是否跌破 50%。一旦触发,就回退到上一个稳定版本,而不是硬着头皮继续用。这个包里app_fn.py的入口设计,方便你把训练和预测拆成两个函数,定时任务里只调预测,训练单独跑。从那以后我每次上线任何时序模型前,都强制走一遍滚动预测加方向准确率,不再只看一次性的 RMSE。希望帮到你。
本文还有配套的精品资源,点击获取