Objetivo

Vamos a llevar a cabo un análisis PCA y a aplicar el algoritmo Isomap con los datos de los retornos anuales del SP500 a partir de diferentes indicadores macroeconómicos. (Descargados de la FRED a través de quantmod -> http://research.stlouisfed.org/fred2/)

Estos son:

El objetivo es reducir el número de dimensiones para analizar como los factores macroeconómicos han podido influir históricamente en los resultados del índice SP500

Carga de librerías

library(quantmod)
library(dplyr)
library(RDRToolbox)
library(knitr)
library(rgl)

Funciones

parsingIndicator

Función para pasar a formato interanual todos los tickers, filtrar a partir de la fecha común como punto de partida del dataset y devolver un vector con el indicador modificado

NOTA –> Consideramos los datos hasta el 2014 inclusive ya que no todos los indicadores tienen datos del 2015 aún

parsingIndicator <- function(indicator){
  
  year.indicator <- to.yearly(indicator)[, 4]
  
  #En los indicadores que son acumulativos, sacaremos el YoY change en %
  #En aquellos que no, se dejarán tal cual son descargos con quantmod
  if (!names(indicator) %in% c('NMFBAI', 'NAPM', 'USSLIND')){
    year.indicator <- round(Delt(year.indicator) * 100, 3)
  }
  
  year.indicator <- year.indicator[paste(first.date, '::2014', sep='')]
  year.indicator <- coredata(year.indicator)[,1]
  return(year.indicator)
}

Main

Descargamos todos los indicadores

indicators <- new.env()
tickers <- c('USSLIND', 'ICSA', 'NAPM', 'NMFBAI', 'GDP', 'FPI',
             'RRSFS', 'PERMITNSA', 'GFDEBTN', 'WRMFSL')

getSymbols(tickers, src='FRED', env = indicators, auto.assign = T)

La primera observación de los datos vendrá dada por la fecha común mas pequeña

oldest.dates <- sapply(indicators, function(x){ 
                                      x <- rownames(as.data.frame(x))
                                      return(min(x)) })
first.date <- format(as.Date(max(oldest.dates)), '%Y')

Aquellos que no son acumulativos, quedarán tal cual vienen:

Ahora, pasaremos todos los valores a retornos interanuales a través de la funcion parsingIndicator.

Dataframe con todos los indicadores agrupados

df.indicators <- as.data.frame(row.names = c(first.date:2014),
                               do.call(cbind, lapply(indicators, parsingIndicator)))

Añadimos a cada fila un ‘+’ o ‘-’ para saber el retorno del índice ese año, desde 1997 hasta 2014

returns.SP500 <- c('+', '+', '+', '-', '-', '-','+',
                   '+', '+', '+', '+', '-', '+', '+', '+', '+', '+', '+')
rownames(df.indicators) <- paste(rownames(df.indicators), returns.SP500, sep = '')

Muestra del dataframe

head(df.indicators)
##         GDP PERMITNSA    ICSA NMFBAI NAPM GFDEBTN    FPI WRMFSL  RRSFS
## 1997+ 6.048     7.258 -15.126   56.5 54.5   3.367  8.462 14.493  2.452
## 1998+ 6.115    17.011  10.891   54.2 46.8   2.032 10.640 23.216  4.965
## 1999+ 6.438    -2.892 -20.238   60.4 57.8   2.883  7.542 13.092  6.596
## 2000- 5.503   -14.888  31.716   57.5 43.9  -1.971  7.613 11.402 -1.226
## 2001- 2.187    10.398  19.263   50.7 45.3   4.967 -4.170  4.530  1.269
## 2002- 3.761    19.542  -2.850   55.5 51.6   7.778 -0.873 -9.032  1.057
##       USSLIND
## 1997+    1.52
## 1998+    1.54
## 1999+    2.13
## 2000-   -0.40
## 2001-   -0.04
## 2002-    0.07

PCA

pr.out = prcomp(df.indicators, scale=TRUE)

Vamos a determinar la proporción de variabilidad explicada para determinar cuantas componentes vamos a coger

pr.var <- pr.out$sdev^2
pve <- pr.var/sum(pr.var)

Con el test del “codo”, vemos que la pendiente decrece sustancialmente a partir de la tercera componente

plot(pve, type = 'b')

Cogeremos en este caso, 2 componentes que engloban un PVE de 82%, aunque con 3 componentes tendríamos un PVE de 89.4%

cumsum(pve) * 100
##  [1]  58.44550  81.98948  89.38688  93.62852  96.10972  98.02818  98.95514
##  [8]  99.57530  99.94634 100.00000

Analizamos correlaciones entre componentes y valores propios, vemos que no hay correlaciones importantes entre las dimensiones y las 2 primeras componentes principales

pr.out$rotation
##                   PC1          PC2         PC3         PC4          PC5
## GDP       -0.30996793 -0.365015657  0.30164189 -0.12782687 -0.004489316
## PERMITNSA -0.26860257  0.230933408 -0.73523262 -0.24318041  0.221313784
## ICSA       0.35778327 -0.235050379 -0.08999100 -0.39843286 -0.069784910
## NMFBAI    -0.37659646 -0.019542968  0.23036757 -0.34202240 -0.397823968
## NAPM      -0.37156740  0.198285014  0.25151777  0.21203973 -0.161315780
## GFDEBTN    0.14151334  0.520450781  0.43022994  0.02723764  0.522030077
## FPI       -0.30585411 -0.328517725  0.14263277 -0.43674326  0.589503755
## WRMFSL     0.09574804 -0.581010077 -0.07117485  0.52510975  0.231092660
## RRSFS     -0.38372898  0.003065117 -0.15371051  0.21199263 -0.151165614
## USSLIND   -0.39228570  0.045943015 -0.09940674  0.30276555  0.256043925
##                    PC6         PC7         PC8         PC9        PC10
## GDP        0.006982558 -0.71137908  0.17955347 -0.17129593 -0.30926536
## PERMITNSA -0.018380977 -0.38237385 -0.15415637 -0.09150152  0.21715105
## ICSA      -0.353442831  0.05551289  0.55388536 -0.29853794  0.34800572
## NMFBAI    -0.306950382  0.19723485 -0.51875510 -0.30747870  0.18086074
## NAPM       0.305341323 -0.10720377  0.32107981  0.09260078  0.68701003
## GFDEBTN   -0.432931280 -0.19813670 -0.08098685 -0.09230887  0.09477566
## FPI        0.129391290  0.32168984  0.01378645  0.33331313  0.09171552
## WRMFSL    -0.216586056 -0.10055836 -0.32828551 -0.08574848  0.38328058
## RRSFS     -0.659919903  0.07252134  0.27370669  0.47532160 -0.14044300
## USSLIND    0.025773849  0.36897194  0.27490473 -0.64586393 -0.21766163

Vemos ahora con un biplot las 2 primeras componentes y sus valores propios

biplot(pr.out, scale = 0)

Podemos observar una alta concentración de retornos positivos para años localizados en PC1 < 0 y -2 < PC2 < 2

Por tanto, vamos a estudiar los indicadores macroeconomicos de esos años

filter_df <- rownames(subset(as.data.frame(pr.out$x), PC1 < 0 & PC2 > -2 & PC2 < 2))

Analizamos cada uno de los indicadores de la selección anterior

df.positive.returns <- df.indicators[filter_df, ]
df.positive.returns
##         GDP PERMITNSA    ICSA NMFBAI NAPM GFDEBTN    FPI  WRMFSL RRSFS
## 1997+ 6.048     7.258 -15.126   56.5 54.5   3.367  8.462  14.493 2.452
## 1999+ 6.438    -2.892 -20.238   60.4 57.8   2.883  7.542  13.092 6.596
## 2003+ 6.421     8.910 -14.670   60.0 60.1   9.246  9.844 -12.152 2.533
## 2004+ 6.308     4.936  -8.309   65.0 57.2   8.548 10.423  -9.239 4.484
## 2005+ 6.523    -2.513  -5.625   60.1 55.1   7.560 10.096   1.018 1.384
## 2010+ 4.556    -3.252 -13.248   62.2 57.5  13.921  5.827 -12.610 5.260
## 2011+ 3.645     8.403  -6.158   55.6 53.1   8.540  9.952  -2.053 2.962
## 2012+ 3.467    30.233  -3.150   60.0 50.4   7.947  7.318  -1.659 3.178
## 2013+ 4.566    12.351  -6.775   54.3 56.5   5.594  7.430  -1.855 2.152
## 2014+ 3.648    10.728 -13.372   58.6 55.1   2.721  7.346  -3.484 2.648
##       USSLIND
## 1997+    1.52
## 1999+    2.13
## 2003+    1.95
## 2004+    1.18
## 2005+    1.53
## 2010+    1.22
## 2011+    1.78
## 2012+    1.62
## 2013+    1.44
## 2014+    1.74
summary(df.positive.returns)
##       GDP          PERMITNSA            ICSA             NMFBAI     
##  Min.   :3.467   Min.   :-3.2520   Min.   :-20.238   Min.   :54.30  
##  1st Qu.:3.875   1st Qu.:-0.6508   1st Qu.:-14.345   1st Qu.:57.02  
##  Median :5.307   Median : 7.8305   Median :-10.778   Median :60.00  
##  Mean   :5.162   Mean   : 7.4162   Mean   :-10.667   Mean   :59.27  
##  3rd Qu.:6.393   3rd Qu.:10.2735   3rd Qu.: -6.312   3rd Qu.:60.33  
##  Max.   :6.523   Max.   :30.2330   Max.   : -3.150   Max.   :65.00  
##       NAPM          GFDEBTN            FPI             WRMFSL        
##  Min.   :50.40   Min.   : 2.721   Min.   : 5.827   Min.   :-12.6100  
##  1st Qu.:54.65   1st Qu.: 3.924   1st Qu.: 7.367   1st Qu.: -7.8003  
##  Median :55.80   Median : 7.753   Median : 8.002   Median : -1.9540  
##  Mean   :55.73   Mean   : 7.033   Mean   : 8.424   Mean   : -1.4449  
##  3rd Qu.:57.42   3rd Qu.: 8.546   3rd Qu.: 9.925   3rd Qu.:  0.3488  
##  Max.   :60.10   Max.   :13.921   Max.   :10.423   Max.   : 14.4930  
##      RRSFS          USSLIND     
##  Min.   :1.384   Min.   :1.180  
##  1st Qu.:2.472   1st Qu.:1.460  
##  Median :2.805   Median :1.575  
##  Mean   :3.365   Mean   :1.611  
##  3rd Qu.:4.157   3rd Qu.:1.770  
##  Max.   :6.596   Max.   :2.130

Conclusiones

Por tanto, parece que lo que tienen en común estos años son unos datos positivos macroeconómicos en general, que pudieron favorecer esos retornos positivos.

Habría que seguir evaluando en un futuro si la mayoría de los años con retornos positivos en bolsa, se sitúan en esa concentración de datos que hemos obtenido a través del PCA para detectar posibles relaciones causales

ISOMAP

Realizamos el mismo análisis anterior en vez de con PCA con ISOMAP, para identificar posibles estructuras no lineales al realizar la exploración de los datos

Estimamos la dimensión intrínseca dibujando los residuos mediante el gráfico del codo entre 1 y 5 dimensiones

Isomap_indicators_1to5 = Isomap(data=as.matrix(df.indicators), dims=1:5, k=4, plotResiduals=TRUE)
## Computing distance matrix ... done
## Building graph with shortest paths (using 4 nearest neighbours) ... done
## Computing low dimensional embedding ... done
## number of samples: 18
## reduction from 10 to 12345 dimensions
## number of connected components in graph: 1

Isomap con 3 dimensiones

Isomap_indicators_3 = Isomap(data = as.matrix(df.indicators), dim = 3, k = 4)
## Computing distance matrix ... done
## Building graph with shortest paths (using 4 nearest neighbours) ... done
## Computing low dimensional embedding ... done
## number of samples: 18
## reduction from 10 to 3 dimensions
## number of connected components in graph: 1

Isomap con 2 dimensiones

Isomap_indicators_2 = Isomap(data = as.matrix(df.indicators), dim = 2, k = 4)
## Computing distance matrix ... done
## Building graph with shortest paths (using 4 nearest neighbours) ... done
## Computing low dimensional embedding ... done
## number of samples: 18
## reduction from 10 to 2 dimensions
## number of connected components in graph: 1

Preparación labels del plot

Agrupamos los años con retornos positivos en un color y los años en negativos con otro color

labels <- lapply(rownames(df.indicators), function(x){ return(ifelse(grepl("\\+$", x), '+', '-'))})
labels <- as.factor(do.call(c, labels))

Configuración plotting 3d inline

knit_hooks$set(webgl = hook_webgl)

Plot con 3 dimensiones intrínsecas

plotDR(Isomap_indicators_3$dim3, labels = labels, text = rownames(df.indicators), axesLabels = c('V1', 'V2', 'V3'))
##   class colour
## 1     -  black
## 2     +    red

You must enable Javascript to view this page properly.

Plot con 2 dimensiones intrínsecas

plotDR(Isomap_indicators_2$dim2, labels = labels, text = rownames(df.indicators), axesLabels = c('V1', 'V2'))

##   class colour
## 1     -  black
## 2     +    red

Vemos que no perdemos información relativa al pasar de 3 a 2 dimensiones con lo cual, nos quedamos con una dimensión intrínseca 2 para seguir analizando

Observamos una alta concentración de retornos positivos para V1>0 y V2<0 como en el análisis PCA previo. Vamos a ver si hay diferencias

filter_isomap <- as.data.frame(row.names = rownames(df.indicators), Isomap_indicators_2$dim2)
filtered_isomap <- rownames(filter_isomap[filter_isomap$V1 > 0 & filter_isomap$V2 < 0,])

Obtenemos el dataframe inicial filtrado por estos años que hemos obtenido

isomap.df <- df.indicators[filtered_isomap, ]

Al estudiar los datos de este dataframe, nos damos cuenta que llegamos a resultados muy similares a los obtenidos en el PCA previo por tanto, las conclusiones son similares a dicho análisis

summary(isomap.df)
##       GDP          PERMITNSA           ICSA             NMFBAI     
##  Min.   :3.645   Min.   :-3.252   Min.   :-20.238   Min.   :55.60  
##  1st Qu.:4.329   1st Qu.:-2.608   1st Qu.:-14.784   1st Qu.:58.08  
##  Median :6.178   Median : 6.097   Median :-13.310   Median :60.05  
##  Mean   :5.448   Mean   : 3.947   Mean   :-12.093   Mean   :59.80  
##  3rd Qu.:6.425   3rd Qu.: 8.530   3rd Qu.: -7.771   3rd Qu.:60.85  
##  Max.   :6.523   Max.   :10.728   Max.   : -5.625   Max.   :65.00  
##       NAPM          GFDEBTN            FPI             WRMFSL       
##  Min.   :53.10   Min.   : 2.721   Min.   : 5.827   Min.   :-12.610  
##  1st Qu.:54.95   1st Qu.: 3.246   1st Qu.: 7.493   1st Qu.: -9.967  
##  Median :56.15   Median : 8.050   Median : 9.153   Median : -2.768  
##  Mean   :56.30   Mean   : 7.098   Mean   : 8.687   Mean   : -1.367  
##  3rd Qu.:57.58   3rd Qu.: 8.723   3rd Qu.: 9.988   3rd Qu.:  4.037  
##  Max.   :60.10   Max.   :13.921   Max.   :10.423   Max.   : 14.493  
##      RRSFS          USSLIND     
##  Min.   :1.384   Min.   :1.180  
##  1st Qu.:2.513   1st Qu.:1.445  
##  Median :2.805   Median :1.635  
##  Mean   :3.540   Mean   :1.631  
##  3rd Qu.:4.678   3rd Qu.:1.823  
##  Max.   :6.596   Max.   :2.130