gs-quant 滚动窗口回归实战:用 RollingLinearRegression 监控参数漂移
【免费下载链接】gs-quantPython toolkit for quantitative finance项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant
gs-quant 是 Goldman Sachs 开源的量化金融工具包。其 timeseries 模块中的RollingLinearRegression(定义于gs_quant/timeseries/statistics.py)在固定观测数的滚动窗口上拟合 OLS 回归,输出"每个窗口末一个点"的参数序列,是监控参数漂移最直接的构件。本文按源码走读这个类,再用仓库自带测试数据复现一次完整的漂移告警。
参数"突然失效"长什么样
策略参数通常在全样本上标定:系数拟合漂亮,R² 达标。但市场结构切换后,解释变量与被解释变量的关系可能已经改变。典型症状:
- 滚动 R² 连续多期低于长期基线
- 关键系数相对历史区间发生符号或量级翻转
- 模型残差标准差显著放大
- 漂移不一定出现在你关注的那个系数上,可能转移到其他系数或截距
全样本回归只能告诉你"整体拟合变差",滚动回归能告诉你"从哪一期开始变差、哪个参数在动"。
RollingLinearRegression 源码走读
构造函数:校验、清洗与对齐一次做完
构造签名是RollingLinearRegression(X, y, w, fit_intercept=True),X可以是单条序列或序列列表。关键逻辑(节选简化):
# 意图:清洗对齐后交给 statsmodels RollingOLS,参数存于每个窗口末端 def __init__(self, X, y, w, fit_intercept=True): df = sm.add_constant(df) if fit_intercept else df if w <= len(df.columns): raise MqValueError('Window length must be larger than the number of explanatory variables') df = df[~df.isin([np.nan, np.inf, -np.inf]).any(axis=1)] y = y[~y.isin([np.nan, np.inf, -np.inf])] df_aligned, y_aligned = df.align(y, 'inner', axis=0) self._res = RollingOLS(y_aligned, df_aligned, w).fit()有四个点值得注意:
fit_intercept必须是 bool,传数字会抛MqTypeErrorw的校验把常数项也算一列:2 个解释变量加截距时,w必须大于 3- 含 nan/inf 的行在对齐之前就被剔除,窗口内有效点数可能小于
w - 底层引擎是 statsmodels 的RollingOLS,
w只接受 int,不支持'1m'这类字符串窗口
四个输出序列各返回什么
每个方法都是对self._res对应字段的包装,序列 index 与对齐后一致,前w-1个点为 NaN:
| 方法 | 返回 | 含义 |
|---|---|---|
coefficient(i) | pd.Series | params[i];fit_intercept=True时i=0是截距 |
r_squared() | pd.Series | 各窗口决定系数 |
fitted_values() | pd.Series | 窗口末端拟合值 |
standard_deviation_of_errors() | pd.Series | sqrt(mse_resid),误差项标准差 |
参数按窗口末端存储:$R_t = \mathrm{OLS}(X_{t-w+1:t},\ y_{t-w+1:t})$,第
t个点记录的是[t-w+1, t]这一窗口的估计值。
日常调用只需几行:
# 意图:一次构造,取出滚动 R² 与斜率两条核心监控序列 from gs_quant.timeseries.statistics import RollingLinearRegression reg = RollingLinearRegression([x1, x2], y, 22) reg.r_squared() # 滚动 R² reg.coefficient(1) # 第 1 个解释变量的斜率(0 是截距) reg.standard_deviation_of_errors() # 滚动误差标准差方法上的@plot_method装饰器把输出标记为可绘图对象;gs_quant/timeseries/statistics.py头部注释写明 Marquee Plot Service 会直接暴露这些公开方法。同文件的LinearRegression是全样本静态版:输出标量、额外支持predict(),RollingLinearRegression没有predict。
用仓库自带测试数据复现一次漂移告警
gs_quant/test/timeseries/test_statistics.py的test_rolling_linear_regression构造了一组"真实关系稳定、中间注入异常观测"的数据,简化后可直接运行:
# 意图:y = 10 + x1 + 3*x2,末两点被压低,模拟参数漂移发生 x1 = pd.Series([0, 1, 4, 9, 16, 25], index=pd.date_range('2019-1-1', periods=6)) x2 = pd.Series([0, 1, 2, 3, 4, 5], index=pd.date_range('2019-1-1', periods=6)) y = pd.Series([10, 14, 20, 28, 28, 40], index=pd.date_range('2019-1-1', periods=6)) reg = RollingLinearRegression([x1, x2], y, 4) print(reg.coefficient(1)) # nan, nan, nan, 1.0, -1.5, 1.0 print(reg.r_squared()) # nan, nan, nan, 1.0, 0.964, 0.902测试断言的期望值与上面一致。把完整参数向量放在一起看:
| 输出 | 末三个窗口取值 | 漂移信号 |
|---|---|---|
coefficient(0)截距 | 10.0 → 2.5 → 19.0 | 截距抬升 |
coefficient(1)x1 斜率 | 1.0 → -1.5 → 1.0 | 符号翻转 |
coefficient(2)x2 斜率 | 3.0 → 12.5 → -1.0 | 漂移转移到其他系数 |
r_squared() | 1.0 → 0.964 → 0.902 | 解释力衰减 |
standard_deviation_of_errors() | 0.0 → 2.24 → 4.47 | 残差标准差翻倍 |
注意最后一个窗口 x1 斜率"恢复"到 1.0,但 x2 系数变成 -1.0、截距抬到 19.0:漂移只是转移了位置。这是参数漂移监控要看完整参数向量、而不是单条系数序列的原因。
拿到参数序列后,告警规则可以自己叠加,常见形态是基线加偏差阈值:
# 意图:斜率偏离长期基线 2 个标准差、或 R² 低于基线 0.2 时触发漂移告警 slope = reg.coefficient(1) base, scale = slope.rolling(126).mean(), slope.rolling(126).std() drift = (slope - base).abs() > 2 * scale weak = reg.r_squared() < reg.r_squared().rolling(126).mean() - 0.2同族滚动窗口函数怎么选
RollingLinearRegression不是孤例,gs_quant/timeseries/__init__.py对多个子模块做了星号导入,按需求取用即可:
| 需求 | 函数 | 窗口形式 |
|---|---|---|
| 滚动斜率/系数向量 | RollingLinearRegression(X, y, w) | 仅 int |
| 滚动 beta | beta(x, b, w, prices=True),见gs_quant/timeseries/econometrics.py | int / str /Window |
| 滚动 std、var、cov、zscore | std、var、cov、zscores等,见gs_quant/timeseries/statistics.py | int / str /Window |
| 绩效报告类滚动度量 | standard_deviation(report_id, rolling_window)等,见gs_quant/timeseries/measures_reports.py | int / str |
gs_quant/documentation/05_factor_models/01_Factor_Models.ipynb里用一行代码生成约三个月的因子 beta 序列,是多因子参数稳定性检验的标准写法:
# 意图:滚动窗口 63 个观测(约 3 个月)估计因子间 beta from gs_quant.timeseries.econometrics import beta factor_beta = beta(growth_ret, momentum_ret, 63, prices=False)其余滚动函数的窗口语义统一收口在gs_quant/timeseries/helper.py的Window与normalize_window:
| 形式 | 含义 |
|---|---|
w=22(int) | 22 个观测,ramp 期默认取 22,前 22 点被丢弃 |
Window(22, 10) | 窗口 22、ramp 10,保留第 10 点之后的值 |
'1m'(str) | 日历窗口,index 必须是 DatetimeIndex |
窗口设置的常见坑
| 现象 | 原因 |
|---|---|
构造即抛MqValueError | w不大于变量数;fit_intercept=True时常数项占一列 |
构造即抛MqTypeError | fit_intercept不是 bool |
coefficient(i)取错列 | fit_intercept=True时索引 0 是截距而非第一个解释变量 |
序列前w-1点为 NaN | 窗口未满,属正常现象 |
与手工 pandasrolling结果对不上 | 本类先剔 nan 行再对齐,RollingOLS在清洗后的 index 上定窗 |
'1m'字符串窗口不可用 | RollingLinearRegression的w只接受 int,字符串窗口由其他滚动统计量支持 |
另外两件事:
X 与 y 日期轴不一致时,构造器取交集(inner join),输出 index 是交集后的 index
若目标只是每天重估一次参数、不做跨期比较,用
LinearRegression静态版更合适;RollingLinearRegression专职产出参数时间序列复现本文测试:运行
pytest gs_quant/test/timeseries/test_statistics.py::test_rolling_linear_regression窗口 ramp 语义与
normalize_window的实现,直接读gs_quant/timeseries/helper.py完整的因子 beta 滚动示例在
gs_quant/documentation/05_factor_models/01_Factor_Models.ipynb
【免费下载链接】gs-quantPython toolkit for quantitative finance项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考