资讯动态

电价预测新视角:ARIMA结合置信区间的Matlab实战解析

发布时间:2026/10/20 6:44:05 来源:尧图企业网站定制
如果你也在做电力现货市场的价格预测大概率见过这种场景模型误差指标跑出来很漂亮可一遇到尖峰或者节假日预测和实际价格能差出一大截。我近几年在项目里用得最顺手的组合反而是ARIMA加置信区间。很多人觉得ARIMA太老但配合置信区间做电价预测在Matlab里实现成本非常低输出也直观尤其适合快速给出报价参考的场景。这篇文章会把整套东西拆开讲怎么判断电价序列能不能用ARIMA怎么定阶、估计、诊断怎么用forecast把置信区间算出来最后附上一个完整的复盘案例。1. 先看清电价序列的底牌非平稳、尖峰和多重周期1.1 电价序列的三大怪均值突变、波动聚集、负价格电力现货市场的出清价格本质是供需实时平衡的结果。这个特性决定了它和普通经济序列完全不同一天内峰谷价差可以拉出好几倍极端天气下可能出现远超均值的尖峰新能源出力大的时段甚至会出现负价格。ARIMA模型的前提是序列弱平稳也就是说均值、方差在时间上保持稳定自相关结构只依赖滞后阶数。可电价序列天生不满足这些条件。均值会随着负荷水平、机组检修、气象条件来回漂移方差在供需紧张时段会突然放大形成明显的波动聚集负价格也不是数据错误而是现货市场真实存在的出清结果不能随手剔除。所以拿到电价数据的第一件事不是直接套arima函数而是先判断序列的“乖戾程度”。我习惯把原始序列画出来看一眼趋势和极端值分布再进入平稳性检验。1.2 平稳性检查adftest和差分该怎么看Matlab的Econometrics Toolbox里自带adftest可以直接做ADF单位根检验。这里有个容易踩的坑ADF检验的默认设定包含常数项和趋势项电价序列往往没有确定性线性趋势所以我会先看数据形态如果只是围绕某个均值波动用默认设定就行。% 假设数据是两列时间戳、价格 data readtable(spot_demo.csv); prices data.price; h1 adftest(prices); % h1 0 表示无法拒绝单位根 dp1 diff(prices); % 一阶差分 h2 adftest(dp1); % 检验差分后是否平稳 fprintf(原始序列ADF检验h值%d\n, h1); fprintf(一阶差分后ADF检验h值%d\n, h2);如果h10、h21说明原始序列非平稳一阶差分后平稳那么d1就是合理选择。大部分电价序列走到这一步就够用了不需要d2。过度差分会把真实的信息结构抹掉导致后面模型参数难解释、预测区间异常变宽。除了ADF还可以用kpss做辅助验证。ADF的原假设是存在单位根kpss的原假设是序列平稳两个检验交叉看比单看一个更稳。1.3 数据里藏着的“伪周期”与离群值电价序列还有一个特征强周期性。现货市场按小时出清就会形成24小时周期按15分钟出清就是96点周期工作日和周末负荷结构不同又会形成周周期。这种周期性会直接体现在自相关函数上。普通ARIMA处理非季节性周期很吃力因为它假设相关结构随着滞后阶数平滑衰减而电价序列的ACF会在24、48、96这些滞后位置出现明显峰值。说白了普通ARIMA只能捕捉到“昨天的价格影响今天”这种短记忆很难自动学会“24小时前的价格影响此刻的价格”。离群值的问题更微妙。尖峰价格确实偏离常规但它是真实交易信号不能像处理传感器坏点那样直接替换。我通常只对超过某个分位数比如99.5%分位的极端值做封顶处理用来防止模型参数被个别点带偏但心里要清楚真实预测时尖峰依然会出现封顶只是为了参数稳定不是要把市场特征抹掉。2. 用Matlab完成ARIMA的定阶、估计与残差诊断2.1 信息准则自动定阶一个小网格就够了定阶是ARIMA建模最让人纠结的一步。我的做法很简单不要靠眼看ACF图硬猜直接跑一个小网格用AIC和BIC选。maxP 4; maxQ 4; results []; id 0; for p 0:maxP for q 0:maxQ try Mdl arima(p, 1, q); [EstMdl, ~, LogL] estimate(Mdl, prices, Display, off); [aic, bic] aicbic(LogL, p q 1, length(prices)); id id 1; results(id, :) [p, q, aic, bic]; catch continue; end end end [~, idx] min(results(:, 3)); pBest results(idx, 1); qBest results(idx, 2); fprintf(最优模型ARIMA(%d,1,%d)AIC%.2f\n, pBest, qBest, results(idx, 3));这里有个容易写错的地方aicbic的第二个参数是模型参数个数。ARIMA(p,1,q)如果包含常数项参数个数是pq1如果不包含常数项才是pq。电价序列一般均值不为零我会保留常数项所以按pq1计算。如果AIC最小的模型参数特别多我倾向再看BIC因为它对参数数量惩罚更重选出来的模型通常更简洁、更抗过拟合。网格范围不用太大p和q在0到4之间已经覆盖了绝大多数电价场景。再大的阶数容易估计失败或者拟合出对样本极度敏感的参数。2.2 自相关图与偏自相关图把自动结果再“人眼确认”一遍自动定阶不是终点。选出一个候选模型后我会把差分序列的ACF和PACF图调出来做一次人工复核。figure; autocorr(dp1); title(一阶差分序列的自相关图); figure; parcorr(dp1); title(一阶差分序列的偏自相关图);如果PACF在滞后1阶处显著后面截尾说明适合加AR(1)如果ACF在滞后1阶处显著后面拖尾说明适合加MA(1)。电网数据里最常见的组合是p1或2、q1因为实时出清价格受上一小时出清价、系统供需信号等共同影响既有一阶自回归成分也有一阶滑动平均成分。如果ACF在滞后24处依然显著那就说明模型需要考虑更复杂的季节结构。这时候普通ARIMA已经不太够用我会在第5章展开讲怎么办。2.3 残差诊断模型“合格证”怎么开模型估计完之后最关键的一步是看残差。如果残差里还残留显著自相关说明模型没把信息提取干净后面算出来的置信区间也会失真。res infer(EstMdl, prices); [hLBQ, pLBQ] lbqtest(res, lags, [10 20 36]); disp(pLBQ);Ljung-Box检验的原假设是残差无自相关。p值大于0.05就说明过关。我习惯同时看滞后10、20、36三档因为电价序列的周期相关性可能出现在长滞后处。如果滞后36的p值开始变小就要警惕周期性残留。另外要看残差的自相关图。如果某几个滞后点的相关值超出了蓝色置信带就说明模型阶数可能还不够。此时我会回到网格循环里把p或q各加一档重新跑而不是直接改一个参数碰运气。这里也要说清楚残差不服从正态分布在电价预测里是常态厚尾、尖峰都很正常。Ljung-Box检验的是自相关不是正态性别被“残差必须是白噪声且正态”的教科书说法吓到。3. 置信区间的计算逻辑与Matlab实现3.1 预测区间到底在量化什么不确定性点预测给的是一个数比如“明天上午10点电价是320元/MWh”。但实际报价决策需要的是一段范围“有95%的信心价格落在280到360之间”。后者就是预测区间。ARIMA的预测误差方差会随预测步长增加而增大所以预测区间的宽度会一天比一天宽。这符合直觉明天上午还勉强能猜后天下午的不确定性就大多了。置信区间本质上是在告诉你模型对每个预测点的把握程度。很多人只盯着预测均值是否准确忽略了区间覆盖是否合理。实际上区间预测才是体现模型质量的地方——如果一个95%区间长期只有70%的实际值落进来说明模型严重低估了不确定性直接拿去报价是会出事的。3.2 forecast函数的区间输出别把上下界搞反Matlab里算置信区间特别简单用forecast函数传模型和历史数据它会把预测均值和区间一起给你。numSteps 24; [Yfor, YCI] forecast(EstMdl, numSteps, Y0, prices); % YCI默认是95%置信区间第一列下界第二列上界 lower95 YCI(:, 1); upper95 YCI(:, 2); % 如果需要90%置信区间 [~, YCI90] forecast(EstMdl, numSteps, Y0, prices, ConfidenceLevel, 0.90);这里有个特别容易错的地方有些教程会教你先手动差分、建模、预测再把差分还原。其实完全没必要arima模型在estimate阶段指定了D1之后forecast会自己处理差分的积分还原直接传原始序列当Y0拿回来的预测值和区间就已经是价格原始尺度了。手动差分反而容易把置信区间算错一个量级。画图的时候可以用fill做一个阴影区域比只画两条虚线直观得多futureIdx (length(prices) 1) : (length(prices) numSteps); figure; plot(1:length(prices), prices, k-, LineWidth, 1); hold on; plot(futureIdx, Yfor, b-o, LineWidth, 1.2); fill([futureIdx fliplr(futureIdx)], [lower95 fliplr(upper95)], ... [0.85 0.9 0.95], EdgeColor, none); xlabel(采样点); ylabel(价格元/MWh); legend(历史实际, 预测均值, 95%置信区间, Location, best);如果你自己手写预测区间原理上要递归计算预测误差方差ARIMA的公式会随着步长逐期累加还要涉及状态空间表示和参数协方差矩阵。Matlab内部已经做了这件事日常使用我不建议自己重写但至少要理解区间宽度的递增逻辑这样才能解释为什么预测第24小时总比第1小时宽。3.3 区间覆盖率一个比MAE更值得盯的指标区间覆盖率是检验预测区间质量的核心指标。定义很简单实际价格落在预测区间内的比例。coverage mean(pricesTest lowerTest pricesTest upperTest);如果名义置信水平是95%那覆盖率长期应接近95%。偏低说明区间过窄模型低估了不确定性偏高太多说明区间过宽模型过于保守报价时失去参考意义。我的经验是电价预测里90%名义置信水平覆盖率达到85%以上已经算可用。因为尖峰价格天然难以预测再好的模型也无法保证每一个极端点都落在区间内。如果你发现覆盖率长期低于80%首先要怀疑的不是区间本身而是残差方差被低估了这时候可以尝试对残差做GARCH建模把波动聚集纳入进来。4. 一次完整复盘某现货市场日前价格预测的36小时验证4.1 数据准备和模型筛选我拿一份脱敏的某区域现货市场出清价格做演示每天24个点一共90天前88天做训练后2天做测试。数据的新鲜度、市场结构、机组组合等细节都做了脱敏处理但序列的统计特征和真实现货市场非常接近。先把数据读进来做ADF检验确认一阶差分平稳。然后跑2.1节的网格循环得到的结果是ARIMA(2,1,1)的AIC最小BIC排名也靠前。这个结果不意外很多现货市场价格序列都是这个结构短时自回归加一阶滑动平均。这里提醒一句不要在测试集上反复调整阶数。定阶只能基于训练集测完了发现效果不好再改模型本质上就是在拿未来信息拟合过去最后报告的指标全是虚的。4.2 次日24点预测的实测表现我让模型预测测试日当天的24个小时一次性给出未来24点而不是滚动更新。这样最贴近实际业务中“每天下午出次日96点报价”的场景。演示数据上的结果比较典型RMSE大概在11%左右MAE在9%左右95%置信区间的覆盖率约88%。从画出来的曲线看预测均值能跟上价格的整体爬坡趋势但在早晚高峰切换的拐点处有明显滞后。区间宽度在凌晨低谷时段较窄在早高峰和晚高峰时段明显变宽。这说明区间宽度本身就在反映市场不确定性而不是一个固定加减值。如果你在业务里只给客户一个固定误差带比如“统一下浮5%”那大概率会在高峰期严重低估风险。4.3 区间过宽或过窄的两难我同时跑了另一个模型ARIMA(1,1,0)参数更简单AIC稍微高一点。结果很有意思AR(1)模型的预测区间比ARIMA(2,1,1)窄了将近20%看起来更“自信”但实际覆盖率只有70%也就是说很多实际价格点跑到了区间外面。窄区间看着舒服但给决策者传递了错误的安全感。反过来某些带高阶项的模型区间宽得离谱覆盖率接近100%但这种区间没有任何信息量等于告诉别人“价格在哪都可能”。所以评估的时候永远要把MAE和覆盖率放在一起看。只有误差小、覆盖率又接近名义水平这个模型才算真正合格。单看任何一个指标都可能被模型的表现骗过去。5. 电价预测的补丁工程周期性、节假日和波动聚集5.1 把节假日和极端时段“告诉”模型ARIMAX思路普通ARIMA只能看到价格自身的历史轨迹看不到外部结构变化的信号。节假日就是最典型的例子工作日和节假日的负荷曲线完全不同价格形态也会整体平移。如果不处理模型会在节假日后第一天出现明显偏差。一个简单的补丁是用ARIMAX也就是在ARIMA的基础上加外生回归变量。Matlab的arima模型支持这个功能直接在模型里指定X就好% dummyHoliday是节假日哑变量1表示节假日0表示非节假日 Mdl arima(2, 1, 1); Mdl.X dummyHoliday(1:end-1); % 注意外生变量长度要和估计样本对齐 EstMdl estimate(Mdl, prices(2:end), Display, off);如果要做含X的预测则要提供未来时段的外生变量值。节假日哑变量通常能从日历准确预知所以这个信息的可获得性很高。除了节假日还可以把峰谷时段、尖峰负荷指标作为外生变量放进去往往能显著改善拐点处的预测。5.2 波动聚集残差异方差怎么处理电价序列的残差几乎都存在条件异方差简单说就是大波动后面跟着大波动小波动后面跟着小波动。如果你做完残差诊断后发现Ljung-Box检验过关了但残差平方序列的自相关还很显著那就要考虑波动聚集了。Matlab里可以直接用archtest对残差做ARCH效应检验。[archH, archP] archtest(res);如果检验结果显示存在ARCH效应理论上可以在ARIMA均值方程之外再建一个GARCH方差方程这样预测均值用ARIMA预测区间用GARCH给出的动态方差区间宽度就能跟着市场情绪变宽变窄。不过这一步已经超出本文“基于ARIMA”的边界我只是提醒你当你发现固定置信区间长期失准时方差建模是下一步该走的方向。5.3 滚动重估模型的保鲜术电价市场结构不是一成不变的。机组检修、新能源装机、市场规则调整都会让历史数据的解释力随时间衰减。我强烈建议不要用一次性估计的模型去预测未来几个月而是采用滚动重估策略。我的做法是以天为周期每天用最近60到90天的数据重新估计一遍模型参数然后预测次日24点。代码骨架大概长这样horizon 24; rollingWin 90 * 24; testDays 5; for d 1:testDays trainIdx 1 : (rollingWin (d - 1) * horizon); testIdx (rollingWin (d - 1) * horizon 1) : (rollingWin d * horizon); Mdl arima(pBest, 1, qBest); EstMdl estimate(Mdl, prices(trainIdx), Display, off); [Yfor, YCI] forecast(EstMdl, horizon, Y0, prices(trainIdx)); % 记录预测和区间计算指标 end滚动窗口的长度是超参数。窗口太短参数估计方差大窗口太长模型响应市场结构变化太慢。我在实际使用中通常是60到90天之间调气温剧烈变化的季节可以适当缩短。最后说一个我自己的习惯每次给一个新市场建模型我会专门把历史极端日和节假日附近的子集拎出来单独回测一次。模型在普通日子的表现通常很接近但在极端时段的分化极其明显。这个小流程救过我很多次也帮我筛掉过不少看起来AIC很低、一到关键日子就翻车的模型。如果你刚开始接触电价预测也别一上来就折腾深度学习先把ARIMA加置信区间这套古典组合跑通它能帮你建立对序列和不确定性的手感比任何花架子都重要。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑