跳转至

因子半衰期

对于一个时间序列,我们可以构建一个逐渐衰减的时间序列模型来估计其半衰期。本文介绍了两个模型,用于估计一个时间序列的半衰期。

在量化研究中,了解各个因子的衰减情况,可以更有效地动态分配因子权重,以适应市场变化。

image-20240612194448624

模型一:自回归模型

定义弱平稳过程 \(X_t\) 为: $$ X_t = c + \lambda X_{t-1} + \epsilon_t, 0 < \lambda < 1 \tag{1}\label{1} $$

公式 \(\eqref{1}\) 中要求 \(0 < \lambda < 1\),因此 \(X_t\) 应是逐渐衰减的。

对于公式 \(\eqref{1}\),我们可以使用线性回归估计 \(c\)\(\lambda\)

弱平稳意味着过程 \(X_t\) 有一个固定的均值:\(E[X] = \mu\)。对 \(X_t\) 取期望得到:

\[ \begin{align*} E[X_t] &= E[c + \lambda X_{t-1} + \epsilon_t] \\ \mu &= c + \lambda E[X_{t-1}] \\ \mu &= c + \lambda \mu \\ \mu &= \frac{c}{1 - \lambda} \end{align*} \]

重新排列 \(c\) 得到:\(c = \mu(1 - \lambda)\)

将此代入公式 \(\eqref{1}\) 得到:

\[ \begin{align*} X_t &= \mu(1 - \lambda) + \lambda X_{t-1} + \epsilon_t \\ &= \mu + \lambda(X_{t-1} - \mu) + \epsilon_t \end{align*} \]

如果我们设 \(Y_t\) 为均值距离 \(X_t - \mu\),则: $$ Y_t = \lambda Y_{t-1} + \epsilon_t \tag{2}\label{2} $$

半衰期定义为 \(X_t\) 衰减到均值一半所需的时间 \(h\)。换句话说,是 \(Y_t\) 衰减到 \(0\) 的一半所需的时间 \(h\)。这可以写为:

\[ E[Y_{t+h}] = \frac{1}{2}Y_t \]

根据公式 \(\eqref{2}\) 我们得到:

\[ E[Y_{t+h}] = \lambda^h Y_t \]

这意味着:

\[ \lambda^h Y_t = \frac{1}{2} Y_t \]

\(h\) 得到: $$ h = -\frac{\log(2)}{\log(\lambda)} \tag{3}\label{3} $$

因此,我们只需要估计出公式 \(\eqref{1}\) 中的 \(\lambda\),再代入公式 \(\eqref{3}\) 中即可得到半衰期。

Tip

对于一个因子,如果我们认为它的预测能力随着滞后期的增大而最终会趋于 \(0\),即认为公式 \(\eqref{1}\) 中的 \(c = 0\),那么只需要将公式 \(\eqref{1}\) 改写为不带截距项的一元线性回归。

模型二:指数衰减模型

构建如下指数衰减模型: $$ X_t = \alpha \lambda^{t}, 0 < \lambda < 1 \tag{4}\label{4} $$

\(h\) 为半衰期,则有:

\[ E[X_t] = \alpha \lambda^{t}\\ E[X_{t+h}] = \frac{1}{2} E[X_t] \]

因此 $$ \alpha \lambda^{t+h} = \frac{1}{2} \alpha \lambda^{t}\ $$

\(h\) 得到: $$ h = -\frac{\log(2)}{\log(\lambda)} \tag{5}\label{5} $$

因此,我们只需要估计出公式 \(\eqref{4}\) 中的 \(\lambda\),再代入公式 \(\eqref{5}\) 中即可得到半衰期。

下面我们估计公式 \(\eqref{4}\) 中的 \(\lambda\)。为推导简便,我们考虑 \(\alpha > 0\),由公式 \(\eqref{4}\) 得: $$ \log(X_t) = \log(\alpha) + t\log(\lambda) \tag{6}\label{6} $$

因此,我们只需要将 \(\log(X_t)\) 序列作为因变量,将 \(t\) 作为自变量,进行带截距项的一元线性回归,估计出的自变量系数即为 \(\log(\lambda)\),再代入公式 \(\eqref{5}\) 中即可得到半衰期。

Note

相比于模型一中的自回归模型,模型二的指数衰减模型有些弊端:

  • 模型二要求 \(X_t\) 为正数,否则无法对其取对数,来进行公式 \(\eqref{6}\) 的回归。而模型一并不要求 \(X_t\) 为正数。

代码实现

我们使用模型一,将时间序列进行滞后一期的自回归,估计回归系数 \(\lambda\)。核心代码如下:

Python
def halflife(series):
    lambda_ = np.linalg.lstsq(
        series[:-1].values[:, np.newaxis],
        series[1:].values[:, np.newaxis],
        rcond=None,
    )[0].item()

    if 0 < lambda_ and lambda_ < 1:
        return -np.log(2) / np.log(lambda_)
    else:
        return np.NaN

完整代码见 GitHub

不同模拟数据的估计结果

完整代码模拟了“真实期望为零”、“真实期望不为零”和“时间序列不收敛”三种情况。

对于真实期望为零的情况,无论是否带截距项 \(c\),估计结果都较为接近。

image-20240612194304271

对于真实期望不为零的情况,是否带截距项 \(c\) 的估计结果有较大差异。(注:图中的红线和橙线、蓝线和绿线实际上是重合的,为观察方便,我加上了随机扰动,因此它们看起来并不重合。)

  • 对于带截距项的回归,意味着模型认为时间序列会收敛到一个不一定为 0 的值,因此估计出的半衰期的含义是:衰减到当前值距离均值的一半所需的时间。
  • 对于不带截距项的回归,意味着模型认为时间序列会收敛到 0,因此估计出的半衰期的含义是:衰减到当前值的一半所需的时间。

image-20240612194448624

对于时间序列不收敛的情况,我们的判断逻辑是:若估计出的 \(\lambda\) 不满足 \(0 < \lambda < 1\),则认为无法估计出半衰期。下图是一个时间序列不收敛而无法估计出半衰期的示例。

image-20240612195054840

计算速度

完整代码还对比了不同实现方法进行一元线性回归的计算速度,结果如下。可以发现,使用纯 numpy 进行实现的计算速度最快,而使用 statsmodelsAutoReg 接口虽然代码更加简洁,但计算速度较慢。

Python
def halflife(series, method="numpy_no_intercept"):
    if method == "numpy_no_intercept":
        lambda_ = np.linalg.lstsq(
            series[:-1].values[:, np.newaxis],
            series[1:].values[:, np.newaxis],
            rcond=None,
        )[0].item()

    elif method == "statsmodels_no_intercept":
        lambda_ = AutoReg(series, lags=1, trend="n").fit().params["y.L1"]

    elif method == "numpy_intercept":
        lambda_ = np.polynomial.Polynomial.fit(
            series.shift(1).iloc[1:], series.iloc[1:], deg=1, domain=[]
        ).coef[1]

    elif method == "statsmodels_intercept":
        lambda_ = AutoReg(series, lags=1).fit().params["y.L1"]

    if 0 < lambda_ and lambda_ < 1:
        return -np.log(2) / np.log(lambda_)
    else:
        return np.NaN

image-20240612193941302

评论