Quant500 Academy

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 periodos

La 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

El resto de Quant500

Lo que estás aprendiendo, aplicado al índice todos los días:

La cartera del modelo, cada semana en tu correo

Cada sábado, su semana en un minuto: cómo fue, lo mejor y lo peor, y qué directivos compraron con su dinero. Y el día que el modelo se rehace, qué entra y qué sale. Es una simulación, no un consejo. Como mucho un correo a la semana, sin publicidad y sin cuenta; te das de baja con un clic. Lo hace una persona: Pol Gracia.

Cartera modelo con resultados simulados, no una recomendación personalizada. Tu correo se usa solo para esto: privacidad.

Detrás de la Academia y del resto de Quant500 hay una sola persona: Pol Gracia Serrahima.