4. Modelos de media móvil (MA): modelar el error, no el nivel
Nivel 9: Series Temporales - ARIMA y GARCH
La diferencia entre lo que se esperaba y lo que se predijo se llama error residual. Y aquí viene la idea elegante de esta sección: los residuos de una previsión forman ellos mismos una serie temporal que puede tener estructura temporal. Si esa estructura existe, podemos modelarla, predecir el error futuro y usar esa predicción para corregir la previsión original.
El modelo que hace esto se llama modelo de media móvil, o MA. Cuidado con el nombre: tiene el mismo nombre que el suavizado por media móvil, pero es algo completamente distinto. El suavizado promedia valores pasados de la serie; el modelo MA hace una suma lineal ponderada de choques (términos de ruido) pasados.
# MA(q) escrito como codigo:
# x[t] = w[t] + beta_1*w[t-1] + beta_2*w[t-2] + ... + beta_q*w[t-q]
#
# w[t] -> ruido blanco, media cero y varianza constante
# beta_j -> peso del choque ocurrido hace j periodosLa motivación es explícita: el modelo MA explica los choques del proceso de error directamente, ajustando un modelo a los propios términos de error. En un AR(p) esos choques se observan solo de forma indirecta, a traves de los valores pasados de la serie.
Propiedad de identificación clave: por definición, la autocorrelación de un MA(q) es cero para todo retardo mayor que q. Por eso un corte brusco en la ACF (y no en la PACF) apunta a un MA.
En finanzas, un MA(1) con beta negativo es la firma típica del *bid-ask bounce*: el precio rebota entre demanda y oferta, generando una reversión a un paso que no tiene nada que ver con información económica.
Autorregresión del error residual. Podemos modelar la serie de residuos con un modelo autorregresivo. Partimos del modelo de persistencia (predecir que el retorno de mañana será el de hoy) y ajustamos un AR sobre sus errores:
# conjunto retardado: columna t predice columna t+1
values = pd.DataFrame(Re.values)
Re3 = pd.concat([values.shift(1), values], axis=1)
Re3.columns = ['t', 't+1']
Re3 = Re3[1:]
X = Re3.values
train_size = int(len(X) * 0.66)
train, test = X[1:train_size], X[train_size:]
train_X, train_y = train[:, 0], train[:, 1]
test_X, test_y = test[:, 0], test[:, 1]
# modelo de persistencia sobre el entrenamiento
train_pred = [x for x in train_X]
train_resid = [train_y[i] - train_pred[i] for i in range(len(train_pred))]
# modelamos los residuos con un AR
model_fit = AutoReg(train_resid, lags=1).fit()
window = len(model_fit.model._lags)
coef = model_fit.params
print('Lag=%d, Coef=%s' % (window, coef))El modelo autorregresivo necesita los errores residuales de los pasos anteriores, así que hay que mantenerlos a mano. Recorremos el test paso a paso: predecimos, calculamos el error real, predecimos el error del siguiente paso y actualizamos el historial:
history = train_resid[len(train_resid) - window:]
history = [history[i] for i in range(len(history))]
predictions, expected_error = [], []
for t in range(len(test_y)):
yhat = test_X[t] # persistencia
error = test_y[t] - yhat # error realmente observado
expected_error.append(error)
length = len(history)
lag = [history[i] for i in range(length - window, length)]
pred_error = coef[0]
for d in range(window):
pred_error += coef[d + 1] * lag[window - d - 1]
predictions.append(pred_error)
history.append(error)Esto es una previsión walk-forward, o previsión rodante, aplicada al error. El resultado son dos series: el error residual esperado y el error residual predicho. Graficarlas juntas da una idea inmediata de si el modelo tiene alguna habilidad prediciendo el error.
Y la aplicación práctica: la previsión corregida es simplemente la previsión original más el error predicho. Si el modelo del error acierta, la corrección reduce el RMSE global.
Para ampliar esta lección
- PER estimado (forward)en el glosario
- S&P 500en el glosario
- Qué es el PER y cómo compararlo con su sectorguía