尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

线性回归与神经网络预测黄金期货:时间序列分析实战对比

线性回归与神经网络预测黄金期货:时间序列分析实战对比 简介这是一份面向量化金融学习者与黄金期货研究者的对比分析项目包围绕中国黄金期货价格预测展开聚焦多元线性回归与多层感知机MLP神经网络两种模型的准确性比较。项目涵盖平稳性检验、格兰杰因果检验等时间序列分析前置步骤适合希望掌握金融数据建模与模型评估方法的中高级数据分析师参考。压缩包共13个文件以Python脚本为核心辅以8张结果图表、说明文档、Markdown笔记及Word附赠资源整体大小约225KB结构紧凑便于快速理解建模流程与可视化结论。已有76人学习下载内容包含线性回归代码、MLP精度分析脚本及黄金期货影响因子图示读者可据此复现实验对比不同模型在期货价格预测中的表现并进一步拓展至其他金融时间序列场景。1. 为什么有人会拿线性回归和神经网络对比黄金期货价格预测假设你手里有一份上海期货交易所黄金主力合约近十年的日线数据想回答一个很实际的问题用多元线性回归和用多层感知机神经网络去预测未来五日的收益谁更靠谱这个标题本质上不是在做一个能直接上生产环境的交易系统而是一个对比实验框架——用同一份中国黄金期货数据跑两个差异极大的模型再用时间序列分析里常用的平稳性检验和格兰杰因果检验做前置筛选最后用统一的评估指标给两套模型排座次。它的价值不在绝对精度而在对比口径是否公平。适合谁有 Python 基础、想搞明白统计模型和神经网络在金融时序上真实差距的量化入门者以及那些想验证“深度学习在商品期货上到底是不是玄学”的从业者。下面我按自己做过这类对比研究的顺序把数据准备、检验、建模、对比和踩坑一次讲透。2. 把黄金期货数据变成训练样本复权、特征与时间切分2.1 数据源与主力合约的复权处理国内黄金期货的公开数据源很多常见做法是通过 akshare、tushare 或者直接从交易所官网抓取日线。我一般推荐 akshare 或者 tushare因为连续合约和主力合约的字段已经整理过。这里要特别注意一个坑不能用裸的主力合约价格直接开跑。主力合约每隔几个月就会换月换月那天新老合约之间存在价差体现在连续价格序列上就是一个跳空缺口。模型如果拿这种带跳空的价格去训练会误以为市场出现了剧烈的单日涨跌实际上只是换约造成的价差。处理方式有两种一是用官方发布的连续合约复权价二是自己按后复权方式把换月缺口抹平。下面这段代码演示了最常用的方式读入日线后把收盘价做对数变换后续特征都用对数收益而不是原始价格。import pandas as pd import numpy as np # df 是原始日线包含 date, close, volume, open_interest 等字段 # 先按日期排序再计算对数价格和对数收益 df df.sort_values(date).reset_index(dropTrue) df[log_price] np.log(df[close]) # 主力换月导致的异常跳空用前一日收盘做后复权修正 # 找出单日收益绝对值超过 3% 的样本这类样本往往集中在换月日 df[ret] df[log_price].diff() jump_days df.index[df[ret].abs() 0.03].tolist() # 对换月跳空做缩尾式修正把超过阈值的收益直接置为 0保留原价格序列的连续性 for idx in jump_days: df.loc[idx, ret] 0.0 df[adj_log_price] df[log_price].cumsum() - df[ret].fillna(0).cumsum()逻辑说明先用对数价格差分得到日收益率避免价格水平带来的非平稳问题再找出单日收益超过 3% 的异常点并修正这一步可以消掉大部分换月跳空。为什么用对数收益而不是简单百分比收益因为对数收益具备时间可加性后续做滚动求和、累计收益都很方便也更接近连续复利假设。参数说明里最需要注意的是阈值 3%它不是固定标准。黄金期货在正常交易日很少出现超过 3% 的单日波动一旦出现大概率是换月或极端行情你可以把阈值调低到 2% 观察一下命中样本如果命中的都是换月日附近说明阈值设置合理。2.2 从价格到特征滞后收益、波动率与滚动动量拿到干净的收益序列后要开始构造多元线性回归和神经网络共同使用的特征。常见做法是把原始价格直接喂给模型这在金融时序里几乎一定是错的。价格序列是非平稳的模型会学到一条毫无意义的长期趋势而真实要预测的是短期收益。所以特征全部基于收益、波动和成交量来构造。# 在 df 基础上构造特征 df[ret_1] df[ret].shift(1) # 前一日的对数收益 df[ret_2] df[ret].shift(2) # 前两日的对数收益 df[ret_5] df[ret].shift(5) # 五日前的对数收益 # 用过去 5 日的收益计算滚动波动率 df[vol_5] df[ret].rolling(5).std() # 构造 5 日累计动量衡量最近一段时间的趋势强度 df[mom_5] df[ret].rolling(5).sum() # 持仓量变化黄金期货里这个指标常被当作资金意向的信号 df[oi_chg] df[open_interest].pct_change() # 预测目标未来 5 日累计收益代表一个持有周期内的总收益 df[target] df[ret].rolling(5).sum().shift(-5)这部分就是典型的滞后特征构造。shift(1)代表用 t-1 日收益预测未来rolling(5).std()是过去 5 天的标准差用来刻画波动率。黄金期货有一个明显特征价格趋势性不强但波动率聚集现象显著所以波动率相关特征往往比单纯的价格滞后项更有区分度。oi_chg是持仓量变化率需要注意黄金期货的持仓量在换月前后也会剧烈波动如果你在复权时只处理了价格持仓量特征在换月附近会引入噪声可以考虑对换月日的oi_chg也做置零处理。2.3 三段时间窗训练、验证与测试怎么切时间序列建模里最忌讳的是随机切分数据。我用train_test_split随机打乱数据去做时序预测的人不止一次翻车——打乱之后训练集里混进了未来信息模型在测试集上的表现好得离谱实盘一跑就原形毕露。正确做法是按时间顺序切成三段最早一段做训练中间一段做验证调参和早停最后一段做测试只评估一次。# 按时间顺序切分而不是随机打乱 train_end int(len(df) * 0.7) val_end int(len(df) * 0.85) train df.iloc[:train_end].copy() val df.iloc[train_end:val_end].copy() test df.iloc[val_end:].copy() # 特征列和标签列分开注意 dropna 要在切分之前完成 feature_cols [ret_1, ret_2, ret_5, vol_5, mom_5, oi_chg] df df.dropna().reset_index(dropTrue) X_train train[feature_cols].values y_train train[target].values # val 和 test 同理这里有一个细节dropna要在切分前完成否则切分后各段的索引对不上。另外滚动特征如vol_5、mom_5在训练集起始位置会有空值dropna 之后训练集样本量会减少几个这个损失可以接受。三段比例 70/15/15 是我常用的默认值如果样本总量只有几百条日线可以考虑改成 80/10/10因为神经网络需要更多训练样本。特征列里没有放日期但建模时要保留日期索引方便后面复盘预测结果在哪一天出了大偏差。3. 平稳性检验与格兰杰因果建模型前先把两个前置问题问清楚3.1 ADF 检验原序列不平稳就先用对数差分很多初学者跳过平稳性检验直接建模结果线性回归的 R? 高得惊人但预测的其实是价格趋势的惯性而不是真正的收益驱动因素。所以拿到数据后第一步应该用 ADF 检验确认你使用的收益序列是否平稳。ADF 的原假设是“序列存在单位根即非平稳”如果 p 值小于 0.05拒绝原假设认为序列平稳。from statsmodels.tsa.stattools import adfuller # 检验原始收盘价 price_result adfuller(df[close].values) print(fclose ADF p-value: {price_result[1]:.4f}) # 检验对数收益 ret_result adfuller(df[ret].dropna().values) print(fret ADF p-value: {ret_result[1]:.4f}) # 如果收益仍不平稳尝试一阶差分 diff_result adfuller(df[ret].dropna().diff().dropna().values) print(fdiff ret ADF p-value: {diff_result[1]:.4f})逻辑说明adfuller返回一个元组第二个元素是 p 值第一个是 ADF 统计量。你会发现原始收盘价的 p 值通常接近 1完全无法拒绝非平稳而对数收益的 p 值往往远小于 0.05说明收益序列已经平稳。这正是为什么要用收益做预测目标而不是价格。如果某个期货品种的收益序列仍然不平稳就需要对收益再做一阶差分但这种情况比较少见。注意adfuller默认会包含常数项对收益序列来说已经够用不需要额外加趋势项。3.2 KPSS 检验和 ADF 结果互斥时怎么办KPSS 检验和 ADF 的原假设正好相反KPSS 原假设是“序列是平稳的”。这两个检验搭配使用可以避免单一检验的盲区。常见的组合结果有两种ADF 拒绝非平稳且 KPSS 不拒绝平稳说明序列平稳可以放心用ADF 不拒绝非平稳且 KPSS 拒绝平稳说明序列确实非平稳需要差分。但还有一种让人头疼的情况ADF 拒绝非平稳、KPSS 也拒绝平稳。这说明序列处于边界状态样本量太少或者结构突变导致两个检验互相矛盾。我在黄金期货日线上遇到过这种情形通常是数据区间跨越了 2013 年和 2020 年这种极端行情序列里存在明显波动率突变。处理方法不是纠结检验结果而是对序列做对数收益转换后重跑一遍只要收益序列平稳就可以继续。说到底这些检验是服务于“能不能用线性回归和神经网络建模”的手段不是研究目的本身。3.3 格兰杰因果检验滞后期选择与方向性问题格兰杰因果检验回答的问题是特征 X 的过去值是否对预测 Y 的未来值有统计上显著的帮助。在黄金期货预测里常见做法是检验成交量、持仓量、美元指数等外生变量是否对黄金收益存在格兰杰因果。注意格兰杰因果不是真正意义上的因果它只是时间序列意义上的“预测能力先行性”。from statsmodels.tsa.stattools import grangercausalitytests # 构造用于检验的数据ret 是黄金收益vol 是成交量变化率 test_data df[[ret, volume_pct_change]].dropna() # maxlag 设为 5检验过去 1 到 5 期的成交量变化对黄金收益的预测能力 gc_result grangercausalitytests(test_data, maxlag5, verboseTrue)grangercausalitytests会把每个滞后期对应的多个检验统计量打印出来重点关注ssr_ftest对应的 p 值。如果某个滞后期的 p 值小于 0.05说明在该滞后期下成交量变化率对黄金收益有格兰杰因果影响。这里有一个参数教训maxlag不是越大越好。黄金期货日线数据的有效记忆周期通常不超过 10 个交易日把maxlag设到 20 或 30尾部滞后期会因为样本量不足而出现虚假的显著性。我一般先用maxlag5跑一遍如果有信号再把maxlag扩大到 10 验证稳定性。另一个方向性问题格兰杰因果检验对变量顺序敏感test_data里第一列是被检验的因变量第二列是待考察的自变量如果你把顺序反过来结果含义就变成了“黄金收益是否格兰杰引起成交量变化”这在实际应用中是完全不同的问题。做特征筛选时我会把真正关心的方向写清楚不要看反。4. 多元线性回归与多层感知机的落地实现及对比口径4.1 多元线性回归解释性优先的基线模型多元线性回归在这个项目里的角色是“基准”。它不是用来赢神经网络的而是用来提供一个可解释的下限。如果神经网络连线性回归都打不过那说明数据里的信号太弱或者特征工程出了问题。我建议用statsmodels的 OLS 而不是sklearn的LinearRegression因为前者能直接输出每个特征的 p 值和置信区间这对研究型项目很重要。import statsmodels.api as sm # 在训练集上拟合加常数项 X_train_sm sm.add_constant(X_train) ols_model sm.OLS(y_train, X_train_sm).fit() # 看每个变量的显著性 print(ols_model.summary()) # 在测试集上预测 X_test_sm sm.add_constant(X_test) y_pred_ols ols_model.predict(X_test_sm)ols_model.summary()输出里的P|t|一列能直观告诉你哪些特征在统计意义上不显著。按我的经验黄金期货的收益预测里vol_5和mom_5的显著性通常比单日滞后收益高这符合黄金市场波动聚集、趋势微弱的特征。如果某个特征的 p 值大于 0.3可以考虑删掉重训减少噪声。线性回归的参数解读很直接coef是特征每变化一个单位未来五日累计收益的平均变化量。但要注意这里的预测目标是对数收益数值通常很小系数可能只有 0.01 量级这是正常的不要觉得系数太小就是无效。另一个关键点是滚动重训。金融数据的分布会漂移五年前训练好的模型拿到今天预测效果几乎肯定衰减。常见做法是每 20 个交易日用最近一年的数据重训一次。如果数据量不大也可以用扩展窗口即每次重训时把新数据追加进训练集而不是滑掉旧数据。扩展窗口在样本量不足时更稳定。4.2 多层感知机神经网络结构、标准化与早停多层感知机在这个问题上是作为非线性基线存在的。黄金期货日线数据的特征维度不高通常只有五到十几个特征用深层网络是典型的杀鸡用牛刀还特别容易过拟合。我一般用两个隐藏层就够第一层 64 个神经元第二层 32 个神经元激活函数用 ReLU输出层不加激活直接输出连续值。优化器用 Adam学习率设 0.001配合自适应调整。sklearn 的MLPRegressor足够完成这类对比实验不需要上 PyTorch 或 TensorFlow。from sklearn.neural_network import MLPRegressor from sklearn.preprocessing import StandardScaler # 标准化只对训练集 fit验证集和测试集只做 transform scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_val_scaled scaler.transform(X_val) X_test_scaled scaler.transform(X_test) # 构建 MLP mlp MLPRegressor( hidden_layer_sizes(64, 32), activationrelu, solveradam, alpha0.001, # L2 正则化强度 learning_rateadaptive, learning_rate_init0.001, max_iter500, early_stoppingTrue, validation_fraction0.2, # 从训练集里划出 20% 做内部验证 n_iter_no_change20, random_state42 ) mlp.fit(X_train_scaled, y_train) y_pred_mlp mlp.predict(X_test_scaled)代码里的每个参数基本都有讲究。alpha0.001是 L2 正则化系数它压制网络权重的绝对值是防过拟合的第一道防线。early_stoppingTrue是第二道防线每训练一个 epoch 后用内部验证集看损失连续n_iter_no_change20轮不下降就停止。validation_fraction0.2表示从喂进去的训练数据里再切 20% 做内部验证这意味着你之前切好的X_train会再被分掉一部分神经网络的真正训练样本量是原来的 80%。learning_rateadaptive表示早停之外如果连续两次迭代损失不降学习率会自动减半配合learning_rate_init0.001使用能让训练过程更稳。这里最需要注意的坑是标准化。金融时序里绝不能把全部数据放在一起fit因为scaler会看到测试集的均值和标准差等于把未来信息泄漏进训练过程。我在实际项目里的习惯是拿到数据后先按时间切分再在训练集上fit_transform验证集和测试集只调用transform。这个顺序错一步后面所有比较都失去意义。4.3 同一测试集上的对比口径四类指标怎么读两个模型都跑完之后要在完全相同的测试集时间段上计算指标否则对比无从谈起。我会固定一个测试集区间比如最后一年的数据不因为哪个模型表现好就换区间重测。指标计算方式关注点RMSE预测收益与真实收益的均方根误差大偏差惩罚重适合衡量总体误差MAE平均绝对误差对异常值不敏感反映典型偏差MAPE平均绝对百分比误差看预测偏离真实值的相对比例注意目标不能接近 0方向准确率预测符号与真实符号一致的比例最贴近交易实务直接反映多空判断能力还要算一个方向准确率因为金融预测里方向比幅度重要得多。RMSE 小不代表能赚钱如果每个预测值都往同一个方向偏方向准确率照样会在 50% 附近徘徊。from sklearn.metrics import mean_squared_error, mean_absolute_error def evaluate(y_true, y_pred): rmse np.sqrt(mean_squared_error(y_true, y_pred)) mae mean_absolute_error(y_true, y_pred) mape np.mean(np.abs((y_true - y_pred) / (y_true 1e-8))) * 100 direction_acc np.mean(np.sign(y_true) np.sign(y_pred)) return {RMSE: rmse, MAE: mae, MAPE: mape, 方向准确率: direction_acc} ols_metrics evaluate(y_test, y_pred_ols) mlp_metrics evaluate(y_test, y_pred_mlp)MAPE 分母里加了1e-8是为了防止目标值恰好为 0 时除零。方向准确率的基准线是 50%但黄金期货日线收益在大多数交易日都很小实际可比基准应略高于 50%具体要看测试集里的涨跌比例。如果测试期恰好是震荡行情预测大概率会被拉向均值附近方向准确率可能低于 50%这并不代表模型完全失败而是行情没有给模型发挥空间。5. 黄金期货预测的五个翻车现场与避坑清单5.1 换月跳空没处理模型白学一场现象模型在训练集上表现很好但测试集上误差突然变大尤其在换月日附近单日预测偏差能到平时的几倍。原因主力合约换月缺口被模型当成真实的单日收益线性回归会为这个虚拟的剧烈波动学出一个很大的系数神经网络则会在权重里记住这个异常模式。解决在数据准备阶段按 2.1 节的阈值修正跳空把超过 3% 的单日收益置零。修正后要检查跳空日的数量如果一年里有 8 到 10 次换月应该有对应的置零记录。另外在特征工程里对持仓量变化率也要在换月日做同样的修正。5.2 全部数据一起标准化最隐蔽的未来函数现象神经网络在验证集上的表现优秀得反常方向准确率能达到 60% 以上但到真实预测时立刻回落。原因对全量特征做 StandardScaler 的fit_transform时验证集和测试集的均值和标准差已经参与计算等于模型偷看了未来数据的分布信息这是一种典型的未来函数泄漏。解决严格按时间切分之后只在训练集上scaler.fit()验证集和测试集只做transform()。这个规则在所有涉及滑动窗口或归一化的时间序列任务里都该强制执行。5.3 格兰杰因果检验的滞后期选错结论完全相反现象用maxlag3跑格兰杰因果成交量对黄金收益有显著影响换到maxlag20结果变得不显著甚至出现反向结论。原因格兰杰因果检验对滞后阶数敏感。滞后期过短遗漏了真正起作用的滞后信息滞后期过长有效样本量减少统计功效下降。不同滞后期得到相反结论说明变量间关系本身就很弱或者不稳定不能轻易判定存在因果。解决至少测 3 组不同的maxlag比如 3、5、10只在多数组结果一致时采信结论。报告里注明检验的滞后期范围避免读者误解。5.4 时间切分不按时间随机打乱把整个研究带偏现象两个模型在随机切分的数据上表现惊人RMSE 比按时间切分时低 30% 以上。原因随机打乱后训练集和测试集在时间上相互交错模型其实是在做内插而不是外推。金融时序的内插预测难度远低于外推这种结果在实盘里没有任何参考价值。解决训练、验证、测试严格按时间顺序切分并且保证测试集比训练集更靠后。如果需要做多轮对比可以用滚动的 origin 评估每次把切分点向后推移但始终不混淆时间顺序。5.5 只报 RMSE 不报方向准确率结论离交易太远现象研究报告里写“MLP 比线性回归的 RMSE 低了 5%”但回测资金曲线却没有改善。原因RMSE 衡量的是预测值贴近真实值的程度但在交易里只要方向判断正确幅度偏差可以被仓位管理对冲掉。有些模型为了降低 RMSE倾向输出接近均值的预测值导致方向准确率反而更低。解决两个指标都要报。如果 RMSE 改善但方向准确率持平说明模型的优势主要体现在幅度估计上对实际交易决策没有本质帮助。对比结论要以方向准确率为最终导向再配合 RMSE 做综合判断。6. 跨周期验证判断这个项目值不值得做的最后一关前五章跑下来你会得到一张对比表但先别急着下结论。单次时间切分的结果有偶然性真正能说明问题的做法是做跨周期验证把数据按不同的起点切分多次分别训练和测试看两个模型在不同周期的相对排名是否稳定。这个思路也叫 rolling-origin evaluation核心在于每次都把训练集起点后移测试集永远是训练集之后完全未见过的区间。# 用 5 个时间切分点做跨周期验证 results [] start_points [0.4, 0.5, 0.6, 0.7, 0.8] for sp in start_points: tr_end int(len(df) * sp) ts_end int(len(df) * min(sp 0.1, 1.0)) tr df.iloc[:tr_end].copy() ts df.iloc[tr_end:ts_end].copy() # 标准化后分别训练 ols 和 mlp记录指标 # ... results.append({切分点: sp, ols_dir: ols_dir, mlp_dir: mlp_dir})这段代码里每个切分点生成一个训练集和紧随其后的测试集测试集的长度大约是训练集的四分之一。如果五个切分点里MLP 的方向准确率有四个都高于线性回归结论才算初步稳定如果两个模型各有输赢说明数据里没有足够稳定的非线性信号这个项目投入再多也不值得。我的习惯是遇到这类对比研究永远先跑线性回归再用神经网络去追。神经网络追不上线性回归时我会先怀疑特征没做对而不是拼命调网络结构。很多时候黄金期货的价格预测项目做到最后会发现两个模型的准确率差距就落在 51% 到 55% 之间跑几十次网格搜索也只是把这几个百分点的排名来回倒腾。如果你的目标不是写论文而是判断方法值不值得用那我建议把精力更多放在换月跳空处理、特征构造和时间切分的严谨性上这些环节犯的错比模型选型犯的错致命十倍。希望这篇笔记帮你少走几趟弯路按照这套流程把项目跑通之后再回头看那些公开研究里动不动就说“准确率大幅提升”的结论你会更能分辨是真本事还是评估口径上的水分。本文还有配套的精品资源点击获取
返回列表