1. 项目背景与核心价值
电价预测在能源交易和电网调度中扮演着关键角色。传统时间序列分析方法中,ARIMA(自回归积分滑动平均)模型因其对非平稳序列的良好处理能力,成为电力市场分析的经典工具。这个项目实现了从数据预处理到预测结果可视化的完整流程,特别加入了置信区间计算功能,为决策者提供了风险量化参考。
在电力市场自由化背景下,发电企业需要提前24小时提交发电计划,准确的日前电价预测直接影响企业收益。我曾在某区域电力交易中心实施过类似系统,实测表明ARIMA模型在无明显政策干预时段,预测误差可控制在5%以内。
2. 数据准备与预处理
2.1 典型电价数据特征
电力现货价格具有三个显著特征:
- 日内周期性:每天24小时呈现固定波动模式
- 周周期性:工作日与周末呈现不同价格水平
- 异方差性:价格波动幅度随时间变化
% 导入示例数据(假设为48周的小时级数据) data = readtable('electricity_price.csv'); price = data.Price; timestamp = datetime(data.Year, data.Month, data.Day, data.Hour, 0, 0);2.2 数据清洗关键步骤
处理电价数据时需要特别注意:
- 异常值处理:采用3σ原则结合电力市场规则(如价格上限)
- 缺失值填补:优先使用同期历史均值,避免简单线性插值
- 多重季节性分解:使用MSTL分解日周期和周周期
% 异常值检测与处理 mean_price = mean(price); std_price = std(price); price(price > mean_price + 3*std_price) = mean_price + 3*std_price; price(price < mean_price - 3*std_price) = mean_price - 3*std_price; % 季节性分解示例 [decomp, ~] = mstl(price, 'Seasonal', [24, 24*7]); trend = decomp(:,1); seasonal = sum(decomp(:,2:end), 2); residual = price - trend - seasonal;重要提示:电价数据节假日效应显著,建议单独建立节假日模型或引入虚拟变量
3. ARIMA模型构建
3.1 平稳性检验与差分
电价序列通常需要1-2阶差分达到平稳。使用ADF检验时,建议设置足够大的滞后阶数(如24小时):
% ADF检验示例 [h, pValue] = adftest(price, 'Model', 'TS', 'Lags', 24); % 自动确定差分阶数 d = 0; while ~h && d < 3 d = d + 1; diffPrice = diff(price, d); [h, pValue] = adftest(diffPrice, 'Model', 'TS', 'Lags', 24); end3.2 参数识别技巧
ACF和PACF图分析中需注意:
- ACF拖尾且PACF截尾 → AR模型
- ACF截尾且PACF拖尾 → MA模型
- 两者都拖尾 → ARMA模型
% 自相关分析 figure subplot(2,1,1) autocorr(diffPrice, 'NumLags', 48) subplot(2,1,2) parcorr(diffPrice, 'NumLags', 48) % 自动定阶(需Econometrics Toolbox) model = arima('Constant', 0); [~, ~, ~, bestMdl] = armaxfilter(diffPrice, 1:3, 1:3);4. 模型训练与验证
4.1 样本划分策略
建议采用滚动时间窗方式:
- 训练集:连续8周数据
- 测试集:紧随其后的1周数据
- 验证集:历史不同季节数据
trainRatio = 0.8; n = length(price); trainEnd = floor(trainRatio * n); trainData = price(1:trainEnd); testData = price(trainEnd+1:end); % 模型拟合 mdl = arima(2,1,2); estMdl = estimate(mdl, trainData);4.2 评估指标选择
除常规RMSE外,电力行业特别关注:
- MAPE(平均绝对百分比误差):<10%为优秀
- DA(方向精度):预测涨跌正确率
- MASE(平均绝对缩放误差):对比基准模型
[Y, YMSE] = forecast(estMdl, 24, 'Y0', trainData); lower = Y - 1.96*sqrt(YMSE); upper = Y + 1.96*sqrt(YMSE); % 计算DA actualChange = sign(diff(testData(1:24))); predChange = sign(diff(Y)); DA = sum(actualChange == predChange)/23;5. 置信区间计算
5.1 传统方法局限
直接使用ARIMA预测方差会低估真实风险,因为:
- 忽略参数估计不确定性
- 未考虑残差分布非正态性
- 未包含模型选择不确定性
5.2 改进方案实现
采用Bootstrap方法计算更可靠的置信区间:
numPaths = 1000; bootY = zeros(24, numPaths); res = infer(estMdl, trainData); for i = 1:numPaths bootRes = res(randi(length(res), length(res), 1)); bootY(:,i) = simulate(estMdl, 24, 'Y0', trainData, 'E0', bootRes); end bootLower = quantile(bootY, 0.025, 2); bootUpper = quantile(bootY, 0.975, 2);6. 完整实现代码
%% 完整电价预测流程 % 步骤1:数据准备 data = readtable('electricity_price.csv'); price = data.Price; % 步骤2:数据预处理 price = filloutliers(price, 'clip', 'mean', 'ThresholdFactor', 3); price = fillmissing(price, 'movmean', 24); % 步骤3:模型识别与拟合 d = 1; % 通过ADF测试确定 mdl = arima(2,d,2); estMdl = estimate(mdl, price(1:end-24)); % 步骤4:预测与区间计算 [Y, YMSE] = forecast(estMdl, 24, 'Y0', price(1:end-24)); lower = Y - 1.96*sqrt(YMSE); upper = Y + 1.96*sqrt(YMSE); % 步骤5:Bootstrap区间 numPaths = 1000; bootY = zeros(24, numPaths); res = infer(estMdl, price(1:end-24)); for i = 1:numPaths bootRes = res(randi(length(res), length(res), 1)); bootY(:,i) = simulate(estMdl, 24, 'Y0', price(1:end-24), 'E0', bootRes); end bootCI = quantile(bootY, [0.025 0.975], 2); % 步骤6:可视化 figure plot(price) hold on h1 = plot(length(price)-23:length(price), Y, 'r', 'LineWidth', 2); h2 = plot(length(price)-23:length(price), lower, 'g--'); plot(length(price)-23:length(price), upper, 'g--'); h3 = plot(length(price)-23:length(price), bootCI(:,1), 'm-.'); plot(length(price)-23:length(price), bootCI(:,2), 'm-.'); legend([h1 h2 h3], {'预测值','正态区间','Bootstrap区间'})7. 实战经验与优化方向
季节ARIMA改进:对于明显双重季节性的电价,考虑SARIMA(p,d,q)(P1,D1,Q1)24(P2,D2,Q2)168模型
混合模型策略:
- 工作日/周末分别建模
- 高峰/低谷时段分别建模
- 极端价格事件单独处理
参数优化技巧:
- 使用贝叶斯优化替代网格搜索
- 目标函数加入经济成本考量
- 滚动回测验证参数稳定性
生产环境注意事项:
- 每日模型重新训练(增量更新)
- 建立预测异常报警机制
- 保留多版本模型结果对比
实际部署中发现,在电力市场改革政策发布后的1-2周内,所有统计模型预测效果都会显著下降。这时需要引入事件虚拟变量或临时切换到人工调整模式。