Presentación

Esta publicación tiene dos objetivos, uno personal y otro general. El objetivo personal es tener en limpio y a la mano el código para poder replicar el índice de tipo de cambio real que Dani Rodrik utiliza para su análisis de esta variable y el crecimiento económico. El segundo objetivo, el general, es simplemente compartirlo a todo aquel que guste, aprovechando que la apreciación del peso mexicano es un tema de moda.

No hay comando en este script que no se encuentre en sitios y foros como stackoverflow, repositorios de github, rpubs, los manuales de Wickham, The R graph gallery, etc.

Aviso: Recomiendo leer el artículo de Rodrik. Como insumo para sus cálculos él utiliza la Penn World Table 6.2, así que haré lo mismo. El cálculo con la versión más reciente, la Penn World Table 10.01, se deja al lector, sin embargo, de tener dificultades pueden pedírmelo personalmente. Si algún enlace no abre con click izquierdo, intenta usar click derecho y abrir en otra pestaña.

Cualquier corrección o comentario házmelo saber por este medio o a través de mi twitter ecodiegoale.

Preparación y Paquetes

rm(list = ls()) #para borrar todos los objetos en la memoria de la consola
options(scipen=999) #para desactivar la notación científica

Los paquetes que usaremos.

#Si no cuentas con los paquetes, instálalos utilizando el comando install.packages("ejemplo")

library(tidyverse)
library(ggthemes)
library(reshape2)
library(readxl)

Ahora la base de datos.

# Este es el URL
url <- "https://www.rug.nl/ggdc/docs/pwt62_data.xlsx"

# Descargamos el archivo
download.file(url, destfile = "pwt62_data.xlsx", mode = "wb")

#Vemos cuántas hojas tiene y sus nombres
excel_sheets("pwt62_data.xlsx")
## [1] "Variables" "Data"

En la hoja Variables viene la descripción de las variables o columnas, en la hoja Data viene la base de datos de la PWT6.2.

tabla <- "Data"
data <- read_excel("pwt62_data.xlsx", sheet = tabla)

variables <- "Variables"
codebook <- read_excel("pwt62_data.xlsx", sheet = variables)

Vamos a ver qué variables trae la PWT6.2 (para más información abrir este enlace).

print(codebook)
## # A tibble: 25 × 2
##    POP   Population                            
##    <chr> <chr>                                 
##  1 XRAT  Exchange Rate                         
##  2 PPP   Purchasing Power Parity over GDP      
##  3 cgdp  Real Gross Domestic Product per Capita
##  4 cc    Consumption Share of CGPD             
##  5 cg    Government Share of CGDP              
##  6 ci    Investment Share of CGDP              
##  7 p     Price Level of Gross Domestic Product 
##  8 pc    Price Level of Consumption            
##  9 pg    Price Level of Government             
## 10 pi    Price Level of Investment             
## # … with 15 more rows

Índice de tipo de cambio real ajustado por el efecto Balassa-Samuelson

1. Tipo de cambio real

Siguiendo los pasos de Rodrik, primero se calcula un índice del tipo de cambio real:

\(lnRER_{it}=ln(XRAT_{it}/PPP_{it})\)

donde:

  • \(RER\) es tipo de cambio real
  • \(XRAT\) es tipo de cambio nominal
  • \(PPP\) es un índice de paridad del poder adquisitivo sobre el PIB

\(i\) es un subíndice para los países y \(t\) es un subíndice para periodos de tiempo (de 5 años).

Values of RER greater than one indicate that the value of the currency is lower (more depreciated) than indicated by purchasing power parity. However, in practice nontradable goods are also cheaper in poorer countries (through the Balassa-Samuelson effect), which requires an adjustment (Rodrik, 2008, p. 371).

Vamos a generar la variable que indique el periodo de tiempo de 5 años. Revisamos cuántos años incluye la base.

unique(data$year)
##  [1] 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964
## [16] 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979
## [31] 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994
## [46] 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004

El periodo abarca 55 años, vamos a generar una variable dummy para los años, cada cinco años tomará el valor \(n+1\), por lo que cada país tendrá una serie (f_time) donde los valores serán desde 1 hasta 11.

data$f_time <- ceiling((data$year - 1949) / 5)
head(data$f_time, n = 15 )
##  [1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3

Ya tenemos las variables que necesitamos, ahora vamos a revisar a grandes rasgos cómo está compuesta la PWT6.2:

summary(data)
##    country          country isocode         year           POP           
##  Length:10340       Length:10340       Min.   :1950   Min.   :      7.3  
##  Class :character   Class :character   1st Qu.:1963   1st Qu.:   1142.5  
##  Mode  :character   Mode  :character   Median :1977   Median :   4388.7  
##                                        Mean   :1977   Mean   :  22848.7  
##                                        3rd Qu.:1991   3rd Qu.:  12942.1  
##                                        Max.   :2004   Max.   :1294845.6  
##                                                       NA's   :11         
##      XRAT               PPP                cgdp                cc           
##  Length:10340       Length:10340       Length:10340       Length:10340      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##       cg                 ci                 p                  pc           
##  Length:10340       Length:10340       Length:10340       Length:10340      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##       pg                 pi               openc               cgnp          
##  Length:10340       Length:10340       Length:10340       Length:10340      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##       y                rgdpl              rgdpch            rgdpeqa         
##  Length:10340       Length:10340       Length:10340       Length:10340      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##    rgdpwok             rgdptt             openk                kc           
##  Length:10340       Length:10340       Length:10340       Length:10340      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##       kg                 ki              grgdpch              f_time  
##  Length:10340       Length:10340       Length:10340       Min.   : 1  
##  Class :character   Class :character   Class :character   1st Qu.: 3  
##  Mode  :character   Mode  :character   Mode  :character   Median : 6  
##                                                           Mean   : 6  
##                                                           3rd Qu.: 9  
##                                                           Max.   :11  
## 

Observamos que las variables que nos interesan son del tipo character, y nosotros necesitamos que sean números. Primero, vamos a quedarnos con las variables que nos importan.

data <- data%>%
  dplyr::select(year, country, f_time, XRAT, PPP, rgdpch)

Luego, convertimos a número las variables para poder operarlas.

data$xrat  <-as.numeric(data$XRAT)
data$ppp   <-as.numeric(data$PPP)
data$rgdpch<-as.numeric(data$rgdpch)
data$f_time<-as.factor(data$f_time)#La función lm() convierte factores a dummies de manera automática.

Otro aspecto a considerar es la cantidad de NAs que hay en nuestra base, para nuestro caso simplemente vamos a omitirlos mediante el siguiente comando:

data <- na.omit(data)

Ya podemos calcular \(lnRER_{it}\) como indica Rodrik.

data<- data%>%
  mutate(xratppp = xrat/ppp,
         ln_rer = log(xratppp),
         ln_rgdpch = log(rgdpch))

2. Ajuste Balassa-Samuelson

Una vez calculado el tipo de cambio real, se ajusta por el efecto Balassa-Samuelson por medio de una regresión de datos panel del siguiente tipo:

\(lnRER_{it}=\alpha+\beta RDPCH_{it}+ f_t + u_{it}\)

donde:

  • \(RDPCH_{it}\) es el PIB per cápita
  • \(f_t\) es el efecto fijo del periodo de tiempo
  • \(u_{it}\) es el término de error

Para Rodrik, usando datos de la Penn World Table 6.2, el estimador \(\hat{\beta}=-0.24\).

Procedemos a calcular dicha regresión:

lnrer <- lm(data = data,
            ln_rer ~ ln_rgdpch + f_time -1)
summary(lnrer)
## 
## Call:
## lm(formula = ln_rer ~ ln_rgdpch + f_time - 1, data = data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -3.1496 -0.2575  0.0057  0.2603  3.9720 
## 
## Coefficients:
##            Estimate Std. Error t value            Pr(>|t|)    
## ln_rgdpch -0.229434   0.005241  -43.78 <0.0000000000000002 ***
## f_time1    2.330466   0.051242   45.48 <0.0000000000000002 ***
## f_time2    2.422105   0.049949   48.49 <0.0000000000000002 ***
## f_time3    2.503465   0.046812   53.48 <0.0000000000000002 ***
## f_time4    2.524475   0.047442   53.21 <0.0000000000000002 ***
## f_time5    2.501989   0.046264   54.08 <0.0000000000000002 ***
## f_time6    2.376269   0.046847   50.72 <0.0000000000000002 ***
## f_time7    2.465214   0.047098   52.34 <0.0000000000000002 ***
## f_time8    2.606330   0.047300   55.10 <0.0000000000000002 ***
## f_time9    2.703895   0.047095   57.41 <0.0000000000000002 ***
## f_time10   2.772996   0.047246   58.69 <0.0000000000000002 ***
## f_time11   2.893721   0.048317   59.89 <0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4956 on 7322 degrees of freedom
## Multiple R-squared:  0.6944, Adjusted R-squared:  0.6939 
## F-statistic:  1386 on 12 and 7322 DF,  p-value: < 0.00000000000000022

Para mí \(\hat{\beta}=-0.229\), además de que es altamente significativa.

…suggesting a strong and precisely estimated Balassa-Samuelson effect: when incomes rise by 10 percent, the real exchange rate falls by around 2.4 percent. (Rodrik, 2008, p. 371).

3. Índice de tipo de cambio real

Por último, se calcula lo que Rodrik llama el índice de subvaluación de tipo de cambio.

\(lnUNDERVAL_{it}=lnRER_{it}-ln\hat{RER_{it}}\)

Esto es la diferencia del tipo de cambio real observado menos el estimado por la regresión anterior.

#Calculamos el tipo de cambio real estimado
data$lnrer_hat <- predict(lnrer)

#Hacemos la diferencia del observado menos el estimado
data<- data%>%
  mutate(lnunder = ln_rer - lnrer_hat)

Defined in this way, UNDERVAL is comparable across countries and over time. Whenever UNDERVAL exceeds unity, it indicates that the exchange rate is set such that goods produced at home are relatively cheap in dollar terms: the currency is undervalued. When UNDERVAL is below unity, the currency is overvalued. (Rodrik, 2008, p. 372).

El tipo de cambio real para México

Filtramos para México.

data.mx <- data%>%
  filter(country == "Mexico")

Ahora graficamos.

tcr_mx <- ggplot(data = data.mx, aes(x = year, y = lnunder))+
  geom_hline(yintercept = 0, size=1, linetype = 'dashed', alpha = 0.5)+
  geom_line(aes(y = lnunder), colour="#00AFBB",cex = 1)+
  theme_bw()+
  labs(title="México: Índice de tipo de cambio real",
       caption="Fuente: Elaboración propia con información de Penn World Table 6.2.")+
  xlab(" ")+
  ylab("Logaritmo natural")
tcr_mx

El tipo de cambio real multilateral de Banxico

Vamos a comparar el comportamiento de nuestro tipo de cambio real que calculamos con el índice de tipo de cambio real multilateral que elabora el Banco de México. Para esto usaremos la API del propio banco. No me detendré para explicar cómo usar la API, eso ya lo hice en una entrada pasada, puedes consultarla aquí.

Cargamos la librería necesaria:

library(siebanxicor)

Cargamos el token y bajamos la serie.

setToken("8f7fb9112cdec753a2e2dedb149dc1d982523f138b05af2e34aebc07c6a5fca4")

idSerie <- c("SR28")
metadata <- getSeriesMetadata(idSerie)
head(metadata)
##   idSerie                                                        title
## 1    SR28 Pesos´s Real Exchange Rate Index P/ Real Exchange Rate Index
##    startDate    endDate frequency dataType          unit
## 1 1968-01-01 2023-04-01   Monthly  Indexes Without units

Esta serie tiene una frecuencia mensual y va desde enero de 1968 hasta abril del 2023. Nuestra serie del tipo de cambio real ajustado por el efecto Balassa-Samuelson es una serie anual de 1950 a 2004. Así que con esto mente procesamos la serie del Banco de México.

tcr_banxico <- getSeriesData(idSerie, '1968-01-01', '2004-12-01')
tcr_banxico <- getSerieDataFrame(tcr_banxico,"SR28")
head(tcr_banxico)
##         date  value
## 1 1968-01-01 64.058
## 2 1968-02-01 64.146
## 3 1968-03-01 63.575
## 4 1968-04-01 63.727
## 5 1968-05-01 63.034
## 6 1968-06-01 63.944

Como la serie es mensual, vamos a obtener un promedio anual.

tcr_banxico <- tcr_banxico %>%
  mutate(year = lubridate::year(date)) %>%
  group_by(year) %>%
  summarise(tcr_promedio = mean(value))

head(tcr_banxico)
## # A tibble: 6 × 2
##    year tcr_promedio
##   <dbl>        <dbl>
## 1  1968         64.1
## 2  1969         65.1
## 3  1970         65.4
## 4  1971         66.1
## 5  1972         69.8
## 6  1973         71.8

Ahora graficamos:

tcr_bxco <- ggplot(data = tcr_banxico, aes(x = year, y = tcr_promedio))+
  geom_line(colour="#FC4E07",cex = 1)+
  theme_bw()+
  labs(title="Índice de tipo de cambio real multilateral",
       caption="Fuente: Elaboración propia con información de Banco de México.")+
  xlab(" ")+
  ylab("Índice")
tcr_bxco

Para tener una mejor visualización vamos a poner ambos índices de tipo de cambio real en un solo gráfico usando grid.arrange(). No me detendré a explicar cómo graficar de esta manera pues ya lo abordé en otra entrada. Puedes consultar dicha entrada aquí.

#Esto para emparejar las series de tiempo
data.mx <- data.mx%>%
  filter(year>1967)

#Hacemos un solo dataframe
tcr.df <- bind_cols(tcr_banxico, data.mx$lnunder)
head(tcr.df)
## # A tibble: 6 × 3
##    year tcr_promedio     ...3
##   <dbl>        <dbl>    <dbl>
## 1  1968         64.1  0.00402
## 2  1969         65.1 -0.0137 
## 3  1970         65.4  0.0361 
## 4  1971         66.1  0.0334 
## 5  1972         69.8  0.0253 
## 6  1973         71.8 -0.0303

Le cambiamos el nombre a la columna 3 que es la que tiene los valores del tipo de cambio de Rodrik.

tcr.df <- tcr.df %>%
  rename(tcr_rodrik = ...3)

Ahora ya podemos graficar.

tcr_rodrik <- ggplot(data = tcr.df, aes(x = year, y = tcr_rodrik))+
  geom_hline(yintercept = 0, size=1, linetype = 'dashed', alpha = 0.5)+
  geom_line(aes(y = tcr_rodrik), colour="#00AFBB",cex = 1)+
  theme_bw()+
  labs(title="Índice de tipo de cambio real a la Rodrik",
       caption="Fuente: Elaboración propia con información de Penn World Table 6.2.")+
  xlab(" ")+
  ylab("Logaritmo natural")

Ahora ambos gráficos:

library(grid)
library(gridExtra)
grid.arrange(tcr_rodrik, tcr_bxco, nrow=2)

Podemos observar ciertas divergencias en ambos índices, un motivo es que tienen objetivos diferentes, el índice de Rodrik tiene como fin determinar cuándo el tipo de cambio real está subvaluado y que pueda ser utilizado en modelaciones de datos panel, sin embargo los periodos de crisis son muy marcados en ambos, ya que las devaluaciones son evidentes. Nuevamente, la elaboración del índice anual de Banxico a fechas más recientes se le deja al lector.