gs-quant 滚动线性回归 RollingLinearRegression 详解:从滚动 OLS 原理到实际应用
2026/9/15 19:47:01 网站建设 项目流程

gs-quant 滚动线性回归 RollingLinearRegression 详解:从滚动 OLS 原理到实际应用

【免费下载链接】gs-quantPython toolkit for quantitative finance项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant

导读

本文以 gs-quant 时间序列统计模块中的RollingLinearRegression类为讲解对象,深入剖析其在金融时序分析中的应用场景:在固定观察窗口内对解释变量 X 与因变量 y 进行滚动普通最小二乘(Rolling OLS)回归,动态捕捉回归系数、拟合值、R² 与残差标准差的时序演化。读完本文,你将掌握该类的构造参数语义、四个核心方法(coefficientfitted_valuesr_squaredstandard_deviation_of_errors)的用法与返回结构,并通过源码与单元测试的对照,理解滚动窗口的参数对齐、缺失值处理等底层实现细节,可直接在因子分析、风格暴露跟踪、价差动态建模等场景中落地使用。

一、类概览与定位

RollingLinearRegression定义于 gs_quant/timeseries/statistics.py(约第 1173 行起),归属于gs_quant.timeseries.statistics子模块,与其同模块的静态回归类LinearRegression互为补充:后者对整个样本拟合一次回归,前者则以固定宽度窗口沿时间轴滚动拟合,参数结果存储在每个窗口的末端。

按照 docs/classes/gs_quant.timeseries.statistics.RollingLinearRegression.rst 的 API 文档,该类对外暴露四个方法,本文章节将逐一展开:

方法返回值语义
coefficient(i)pd.Series第 i 个预测变量的滚动估计系数
fitted_values()pd.Series每个滚动窗口末端的拟合值
r_squared()pd.Series滚动回归的决定系数 R²
standard_deviation_of_errors()pd.Series滚动回归残差的标准差

需要特别注意的是:与静态LinearRegression返回标量不同,滚动版本的四个方法全部返回与输入时间序列等长的时间序列,未达到完整窗口的前导位置为NaN

二、构造参数与初始化语义

类的构造函数签名如下(源码见 statistics.py):

def __init__(self, X: Union[pd.Series, list[pd.Series]], y: pd.Series, w: int, fit_intercept: bool = True):
参数类型说明
Xpd.Serieslist[pd.Series]一个或多个解释变量(自变量)观测序列
ypd.Series因变量(被解释变量)观测序列
wint每个滚动窗口包含的观测数量,必须大于解释变量个数
fit_interceptbool,默认True是否在模型中计算截距项

初始化阶段做了四件关键事情,源码与注释可以互相印证:

  1. 数据整形:若X是多个序列,用pd.concat(X, axis=1)沿列方向拼接成数据框;若X是单个序列,则调用to_frame()转为单列数据框。
  2. 截距项fit_intercept=True时通过sm.add_constant(df)加入常数项列,随后列重命名为0, 1, 2, ...(0 号对应截距);fit_intercept=False时列重命名为1, 2, ...,此时coefficient(0)不再可用,必须从coefficient(1)开始取斜率系数。
  3. 窗口宽度校验:若w <= len(df.columns)(即窗口长度不大于解释变量个数),抛出MqValueError,提示信息为'Window length must be larger than the number of explanatory variables'。这与文档字符串中"窗口观测数必须大于解释变量数"的约束完全一致,也是防止 OLS 设计矩阵秩亏的最小可行性检查。
  4. 脏数据过滤与对齐:先剔除X中任何含NaN+inf-inf的行,同样剔除y中的非法值,再通过df.align(y, 'inner', axis=0)仅保留两个序列日期/时间索引的交集。这正是类文档中"若 X 与 y 未对齐,则仅使用日期/时间的交集"的实现。

此外,fit_intercept若传入非布尔值(例如整数1),构造阶段会抛出MqTypeError,提示'expected a boolean value for "fit_intercept"'

窗口拟合的核心一行是:

self._res = RollingOLS(y_aligned, df_aligned, w).fit()

这里直接使用 statsmodels 的RollingOLS(导入语句见 statistics.py:from statsmodels.regression.rolling import RollingOLS),因此该类的数值计算完全复用 statsmodels 成熟的滚动回归实现,gs-quant 在此之上封装了金融时序友好的接口与返回值。

三、核心方法逐一拆解

3.1 coefficient(i):滚动回归系数

@plot_method def coefficient(self, i: int) -> pd.Series: return self._res.params[i]
  • i=0对应截距项(前提是fit_intercept=True);
  • i=1起对应各个解释变量的回归斜率;
  • 返回pd.Series,索引与输入序列对齐,窗口未满的前导位置为NaN

参数语义在 LinearRegression.coefficient 与 RollingLinearRegression.coefficient 的 docstring 中保持一致:0 for intercept (available if intercept is used), 1 for regression slope

3.2 fitted_values():窗口末端拟合值

@plot_method def fitted_values(self) -> pd.Series: comp = self._X.mul(self._res.params.values) return comp.sum(axis=1, min_count=len(comp.columns))

滚动版本并未直接返回RollingOLSfittedvalues(因为 RollingOLS 的拟合值矩阵结构不同),而是手工重算:将保存的原始解释变量矩阵self._X逐列乘以对应位置的滚动系数矩阵self._res.params.values,再按行求和。min_count=len(comp.columns)保证当某行存在缺失系数时整行结果仍为NaN,从而保持时序完整性。返回值语义为"每个滚动窗口末端的拟合值"。

3.3 r_squared():滚动决定系数

@plot_method def r_squared(self) -> pd.Series: return self._res.rsquared

直接透传 statsmodels 滚动回归结果的rsquared属性,返回每个窗口的拟合优度 R² 时序,用于评估各时间切片上模型的解释力度。

3.4 standard_deviation_of_errors():滚动残差标准差

@plot_method def standard_deviation_of_errors(self) -> pd.Series: return np.sqrt(self._res.mse_resid)

对滚动回归的残差均方误差mse_resid开平方,得到每个窗口误差项的标准差时序,反映模型在每个窗口上的拟合误差水平。静态版本的同类实现见 LinearRegression.standard_deviation_of_errors,逻辑完全一致。

四个方法均被@plot_method装饰(装饰器定义见 gs_quant/timeseries/helper.py),其作用是:标记该方法可被 Marquee Plot Service 导出为绘图工具方法,并允许调用方传入real_timeintervaltime_filter等绘图场景参数而不报错(装饰器内部会将这些多余关键字参数静默剔除)。

四、完整可运行示例

类文档(statistics.py)给出如下使用范式:基于 100 个观测,在 22 个观测的滚动窗口内对 y 与 x1、x2 做回归并计算 R²:

from gs_quant.timeseries.statistics import generate_series from gs_quant.timeseries.statistics import RollingLinearRegression x1 = generate_series(100) x2 = generate_series(100) y = generate_series(100) r = RollingLinearRegression([x1, x2], y, 22) r.r_squared() # 滚动 R² 时序,前 21 个位置为 NaN r.coefficient(0) # 滚动截距项 r.coefficient(1) # x1 的滚动斜率 r.coefficient(2) # x2 的滚动斜率 r.fitted_values() # 每个滚动窗口末端的拟合值 r.standard_deviation_of_errors() # 滚动残差标准差

要点解读:

  • 传入两个解释变量时使用列表[x1, x2],单变量时可直接传pd.Series
  • 窗口w=22可理解为"过去约一个月的日频观测",这是一种常见的金融回归窗口设定;
  • 由于每个参数都存储在窗口末端,因此输出序列的前w-1个位置为NaN,从第w个位置起才有有效值。

五、单元测试实证:行为细节与边界条件

gs_quant/test/timeseries/test_statistics.py 中的test_rolling_linear_regression完整验证了该类的行为,是理解其语义的绝佳实证:

x1 = pd.Series([0.0, 1.0, 4.0, 9.0, 16.0, 25.0, np.nan], index=pd.date_range('2019-1-1', periods=7), name='x1') x2 = pd.Series([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], index=pd.date_range('2019-1-1', periods=8)) y = pd.Series([10.0, 14.0, 20.0, 28.0, 28.0, 40.0, 60.0], index=pd.date_range('2019-1-1', periods=7))

测试覆盖了以下关键行为:

  1. 窗口宽度过小报错RollingLinearRegression([x1, x2], y, 3, True)抛出MqValueError。两个解释变量加上截距共 3 列,w=3不满足"窗口长度必须大于解释变量个数"的约束(这里要求w > 列数,即至少为 4)。
  2. fit_intercept 类型校验RollingLinearRegression([x1, x2], y, 4, 1)抛出MqTypeError,因为fit_intercept传入了整数而非布尔值。
  3. NaN 过滤与索引对齐x1长度为 7、x2长度为 8、y长度为 7,且x1最后一位为NaN。经过过滤与交集对齐后,最终只有 6 个有效观测参与滚动回归,输出索引为2019-1-12019-1-6,前 3 个位置(未凑满窗口 4)为NaN
  4. 数值结果逐项断言w=4、含截距):
方法前 3 位第 4~6 位期望值
coefficient(0)截距NaN10.0, 2.5, 19.0
coefficient(1)x1 斜率NaN1.0, -1.5, 1.0
coefficient(2)x2 斜率NaN3.0, 12.5, -1.0
r_squared()NaN1.0, 0.964029, 0.901961
fitted_values()NaN28.0, 28.5, 39.0
standard_deviation_of_errors()NaN0.0, 2.236068, 4.472136

从中可以看到:第一个完整窗口数据恰好严格线性(截距 10、x1 斜率 1、x2 斜率 3),因此 R²=1、残差标准差为 0;后续窗口数据扰动后 R² 与残差标准差相应变化,系数也随之滚动更新——这正是"滚动回归动态捕捉关系漂移"的核心价值。

六、底层原理与实现细节

6.1 与静态回归的关系

RollingLinearRegression是 LinearRegression 的滚动推广。两者在数据预处理(pd.concatsm.add_constant→ 非法值过滤 →align)上完全一致,区别在于:

  • LinearRegression使用sm.OLS(...).fit()一次性拟合,方法返回标量
  • RollingLinearRegression使用RollingOLS(y, X, w).fit(),方法返回时间序列

对照测试test_linear_regression(test_statistics.py)与test_rolling_linear_regression的断言方式,可以直观看出两者返回类型的差异。

6.2 滚动窗口的参数存储位置

类文档明确说明:"The parameters of each rolling window are stored at the end of each window."(每个滚动窗口的参数存储在窗口末端)。这意味着第t个有效输出位置对应的是以t为右端点的最近w个观测拟合出的模型,输出的每个值天然携带"截至该时点的最新回归结论",非常契合金融研究中对最新关系状态的持续追踪需求。

6.3 对齐与脏数据处理是金融时序的常见陷阱

真实行情数据常出现序列长度不一致、停牌日缺失、异常值等情况。该类通过"先过滤NaN/inf,再取索引交集"的两步处理,保证进入RollingOLS的矩阵完全干净且对齐。这一点在测试用例中体现得淋漓尽致:三个序列长度分别为 7、8、7,加上一处NaN,最终有效样本被自动规整到 6 个。

七、典型应用场景

结合 gs-quant 的定位(面向量化金融的 Python 工具包),RollingLinearRegression的典型用法包括:

  • 因子暴露的动态追踪:以个股收益为y,以风格因子(市值、价值、动量等)收益为X,用滚动回归估计各因子暴露随时间的变化,识别风格切换;
  • 配对交易与价差建模:对两只相关性资产的价差或对数价格做滚动回归,观察协整关系是否稳定,作为开平仓信号的依据;
  • 市场状态监测:滚动 R² 与残差标准差可用于刻画模型解释力与波动环境的演化,例如 R² 骤降可能提示关系结构性变化;
  • 风险模型的滚动校准:将滚动回归系数序列作为输入,进一步进入 econometrics 子模块(如betacorrelation)或 technicals 子模块进行衍生分析。

八、注意事项与局限

  1. 窗口宽度选择w必须严格大于解释变量列数(含截距),否则直接抛MqValueError;更宽泛地说,w过小会导致估计噪声大,过大则响应迟缓,需要根据数据频率与研究目标权衡;
  2. 前导 NaN:滚动输出的前w-1个位置恒为NaN,下游计算(如取均值、绘图)需自行处理;
  3. 必须使用pd.Series输入Xy需为 pandas 序列,且索引类型应一致(如均为日期),否则交集对齐可能产生空结果;
  4. LinearRegression的返回值差异:注意区分静态版本返回标量、滚动版本返回序列,避免在代码中混用;
  5. 依赖 statsmodels:底层由statsmodels.regression.rolling.RollingOLS驱动,其数值行为与 statsmodels 版本相关,升级依赖时建议回归测试(仓库中的 test_statistics.py 可直接作为验证基准)。

九、延伸阅读

  • API 文档原始出处:docs/classes/gs_quant.timeseries.statistics.RollingLinearRegression.rst
  • 类实现与完整 docstring:gs_quant/timeseries/statistics.py
  • 单元测试:gs_quant/test/timeseries/test_statistics.py
  • 同模块静态回归类LinearRegression:gs_quant/timeseries/statistics.py
  • plot_method装饰器实现:gs_quant/timeseries/helper.py
  • 子模块导出入口:gs_quant/timeseries/init.py(from .statistics import *
  • 关联的统计与计量功能:滚动窗口工具定义于 gs_quant/timeseries/helper.py,回归相关的betacorrelation等函数见 gs_quant/timeseries/econometrics.py

【免费下载链接】gs-quantPython toolkit for quantitative finance项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询