ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

机器学习股票价格趋势预测仿真项目:从特征工程到滚动回测

机器学习股票价格趋势预测仿真项目:从特征工程到滚动回测 简介一套基于机器学习实现股票价格趋势预测的Python仿真项目重点解决历史行情数据的读取、特征处理与趋势建模问题面向毕业设计、课程设计、期末大作业等应用场景也适合想了解LSTM时序预测的初学者。项目打包为zip格式共13个文件包含5个Python源码文件、编译缓存.pyc、说明文档.md、股票历史数据.csv、训练好的模型.pkl以及结果对比图.png整体大小仅356KB轻量且便于快速部署。源码带有详细注释覆盖数据解析、数据集构造、LSTM模型定义、训练与评估的完整流程并配有独立的说明文档解释关键参数与运行方式随附可用的样例数据和预训练模型文件下载后简单配置即可运行。目前已有303人学习/下载项目目录结构清晰源码、数据、文档与图片分类存放便于逐模块研读是机器学习方向高完成度的参考实践无论是用于答辩展示还是实操复现都具有很强的参考价值。1. 基于机器学习的股票价格趋势预测仿真项目到底在解决什么问题这类标题在课程设计和毕业设计里非常常见基于机器学习实现预测股票价格趋势的Python仿真附带完整代码、说明文档和数据。很多同学拿到手第一反应是“赶紧跑通代码”但我建议你先想清楚一件事——你预测的到底是什么。股票价格趋势预测可以拆成三个目标预测未来几天涨跌方向、预测未来N日的收益率、预测具体收盘价。这三个目标对应完全不同的模型形态方向是分类问题收益率和价格是回归问题。把价格当分类做或者把方向当回归做是这类项目里翻车最频繁的根因。这篇文章我按自己做这类项目的路径来讲先明确预测目标然后准备数据、做特征工程再选模型、搭滚动回测框架最后用一套验证指标证明你的模型不是“事后诸葛亮”。适合正在做课程设计、毕业设计或者刚入门机器学习想拿金融时序数据练手的人。整套方案不依赖高端硬件一台普通笔记本就能跑完。2. 数据准备与特征工程别让数据预处理毁掉整个仿真很多人拿到这类项目第一件事就是训练模型但经验告诉我这类项目里七八成的问题都出在数据阶段。股票数据有几个非常阴险的细节复权、缺失值、未来函数、特征泄漏。任何一个处理不当模型在训练集上表现优秀一进回测就原形毕露。2.1 用yfinance拉取日线行情复权参数决定数据质量获取股票数据最常用的库是yfinance它基于Yahoo Finance接口不需要注册API Key适合教学和仿真场景。安装方式很简单pip install yfinance pandas numpy matplotlib拉取数据的核心代码import yfinance as yf # 拉取苹果公司2015年到2024年的日线数据 df yf.download( AAPL, start2015-01-01, end2024-12-31, auto_adjustTrue, # 自动后复权消除除权除息跳空 progressFalse ) # 只保留建模需要的列收盘价是最核心的 df df[[Open, High, Low, Close, Volume]].copy() df.rename(columns{Close: close}, inplaceTrue) print(df.head()) print(df.tail())这里auto_adjustTrue是关键参数。它会让Yahoo Finance自动对所有OHLC价格做后复权处理。如果不做复权股票在除权除息日会出现人为的价格跳空——比如股价从100块突然变成80块但这不是市场行为是分红派息导致的。模型会把这种跳空当成真实的下跌信号学出一堆错误的规律。拉完数据要注意检查有没有缺失值。美股偶尔会有停牌日A股有节假日这些在数据里表现为NaN或者干脆缺行。处理方式很简单# 检查缺失值 print(df.isnull().sum()) # 对少量缺失用前向填充注意这是假设价格不变的近似 df.ffill(inplaceTrue) # 如果缺失比例超过1%建议直接丢弃对应日期 df.dropna(inplaceTrue)ffill()是用上一个有效值填充当前缺失值适合处理零星缺失。但如果连续缺失超过两三个交易日前向填充会把“停牌”错误地编码成“价格不变”这会误导模型。实际项目中我一般会先看缺失比例超过1%就直接删掉对应行。2.2 特征工程从裸价格到模型可用的特征矩阵裸的收盘价序列直接喂给模型效果通常很差。原因很简单模型看到的是绝对价格而股票分析关心的是相对变化。100块的股票涨1块和10块的股票涨1块含义完全不同。所以特征工程的第一步是构造收益率序列import numpy as np # 对数收益率比简单收益率更接近正态分布数值稳定性更好 df[return_1d] np.log(df[close] / df[close].shift(1)) # 5日、20日移动平均线与收盘价的比值衡量短期趋势强弱 df[ma_5] df[close].rolling(window5).mean() df[ma_diff_5] df[close] / df[ma_5] - 1.0 df[ma_diff_20] df[close] / df[close].rolling(window20).mean() - 1.0 # 波动率特征过去20日日收益率的标准差刻画风险水平 df[volatility_20] df[return_1d].rolling(window20).std() # RSI相对强弱指标衡量超买超卖状态窗口常用14 delta df[close].diff() gain delta.clip(lower0).rolling(window14).mean() loss (-delta.clip(upper0)).rolling(window14).mean() rs gain / loss df[rsi_14] 100.0 - (100.0 / (1.0 rs)) # 成交量变化率放量缩量是趋势判断的重要辅助信号 df[volume_ratio] df[Volume] / df[Volume].rolling(window20).mean()这套特征几乎是这类项目的标配。return_1d是模型最核心的输入它代表“今天相对于昨天涨跌了多少”ma_diff_5和ma_diff_20让模型知道当前价格在短期和中期均线的上方还是下方volatility_20提供风险度量rsi_14是技术分析里最常用的超买超卖指标volume_ratio代表量能变化。特征构造完成后必须删除前期的NaN行因为滚动窗口在数据开头计算不出值feature_cols [return_1d, ma_diff_5, ma_diff_20, volatility_20, rsi_14, volume_ratio] df.dropna(subsetfeature_cols, inplaceTrue)这里有个细节RNN类模型需要的输入形状是三维的(samples, timesteps, features)所以后面建模时还要把特征矩阵再切片成窗口序列。这部分放到模型章节细讲。2.3 标签构造与时序泄漏预测目标和数据切分必须严谨标签怎么构造直接决定了模型的预测能力。最常用的方案是预测未来5个交易日是涨还是跌构造方式如下# 预测未来5日收益率 df[future_return_5d] df[close].shift(-5) / df[close] - 1.0 # 二分类标签未来5日收益率是否大于0 df[label] (df[future_return_5d] 0).astype(int) # 删除最后5行因为它们的未来数据不存在 df.dropna(subset[future_return_5d], inplaceTrue)shift(-5)是把未来第5天的收盘价搬到当前行从而计算未来5天的累计收益。这一步必须放在特征构造之后、数据切分之前。很多翻车案例就是在这里出了问题有人用全样本的均值和方差做归一化然后再切训练集和测试集这会导致测试集的信息间接进入训练过程这种问题叫“时序泄漏”。正确的归一化方式必须保证只用训练集的数据计算均值和方差然后应用到测试集。更严格的做法是滚动归一化——每个训练窗口独立计算统计量。我一般会用后一种from sklearn.preprocessing import MinMaxScaler # 以训练集fit scaler再transform测试集防止未来信息泄漏 scaler MinMaxScaler(feature_range(0, 1)) train_size int(len(df) * 0.8) train_data df[feature_cols].iloc[:train_size].values test_data df[feature_cols].iloc[train_size:].values scaler.fit(train_data) train_scaled scaler.transform(train_data) test_scaled scaler.transform(test_data)scaler.fit(train_data)这一步只看到了训练集的数据分布。如果用全量数据做fit测试集的极值和分布就会影响训练时的特征缩放这在实盘中根本不可能发生——因为未来数据你不可能提前知道。这个问题是这类项目里最容易踩、也最容易被扣分的点。3. 模型选型对比LSTM、XGBoost和基线模型的取舍模型选型是这类项目最纠结的部分。我的建议是不要一上来就上LSTM先跑一个简单的逻辑回归作为基线然后对比XGBoost和LSTM的表现。这样做的好处是你能清楚地知道复杂模型到底有没有带来真正的提升而不是“看起来很厉害”。3.1 先跑一个逻辑回归基线没有比较就没有伤害基线模型的作用是设定一个最低标准。如果LSTM和XGBoost连逻辑回归都打不过说明问题不在模型复杂度而在特征工程或数据切分上。from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, f1_score # 构造滑动窗口样本用过去20天的特征预测未来方向 def create_sequences(data, label, window20): X, y [], [] for i in range(window, len(data)): X.append(data[i-window:i]) y.append(label[i]) return np.array(X), np.array(y) window 20 train_data_label df[label].values[:train_size] test_data_label df[label].values[train_size:] # 注意这里用展平后的2D特征喂给逻辑回归 X_train_flat train_scaled.reshape(train_scaled.shape[0], -1) X_test_flat test_scaled.reshape(test_scaled.shape[0], -1) # 对齐标签因为窗口会吞掉前window个样本 y_train train_data_label[window:] y_test test_data_label[window:] X_train_flat X_train_flat[window:] X_test_flat X_test_flat[window:] # 这里简化为用滚动方式构造后面回测章节会展开 lr LogisticRegression(max_iter1000, C1.0) lr.fit(X_train_flat, y_train) y_pred_lr lr.predict(X_test_flat) print(fLogisticRegression Accuracy: {accuracy_score(y_test, y_pred_lr):.4f}) print(fLogisticRegression F1: {f1_score(y_test, y_pred_lr):.4f})这段代码有几个关键点。reshape(train_scaled.shape[0], -1)是把20天×6个特征展平成120维的向量逻辑回归无法直接处理三维输入。逻辑回归的优势是训练快、结果可解释、几乎不过拟合缺点是它的决策边界是线性的无法捕捉特征之间的非线性交互。基线模型的意义不在效果而在参照。如果你的LSTM准确率只有53%而逻辑回归有52%那LSTM的复杂度和训练成本完全不值得。这种对比在项目答辩时也是很加分的说明你做过系统性验证不是盲目堆模型。3.2 LSTM模型序列建模为什么适合股价趋势LSTM长短期记忆网络是RNN的一种变体专门解决长期依赖问题。对于股票数据它天然适合处理“过去20个交易日的形态决定了未来走势”这类问题。核心代码如下from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping model Sequential() model.add(LSTM(64, return_sequencesTrue, input_shape(window, len(feature_cols)))) model.add(Dropout(0.2)) model.add(LSTM(32, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(16, activationrelu)) model.add(Dense(1, activationsigmoid)) model.compile(optimizeradam, lossbinary_crossentropy, metrics[accuracy]) model.summary()这里input_shape(window, len(feature_cols))就是之前说的三维输入。window20代表用过去20个交易日的数据做预测这在金融时序里是一个常见的经验值——大约等于一个自然月的交易日数量既能捕捉中期趋势又不会引入太多噪声。return_sequencesTrue让第一层LSTM输出完整的时间步序列给第二层LSTM第二层return_sequencesFalse只输出最后一个时间步的结果然后接全连接层。Dropout(0.2)是防止过拟合金融时序数据量通常不大模型一大就妥妥过拟合。训练时用早停机制避免在验证集上持续恶化# 构建训练序列 X_train, y_train_seq create_sequences(train_scaled, train_data_label, window) X_test, y_test_seq create_sequences(test_scaled, test_data_label, window) # 切片对齐create_sequences返回的样本数 原样本数 - window # 而y_train已经在前面的代码中做了切片注意长度匹配 early_stop EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) history model.fit( X_train, y_train_seq, validation_split0.2, epochs100, batch_size32, callbacks[early_stop], verbose1 )patience10的意思是验证集损失连续10轮不下降就停止训练。金融数据噪声极大模型很容易在训练集上反复震荡早停是最便宜的后悔药。validation_split0.2是从训练集尾部切出20%做验证注意不能随机切时序数据必须保持顺序——Keras的validation_split默认就是从尾部切这一点恰好符合时序要求。3.3 XGBoost模型树模型对表格特征的另一种拟合路径XGBoost是梯度提升树的最流行实现它的特点是对特征缩放不敏感、能自动处理特征交互、训练速度快。对于股票数据XGBoost在很多场景下效果不比LSTM差而且调参更加可控。import xgboost as xgb # XGBoost输入是2D特征不需要做滑动窗口直接展开或用统计特征 # 这里为了公平对比同样使用窗口展平特征 model_xgb xgb.XGBClassifier( n_estimators200, max_depth4, learning_rate0.05, subsample0.8, colsample_bytree0.8, eval_metriclogloss, early_stopping_rounds20, random_state42 ) # 用前80%的展平特征做训练 model_xgb.fit( X_train_flat, y_train, eval_set[(X_test_flat, y_test)], verboseFalse ) y_pred_xgb model_xgb.predict(X_test_flat)参数说明n_estimators200控制树的数量树太多会过拟合配合early_stopping_rounds20可以在验证集不再提升时自动停止max_depth4限制单棵树的深度防止学到过于细碎的噪声subsample0.8每次迭代随机采样80%的样本训练这是XGBoost内建的正则化手段colsample_bytree0.8同理对特征做列采样。XGBoost在表格类特征上通常能拿到比LSTM更有竞争力的结果而且训练时间可能只有LSTM的十分之一。实际项目里我一般两个都会跑然后在回测阶段对比真实表现而不是只看训练集上的准确率。3.4 模型对比什么时候LSTM值得、什么时候XGBoost更优维度逻辑回归XGBoostLSTM输入形态2D展平特征2D展平特征3D时序窗口训练速度秒级秒到分钟级分钟级特征缩放需要不需要需要非线性捕捉弱强强时序依赖建模无需人工构造滞回特征内建循环结构过拟合风险低中需正则高需Dropout早停可解释性高系数可直接看中特征重要性低黑匣子我的经验是如果你的特征工程做得够好有足够的滞后特征和统计特征XGBoost通常能逼近甚至超过LSTM但如果你觉得某些规律藏在20天的连续走势形态里LSTM更有优势。一个务实的做法是两个都保留在回测章节用统一的评价框架PK选实战表现更好的。4. 滚动回测框架训练窗口、预测窗口和未来函数的三重校验模型训练好了、测试集准确率也看了但这类项目真正见真章的地方是回测——模拟真实交易场景看看模型发出的买卖信号到底能不能赚钱。很多人忽视了这个环节导致交付的“高分”项目一上实盘就崩。回测框架的核心有两点滚动训练避免过时模型、消除未来函数避免“开卷考试”。4.1 滚动训练模型必须面对“没见过”的未来静态切分训练集和测试集的问题在于模型是用2015-2023年的数据训练的如果用它预测2024年全年市场环境早就变了。真实量化交易里模型需要定期重训。常见的做法是滚动窗口训练比如用过去250个交易日训练预测未来5个交易日然后窗口向前推进。def rolling_predict(model_fn, df_scaled, labels, train_days252, test_days5, window20): 滚动回测每次用train_days天的数据训练预测未来test_days天 返回所有预测结果和对应的真实标签 preds, y_true [], [] total len(df_scaled) # 从足够长的起点开始至少要有train_days window个样本 start train_days window while start test_days total: # 训练区间[start - train_days - window, start - window] train_begin start - train_days - window feature_train df_scaled[train_begin:start] label_train labels[train_begin window:start] # 拟合模型传入可调用对象返回训练好的模型 model model_fn() # 构造训练序列 X_tr, y_tr create_sequences(feature_train, label_train, window) model.fit(X_tr, y_tr, verbose0) # 预测未来test_days天 for t in range(test_days): idx start t - window if idx window total: break x_test df_scaled[idx:idxwindow].reshape(1, window, -1) pred (model.predict(x_test) 0.5).astype(int)[0][0] preds.append(pred) y_true.append(labels[start t]) start test_days return np.array(preds), np.array(y_true)这里train_days252大约是美股一年的交易日数test_days5代表每次预测未来5个交易日。滚动窗口的长度要兼顾两件事窗口太短模型学不到足够的市场规律太长模型又要花很多时间重训。252是一个常见的经验起点实际项目里我会对200/252/300做敏感性测试看结果是否稳定。这段代码里最右侧的索引计算容易把人绕晕建议仔细画一条时间轴。核心逻辑训练集和预测集永远不重叠而且预测时用的特征窗口完全在训练集结束时间点之前。如果你发现自己的回测代码里出现了“用第t天的数据预测第t天涨跌”那基本就是提前偷看了答案。4.2 未来函数检查回测里最隐蔽的翻车点未来函数是指回测时使用了当时不可能拿到的新数据。比如用未来5天的收益来构建今天买卖信号的评价指标或者归一化时用到了全量数据的统计量。检查方法很简单把回测结果和真实交易日志逐笔对照看信号的产生时间是否早于数据可用时间。# 手动检查未来函数记录每笔预测产生的时间点 for i in range(len(preds)): if preds[i] ! 0 or y_true[i] ! 0: # 在实盘中第t天的预测只能基于第t-1天收盘前的数据 print(fPrediction at step {i}: signal{preds[i]}, actual{y_true[i]})更实用的检查方法是构造一个“延迟测试”把特征整体向后平移一天再跑一遍回测。如果模型表现没有显著下降说明它对昨天的数据不敏感很可能是偷偷用了当天的数据。4.3 模拟交易把预测信号翻译成资金曲线光看预测准确率不够还要看信号能否转化为稳定的资金曲线。模拟交易规则可以很简单预测上涨就持有预测下跌就空仓。核心代码如下def backtest_trading(preds, returns, initial_cash100000, fee_rate0.001): 模拟交易回测根据预测信号做次日开盘买入/卖出 简化版当日收盘前产生信号次日按收盘价成交 cash initial_cash position 0 # 持仓股数 equity_curve [] for i in range(len(preds)): # 信号产生于第i天收盘次日生效 if preds[i] 1 and position 0: # 全仓买入 position cash / (returns[i1] 1e-9) cash 0 elif preds[i] 0 and position 0: # 清仓 cash position * returns[i1] position 0 # 记录每日总资产 if position 0: equity position * (1 returns[i1]) else: equity cash equity_curve.append(equity) return np.array(equity_curve)这段代码做了极大的简化实际回测还要考虑滑点、手续费、涨跌停限制等。它的意义在于把“预测准确率”翻译成“资金曲线”让你直观看到信号带来的真实盈亏。我的经验是一个准确率55%但回撤控制良好的策略远好于准确率60%但偶尔巨亏的策略。5. 避坑笔记从数据漂移到过拟合的5个真实翻车现场这一章我想把这类项目里最常见的坑集中写出来。每一条都是我或身边人真实踩过的按照“现象→原因→解决”来呈现。5.1 股票分割导致的价格断崖后复权数据是底线现象回测曲线在某一天突然大幅下跌但当天大盘并没有异常。 原因股票进行了拆股比如1拆4价格从千元级一下降到百元级如果数据没有做复权处理模型会把这次拆股当成“暴跌”进而发出恐慌卖出信号。 解决拉取数据时使用auto_adjustTrue做后复权或者在特征工程前手工复权。复权后的数据在拆股日前后的收益率连续不会出现虚假波动。5.2 归一化泄漏模型在测试集上“作弊”现象训练集准确率正常测试集准确率异常高比如85%以上但回测资金曲线惨不忍睹。 原因对全部数据做了一次fit_transform测试集的均值和方差被模型间接使用。这就相当于考试前偷看了答案训练时的分布包含了未来信息。 解决严格分为fit和transform两步只用训练集计算统计量。更严格的做法是滚动标准化——每个训练窗口重新计算统计量。判断方法很简单把归一化后的数据画出来看训练集和测试集的极值是否明显不同。如果测试集有训练集里从未出现过的极端值说明标准化做对了。5.3 训练集与测试集时间重叠预测变成了“曲线的外推猜谜”现象模型准确率很高但换成其他股票或新时间段效果急剧下降。 原因训练集和测试集有交叠或者特征构造时shift(-5)操作后忘记dropna导致测试集里包含了训练集边缘的标签信息。 解决训练与测试严格按时间切分常用80%训练/20%测试且确保切分点前后的数据不重叠。检查方式打印训练集和测试集的日期范围确认没有交叉。5.4 涨跌类别不平衡准确率再高也可能是废物现象模型预测“跌”的准确率极低但整体准确率看起来还行。 原因股票市场上涨和下跌的天数往往不均衡尤其牛市期间上涨天数远多于下跌天数。模型学会了“永远预测上涨”自然能拿到不错的基础准确率。 解决使用F1-score和混淆矩阵辅助评价而不是只看准确率。如果发现多数类是上涨且模型总是预测上涨可以用class_weightbalanced调整惩罚权重让模型更重视少数类。# 处理类别不平衡的方案 from sklearn.utils.class_weight import compute_class_weight classes np.array([0, 1]) weights compute_class_weight(class_weightbalanced, classesclasses, yy_train) class_weight_dict {0: weights[0], 1: weights[1]}5.5 模型在训练集上完美拟合、在回测中一败涂地现象LSTM在训练集上能学到近乎完美的准确率但滚动回测时亏得一塌糊涂。 原因模型过大、训练轮数过多、金融数据强噪声。模型把历史特定形态死记硬背下来一旦有新形态出现就失效。 解决用早停机制在验证集损失不再下降时停止训练并降低模型复杂度。经验值是LSTM单层单元数不超过128Dropout不低于0.2训练轮数限制在100以内。宁可欠拟合不要过拟合。金融时序数据有效信号极弱一个老实巴交的模型比一个聪明过头的模型可靠得多。6. 验证与进阶让你的模型经得起推敲模型训练完毕、回测框架搭好最后一步是验证模型是否值得投入。这一章我给出一套实操验证流程以及几个能显著提升项目完成度的进阶方向。6.1 三个核心回测指标资金曲线不是玄学只看资金曲线形状判断模型好坏不靠谱。三个指标必须算年化收益率、最大回撤、夏普比率。年化收益率衡量赚钱能力最大回撤衡量风险夏普比率衡量风险调整后的收益。计算夏普比率的代码# 基于每日收益率计算夏普比率 def sharpe_ratio(returns, risk_free_rate0.02, periods252): returns: 策略每日收益率序列 risk_free_rate: 年化无风险利率常见取2% periods: 年化周期数日频数据用252 excess_returns returns - risk_free_rate / periods if np.std(excess_returns) 0: return 0.0 return np.sqrt(periods) * np.mean(excess_returns) / np.std(excess_returns) # 计算每日收益率 equity_curve np.array(equity_curve) daily_returns np.diff(equity_curve) / equity_curve[:-1] print(fSharpe Ratio: {sharpe_ratio(daily_returns):.2f})夏普比率的经验分界大于1算合格大于2算优秀。如果模型回测夏普比率低于0.5说明它的超额收益不稳定投入实盘大概率亏钱。最大回撤的计算def max_drawdown(equity_curve): 最大回撤 从历史高点到后续最低点的最大跌幅 peak np.maximum.accumulate(equity_curve) drawdown (equity_curve - peak) / peak return drawdown.min() print(fMax Drawdown: {max_drawdown(equity_curve):.2%})最大回撤超过20%的策略在真实交易里很难拿住因为心理压力极大。这类项目里我一般会把“回撤不超过15%”作为筛选红线。6.2 与买入持有策略对比证明你的模型有存在价值一个残酷的现实很多机器学习策略跑不过简单的“买入并持有”。所以验证环节必须加一个对比实验。# 买入持有策略始终满仓 buy_hold_returns df[close].pct_change().iloc[train_sizewindow:train_sizewindowlen(daily_returns)] buy_hold_equity (1 buy_hold_returns).cumprod() # 比较最终资产和夏普比率 strategy_final equity_curve[-1] bh_final buy_hold_equity[-1] * initial_cash print(fStrategy Final: {strategy_final:.2f} vs BuyHold: {bh_final:.2f})如果策略最终跑不赢买入持有这个模型就没有实际价值——哪怕准确率再高。这类项目的答辩中评委几乎必问这个问题。提前准备好“策略夏普比率高于买入持有”或者“最大回撤低于买入持有”这个论据比讲一百句模型原理都管用。6.3 进阶方向从单股票到组合与多因子如果你想让项目从“课程设计”升级到“准量化系统”两个方向最值得加。第一个方向是多股票组合对五六只不同行业的股票分别跑一次回测然后按等权或风险平价分配资金。这一步能有效分散单只股票的偶然风险资金曲线会平滑很多。第二个方向是多因子融合把技术指标动量、均线、波动率和基本面数据PE、PB、营收增速一起作为特征输入。金融时序的技术信号信噪比极低融合基本面因子是提升模型稳健性的经典思路。我自己在这类项目上吃过的最大教训是永远不要相信一个只在某一只股票上有效的模型——换一只股票就失效的策略大概率是过拟合了历史数据而不是学到了真正普适的规律。所以我现在的习惯是任何模型都要在至少三只不同行业的股票上做滚动回测再用统一指标做综合评估。加这个动作项目报告的分数通常能上一个档次。做这类仿真项目的重点不是模型多复杂而是每一步都经得起推敲。数据有没有泄漏回测有没有未来函数指标是不是只看准确率。把这些细节做扎实你的机器学习股票价格趋势预测仿真才是真正的高分项目。希望这篇笔记能帮你少走几条弯路。本文还有配套的精品资源点击获取
返回列表