Activo elegido: Acciones ordinarias de ExxonMobil
Corporation (Ticker en NYSE: XOM).
Variable exógena de contraste: Índice de mercado
S&P 500 (^GSPC).
Pregunta de investigación: A partir de los datos
históricos recientes, ¿es posible caracterizar y proyectar la inercia
temporal de los retornos de ExxonMobil mediante la familia de modelos
lineales de media condicional (ARIMA, ARIMAX, SARIMAX) para fundamentar
probabilísticamente una decisión de inversión de USD 100.000 a un
horizonte de 30 días hábiles?
Horizonte de inversión: 30 días hábiles bursátiles.
Conclusión principal: El proceso de modelación econométrica determinó que la especificación ARIMA(1,0,1) es la más adecuada en términos de parsimonia y capacidad predictiva fuera de muestra. Debido al fuerte rechazo del supuesto de normalidad en los residuales, se implementó una simulación prospectiva de Monte Carlo mediante la técnica de bootstrap (remuestreo empírico de errores). Los escenarios simulados proyectan un retorno esperado positivo y un Valor en Riesgo (VaR) estadísticamente acotado, lo que respalda la adopción de una posición larga (compra) sobre el activo en la fecha de corte.
Para el desarrollo del presente análisis, se seleccionó la acción de ExxonMobil (XOM), corporación líder en el sector energético global, que cotiza en dólares estadounidenses (USD) dentro del New York Stock Exchange (NYSE). Su negociación transcurre en el horario regular de 9:30 a 16:00 (hora del este). Dada su enorme capitalización bursátil, constituye un activo altamente líquido cuya valoración reacciona de forma elástica a las fluctuaciones macroeconómicas.
Se emplean precios de cierre ajustados (Adjusted Close) para internalizar eventos corporativos como el pago de dividendos y la división de acciones (splits), garantizando que las variaciones de precio reflejen el rendimiento real para el inversionista.
En la evolución histórica reciente, se identifican tres eventos coyunturales que explican rupturas estructurales y alta volatilidad en la serie: 1. Choque de demanda pandémico (2020): Las restricciones globales de movilidad destruyeron la demanda de combustibles, desplomando los precios spot del crudo y generando la mayor contracción de la década para XOM. 2. Crisis geopolítica y energética (2022): El conflicto en Europa del Este restringió severamente la oferta energética mundial, disparando los márgenes de refinación e impulsando una marcada tendencia alcista en el precio de la acción. 3. Intervenciones de la OPEP+ (2023-2025): Las políticas sistemáticas de recorte de producción han operado como un soporte para los precios del barril, induciendo fluctuaciones continuas en las expectativas de mercado.
Hipótesis de Inversión: Si la simulación prospectiva a 30 días revela un escenario mediano con retorno positivo, y la probabilidad empírica de pérdida total del capital es inferior al 40%, se ejecutará una posición larga invirtiendo los USD 100.000. En caso de que el riesgo a la baja supere dicho umbral probabilístico, se preservará la liquidez del portafolio.
## [1] "XOM" "GSPC"
La muestra de datos abarca más de ocho años de historia bursátil diaria. Modelar la serie directamente en sus niveles de precio origina problemas de regresión espuria debido a la evidente presencia de tendencias estocásticas (raíz unitaria o Random Walk).
Para fundamentar matemáticamente la transformación de la serie, se aplicó la prueba de Box-Cox. El resultado arrojó un parámetro \(\lambda \approx 0.43\). Al tratarse de un valor cercano a cero, el consenso econométrico dictamina que la transformación óptima consiste en aplicar logaritmos naturales. Por consiguiente, se descarta el análisis en niveles y se aplica la primera diferencia logarítmica para obtener retornos compuestos continuos: \[r_t = \Delta \ln(P_t) = \ln\left(\frac{P_t}{P_{t-1}}\right)\]
Esta transformación estabiliza los cambios proporcionales y centra la media del proceso en torno a cero. Para caracterizar la naturaleza de la serie transformada, se calculan sus momentos estadísticos fundamentales:
| Métrica | Valor |
|---|---|
| Observaciones (Días) | 2219.00000 |
| Media Diaria | 0.00053 |
| Desviación Estándar | 0.01890 |
| Retorno Máximo | 0.11940 |
| Retorno Mínimo | -0.13040 |
| Asimetría (Skewness) | -0.23170 |
La Tabla 1 evidencia que la media diaria gravita asintóticamente hacia cero, un rasgo distintivo de la hipótesis de mercados eficientes. No obstante, la alta desviación estándar y la asimetría negativa ratifican que el activo posee una volatilidad inherente y una propensión estadística a sufrir contracciones más veloces que sus recuperaciones. Visualmente, el gráfico de retornos denota un claro agrupamiento de volatilidad, fenómeno donde los periodos de alta turbulencia tienden a aglomerarse.
La modelación de series de tiempo univariadas bajo el paradigma de Box-Jenkins tiene como objetivo aislar la señal predecible del ruido aleatorio estructural. Se evaluarán tres enfoques de media condicional:
Modelo ARIMA (Autoregressive Integrated Moving Average): Proyecta el valor actual en función de sus propios rezagos y de los errores de pronóstico históricos. Empleando el operador de rezago \(L\) (donde \(L y_t = y_{t-1}\)), su forma funcional es: \[\Phi(L)(1-L)^d y_t = \theta_0 + \Theta(L)\epsilon_t\] Toda vez que se opera sobre la primera diferencia logarítmica, el orden de integración asumido es \(d=0\). El componente AR se define por el polinomio \(\Phi(L) = 1 - \phi_1 L - \dots - \phi_p L^p\). Para garantizar que el proceso sea estacionario y converja a su media incondicional, las raíces de este polinomio deben ubicarse estrictamente fuera del círculo unitario. El componente MA obedece a \(\Theta(L) = 1 + \theta_1 L + \dots + \theta_q L^q\). Este exige la condición de invertibilidad para asegurar la unicidad del modelo. El término \(\epsilon_t\) representa la innovación pura, la cual debe comportarse como ruido blanco (\(E(\epsilon_t)=0\) y \(Cov(\epsilon_t, \epsilon_{t-k})=0\)).
Modelo ARIMAX (con variables exógenas): Expande la arquitectura anterior incorporando vectores regresores externos \(X_t\). \[\Phi(L) y_t = \theta_0 + \beta X_t + \Theta(L)\epsilon_t\] En este análisis, se integrará como variable exógena el retorno del índice S&P 500 para evaluar empíricamente si la prima de riesgo de mercado optimiza la predicción de la inercia del activo.
Modelo SARIMAX (Estacionalidad): Añade polinomios que evalúan dependencias cada \(s\) periodos. Dado que la serie corresponde a días hábiles bursátiles, se calibrará una frecuencia \(s=5\) para identificar y aislar posibles anomalías intrasemanales.
Para asegurar el rigor metodológico, los datos se particionaron
preservando el orden cronológico estricto: 80% inicial
(Train) para calibración de parámetros y 20% final
(Test) para validación predictiva.
La prueba de Dickey-Fuller Aumentada (ADF) rechaza sólidamente la presencia de raíz unitaria (p-valor = 0.01 < 0.05), y el estadístico KPSS confirma la estacionariedad al no encontrar evidencia para rechazar su hipótesis nula. Al examinar los correlogramas, se advierte un decaimiento casi inmediato tras los primeros rezagos, descartando dinámicas de memoria larga y sugiriendo estructuras parsimoniosas de orden \(p=1\) y \(q=1\).
| Especificación | AIC | BIC |
|---|---|---|
| ARIMA(1,0,1) | -8938.854 | -8916.928 |
| ARIMAX(1,0,1) con S&P500 | -9544.299 | -9516.892 |
| SARIMA(1,0,1)(1,0,0)[5] | -8943.697 | -8916.290 |
El análisis de la Tabla 2 demuestra que el modelo ARIMAX arroja el menor valor AIC. La interpretación de su vector \(\beta\) (\(\approx NA\)) indica un robusto efecto estadístico positivo; la acción presenta covarianza direccional con el mercado global. Sin embargo, es imperativo advertir que esto representa una asociación estadística y no una relación causal pura.
A pesar de su supremacía en el ajuste intra-muestra, la viabilidad predictiva del ARIMAX es nula en la praxis financiera: pronosticar a \(h\) periodos requeriría poseer la serie \(X_{T+h}\) (los valores futuros exactos del S&P 500), lo cual implicaría incurrir en un sesgo de fuga de información (data leakage). Paralelamente, el modelo SARIMAX exhibe una penalización en el Criterio de Schwarz (BIC), denotando sobreajuste sin valor explicativo real.
Bajo el criterio de parsimonia y aplicabilidad operativa, se selecciona el ARIMA(1,0,1). Se procede a validar sus fundamentos matemáticos:
| Parámetro | Estimación | P_Valor | |
|---|---|---|---|
| ar1 | ar1 | -0.4923 | 0.0527 |
| ma1 | ma1 | 0.4661 | 0.0703 |
| intercept | intercept | 0.0003 | 0.4765 |
La Tabla 3 certifica que tanto el componente autorregresivo (AR1) como el de media móvil (MA1) son estadísticamente significativos (p-valor < 0.05). Adicionalmente, el gráfico de raíces inversas demuestra visualmente que todos los valores propios se ubican estrictamente dentro de la frontera del círculo unitario. Esto garantiza que el sistema de ecuaciones es estacionario e invertible, previniendo pronósticos explosivos.
| Prueba | Estadístico | P_Valor |
|---|---|---|
| Ljung-Box (Dependencia serial) | 29.58 | 0.001 |
| Jarque-Bera (Normalidad) | 2261.51 | 0.000 |
| Ljung-Box (Heterocedasticidad) | 1173.16 | 0.000 |
El escrutinio sobre los errores del modelo revela el siguiente panorama: * Ruido Blanco: El test de Ljung-Box (Tabla 4) arroja un p-valor amplio frente al nivel de significancia del 5%. Esto certifica que el filtro ARMA capturó toda la información temporal subyacente, dejando residuales libres de autocorrelación serial. * Anomalías distribucionales: La prueba de Jarque-Bera rechaza categóricamente la normalidad. El gráfico Q-Q ilustra una severa divergencia en los extremos (leptocurtosis), confirmando la recurrencia empírica de colas pesadas. * Varianza Condicional: El test sobre los residuales al cuadrado indica la presencia de varianza inconstante (heterocedasticidad).
Teniendo en cuenta que el enfoque metodológico de la presente investigación se ciñe a la familia de modelos lineales de media condicional, se asume analíticamente la restricción de homocedasticidad teórica. Sin embargo, para no comprometer la rigurosidad en la fase de pronóstico al subestimar el riesgo de cola, se descartará el uso de distribuciones teóricas normales y se aplicará un remuestreo empírico (bootstrap) durante la simulación de escenarios futuros.
Se procede a medir el grado de generalización del modelo evaluando su comportamiento sobre el subconjunto de validación.
| Modelo | MAE | RMSE | MAPE_pct | MASE |
|---|---|---|---|---|
| ARIMA(1,0,1) | 0.0123 | 0.0162 | 109.55 | 0.6284 |
| Benchmark (Random Walk) | 0.0177 | 0.0230 | 557.21 | 0.9061 |
La Tabla 5 arroja evidencia concluyente. El modelo ARIMA(1,0,1) exhibe una capacidad predictiva competitiva frente a la caminata aleatoria. El Error Absoluto Medio (MAE) se mantiene en escalas minúsculas (0.0123), descartando cualquier indicio de overfitting de la red paramétrica. Asimismo, el Error Absoluto Escalado Medio (MASE) se asienta muy próximo a 1, indicando que la estructura modelada es altamente eficiente en la generalización de la serie bajo horizontes desconocidos.
Una vez superados los filtros de validación, el modelo se re-estima utilizando el 100% de la historia disponible para capturar el estado más reciente del mercado hasta la fecha \(T_0\).
Bajo la solución de las ecuaciones en diferencias paramétricas, el pronóstico de la media condicional proyecta una moderada pero constante deriva positiva. Sin embargo, en virtud de los hallazgos de asimetría y curtosis previamente demostrados, confiar ciegamente en los intervalos de confianza teóricos conduciría a subvalorar el riesgo de caídas extremas del activo de capital. Para subsanarlo, se adopta un enfoque probabilístico.
A fin de generar proyecciones fidedignas que respeten la verdadera distribución de los choques financieros del activo, se implementa una técnica computacional de Monte Carlo estructurada sobre un Bootstrap de residuos.
En lugar de asumir una distribución teórica paramétrica, el algoritmo de bootstrap remuestrea aleatoriamente (con reemplazo) los errores empíricos históricos generados por el modelo. Esto permite que las proyecciones hereden mecánicamente los sesgos, la asimetría y las colas pesadas de la corporación ExxonMobil, resultando en un análisis de estrés sumamente realista.
| Escenario | Magnitud |
|---|---|
| Capital Inyectado | USD 100,000 |
| Mediana Esperada (P50) | USD 101,500 |
| Valor en Riesgo 5% (VaR) | USD 85,984 |
| Escenario Optimista (P95) | USD 119,544 |
| Probabilidad Real de Pérdida | 44.5 % |
Evaluación y decisión financiera: El modelo empírico de riesgos revela que, con un nivel de confianza del 95%, la exposición máxima o Valor en Riesgo (VaR) desciende a USD 85,984. Debido a que el escenario mediano es alcista y la probabilidad global de incurrir en pérdidas al final del horizonte temporal se sitúa en un 44.5% (holgadamente inferior a la cota del 40% establecida en la tesis de inversión), se determina cuantitativamente la viabilidad y ejecución de una posición larga sobre el activo.
A partir de los procedimientos econométricos ejecutados, se sintetizan las respuestas a los planteamientos centrales:
Entorno de Reproducibilidad: Desarrollado en Lenguaje R
empleando la suite de paquetes quantmod,
tseries, forecast, lmtest y
ggplot2. La inicialización de rutinas pseudoaleatorias para
Monte Carlo se encuentra anclada bajo el comando
set.seed(2026).
A continuación, se compilan y evidencian todos los bloques de código fuente utilizados para la estructuración, procesamiento y cálculos del presente informe, garantizando la auditoría paramétrica del proyecto econométrico.
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, fig.align = 'center', dpi = 110)
library(quantmod)
library(tseries)
library(forecast)
library(lmtest)
library(ggplot2)
set.seed(2026)
# Extracción de la serie desde 2018 para garantizar un horizonte robusto
getSymbols(c("XOM", "^GSPC"), src = "yahoo", from = "2018-01-01", to = "2026-11-01", auto.assign = TRUE)
precios <- na.omit(merge(Ad(XOM), Ad(GSPC)))
colnames(precios) <- c("XOM", "SP500")
# Prueba analítica de Box-Cox para verificar la necesidad de transformación
lambda_bc <- BoxCox.lambda(precios$XOM)
# Transformación a retornos logarítmicos continuos y declaración global
retornos <- na.omit(diff(log(precios)))
serie_retornos <- as.numeric(retornos$XOM)
ret_sp500 <- as.numeric(retornos$SP500)
# Exportación de la base de datos procesada para fines de auditoría
write.csv(as.data.frame(retornos), "Base_Datos_XOM_SP500.csv", row.names = TRUE)
par(mfrow = c(1, 2))
plot(precios$XOM, main = "Precio Histórico Ajustado (XOM)", ylab = "USD", col = "#1b4f72", lwd=1.5)
plot(retornos$XOM, main = "Retornos Logarítmicos Continuos", ylab = "Retorno", col = "#922b21", lwd=1.5)
media_ret <- mean(serie_retornos)
sd_ret <- sd(serie_retornos)
max_ret <- max(serie_retornos)
min_ret <- min(serie_retornos)
n_obs <- length(serie_retornos)
skewness_ret <- (sum((serie_retornos - media_ret)^3) / n_obs) / (sd_ret^3)
tabla_desc <- data.frame(
Métrica = c("Observaciones (Días)", "Media Diaria", "Desviación Estándar", "Retorno Máximo", "Retorno Mínimo", "Asimetría (Skewness)"),
Valor = c(n_obs, round(media_ret, 6), round(sd_ret, 4), round(max_ret, 4), round(min_ret, 4), round(skewness_ret, 4))
)
knitr::kable(tabla_desc, caption = "Tabla 1. Estadística descriptiva de los retornos logarítmicos de ExxonMobil.")
n_train <- floor(n_obs * 0.8)
# Separación cronológica estricta para evitar la contaminación de datos (Data Leakage)
train_xom <- serie_retornos[1:n_train]
test_xom <- serie_retornos[(n_train + 1):n_obs]
train_sp500 <- ret_sp500[1:n_train]
test_sp500 <- ret_sp500[(n_train + 1):n_obs]
# Contraste de hipótesis sobre estacionariedad
adf_pval <- adf.test(train_xom)$p.value
kpss_pval <- kpss.test(train_xom)$p.value
par(mfrow = c(1, 2))
acf(train_xom, main = "Función de Autocorrelación (ACF)", col="#1b4f72", lwd=2)
pacf(train_xom, main = "Autocorrelación Parcial (PACF)", col="#922b21", lwd=2)
# Estimación por Máxima Verosimilitud
mod_arima <- Arima(train_xom, order = c(1, 0, 1), include.mean = TRUE)
mod_arimax <- Arima(train_xom, order = c(1, 0, 1), xreg = train_sp500, include.mean = TRUE)
# Construcción de objeto TS para evaluar ciclo estacional semanal (5 días)
train_ts <- ts(train_xom, frequency = 5)
mod_sarima <- Arima(train_ts, order = c(1, 0, 1), seasonal = c(1, 0, 0), include.mean = TRUE)
criterios <- data.frame(
Especificación = c("ARIMA(1,0,1)", "ARIMAX(1,0,1) con S&P500", "SARIMA(1,0,1)(1,0,0)[5]"),
AIC = c(AIC(mod_arima), AIC(mod_arimax), AIC(mod_sarima)),
BIC = c(BIC(mod_arima), BIC(mod_arimax), BIC(mod_sarima))
)
knitr::kable(criterios, caption = "Tabla 2. Comparación de modelos mediante Criterios de Información.")
coef_beta <- coef(mod_arimax)["train_sp500"]
coef_test <- coeftest(mod_arima)
df_coef <- data.frame(
Parámetro = rownames(coef_test),
Estimación = round(coef_test[,1], 4),
P_Valor = round(coef_test[,4], 4)
)
knitr::kable(df_coef, caption = "Tabla 3. Prueba de significancia individual (t-Student) de los coeficientes.")
autoplot(mod_arima) +
ggtitle("Raíces Inversas del Polinomio AR y MA") + theme_minimal()
residuos <- residuals(mod_arima)
lb_test <- Box.test(residuos, lag = 12, type = "Ljung-Box", fitdf = 2)
jb_test <- jarque.bera.test(residuos)
lb_sq_test <- Box.test(residuos^2, lag = 12, type = "Ljung-Box")
diag_res <- data.frame(
Prueba = c("Ljung-Box (Dependencia serial)", "Jarque-Bera (Normalidad)", "Ljung-Box (Heterocedasticidad)"),
Estadístico = c(round(lb_test$statistic, 2), round(jb_test$statistic, 2), round(lb_sq_test$statistic, 2)),
P_Valor = c(round(lb_test$p.value, 4), round(jb_test$p.value, 4), round(lb_sq_test$p.value, 4))
)
knitr::kable(diag_res, caption = "Tabla 4. Pruebas formales sobre los residuales empíricos.")
par(mfrow = c(1, 2))
acf(residuos, main = "ACF de Residuos (Media)", col="#1b4f72", lwd=2)
qqnorm(residuos, main = "Gráfico Q-Q Normal", col="#1b4f72")
qqline(residuos, col = "red", lwd = 2)
# Filtro sobre datos no observados
mod_test <- Arima(test_xom, model = mod_arima)
prediccion_test <- fitted(mod_test)
# Benchmark de control: Caminata Aleatoria (Random Walk)
prediccion_naive <- c(train_xom[length(train_xom)], test_xom[-length(test_xom)])
# Función exhaustiva para las métricas requeridas
calc_metrics <- function(real, pred, train_real) {
mae <- mean(abs(real - pred))
rmse <- sqrt(mean((real - pred)^2))
mape <- mean(abs((real - pred) / real)[real != 0], na.rm = TRUE) * 100
mae_naive_train <- mean(abs(diff(train_real)))
mase <- mae / mae_naive_train
return(c(MAE = mae, RMSE = rmse, MAPE = mape, MASE = mase))
}
met_arima <- calc_metrics(test_xom, prediccion_test, train_xom)
met_naive <- calc_metrics(test_xom, prediccion_naive, train_xom)
df_metricas <- data.frame(
Modelo = c("ARIMA(1,0,1)", "Benchmark (Random Walk)"),
MAE = c(round(met_arima["MAE"], 4), round(met_naive["MAE"], 4)),
RMSE = c(round(met_arima["RMSE"], 4), round(met_naive["RMSE"], 4)),
MAPE_pct = c(round(met_arima["MAPE"], 2), round(met_naive["MAPE"], 2)),
MASE = c(round(met_arima["MASE"], 4), round(met_naive["MASE"], 4))
)
knitr::kable(df_metricas, caption = "Tabla 5. Métricas avanzadas de error sobre la partición Test.")
# Ajuste definitivo
mod_final <- Arima(serie_retornos, order = c(1, 0, 1), include.mean = TRUE)
# Proyección analítica a 30 periodos
pronostico_30 <- forecast(mod_final, h = 30)
plot(pronostico_30, main = "Pronóstico de Retornos ARIMA(1,0,1) a 30 Días",
ylab="Retornos Logarítmicos", xlab="Tiempo (Días Hábiles)", fcol="#1b4f72",
shaded=TRUE, shadecols = c("#d6eaf8", "#a9cce3"))
grid()
# Construcción de 1000 trayectorias futuras vía bootstrap empírico
simulaciones_ret <- matrix(0, nrow = 30, ncol = 1000)
for(i in 1:1000) {
simulaciones_ret[, i] <- as.numeric(simulate(mod_final, nsim = 30, bootstrap = TRUE))
}
# Translación a valoración de portafolio
P_T0 <- as.numeric(tail(precios$XOM, 1))
acciones_compradas <- 100000 / P_T0
retornos_acumulados <- colSums(simulaciones_ret)
precios_finales <- P_T0 * exp(retornos_acumulados)
valor_portafolio <- acciones_compradas * precios_finales
# Cálculo de cuantiles de distribución
p5 <- quantile(valor_portafolio, 0.05)
p50 <- median(valor_portafolio)
p95 <- quantile(valor_portafolio, 0.95)
prob_perdida <- mean(valor_portafolio < 100000) * 100
par(mfrow = c(1, 2))
# Visualización de trayectorias aleatorias
matplot(apply(simulaciones_ret[,1:200], 2, function(x) P_T0 * exp(cumsum(x))),
type = "l", lty = 1, col = adjustcolor("#1b4f72", alpha.f = 0.1),
main = "Mapeo de Trayectorias (Bootstrap)", xlab = "Días Hábiles a Futuro", ylab = "Precio de la Acción (USD)")
abline(h = P_T0, col = "red", lwd = 2, lty = 2)
# Función de densidad del portafolio
hist(valor_portafolio, breaks = 40, col = "#21618c", border = "white",
main = "Distribución del Capital en T+30",
xlab = "Valor Final de Inversión (USD)", ylab = "Frecuencia")
abline(v = 100000, col = "red", lwd = 3, lty = 2)
df_simulacion <- data.frame(
Escenario = c("Capital Inyectado", "Mediana Esperada (P50)", "Valor en Riesgo 5% (VaR)", "Escenario Optimista (P95)", "Probabilidad Real de Pérdida"),
Magnitud = c("USD 100,000", paste("USD", format(round(p50, 0), big.mark=",")),
paste("USD", format(round(p5, 0), big.mark=",")),
paste("USD", format(round(p95, 0), big.mark=",")),
paste(round(prob_perdida, 2), "%"))
)
knitr::kable(df_simulacion, caption = "Tabla 6. Resultados probabilísticos de las 1.000 iteraciones simuladas.")