Quant500 Academy

5. ARIMA: integración, ajuste y predicción con ventana móvil

Nivel 9: Series Temporales - ARIMA y GARCH

El modelo autorregresivo integrado de media móvil, o ARIMA, es el modelo estadístico estándar para analizar y predecir series temporales. Sus siglas describen exactamente sus tres piezas:

Sus tres parámetros:

Cuando d es cero tenemos un ARMA(p,q), que combina ambos efectos:

# ARMA(p,q):
# x[t] = alpha_1*x[t-1] + ... + alpha_p*x[t-p]
#        + w[t] + beta_1*w[t-1] + ... + beta_q*w[t-q]

Un ARMA suele necesitar menos parámetros que un AR(p) o un MA(q) por separado para el mismo ajuste. Y una advertencia que será el puente hacia los dos últimos modulos: un ARMA no tiene en cuenta la agrupación de volatilidad, un fenómeno empírico clave de muchas series financieras.

Ajuste basico (usa la clase moderna, no la obsoleta):

from statsmodels.tsa.arima.model import ARIMA

model = ARIMA(Re, order=(5, 1, 0))
model_fit = model.fit()
print(model_fit.summary())

residuals = pd.DataFrame(model_fit.resid)
residuals.plot()
residuals.plot(kind='kde')
print(residuals.describe())

Una media de residuos cercana a cero indica ausencia de sesgo; una media positiva o negativa indica un sesgo que puede corregirse directamente antes de usar las previsiones.

Ojo con un detalle que el código del curso deja pasar: Re ya son retornos logarítmicos, es decir, la serie ya está diferenciada una vez. Poner d = 1 encima significa sobrediferenciar, lo que introduce una estructura MA artificial en los residuos e infla la varianza. Sobre retornos, d = 0 es casi siempre lo correcto.

Previsión ARIMA con ventana móvil. Igual que con el AR, queremos reconstruir el modelo cada día. Aquí si se reajusta desde cero en cada paso:

from tqdm import tqdm

X = Re.values
size = int(len(X) * 0.66)
train, test = X[0:size], X[size:len(X)]
history = [x for x in train]
predictions = []

for t in tqdm(range(len(test)), desc='Procesando'):
    model = ARIMA(history, order=(5, 1, 0))
    model_fit = model.fit()
    output = model_fit.forecast()
    yhat = output[0]
    predictions.append(yhat)
    history.append(test[t])       # solo despues de predecir

rmse = np.sqrt(mean_squared_error(test, predictions))
print('Test RMSE: %.3f' % rmse)

Esto es honesto pero costoso: son tantos ajustes de máxima verosimilitud como días de prueba.

Busqueda en rejilla de hiperparametros. En lugar de fijar (5,1,0) a ojo, evaluamos combinaciones y nos quedamos con la de menor RMSE fuera de muestra:

def evaluate_arima_model(X, arima_order):
    train_size = int(len(X) * 0.66)
    train, test = X[0:train_size], X[train_size:]
    history = [x for x in train]
    predictions = list()
    for t in range(len(test)):
        model_fit = ARIMA(history, order=arima_order).fit()
        predictions.append(model_fit.forecast()[0])
        history.append(test[t])
    return np.sqrt(mean_squared_error(test, predictions))

def evaluate_models(dataset, p_values, d_values, q_values):
    dataset = dataset.astype('float32')
    best_score, best_cfg = float('inf'), None
    for p in p_values:
        for d in d_values:
            for q in q_values:
                order = (p, d, q)
                try:
                    rmse = evaluate_arima_model(dataset, order)
                    if rmse < best_score:
                        best_score, best_cfg = rmse, order
                    print('ARIMA%s RMSE=%.3f' % (order, rmse))
                except:
                    continue
    print('Mejor ARIMA%s RMSE=%.3f' % (best_cfg, best_score))

evaluate_models(Re.values, [0, 1, 2], [0, 1], [0, 1])

El except: continue desnudo es cómodo (muchas combinaciones no convergen) pero peligroso: silencia también errores de programación. En producción, captura la excepción y regístrala.

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.