library(readxl)
## Warning: package 'readxl' was built under R version 4.5.2
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.5.1
library(ggcorrplot)
library(imputeTS)
## Warning: package 'imputeTS' was built under R version 4.5.2
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
# Con esta librería podemos utilizar la función "dollar" para expresar cifras en unidades monetarias.
library(scales)
## Warning: package 'scales' was built under R version 4.5.1
library(quantmod)
## Warning: package 'quantmod' was built under R version 4.5.2
## Cargando paquete requerido: xts
## Cargando paquete requerido: zoo
## 
## Adjuntando el paquete: 'zoo'
## The following object is masked from 'package:imputeTS':
## 
##     na.locf
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## Cargando paquete requerido: TTR
library(writexl)
## Warning: package 'writexl' was built under R version 4.5.2
library(xts)
getSymbols(Symbols= c("MXN=X"), from="2020-01-01",to="2025-10-31", 
           periodicity = "daily", src = "yahoo")
## Warning: MXN=X contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## [1] "MXN=X"
getSymbols(Symbols= c("ZC=F"), from="2020-01-01",to="2025-10-31", 
           periodicity = "daily", src = "yahoo")
## Warning: ZC=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## [1] "ZC=F"
getSymbols(Symbols= c("ZW=F"), from="2020-01-01",to="2025-10-31", 
           periodicity = "daily", src = "yahoo")
## Warning: ZW=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## [1] "ZW=F"
usdmxn<-(`MXN=X`[,6])
Fwheat<-(`ZW=F`[,6])
Fcorn<-(`ZC=F`[,6])
## Esta instrucción es la verificar si tenemos espacios en blanco
usdmxn[!complete.cases(usdmxn),]
##            MXN=X.Adjusted
## 2025-01-01             NA
## 2025-04-17             NA
## 2025-04-20             NA
Fwheat[!complete.cases(Fwheat),]
##            ZW=F.Adjusted
## 2023-11-23            NA
## 2025-05-26            NA
## 2025-06-19            NA
## 2025-07-04            NA
Fcorn[!complete.cases(Fcorn),]
##            ZC=F.Adjusted
## 2023-11-23            NA
## 2025-05-26            NA
## 2025-06-19            NA
## 2025-07-04            NA
usdmxn.na<-na.omit(usdmxn)
Fwheat.na<-na.omit(Fwheat)
Fcorn.na<-na.omit(Fcorn)
rets.usdmxn <- diff(log(usdmxn.na))
rets.Fwheat <- diff(log(Fwheat.na))
rets.Fcorn  <- diff(log(Fcorn.na))
# Primero convertimos cada serie a xts si no lo es
usdmxn.xt  <- as.xts(usdmxn)
Fwheat.xt  <- as.xts(Fwheat)
Fcorn.xt   <- as.xts(Fcorn)

# Hacemos el merge por fecha con NAs permitidos
merged <- merge(usdmxn.xt, Fwheat.xt, Fcorn.xt)
merged <- na.omit(merged)
rets <- diff(log(merged))
rets <- na.omit(rets)
colnames(rets) <- c("rets.usdmxn", "rets.Fwheat", "rets.Fcorn")
write_xlsx(as.data.frame(rets), "rets_series.xlsx")
  1. Estadísticos
## Para obtener la varianza de los cambios porcentuales
rets <- na.omit(rets)

mean.usdmxn <- mean(rets$rets.usdmxn, na.rm = TRUE)
var.usdmxn  <- var(rets$rets.usdmxn,  na.rm = TRUE)
vol.usdmxn  <- sd(rets$rets.usdmxn,   na.rm = TRUE)

mean.usdmxn
## [1] -1.798561e-05
var.usdmxn
##              rets.usdmxn
## rets.usdmxn 7.647583e-05
vol.usdmxn
## [1] 0.008745046
mean.Fwheat <- mean(rets$rets.Fwheat, na.rm = TRUE)
var.Fwheat  <- var(rets$rets.Fwheat,  na.rm = TRUE)
vol.Fwheat  <- sd(rets$rets.Fwheat,   na.rm = TRUE)

mean.Fwheat
## [1] -5.152401e-05
var.Fwheat
##              rets.Fwheat
## rets.Fwheat 0.0005037217
vol.Fwheat
## [1] 0.02244374
mean.Fcorn <- mean(rets$rets.Fcorn, na.rm = TRUE)
var.Fcorn  <- var(rets$rets.Fcorn,  na.rm = TRUE)
vol.Fcorn  <- sd(rets$rets.Fcorn,   na.rm = TRUE)

mean.Fcorn
## [1] 7.322026e-05
var.Fcorn
##              rets.Fcorn
## rets.Fcorn 0.0003476655
vol.Fcorn
## [1] 0.01864579
max(rets$rets.usdmxn)
## [1] 0.0608428
min(rets$rets.usdmxn)
## [1] -0.037751
max(rets$rets.Fwheat)
## [1] 0.1970139
min(rets$rets.Fwheat)
## [1] -0.1129709
max(rets$rets.Fcorn)
## [1] 0.07657358
min(rets$rets.Fcorn)
## [1] -0.1909974

0.1 Creación de un excel de los precios de los 3 índices

library(xts)
library(writexl)
library(readxl)

## 1. Unir las tres series por fecha
merged <- merge(usdmxn.xt, Fwheat.xt, Fcorn.xt)   # índices por fecha
merged <- na.omit(merged)                         # quita filas con NA

## 2. Convertir a data.frame y poner nombres de columnas
df_precios <- data.frame(
  Fecha     = index(merged),
  P.usdmxn  = as.numeric(merged[, 1]),
  P.Fwheat  = as.numeric(merged[, 2]),
  P.Fcorn   = as.numeric(merged[, 3])
)

getwd()
## [1] "C:/Users/GTX/OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey/Documents/Tec/Negocios/5to semestre/Periodo 5/Financial modelling and programming/modelopoisson"
list.files()
## [1] "dailygccdatavar.xlsx"     "modelopoisson.Rmd"       
## [3] "multassets_mc.Rmd"        "PF_sec_agropecuario.html"
## [5] "PF_sec_agropecuario.Rmd"  "precios_series.xlsx"     
## [7] "rets_eruo.xlsx"           "rets_series.xlsx"
## 3. Exportar a Excel
write_xlsx(df_precios, "precios_series.xlsx")

1 Data wrangling

1.1 Lectura de datos

datagcc<-read_excel("precios_series.xlsx")
# Con esta instrucción conocemos la cantidad de filas con espacios tipo NA (Not Available)
datagcc[!complete.cases(datagcc),]
## # A tibble: 0 × 4
## # ℹ 4 variables: Fecha <dttm>, P.usdmxn <dbl>, P.Fwheat <dbl>, P.Fcorn <dbl>
# Hacemos interpolación tipo spline para ocupar los espacios en blanco, de esa forma se evita la pérdida de información.
precios.int<-na_interpolation(datagcc,option = "spline")
# Nuevamente se verifica la presencia de NAs
precios.int[!complete.cases(precios.int),]
## # A tibble: 0 × 4
## # ℹ 4 variables: Fecha <dttm>, P.usdmxn <dbl>, P.Fwheat <dbl>, P.Fcorn <dbl>

2 Transformación a cambios porcentuales

La transformación se realiza con cambios porcentuales de su forma normal, en vez de utilizar logaritmo de precios, ya que se tienen precios negativos de los futuros del WTI y al obtener las primeras diferencias se genera error.

# Convertimos a objeto tipo matriz
precios.matriz<-as.matrix(precios.int)
# Quitar la columna Fecha (asumo que es la 1)
precios.matriz <- data.matrix(precios.int[, -1])  # o precios.int[ , c("P.usdmxn","P.Fwheat","P.Fcorn")]

# Primeras diferencias simples
precios.diff <- diff(precios.matriz, lag = 1)

rets <- diff(log(precios.matriz), lag = 1)
colnames(rets) <- c("rets.usdmxn", "rets.Fwheat", "rets.Fcorn")
# Obtenemos primeras diferencias
precios.diff<-diff(precios.matriz,lag = 1)
# Se calcula el cambio porcentual. Tener mucho cuidado que en el denominador se refiere al objeto de los precios.matriz; de esa forma: precio de hoy menos precio ayer entre precio de ayer.
rets.precios<-as.data.frame(precios.diff/precios.matriz[-1,])

3 Correlaciones y matriz de Cholesky

3.1 Matriz de correlaciones

correts<-cor(rets.precios)
ggcorrplot(correts,lab=TRUE,tl.cex = 6,lab_size = 2)
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the ggcorrplot package.
##   Please report the issue at <https://github.com/kassambara/ggcorrplot/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

4 Matriz de Cholesky

La descomposición de Cholesky permite “descomponer” una matriz original en una matriz triangular [A], que después al obtener su traspuesta, será la matriz que buscamos como insumo para obtener números aleatorios correlacionados.

# cor=A*AT
# A es la matriz de Cholesky "mat.chol"
mat.chol<-chol(correts)
# AT es la traspuesta de la matriz de Cholesky
tmat.chol<-t(mat.chol)
tmat.chol
##             P.usdmxn  P.Fwheat   P.Fcorn
## P.usdmxn  1.00000000 0.0000000 0.0000000
## P.Fwheat -0.06645563 0.9977894 0.0000000
## P.Fcorn  -0.02311026 0.4354520 0.8999153

5 Números aleatorios correlacionados

Primero obtengamos una matriz de números aleatorios independientes de un renglón y después generalizamos a “n”. Se observa que el renglón corresponde a 10 columnas ya que son 10 factores de riesgo.

set.seed(2425)

obs       <- 100        # número de escenarios
n_factores <- 3         # usd/mxn, wheat, corn

# 1. Matriz de choques independientes N(0,1)
matrands <- matrix(rnorm(obs * n_factores, mean = 0, sd = 1),
                   nrow = obs,
                   ncol = n_factores)

# 2. Matriz de Cholesky (de tu matriz de correlaciones o covarianzas)
#    correts ya es cor(rets.precios) o similar
mat.chol  <- chol(correts)     # 3x3
tmat.chol <- t(mat.chol)       # 3x3, triangular inferior

# 3. Obtener choques correlacionados
#    (100x3) %*% (3x3) = (100x3)
aleafactores.correl <- as.data.frame(matrands %*% tmat.chol)

head(aleafactores.correl)
##     P.usdmxn   P.Fwheat      P.Fcorn
## 1 -0.5567355 -0.3644732 -1.123517244
## 2  0.7306805  0.6334775  1.141765582
## 3  0.8900516 -0.4588709  0.848406457
## 4  1.4049766 -0.2294881  0.009080274
## 5  0.7621400  1.9827812  0.890469801
## 6  0.3944832 -1.0071678 -1.164503311

Después, extraemos la columna de los números aleatorios correlacionados de dos factores de riesgos USDMXN, Fwheat y Fcorn.

# USDMXN
alea.usdmxn<-as.matrix(aleafactores.correl[c("P.usdmxn")])
# Fwheat
alea.Fwheat<-as.matrix(aleafactores.correl[c("P.Fwheat")])
#Fcorn
alea.Fcorn<-as.matrix(aleafactores.correl[c("P.Fcorn")])

6 Simulación de precios

Se simulan los índices USDMXN, Fwheat y Fcorn

6.1 Estadísticos de los rendimientos de los índices

# Obtenemos el último precio de usdmxn, se trabaja sobre el objeto "precios.int" para poder aplicar el símbolo $ y pueda leer sobre la columna P.usdmxn
lastusdmxn<-tail(precios.int$P.usdmxn,1)
lastusdmxn
## [1] 18.4657
meanusdmxn<-mean(rets.precios$P.usdmxn)
meanusdmxn
## [1] -5.610813e-05
varusdmxn<-var(rets.precios$P.usdmxn)
varusdmxn
## [1] 7.597163e-05
sdusdmxn<-sd(rets.precios$P.usdmxn)
sdusdmxn
## [1] 0.008716171
# Último precio de Wheat
lastFwheat <- tail(precios.int$P.Fwheat, 1)
lastFwheat
## [1] 524.25
# Estadísticos de rendimientos
meanFwheat <- mean(rets.precios$P.Fwheat)
varFwheat  <- var(rets.precios$P.Fwheat)
sdFwheat   <- sd(rets.precios$P.Fwheat)

meanFwheat
## [1] -0.000302285
varFwheat
## [1] 0.0004989606
sdFwheat
## [1] 0.02233743
# Último precio de Corn
lastFcorn <- tail(precios.int$P.Fcorn, 1)
lastFcorn
## [1] 430.25
# Estadísticos de rendimientos
meanFcorn <- mean(rets.precios$P.Fcorn)
varFcorn  <- var(rets.precios$P.Fcorn)
sdFcorn   <- sd(rets.precios$P.Fcorn)

meanFcorn
## [1] -0.0001025247
varFcorn
## [1] 0.0003608217
sdFcorn
## [1] 0.01899531

6.2 Simulación de usdmxn

h=1
simulusdmxn<-NULL;
for (rands in alea.usdmxn) {
  preciotemp<-lastusdmxn*exp((meanusdmxn-0.5*varusdmxn)*h+sdusdmxn*sqrt(h)*rands)
  simulusdmxn<-rbind(simulusdmxn,preciotemp)
}

6.3 Simulación de Fwheat

h <- 1
simulFwheat <- NULL
for (rands in alea.Fwheat) {
  preciotemp <- lastFwheat * exp((meanFwheat - 0.5 * varFwheat) * h +
                                   sdFwheat * sqrt(h) * rands)
  simulFwheat <- rbind(simulFwheat, preciotemp)
}

6.4 Simulación de Fcorn

h <- 1
simulFcorn <- NULL
for (rands in alea.Fcorn) {
  preciotemp <- lastFcorn * exp((meanFcorn - 0.5 * varFcorn) * h +
                                  sdFcorn * sqrt(h) * rands)
  simulFcorn <- rbind(simulFcorn, preciotemp)
}

7 Valor en Riesgo

7.1 Preliminares

Definimos nivel de significancia y posición

alfa=0.05
posicion<-alfa*obs

Reordenamos los precios simulados

sortusdmxn<-as.matrix(sort(simulusdmxn))
sortFwheat<-as.matrix(sort(simulFwheat))
sortFcorn<-as.matrix(sort(simulFcorn))

Obtenemos la posición de los precios ordenados

qalfa.usdmxn<-sortusdmxn[posicion,]
qalfa.Fwheat<-sortFwheat[posicion,]
qalfa.Fcorn<-sortFcorn[posicion,]
qalfa.usdmxn
## [1] 18.21177
qalfa.Fwheat
## [1] 501.7428
qalfa.Fcorn
## [1] 417.2687

7.2 Valor en riesgo

# usdmxn
VaR.usdmxn<-((qalfa.usdmxn/lastusdmxn)-1)
VaR.usdmxn
## [1] -0.01375164
# Suponemos posición de 1 millón de USD en ADX
VaR.m.usdmxn<-1000000*VaR.usdmxn
VaR.m.usdmxn
## [1] -13751.64
# Fwheat
VaR.Fwheat <- ((qalfa.Fwheat / lastFwheat) - 1)
VaR.Fwheat
## [1] -0.04293216
# Supongamos posición de 1 millón
VaR.m.Fwheat <- 1000000 * VaR.Fwheat
VaR.m.Fwheat
## [1] -42932.16
# Fcorn
VaR.Fcorn <- ((qalfa.Fcorn / lastFcorn) - 1)
VaR.Fcorn
## [1] -0.03017149
# Supongamos posición de 1 millón
VaR.m.Fcorn <- 1000000 * VaR.Fcorn
VaR.m.Fcorn
## [1] -30171.49

La pérdida esperada en los 3 factores de riesgo, después de haber reconocido el efecto de la correlación entre ellos.

VaR.int=dollar(VaR.m.usdmxn+VaR.m.Fwheat+VaR.m.Fcorn)
VaR.int
## [1] "-$86,855.28"

8 Contraste con VaR sin números aleatorios correlacionados

Vamos a suponer que los factores de riesgos son independientes, ¿cuál sería la pérdida en la posición total en los 3 factores? Primero creamos números aleatorios independientes.

8.1 Simulación de precios no correlacionados

8.1.1 Simulación independiente de usdmxn

# USD/MXN sin correlación
set.seed(2121)
h <- 1

rand.usdmxn <- as.matrix(rnorm(obs, mean = 0, sd = 1))
simul.usdmxn.sc <- NULL

for (rands in rand.usdmxn) {
  preciotemp <- lastusdmxn * exp((meanusdmxn - 0.5 * varusdmxn) * h +
                                 sdusdmxn * sqrt(h) * rands)
  simul.usdmxn.sc <- rbind(simul.usdmxn.sc, preciotemp)
}

8.1.2 Simulación independiente de Fwheat

# Wheat sin correlación
rand.Fwheat <- as.matrix(rnorm(obs, mean = 0, sd = 1))
simul.Fwheat.sc <- NULL

for (rands in rand.Fwheat) {
  preciotemp <- lastFwheat * exp((meanFwheat - 0.5 * varFwheat) * h +
                                 sdFwheat * sqrt(h) * rands)
  simul.Fwheat.sc <- rbind(simul.Fwheat.sc, preciotemp)
}

8.1.3 Simulación independiente de Fcorn

# Corn sin correlación
rand.Fcorn <- as.matrix(rnorm(obs, mean = 0, sd = 1))
simul.Fcorn.sc <- NULL

for (rands in rand.Fcorn) {
  preciotemp <- lastFcorn * exp((meanFcorn - 0.5 * varFcorn) * h +
                                sdFcorn * sqrt(h) * rands)
  simul.Fcorn.sc <- rbind(simul.Fcorn.sc, preciotemp)
}

Reordenamos los precios simulados

sortusdmxn.sc<-as.matrix(sort(simul.usdmxn.sc))
sortFwheat.sc<-as.matrix(sort(simul.Fwheat.sc))
sortFcorn.sc<-as.matrix(sort(simul.Fcorn.sc))

Obtenemos la posición de los precios ordenados

qalfa.usdmxn.sc<-sortusdmxn.sc[posicion,]
qalfa.Fwheat.sc<-sortFwheat.sc[posicion,]
qalfa.Fcorn.sc<-sortFcorn.sc[posicion,]
qalfa.usdmxn.sc
## [1] 18.2287
qalfa.Fwheat.sc
## [1] 500.5353
qalfa.Fcorn.sc
## [1] 417.8066

8.2 Valor en riesgo

# usdmxn
lastusdmxn
## [1] 18.4657
VaR.usdmxn.sc<-((qalfa.usdmxn.sc/lastusdmxn)-1)
VaR.usdmxn.sc
## [1] -0.01283445
# Suponemos posición de 1 millón de USD en usdmxn
VaR.m.usdmxn.sc<-1000000*VaR.usdmxn.sc
VaR.m.usdmxn.sc
## [1] -12834.45
# Wheat
lastFwheat
## [1] 524.25
VaR.Fwheat.sc <- ((qalfa.Fwheat.sc / lastFwheat) - 1)
VaR.Fwheat.sc
## [1] -0.04523544
# Suponemos posición de 1 millón en Wheat
VaR.m.Fwheat.sc <- 1000000 * VaR.Fwheat.sc
VaR.m.Fwheat.sc
## [1] -45235.44
# Wheat
lastFcorn
## [1] 430.25
VaR.Fcorn.sc <- ((qalfa.Fcorn.sc / lastFcorn) - 1)
VaR.Fcorn.sc
## [1] -0.02892142
# Suponemos posición de 1 millón en Corn
VaR.m.Fcorn.sc <- 1000000 * VaR.Fcorn.sc
VaR.m.Fcorn.sc
## [1] -28921.42

La pérdida esperada en los 3 factores de riesgo

VaR.int.sc=dollar(VaR.m.usdmxn.sc+VaR.m.Fwheat.sc+VaR.m.Fcorn.sc)
VaR.int.sc
## [1] "-$86,991.32"