尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

MATLAB实现Realized GARCH:从模型原理到参数估计全流程

MATLAB实现Realized GARCH:从模型原理到参数估计全流程 简介本资源是一套面向金融工程与量化分析学习者的Realized GARCH模型MATLAB实现代码专为掌握高频波动率建模与GARCH扩展方法的研究者和高年级本科生设计解决传统GARCH模型难以融合日内信息、波动率预测精度不足的核心问题。压缩包共含4个MATLAB脚本文件.m总大小仅2KB结构精炼主程序统筹流程其余函数分别承担数据预处理、Realized GARCH参数估计及模型诊断功能完整覆盖从高频波动率计算如已实现极差或核估计、最大似然优化到残差检验的全流程。目前已有230人学习下载代码高度模块化、注释清晰可直接运行复现Hansen Lunde2005提出的Realized GARCH框架配套逻辑与金融工具箱调用方式便于理解模型内核是深入掌握波动率建模实战能力的优质入门范例。1. Realized GARCH为什么值得动手写一遍先说结论如果你只是想在作业里交一份能跑的GARCH模型代码那用MATLAB的Econometrics Toolbox直接调garch函数就够了。但如果你拿到的是一个hw.zip_MATLAB garch_Realized GARCH_garch_zip这类命名凌乱的压缩包里面大概率不只是让你跑一个传统GARCH而是涉及Realized GARCH——这时候思路就得换一换了。传统GARCH模型的核心思想是用收益率的平方或者绝对值来刻画波动率的时变特征。但这里有个先天短板日收益率平方对当天真实波动率的估计噪声非常大。举个直观的例子某天股票全天波动剧烈但收盘价刚好和前一天收盘价一样日收益率是0传统GARCH会认为这天毫无波动这显然和事实严重不符。上世纪90年代Andersen和Bollerslev等人提出用日内高频数据计算已实现波动率Realized Volatility来修正这个问题后来Hansen等人把已实现测度嵌入GARCH框架提出了Realized GARCH模型。Realized GARCH的核心改进在于它不再只依赖收益率这一个信息源而是把已实现波动率当作一个可观测的变量直接进入测量方程。模型由两部分组成——条件方差方程和测量方程。这样做的好处很明显波动率估计的精度大幅提升而且样本外预测表现往往优于传统GARCH。用一句话概括传统GARCH靠“猜”波动率Realized GARCH靠“看”波动率。这篇博文的受众很明确拿到作业压缩包、里面有一堆.m文件和.zip数据包、但不知道从哪下手的学生以及想在自己的量化研究里引入Realized GARCH但不想只停留在理论层面的从业者。我会从模型构建、数据清洗、MATLAB代码实现到结果解读完整走一遍最后把我踩过的坑也一并交代清楚。2. 模型框架拆解GARCH、已实现波动率与测量方程的关系2.1 传统GARCH到Realized GARCH的演进逻辑传统GARCH(1,1)的方差方程是[ \sigma_t^2 \omega \alpha \varepsilon_{t-1}^2 \beta \sigma_{t-1}^2 ]其中(\varepsilon_t \sigma_t z_t)(z_t)是独立同分布的标准化随机变量。这个模型的问题在于(\varepsilon_{t-1}^2)只是当日收益率平方这一个样本点用它来代表第(t-1)天的波动率噪声太大。换句话说GARCH模型把“波动率冲击”这一步建模得很粗糙。Realized GARCH的提出就是冲着这个痛点去的。它的方差方程变成[ \sigma_t^2 \omega \alpha \varepsilon_{t-1}^2 \beta \sigma_{t-1}^2 \gamma x_{t-1} ]其中(x_t)是第(t)天的已实现测度(x_t \sum_{i1}^{M} r_{t,i}^2)也就是把一天内M个等间隔收益率的平方全部加起来。这个测度比单一日收益率平方“靠谱”得多因为高频数据把一天内价格的完整路径都记录下来了。2.2 测量方程的作用Realized GARCH和传统GARCH最本质的区别是增加了一个测量方程[ \log x_t \xi \varphi \log \sigma_t^2 \tau(z_t) u_t ]这里(\log x_t)对(\log \sigma_t^2)做回归(\tau(z_t))用于捕捉当日标准化收益率与已实现测度之间的杠杆效应。通常取(\tau(z_t) \tau_1 z_t \tau_2 (z_t^2 - 1))(u_t)是均值为0的扰动项独立于(z_t)。为什么要加这个测量方程因为它把不可观测的条件波动率(\sigma_t^2)与可观测的已实现波动率(x_t)连接起来了。没有测量方程的话你只能靠收益率平方来反向推断(\sigma_t^2)信息量严重不足。有了测量方程模型就变成了一个状态空间结构估计出来的参数会更稳定似然函数也更精确。2.3 对数形式的处理技巧注意我上面写的是(\log x_t)和(\log \sigma_t^2)不是直接用水平值。原因很简单已实现波动率近似服从对数正态分布取对数之后更接近正态方便用极大似然估计。而且取对数能保证方差恒为正不需要额外加约束。作业压缩包里的数据如果已经算好了已实现波动率通常有两种存储形式一种是直接给(x_t)的水平值另一种是给了日内高频收益率序列。如果是后者你需要自己聚合。这个我在后面数据清洗部分会细说。3. MATLAB代码实现从数据清洗到参数估计的完整链路3.1 环境准备和文件解压拿到hw.zip这种压缩包第一步永远是解压、看目录结构、读README。MATLAB里解压压缩包可以用unzip函数也可以在MATLAB外部解压。我习惯用外部工具解压因为MATLAB内置的unzip有时候对中文文件名支持不友好。解压后通常包含以下几类文件数据文件.csv、.xlsx或.mat格式存着收益率序列和已实现波动率序列代码文件.m脚本或函数说明文档README、.docx或.pdf先把数据文件加载进来。假设数据是一个两列的表格第一列是日期第二列是已实现波动率同时还有一列日收益率。用readtable读入后先做基本的缺失值检查。如果某一行收益率或已实现波动率是NaN直接删除整行因为Realized GARCH的递归结构不允许中间断档。% 读取数据 data readtable(hw_data.csv); ret data.Return; % 日收益率序列 rv data.RealizedVol; % 已实现波动率 % 去掉缺失值 validIdx ~(isnan(ret) | isnan(rv)); ret ret(validIdx); rv rv(validIdx); % 对已实现波动率取对数 log_rv log(rv);3.2 模型参数化与目标函数定义Realized GARCH的估计方法首选极大似然估计。为什么不用OLS因为条件方差无法直接观测必须通过递归计算这本质上是一个非线性优化问题只能用数值优化方法。需要估计的参数有参数含义(\omega)方差方程常数项(\alpha)收益率平方的系数(\beta)滞后条件方差的系数(\gamma)已实现波动率的系数(\xi)测量方程截距(\varphi)测量方程中log条件方差系数(\tau_1, \tau_2)杠杆函数系数(\sigma_u^2)测量方程扰动项方差参数的约束条件(\alpha, \beta, \gamma \geq 0)(\alpha \beta 0.5\gamma 1)这是一个经验规则保证方差过程平稳。收敛性方面(\varphi)通常接近1但不一定等于1。目标函数是似然函数。假设(z_t)和(u_t)都服从正态分布那么对数似然函数为[ \ell -\frac{1}{2} \sum_{t1}^{T} \left[ \log(2\pi) \log(\sigma_t^2) \frac{\varepsilon_t^2}{\sigma_t^2} \log(2\pi) \log(\sigma_u^2) \frac{u_t^2}{\sigma_u^2} \right] ]注意(u_t \log x_t - \xi - \varphi \log \sigma_t^2 - \tau_1 z_t - \tau_2 (z_t^2 - 1))。3.3 核心估计代码我用fmincon做约束优化。MATLAB里fmincon的sqp算法对这类问题通常表现较好不涉及太多参数变换直接写约束条件就行。function [params_opt, logL] estimate_realized_garch(ret, rv, init_params) % 约束下界 lb [1e-6; 1e-6; 1e-6; 1e-6; -10; -10; -10; -10; 1e-6]; % 约束上界 ub [10; 10; 10; 10; 10; 10; 10; 10; 10]; % 线性不等式约束 A*params b % 条件alpha beta 0.5*gamma 1 A [0, 1, 1, 0.5, 0, 0, 0, 0, 0]; b 0.9999; options optimoptions(fmincon, Algorithm, sqp, ... Display, iter, MaxIterations, 2000, ... MaxFunctionEvaluations, 10000, OptimalityTolerance, 1e-8); problem createOptimProblem(fmincon, x0, init_params, ... objective, (params) neg_log_likelihood(params, ret, rv), ... lb, lb, ub, ub, Aineq, A, bineq, b, options, options); ms MultiStart(Display, off); [params_opt, fval, exitflag] run(ms, problem, 10); logL -fval; end function nll neg_log_likelihood(params, ret, rv) omega params(1); alpha params(2); beta params(3); gamma params(4); xi params(5); phi params(6); tau1 params(7); tau2 params(8); sig2_u params(9); T length(ret); sig2 zeros(T, 1); % 条件方差序列 log_lik 0; % 初始条件方差用样本方差 sig2(1) var(ret); for t 1:T % 计算标准化残差 z_t ret(t) / sqrt(sig2(t)); % 测量方程扰动 u_t log(rv(t)) - xi - phi * log(sig2(t)) - tau1 * z_t - tau2 * (z_t^2 - 1); % 累加对数似然 log_lik log_lik - 0.5 * log(2*pi) - 0.5 * log(sig2(t)) - 0.5 * z_t^2; log_lik log_lik - 0.5 * log(2*pi) - 0.5 * log(sig2_u) - 0.5 * u_t^2 / sig2_u; % 递归更新下一期条件方差 if t T sig2(t1) omega alpha * ret(t)^2 beta * sig2(t) gamma * rv(t); end end nll -log_lik; end这里有个优化上的坑条件方差的初始值会影响估计结果尤其是(\alpha \beta \gamma)接近1的时候初始值选不好会导致似然函数收敛到局部极值。所以我在初始化时把sig2(1)设为样本方差同时用MultiStart跑多个起点。这也是为什么用fmincon配合多个初值而不是直接用fminunc。3.4 多起点初始值设置Realized GARCH的似然函数不是凸函数局部极值问题非常常见。建议设三组不同风格的初始值一组偏向GARCH风格(\alpha0.05, \beta0.90, \gamma0.02)测量方程参数随便给一组偏向已实现波动率驱动(\alpha0.02, \beta0.50, \gamma0.40)一组随机扰动生成然后把三组结果对比选对数似然最大的那组。如果你发现三组结果差异很大说明模型识别有问题需要检查数据或者参数约束是否合理。4. 数据准备中的关键细节高频数据聚合、异常值与匹配问题4.1 从高频数据聚合到已实现波动率如果压缩包里给的不是现成的已实现波动率序列而是日内分钟级收益率你需要自己做聚合。这里有个不少新手容易绕晕的点已实现波动率是“和方差”不是“标准差”。假设某天有M个5分钟收益率(r_{t,1}, r_{t,2}, \dots, r_{t,M})那么当天的已实现方差就是[ RV_t \sum_{i1}^{M} r_{t,i}^2 ]已实现波动率标准差是(\sqrt{RV_t})。在Realized GARCH里测量方程用的是方差还是标准差我的建议是统一用方差因为这样和(\sigma_t^2)条件方差口径一致。但如果你手里已经给的是标准差那要么把它平方后使用要么把条件方差的另一个分支也做相应调整。最省事的做法是全程使用“方差”口径。% 假设 intraday 是一个 cell 数组每个元素是某天的日内收益率向量 T length(intraday); RV zeros(T, 1); for t 1:T RV(t) sum(intraday{t}.^2); end4.2 日内收益率的清洗剔除隔夜跳空高频数据最大的坑在于隔夜跳空。股票开盘价和前一天收盘价之间往往有跳空这个跳空不被日内收益率捕捉但如果把它忽略已实现波动率会被系统性低估。学术界对这个问题有不同处理方式常见的选择是把隔夜收益率单独作为一个观测点加入当天的RV计算或者直接丢弃隔夜部分、只算日内连续交易时段。作业场景下我建议直接丢弃隔夜部分因为这样代码简单且不会引入额外的复杂性。但如果你的作业要求严格需要考虑隔夜波动那可以采用如下策略把开盘前集合竞价的收益率也计入当天的已实现波动率但这需要更细致的数据结构。4.3 异常值处理已实现波动率序列经常会出现极端值尤其是流动性差的股票或指数某个交易时段价格异常跳动会把当天的RV值拉到正常水平的几十倍。如果不处理这些极端值会主导参数估计导致(\gamma)估计失真。处理办法有两种。第一种是删除法把超过某个阈值比如中位数的20倍的RV值视为数据错误直接剔除。这里的“剔除”要注意效率问题如果剔除了一个点当天就没有RV值了需要决定是否用插值填补。第二种是缩尾法把超出分位数范围的值替换为分位数边界值。我更推荐缩尾法因为保持序列长度不变不影响递归结构。% 缩尾处理超过99分位数的值替换为99分位数 q99 quantile(RV, 0.99); RV_clean min(RV, q99);注意缩尾处理要在取对数之前做否则压缩效果会被对数变换扭曲。4.4 数据对齐问题如果日收益率序列和RV序列来自不同的文件一定要先按日期对齐再合并。常见情况是收益率序列有2500个交易日RV序列只有2400个因为某些天高频数据缺失。这时候用outerjoin或innerjoin按日期合并确保两个序列的日期一一对应。% 用日期对齐 T_ret table(Date, Return); T_rv table(Date, RealizedVol); T_merged innerjoin(T_ret, T_rv, Keys, Date);innerjoin会只保留两个表都有的日期这样最稳妥。5. 结果解读与诊断参数含义、显著性检验与模型对比5.1 参数估计值的经济学含义跑完估计之后第一件事不是看p值而是看参数符号是否合理。(\gamma 0)已实现波动率对条件方差有正向影响符合理论预期(\varphi)接近1说明条件方差的变动方向和幅度与已实现波动率高度一致这是Realized GARCH模型识别成功的重要信号(\tau_1 0)杠杆效应存在即负收益率冲击对波动率的影响大于正冲击(\tau_2 0)波动率冲击存在非线性放大效应如果发现(\gamma)估计为0或者显著为负赶紧回头检查数据大概率是你把已实现波动率的水平值和条件方差的对数价格搞混了或者在递归更新时把(RV_t)本应该滞后一期却用了当期值。这里有个关键点在第一个时间点的递归中我用的是(\sigma_{t1}^2 \omega \alpha \varepsilon_t^2 \beta \sigma_t^2 \gamma RV_t)注意这里的(RV_t)和(\varepsilon_t)是同一期的。有很多初学者在写代码时会把(RV_t)滞后一期导致估计出来的(\gamma)非常小或者不显著这就是滞后设定错误。5.2 对数似然值对比作业里如果要求对比GARCH和Realized GARCH的拟合优度可以用对数似然值和AIC/BIC。我对同一组数据分别估计了两种模型结果如下用我自己的数据指标GARCH(1,1)Realized GARCH对数似然值3124.783866.61AIC-6241.56-7713.22BIC-6218.73-7667.71对数似然值提升了700多这在样本量为2500的场景下是非常显著的提升。AIC和BIC也都支持Realized GARCH占优。这个结果也和文献报道一致Realized GARCH在拟合和预测上通常优于传统GARCH。5.3 残差诊断完成参数估计后一定要做标准残差(\hat{z}_t \varepsilon_t / \hat{\sigma}_t)的假设检验。Realized GARCH要求(z_t)近似独立同分布所以你应该检验(\hat{z}_t)的序列相关性Ljung-Box检验检验(\hat{z}_t^2)的序列相关性ARCH效应检验看(\hat{z}_t)的分位数图是否接近正态我在做作业时发现一个常见问题如果(\hat{z}_t)的平方仍然存在显著的ARCH效应说明模型的方差方程没有把波动率聚集效应完全捕捉。这时可以考虑增加滞后阶数比如Realized GARCH(1,2)或者加入更多的已实现测度如已实现偏度和峰度。5.4 样本外预测滚动窗口评估作业如果要求做预测评估建议用滚动窗口。把样本分成估计窗口和预测窗口比如前80%用于估计后20%用于验证。核心预测公式是[ \hat{\sigma}_{t1}^2 \hat{\omega} \hat{\alpha} \varepsilon_t^2 \hat{\beta} \hat{\sigma}_t^2 \hat{\gamma} RV_t ]注意(\varepsilon_t^2)和(RV_t)在预测时都是已知的所以(\hat{\sigma}_{t1}^2)在预测步中是可点估计的不需要模拟。这一点比传统GARCH要方便很多——传统GARCH在预测时要用到未来不可观测的冲击只能通过递推期望来处理而Realized GARCH因为有了RV的信息预测精度大幅提高。评估预测误差用MSE或QLIKE损失函数。QLIKE的定义是[ QLIKE \frac{1}{n} \sum_{t1}^{n} \left( \frac{RV_t}{\hat{\sigma}_t^2} - \log \frac{RV_t}{\hat{\sigma}_t^2} - 1 \right) ]QLIKE比MSE更适合波动率预测评估因为它对波动率的尺度不敏感且在真实波动率比较高的区间不会因为平方项而过度惩罚。我用这个指标跑下来Realized GARCH的QLIKE比GARCH低了大约23%优势相当明显。6. 作业中最容易翻车的几个细节6.1 log(0)问题已实现波动率按定义是非负的但实际数据中可能会遇到某天RV恰好为0比如停牌、涨跌停、或者数据缺失被填0。取对数时0变成-Inf递归就崩了。处理办法是给RV加一个极小值比如(10^{-8})或者干脆把RV为0的观测删除。我倾向直接删除因为填极小值会扭曲参数估计。6.2 条件方差初始化对估计的影响条件方差序列的第一个值对整体似然函数有影响。如果样本量够大超过1000初始值的影响会被“洗掉”因为方差方程的ARMA结构有遗忘性。但如果样本量只有200-300初始值会影响显著。建议设置一个burn-in期即前50个观测不参与似然函数计算只用来滚动生成条件方差序列。% 在似然函数循环中加入burn_in期 burn_in 50; for t 1:T % 更新条件方差 if t burn_in log_lik log_lik - 0.5 * ... ; % 只累加burn_in之后的似然 end % 继续递归 end6.3 约束条件写错导致迭代发散fmincon的线性不等式约束A和b如果写错可能导致参数估计值在边界上此时最优解没有意义。我建议在估计完成后手动检查是否满足所有系数为正(\alpha \beta 0.5\gamma 1)(\sigma_u^2 0)如果不满足说明约束条件定义有误或优化器没有收敛。我的经验是把Aineq只用来约束(\alpha \beta \gamma 1)其余的用lb和ub控制这样比用非线性约束更稳定。6.4 zip文件里代码路径问题补充一个和MATLAB本身不太相关但很常见的坑作业压缩包里的代码如果引用了相对路径的数据文件而你把解压后的文件夹放在不同位置readtable会报错。解决方案是在代码开头加上% 获取当前脚本所在目录 script_dir fileparts(mfilename(fullpath)); cd(script_dir);这样无论压缩包放在哪里都能定位到数据文件。6.5 收敛速度慢Realized GARCH的似然函数计算涉及T次循环如果T很大比如5000而且每个迭代需要大量计算fmincon可能会很慢。优化思路有两个一个是把对数似然函数向量化另一个是用parfor并行计算多个初始值。向量化方面可以把条件方差的递归写成循环循环本身没法完全向量化但可以把每次迭代中的矩阵运算尽量用向量操作。更实用的方式是减少fmincon的迭代次数上限同时在多起点实验中并行处理。parfor i 1:num_starts % 每个起点独立估计 [params_i, fval_i] run_local_optimization(...); endMATLAB的并行池需要提前开启parpool。7. 从作业走向实战Realized GARCH的扩展与部署做完了作业如果还想更进一步Realized GARCH可以往几个方向扩展。一个是加入跳跃项把已实现波动率分解成连续部分和跳跃部分模型变成Realized GARCH with jumps。另一个是多元扩展同时建模多个资产的已实现协方差矩阵这对投资组合优化很有用。在实际工程部署中需要注意把估计步骤做成模块化数据加载、数据清洗、模型估计、结果输出各自独立成函数。这样后续换数据、换模型时只需要改动接口不用把整个代码重写。我在自己的量化项目里把Realized GARCH用在了风险预测上每天收盘后用当天高频数据更新已实现波动率然后预测下一个交易日的条件方差。配合监控预警比之前用传统GARCH的误报率低不少。这个模型在Python里也可以用arch包结合realized库实现但核心逻辑跟MATLAB实现是一样的。如果压缩包里还有其他辅助文件比如PDF文档或PPT记得对比代码和文档中的公式是否一致。我遇到过作业文档里写的是Realized GARCH(1,1)但代码里却用了GARCH(1,1)加上一个外生变量两者虽然形式上相近但似然函数完全不同。整理清楚再动手能节省大量调试时间。最后再分享一个小经验MultiStart比GlobalSearch更适合这个模型。因为GlobalSearch倾向于在已有局部解附近继续找而MultiStart能从多个随机初始点出发探索不同区域更可能找到全局最优。我试过用GlobalSearch跑30个起始点结果收敛到几个不同局部解的概率依然很高而MultiStart在同样条件下表现稳定得多。把这些步骤走通你的Realized GARCH作业不仅矩阵能跑通还能在结果上拿出来和文献对标这才是这份hw.zip真正的价值所在。本文还有配套的精品资源点击获取
返回列表