¿Cómo analizar los componentes del S&P 500?
El S&P 500 es por excelencia el índice financiero más monitoreado por los inversores profesionales a nivel mundial. Esto debido a que los Estados Unidos tiene el mercado de acciones más grande del mundo y sus variaciones tienden a tener efectos colaterales en la economía. Es por eso que el Standard & Poor’s se encarga de calcular y publicar los resultados oficiales de este índice en tiempo real. Pero la manera en la que presentan sus resultados no muestra el comportamiento de cada uno de los componentes del S&P 500. Comportamiento que puede ayudarnos a comprender mejor el mercado estadounidense y que analizaremos a lo largo de este artículo.
Ponderación de las acciones
Para comenzar con nuestro análisis, a continuación encontrarás un reporte de Power BI con la ponderación de cada una de las acciones que componen el S&P 500.
De este reporte, la conclusión más importante a la que podemos llegar es que al día en que se redacta este artículo. El Top 10 de las empresas con mayor ponderación en el S&P 500 suman un total del 31.81%. Y considerando que el S&P 500 es compuesto por aproximadamente 500 acciones, es de resaltar que 10 acciones abarquen casi un tercio de la ponderación total de este índice.
Análisis de componentes del S&P 500
Teniendo en mente el hallazgo que encontramos en nuestro reporte de Power BI. A continuación encontrarás un script desarrollado en Python mediante el cuál analizamos las acciones que componen el s&P500.
Módulos
Para iniciar, importaremos los módulos que usaremos para la primera parte de nuestro análisis de precios de las acciones que componen el S&P500.
import yfinance as yf
import pandas as pd
import datetime as dt
import matplotlib.pyplot as plt
import seaborn as sns
import requests
end = dt.datetime.now()
Extracción de precios de cada acción que compone el S&P 500
Para tener acceso a los precios de las acciones, procederemos por acceder a los tickers de cada acción publicados en Wikipedia. Y sus respectivas ponderaciones las obtendremos de SlickCharts.
# Tickers
ticker = pd.read_html(
'https://en.wikipedia.org/wiki/List_of_S%26P_500_companies')[0]
tickers = ticker.drop(index=[78,62])
# Ponderación por acción
df = pd.read_html(requests.get('https://www.slickcharts.com/sp500',headers={'User-agent': 'Mozilla/5.0'}).text)[0]
df['Portfolio%'] = df['Portfolio%'].str.replace('%', '')
# Precios de las acciones
data = yf.download(tickers.Symbol.to_list(),'2020-3-20', end, auto_adjust=True)['Close']
Precios de las acciones ajustado a su ponderación
Una vez tenemos acceso a los precios de las acciones y sus respectivas ponderaciones. Vamos a ajustar los precios de las acciones a su respectiva ponderación dentro del S&P500.
prices = pd.DataFrame()
for column in data.columns:
for row in df['Symbol']:
if column == row:
prices[column] = data[column]*float(df.loc[df['Symbol']==row]['Portfolio%'])
else:
continue
Para analizar las variaciones de los precios de las acciones. Las transformaremos a una escala porcentual, tomando como punto de referencia los precios del 2020-3-20. Tomamos esta fecha como referencia, dado que durante la pandemia del COVID-19, la basta mayoría las acciones en multiples sectores de la economía, convergieron a la baja.
for column in data:
data[column] = ((data[column]-data.iloc[0][column])/data.iloc[0][column])
Componentes del S&P 500 en escala %
Por medio de un gráfico de líneas, visualizaremos los precios de las acciones que componen el S&P500 en una escala porcentual. De esta manera podemos comenzar a notar aquellas acciones que han venido experimentando mayor subida o menor subida a lo largo del tiempo.
data.plot(figsize=(10, 7),legend=None)
plt.title('Componentes del S&P 500 - Escala Porcentual')
Dada la gran cantidad de información que supone analizar cada componente del S&P 500. A continuación, calcularemos el TOP 10 de empresas con mayor valor en la escala % al día en que se publica este artículo.
top_10 = data.iloc[len(data)-1]
print(top_10.nlargest(n=10))
BLDR 14.609649 NVDA 12.307149 TRGP 11.807557 MPC 9.063945 FANG 8.681285 URI 8.469284 DVN 7.646927 PWR 7.293547 FCX 6.590310 HAL 6.241400 Name: 2024-02-06 00:00:00, dtype: float64
data.loc[:, ['BLDR','NVDA','TRGP','MPC','FANG','URI','DVN','PWR','FCX','FCX','JBL']].plot()
plt.title("TOP 10 Acciones con mayor valor en escala % en la actualidad")
Al aislar las 10 empresas con mayor crecimiento en escala porcentual. Podemos notar algo importante, ya que de entre todas estas, hay unas cuantas cuyo crecimiento está muy por encima del resto. Tal es el caso de NVIDIA [NVDA] cuyo crecimiento coincide con la fuerte tendencia de Inteligencia Artificial. Por otro lado, Builders FirstSource Inc [BLDR] cuyo crecimiento coincide con la estabilización de los permisos de construcción en los Estados Unidos. Y, si prestamos atención al comportamiento de nuestro conjunto de acciones, podemos observar que aquellas acciones que suben de valor hasta el punto de alejarse demasiado del grupo, tienden a bajar de precio y ajustarse a los niveles del grupo.
Index S&P 500 calculado
Ahora, indexaremos el precio de todas nuestras acciones para crear nuestro propio índice S&P 500. Nuestros cálculos consistirán en sumar todos los precios de nuestras acciones previamente ajustadas por su ponderación dentro del S&P 500. Y, a esta suma la transformaremos a una escala porcentual.
index_full = prices.sum(axis=1)
index_full = index_full.to_frame()
index_full.columns=['Full Index Price']
index_full['Full Index Price'] = ((index_full['Full Index Price']-index_full.iloc[0]['Full Index Price'])/index_full.iloc[0]['Full Index Price'])*100
Por otro lado, repetiremos el mismo proceso anterior. Pero eliminaremos de nuestro dataset aquellas empresas con mayor ponderación dentro de los cálculos del S&P 500. Para esto, utilizaremos el TOP 10 de empresas con mayor ponderación que se encuentra en nuestro reporte de Power BI. Cabe recalcar que para cuando estés leyendo este artículo, la información del reporte de Power BI puede haber cambiado porque este se actualiza con información en tiempo real de la web.
data.loc[:, ['MSFT','AAPL','NVDA','AMZN','META','GOOGL','GOOG','AVGO','LLY','TSLA']].plot()
plt.title("TOP 10 Acciones con mayor ponderación % al día de hoy")
index_filtered = prices.drop(['MSFT','AAPL','NVDA','AMZN','META','GOOGL','GOOG','AVGO','LLY','TSLA'],axis=1)
index_filtered = index_filtered.sum(axis=1)
index_filtered = index_filtered.to_frame()
index_filtered.columns=['Full Index Price - Sin Top 10']
index_filtered['Full Index Price - Sin Top 10'] = ((index_filtered['Full Index Price - Sin Top 10']-index_filtered.iloc[0]['Full Index Price - Sin Top 10'])/index_filtered.iloc[0]['Full Index Price - Sin Top 10'])*100
index_full['Diff'] = index_full['Full Index Price'] - index_filtered['Full Index Price - Sin Top 10']
Una vez realizados los cálculos para obtener nuestro propio índice S&P 500, graficamos los resultados mediante un gráfico de líneas. En este punto, vale la pena aclarar que nuestros resultados difieren en cuanto a los cálculos oficiales publicados por el Standard & Poor’s. ya que ellos utilizan datos privados de free-flat shares.
plt.figure(figsize=(10,6))
plt.plot(index_full[:-1]['Full Index Price'])
plt.plot(index_full[:-1]['Diff'])
plt.plot(index_filtered[:-1]['Full Index Price - Sin Top 10'])
plt.legend(['Índice completo','Diferencia','Índice sin TOP 10'])
plt.title('Índice completo vs. Índice - Sin TOP 10')
plt.show()
Visualmente podemos observar el comportamiento de las dos versiones de nuestro índice. A simple vista, los dos se mueven de manera similar. Pero es evidente notar la brecha tan pronunciada que se ha formado a partir del 2023. Esto evidencia el nivel relevancia que han tomado las 10 empresas con mayor ponderancia en el S&P 500 en los últimos 4 años. Al momento de redactar este artículo, las dos versiones de nuestro índice presentan una diferencia de aproximadamente 50 en escala porcentual. Lo que conduce a preguntarnos ¿Qué tan seguro es para el mercado estadounidense depender tanto de estas 10 empresas? Solo el tiempo lo dirá, pero lo que podemos concluir al día de hoy es que si empresas como Tesla [TSLA], Nvidia [NVDA] y Broadcom Inc. [AVGO] caen de valor, los efectos serán evidentes en el S&P 500.
Módulos adicionales
Para complementar nuestro análisis, importaremos algunos módulos adicionales para aplicar una técnica de machine learning. Con la cual clasificaremos nuestro conjunto de acciones que componen el S&P 500 de acuerdo a sus patrones de movimiento en el tiempo.
import numpy as np
from sklearn.preprocessing import Normalizer
from sklearn.pipeline import make_pipeline
from sklearn.cluster import KMeans
Antes de generar nuestros grupos (clusters), iniciamos por asegurarnos de que nuestro dataset no tenga columnas con espacios en blanco.
data_nonull = data.dropna(axis=1, how='all')
sns.heatmap(data_nonull, cbar=False)
Con nuestro dataset sin espacios en blanco, procedemos a calcular nuestros clusters con el uso de sklearn.
normalizer = Normalizer()
clustering_model = KMeans(n_clusters = 5, max_iter = 1000)
pipeline = make_pipeline(normalizer, clustering_model)
pipeline.fit(data_nonull[:-1].T)
clusters = pipeline.predict(data_nonull[:-1].T)
results = pd.DataFrame(
{'clusters': clusters,
'tickers': data_nonull.columns}
).set_index('tickers').sort_values(by=['clusters'], axis = 0)
Por último, graficaremos los promedios históricos de las acciones. Y los clasificaremos de acuerdo a nuestros clusters.
means = data_nonull[:-1].median().to_frame()
means.columns = ['means']
means_cluster = means.merge(results, how='left', left_index=True, right_index=True)
sns.swarmplot(data=means_cluster, y="means", x="clusters", dodge=True, s = 2)
Ahora, con los promedios históricos clasificados por clusters. Podemos concluir que nuestros clusters 0 y 1 son los que mayor observaciones tienen, siendo estos mismos los que poseen un mayor rango de distribución de promedios históricos. Por el contrario, los cluster 3 y 4 son los que menos observaciones tienen y menor rango en cuento a la distribución de promedios históricos. Lo que nos ayuda a caracterizar aquellos grupos de acciones que han tendido a crecer o decrecer porcentualmente a partir de los precios de referencia del 2020-3-20.
groups = pd.DataFrame()
for row in means_cluster['clusters']:
if row == 0:
groups['Cluster 0'] = pd.Series(means_cluster.loc[means_cluster['clusters']==row].index)
elif row == 1:
groups['Cluster 1'] = pd.Series(means_cluster.loc[means_cluster['clusters']==row].index)
elif row == 2:
groups['Cluster 2'] = pd.Series(means_cluster.loc[means_cluster['clusters']==row].index)
elif row == 3:
groups['Cluster 3'] = pd.Series(means_cluster.loc[means_cluster['clusters']==row].index)
elif row == 4:
groups['Cluster 4'] = pd.Series(means_cluster.loc[means_cluster['clusters']==row].index)
else:
continue
Y, por último, teniendo en mente la distribución de los promedios históricos de nuestras acciones. A continuación, encontrarás una tabla ordenada con los tickers de las acciones. Con los cuales, en tu tiempo libre, puedes buscar sus respectivos precios en Yahoo Finance, y vas a encontrarte con que por cada cluster hay un cierto patrón de movimiento que siguen las acciones. Lo que te puede ayudar a la hora de invertir, evitando aquellas acciones que tienden a moverse de manera similar y diversificando en aquellas que se mueven en direcciones contrarias para reducir el riesgo.
with pd.option_context('display.max_rows', None,
'display.max_columns', None,
'display.precision', 3,
):
print(groups.fillna('').reindex(sorted(groups.columns), axis=1))
Cluster 0 Cluster 1 Cluster 2 Cluster 3 Cluster 4 0 ABBV A AAL BAX BALL 1 ACGL AAPL ADBE BIIB BXP 2 ADM ABT ADSK CLX CCI 3 ADP ACN AKAM FIS CCL 4 AFL ADI ALGN INTC CHTR 5 AIG AEE ALLE VFC D 6 AJG AEP AMT VTRS DIS 7 AMGN AES AMZN VZ GPN 8 AMP AIZ ANSS WBA HRL 9 ANET ALB APTV WBD IFF 10 AON ALL ARE ILMN 11 APA AMAT AWK INCY 12 ATO AMCR BAC LUV 13 AVGO AMD BBWI MKTX 14 AXON AME BBY MMM 15 AZO AOS BIO MTCH 16 BG APD C NFLX 17 BKNG APH CE PARA 18 BKR AVB CFG PYPL 19 BLDR AVY CMCSA SWK 20 BRO AXP COF T 21 BSX BA COO TFX 22 CAH BDX CRL XRAY 23 CAT BEN CRM 24 CB BK CTLT 25 CBOE BLK CTSH 26 CDNS BMY CZR 27 CF BR DAY 28 CI BWA DG 29 COP BX DLR 30 COR CAG DPZ 31 COST CARR DVA 32 CPB CBRE EBAY 33 CPRT CDW ECL 34 CTAS CHD EL 35 CTRA CHRW EMN 36 CTVA CINF EPAM 37 CVX CL EQR 38 DHI CMA ES 39 DLTR CME ESS 40 DVN CMG ETSY 41 ED CMI EW 42 EG CMS EXPE 43 EIX CNC FFIV 44 ELV CNP FLT 45 EOG CPT GEN 46 EQT CSCO GM 47 ETN CSGP GNRC 48 EXC CSX GRMN 49 FANG CVS HAS 50 FE DAL IDXX 51 FICO DD IP 52 FSLR DE IVZ 53 GD DFS KEY 54 GE DGX KMX 55 GILD DHR MDT 56 GIS DOV META 57 GL DOW MHK 58 GPC DRI MKC 59 GWW DTE MRNA 60 HAL DUK NCLH 61 HES DXCM NEM 62 HIG EA NKE 63 HII EFX NTRS 64 HLT EMR PAYC 65 HPE ENPH PEAK 66 HSY EQIX PNC 67 HUBB ETR POOL 68 HWM EVRG QRVO 69 IBM EXPD RVTY 70 IR EXR SBAC 71 IRM F SWKS 72 IT FAST SYF 73 JBL FCX TECH 74 KLAC FDS TER 75 KMI FDX TFC 76 KO FI TGT 77 KR FITB TRMB 78 L FMC TROW 79 LIN FOX TSN 80 LLY FOXA TTWO 81 LMT FRT TYL 82 LVS FTNT UBER 83 LW FTV UDR 84 MAR GLW USB 85 MCD GOOG WDC 86 MCK GOOGL WHR 87 MDLZ GS WRK 88 MMC HBAN ZBH 89 MNST HCA ZBRA 90 MOH HD ZION 91 MPC HOLX 92 MRK HON 93 MRO HPQ 94 MSI HSIC 95 NI HST 96 NOC HUM 97 NUE ICE 98 NVDA IEX 99 ODFL INTU 100 OKE INVH 101 OMC IPG 102 ON IQV 103 ORCL ISRG 104 ORLY ITW 105 OXY J 106 PANW JBHT 107 PCAR JCI 108 PCG JKHY 109 PEP JNJ 110 PFG JNPR 111 PGR JPM 112 PH K 113 PHM KDP 114 PSX KEYS 115 PWR KHC 116 PXD KIM 117 REGN KMB 118 RJF LDOS 119 RSG LEN 120 SJM LH 121 SLB LHX 122 SNA LKQ 123 SNPS LNT 124 SO LOW 125 SRE LRCX 126 STLD LULU 127 TAP LYB 128 TDG LYV 129 TJX MA 130 TRGP MAA 131 TRV MAS 132 ULTA MCHP 133 UNH MCO 134 URI MET 135 VLO MGM 136 VRTX MLM 137 WAB MO 138 WM MOS 139 WMB MPWR 140 WMT MS 141 WRB MSCI 142 XOM MSFT 143 MTB 144 MTD 145 MU 146 NDAQ 147 NDSN 148 NEE 149 NOW 150 NRG 151 NSC 152 NTAP 153 NVR 154 NWS 155 NWSA 156 NXPI 157 O 158 OTIS 159 PAYX 160 PEG 161 PFE 162 PG 163 PKG 164 PLD 165 PM 166 PNR 167 PNW 168 PODD 169 PPG 170 PPL 171 PRU 172 PSA 173 PTC 174 QCOM 175 RCL 176 REG 177 RF 178 RHI 179 RL 180 RMD 181 ROK 182 ROL 183 ROP 184 ROST 185 RTX 186 SBUX 187 SCHW 188 SHW 189 SPG 190 SPGI 191 STE 192 STT 193 STX 194 STZ 195 SYK 196 SYY 197 TDY 198 TEL 199 TMO 200 TMUS 201 TPR 202 TSCO 203 TSLA 204 TT 205 TXN 206 TXT 207 UAL 208 UHS 209 UNP 210 UPS 211 V 212 VICI 213 VMC 214 VRSK 215 VRSN 216 VTR 217 WAT 218 WEC 219 WELL 220 WFC 221 WST 222 WTW 223 WY 224 WYNN 225 XEL 226 XYL 227 YUM 228 ZTS
El contenido de la presente comunicación o mensaje no constituye una recomendación profesional para realizar inversiones en los términos del artículo 2.40.1.1.2 del Decreto 2555 de 2010 o las normas que lo modifiquen, sustituyan o complementen.