电工杯 2020 B 范文:光伏建筑一体化(BIPV)板块指数的数据清洗、联动刻画与趋势预测(确定性合成 · 纯标准库)
一、摘要
针对 2020 年电工杯 B 题「光伏建筑一体化板块指数发展趋势分析及预测」,本文给出一条「数据清洗 → 板块联动刻画 → 趋势与波动率分析 → 双方法预测对比 → 情景规划」的确定性分析主线。基于固定随机种子合成的 5 个光伏产业链板块(BIPV、硅料、组件、逆变器、电站运营)共 750 个交易日指数面板,先对 BIPV 序列做缺失(22 个)与异常跳变(检出 10 个)的滚动中位数—MAD 检测与线性插值修复;进而用 Pearson 相关系数矩阵刻画联动结构,发现 BIPV 与**组件(0.960)、逆变器(0.940)**联动最强,与上游硅料(0.806)、电站运营(0.790)次之,且组件—逆变器两两相关高达 0.989,印证「设备链一体化」的产业特征。趋势层面,BIPV 全样本年化波动率 21.07%、最大回撤 −41.33%、滚动 30 日年化波动率介于 14.24%–28.83%。预测环节以 Holt 双参数指数平滑(统计法)与带 L2 正则的监督岭回归 AR(3)(机器学习法)做滚动 1 步伪样本外对比,二者 MAPE 分别为 1.13% 与 1.02%,均表现优异、机器学习法略优;基于全样本 Holt 的 30 日规划显示基准点位 1148.5(较当前 +6.10%),政策利好与硅料涨价情景分别为 1169.4(+8.03%)与 1131.4(+4.52%)。全文零依赖、确定性、可复现,正文、配图、附录与真源四路数字完全一致。
二、问题重述
赛题以 BIPV 及相关产业链板块的资本市场指数为对象,要求完成五个递进任务:其一,对板块指数序列做缺失值、异常值的识别与清洗,并说明处理逻辑;其二,刻画 BIPV 板块与上下游(硅料、组件、逆变器、电站运营等)板块的相关性与联动强弱;其三,刻画发展趋势(阶段性涨跌、波动率、回撤、动量);其四,建立预测模型,给出短中期走势预测与置信区间,并对比至少一种统计与一种机器学习方法;其五,讨论外部冲击(政策、原料价格、装机量)下的情景差异。本文把这五问组织为同一条主线:清洗提供可信数据底座,联动刻画回答「谁影响谁」,趋势/波动率回答「现在处在什么状态」,预测与情景回答「未来会怎样、不确定性多大」。
三、模型假设与符号
- H1(确定性合成):官方原题数据未公开可下载,本文采用固定随机种子的确定性合成面板(5 板块、750 日、真实量级与产业联动结构),仅用于跑通方法;正式参赛须替换为官方数据并做样本外回测。
- H2(联动由共同市场因子驱动):各板块日收益由共同市场因子(产业链系统性风险)与特异噪声叠加,板块间相关主要来自共同因子,故相关系数矩阵应呈「产业链内高、跨链低」的结构。
- H3(缺失与异常可检测):缺失表现为序列断点,异常表现为相对邻域的中断式大幅跳变(如单日 ±10% 以上的非连续跳变),可用稳健的滚动中位数—MAD 准则识别。
- H4(短程可预测性来自惯性):指数短期运动含趋势与均值回复成分,可用平滑/自回归类方法做 1 步预测;更长周期受外生冲击主导,预测仅作区间而非单点。
主要符号:第 板块第 日指数水平 ,日对数收益 ; 为板块 的 Pearson 相关系数; 为年化波动率; 为最大回撤; 为平均绝对百分比误差。
四、数据清洗(问题 1 核心)
金融指数序列常因停牌、除权、录入错误出现缺失与异常。本文在 BIPV 序列上注入 22 个缺失(约 3%)与 10 个单日大幅跳变,模拟真实脏数据,再用两步法修复:
异常检测:以窗口 的滚动中位数 与中位数绝对偏差 为稳健位置/尺度估计,当
时判为异常。该准则对正态噪声稳健(误报率低),又能抓住数倍尺度之外的跳变。
缺失与异常修复:将判出的异常点先置为缺失,再对整条含缺口序列做线性插值(端点用最近邻外推)。线性插值假设缺口两侧趋势连续,符合指数「平滑演进」的特性,且不会引入人为的阶跃或均值漂移。
经此处理,22 个缺失与 10 个异常均被定位并修复,BIPV 序列恢复为连续的 750 点清洗序列,作为后续全部分析的统一输入。
图1 叠加展示原始序列(红色异常点清晰可见)、清洗后序列、以及 30 日/60 日移动平均(MA30/MA60,虚线)。MA 平滑了日频噪声,揭示出指数并非单调上行,而是伴随多次回撤与反弹的「带漂移的随机游走」——这正是后续波动率为正、回撤显著的根源。
五、板块联动刻画(问题 2)
以清洗后的 5 板块序列计算 Pearson 相关矩阵(图2),BIPV 所在行(列)结果为:
| 板块对 | 相关系数 |
|---|---|
| BIPV — 组件 | 0.960 |
| BIPV — 逆变器 | 0.940 |
| BIPV — 硅料 | 0.806 |
| BIPV — 电站运营 | 0.790 |
| 组件 — 逆变器 | 0.989 |
完整的 矩阵(行/列顺序:BIPV、硅料、组件、逆变器、电站运营)为:
| BIPV | 硅料 | 组件 | 逆变器 | 电站运营 | |
|---|---|---|---|---|---|
| BIPV | 1.000 | 0.806 | 0.960 | 0.940 | 0.790 |
| 硅料 | 0.806 | 1.000 | 0.904 | 0.929 | 0.658 |
| 组件 | 0.960 | 0.904 | 1.000 | 0.989 | 0.793 |
| 逆变器 | 0.940 | 0.929 | 0.989 | 1.000 | 0.760 |
| 电站运营 | 0.790 | 0.658 | 0.793 | 0.760 | 1.000 |
图2 以热力图直观呈现联动结构。两点结论:第一,BIPV 与组件、逆变器(光伏设备链)联动最强(0.94–0.96),说明 BIPV 行情高度同步于「造组件、做逆变器」的装备环节——这与「建筑一体化本质是光伏设备的下游应用」的产业逻辑一致;第二,组件—逆变器两两相关高达 0.989,几乎同涨同跌,是产业链内耦合最紧的一对,分析时应警惕二者作为「双变量」实则近乎同一因子的风险(共线),不宜在联动回归中同时作为独立解释变量。相对而言,上游硅料(原材料)与下游电站运营(运营端)与 BIPV 的联动偏弱(0.79–0.81),提示其受各自独立因素(硅料价格周期、电价政策)扰动更大。
六、趋势与波动率刻画(问题 3)
预测之前先回答「当前处于什么状态」。对 BIPV 清洗序列计算:
- 移动平均:MA30/MA60(见图1)显示中长期趋势方向;
- 年化波动率: 21.07%,属中等偏高风险资产;
- 最大回撤: −41.33%,提示即便长期向上,途中也可能出现深幅回撤;
- 滚动 30 日年化波动率:刻画风险随时间的聚集与切换。
图3 显示滚动波动率并非恒定:全样本介于 14.24%–28.83% 之间,存在明显的「平静—动荡」交替(波动率聚集效应)。这意味着用全样本单一波动率估计未来风险会低估动荡期的尾部,规划时应以滚动窗口或情景分位数替代固定值。BIPV 起点 993.8、终点 1082.4,整体上行约 9%,但 −41% 的最大回撤提醒:趋势收益与路径风险必须分开陈述,不能仅用一条预测线掩盖不确定性。
七、预测模型对比与情景规划(问题 4)
本文对比两类方法:统计法 Holt 双参数指数平滑(捕捉水平 + 趋势)与机器学习法 带 L2 正则的监督岭回归 AR(3)(用前 3 期水平预测下期,闭式求解 )。评估采用滚动 1 步(扩张窗口)伪样本外协议:对测试区每一天 ,用 全部历史拟合,预测第 日,累计 MAPE。该协议避免了「单次 60 步外推」在趋势拐点处的运气成分,更能反映方法的稳健性。
| 方法 | 类型 | 滚动 1 步 MAPE |
|---|---|---|
| Holt 双参数指数平滑 | 统计 | 1.13% |
| 岭回归 AR(3)(L2 正则) | 机器学习 | 1.02% |
图4 取末 60 日,叠实际、Holt 1 步、岭回归 1 步与 Holt 的 95% 区间(±1.96σ,σ=15.29)。两法 1 步预测均紧贴实际,MAPE 同处 1% 量级、机器学习法略优——这是因为 1 步预测主要依赖近期水平惯性,两种方法都能较好地提取该惯性。差异在多步外推才显现:Holt 显式外推趋势分量,方向性强但在拐点易失真;岭回归 AR(3) 偏均值回复,外推更保守。因此实践建议:短期(1–5 日)二者皆可用,中期规划应以区间而非单点表达。
基于全样本 Holt 的 30 日规划(含趋势外推)给出情景:
| 情景 | 30 日点位 | 较当前(1082.4) |
|---|---|---|
| 基准(趋势延续) | 1148.5 | +6.10% |
| 政策利好(日漂移 +0.06%) | 1169.4 | +8.03% |
| 硅料涨价(日漂移 −0.05%) | 1131.4 | +4.52% |
三种情景点位差距约 ±1.5%–2%,说明在趋势惯性主导下,30 日尺度对温和外生冲击并不极度敏感;但若冲击引发趋势反转(如硅料持续紧缺、补贴退坡),单点预测的偏差将被放大,这正是区间与情景并重的意义。
八、模型验证(四路一致)
做三处交叉验证:①将清洗后序列代回相关性函数,重新算得 BIPV—组件=0.960、BIPV—逆变器=0.940,与正文表格、图2 完全一致;②将附录真源脚本独立运行,输出 750 日、缺失 22、异常 10、年化波动率 21.07%、最大回撤 −41.33%、两法 MAPE 1.13%/1.02%、30 日基准 1148.5,与正文逐项吻合;③对 Holt 的 α、β 网格(α∈{0.2,…,0.7}、β∈{0.05,…,0.4})重选,最优仍为 α=0.70、β=0.05,且岭回归 λ 在 {1,5,10} 间变化时效能稳定(MAPE 波动 <0.1%),说明结论对超参数不敏感。三类校验均通过,正文—配图—附录—真源四路数字一致。
九、结论
本文以「清洗 → 联动 → 趋势/波动 → 预测对比 → 情景」为主线,对 BIPV 板块指数做了可复现的系统分析。核心结论:其一,板块联动呈清晰产业链结构,BIPV 与组件、逆变器(设备链)最强(0.94–0.96),而组件—逆变器近乎共线(0.989),建模时须防多重共线性;其二,BIPV 年化波动率 21.07%、最大回撤 −41.33%、滚动波动率 14%–29% 聚集成簇,趋势与风险须分开陈述;其三,Holt 与岭回归在 1 步预测上均达 ~1% MAPE,但中期应以区间/情景表达不确定性;其四,30 日规划点位约 1148(基准)、1169(利好)、1131(涨价),对外生冲击呈温和敏感。全文零依赖、确定性合成,为真实参赛数据下的同类分析提供了一条清晰、可解释的工程化范式。
参考文献
[1] 2020 年电工杯数学建模竞赛 B 题:光伏建筑一体化板块指数发展趋势分析及预测(赛题原文,官方附件未公开)。
[2] Box G E P, Jenkins G M, Reinsel G C. Time Series Analysis: Forecasting and Control[M]. Wiley.(指数平滑与 ARIMA 经典)
[3] Hoerl A E, Kennard R W. Ridge Regression: Biased Estimation for Nonorthogonal Problems[J]. Technometrics, 1970.(岭回归 L2 正则)
[4] 中国光伏行业协会. 中国光伏产业发展路线图[R].(产业链结构参考)
附录:核心 Python 实现(可独立运行复现上述数字)
import tools.gen_dgcup2020b as G # 真源:tools/gen_dgcup2020b.py(零依赖、确定性、种子 20200418)
D = G.gen_dgcup2020b()
print("样本 %d 日 | 缺失 %d | 异常 %d" % (len(D["cleaned"]), D["n_miss"], D["n_anom"]))
print("相关系数 BIPV-组件=%.3f BIPV-逆变器=%.3f" % (D["corr"][0][2], D["corr"][0][3]))
print("年化波动率=%.2f%% 最大回撤=%.2f%%" % (D["ann_vol"]*100, D["mdd"]*100))
print("滚动1步 Holt MAPE=%.2f%% 岭回归 MAPE=%.2f%%" % (D["holt_mape"], D["ridge_mape"]))
print("30日: 基准=%.1f 利好=%.1f 涨价=%.1f" % (D["end_base"], D["end_bull"], D["end_bear"]))
# —— 关键算法节选(与真源一致)——
# 1) 异常检测:滚动中位数 + MAD
def detect(xs, w=5, k=3.0):
flags = set()
for i in range(len(xs)):
if xs[i] is None:
continue
seg = [xs[j] for j in range(max(0,i-w), min(len(xs),i+w+1)) if xs[j] is not None and j!=i]
med = sorted(seg)[len(seg)//2]; mad = sorted(abs(v-med) for v in seg)[len(seg)//2] or 1e-9
if abs(xs[i]-med) > k*1.4826*mad and abs(xs[i]-med)/med > 0.05:
flags.add(i)
return flags
# 2) Holt 双参数递推(返回末水平 l、趋势 b、残差标准差 sigma)
def holt_train(y, alpha, beta):
l, b = y[0], y[1]-y[0]; resid = []
for t in range(1, len(y)):
resid.append(y[t]-(l+b)); prev = l
l = alpha*y[t] + (1-alpha)*(l+b); b = beta*(l-prev) + (1-beta)*b
sigma = (sum(r*r for r in resid)/len(resid))**0.5
return l, b, sigma
# 3) 岭回归闭式解(高斯消元,无第三方依赖)
def mat_solve(A, b):
n = len(A); M = [row[:]+[b[i]] for i,row in enumerate(A)]
for c in range(n):
p = max(range(c,n), key=lambda r: abs(M[r][c])); M[c], M[p] = M[p], M[c]
d = M[c][c]; M[c] = [v/d for v in M[c]]
for r in range(n):
if r!=c and M[r][c]:
f = M[r][c]; M[r] = [M[r][k]-f*M[c][k] for k in range(n+1)]
return [M[i][n] for i in range(n)]
def ridge_fit(X, y, lam=5.0):
nf = len(X[0]); XtX = [[0.0]*nf for _ in range(nf)]; e = [0.0]*nf
for s in range(len(X)):
for i in range(nf):
e[i] += X[s][i]*y[s]
for j in range(nf): XtX[i][j] += X[s][i]*X[s][j]
for i in range(nf): XtX[i][i] += lam
return mat_solve(XtX, e)