Skip to content

HAR-RV Forecast (rolling OLS)

Intuição

O HAR-RV clássico usa regressão (HAR) para prever volatilidade futura a partir de múltiplas escalas da volatilidade realizada.

Nesta feature, fazemos uma regressão rolling para produzir, em cada tempo t, uma previsão de log(RV_{t+horizon}) usando como regressoras:

  • log(RV_t)
  • mean(log(RV)) em janela semanal
  • mean(log(RV)) em janela mensal

Isso evita look-ahead: os coeficientes em t são ajustados apenas com dados até t-horizon.

Definição

Para cada t, ajustamos uma OLS em janela estimation_window e calculamos:

  • y_{t+h} = beta0 + beta1*x_d(t) + beta2*x_w(t) + beta3*x_m(t)

onde x_d, x_w, x_m são as features do HAR sobre log(RV).

Uso

from quantmaster.features.volatility import har_rv_forecast

df["har_rv_forecast_1_100"] = har_rv_forecast(df, horizon=1, estimation_window=100)

API

Source code in src/quantmaster/features/volatility.py
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def har_rv_forecast(
    data: pd.DataFrame | pd.Series,
    *,
    price_col: str = "close",
    horizon: int = 1,
    estimation_window: int = 100,
    weekly_window: int = 5,
    monthly_window: int = 22,
    log_rv: bool = True,
) -> pd.Series:
    horizon = validate_positive_int(horizon, name="horizon")
    estimation_window = validate_positive_int(estimation_window, name="estimation_window")
    weekly_window = validate_positive_int(weekly_window, name="weekly_window")
    monthly_window = validate_positive_int(monthly_window, name="monthly_window")

    rv = realized_variance(data, price_col=price_col)
    rv = rv.astype(float)

    if log_rv:
        base = np.log(rv.where(rv > 0))
    else:
        base = rv

    x_d = base
    x_w = base.rolling(weekly_window).mean()
    x_m = base.rolling(monthly_window).mean()

    y = base.shift(-horizon)

    x = np.column_stack(
        [
            x_d.to_numpy(dtype=float),
            x_w.to_numpy(dtype=float),
            x_m.to_numpy(dtype=float),
        ]
    )
    y_arr = y.to_numpy(dtype=float)

    n = len(rv)
    out_arr = np.full(n, np.nan, dtype=float)

    for i in range(n):
        train_end = i - horizon
        if train_end < 0:
            continue

        train_start = train_end - estimation_window + 1
        if train_start < 0:
            continue

        x_win = x[train_start : train_end + 1]
        y_win = y_arr[train_start : train_end + 1]

        mask = np.isfinite(y_win)
        mask &= np.isfinite(x_win).all(axis=1)

        if mask.sum() < 4:
            continue

        a = np.column_stack([np.ones(mask.sum(), dtype=float), x_win[mask]])
        beta, *_ = np.linalg.lstsq(a, y_win[mask], rcond=None)

        x_i = x[i]
        if not np.isfinite(x_i).all():
            continue

        out_arr[i] = float(beta[0] + np.dot(beta[1:], x_i))

    out = pd.Series(out_arr, index=rv.index)
    out.name = f"har_rv_forecast_{horizon}_{estimation_window}"
    return out