Conocer diariamente el número de activos que suben, bajan o se quedan igual, los máximos y mínimos que se generan, etc... nos puede ofrecer un punto de vista adicional que haga decantar nuestra opinión hacia un lado u otro, tratando de determinar la salud de una tendencia.
En este artículo vamos a realizar una herramienta sencilla que nos permita evaluar todo esto con el menor número de líneas de código posible.
import yfinance as yf
import investpy
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
data = investpy.get_stocks()
data = data[data['country'] == 'united states']
tickers = list(data['symbol'])
start = '2010-01-01'
df = yf.download(tickers, start, progress=True)['Close']
Utilizando get_stocks()
de Investpy
vamos a disponer de una lista curada de tickers, independientemente de su capitalización, para poder delimitar un posible mercado sobre el que trabajar la amplitud. Concretamente filtramos los valores estadounidenses para analizar el mercado USA.
Descargamos los precios de cierre de un histórico relevante ya que para delimitar máximos y mínimos vamos a utilizar el periodo seleccionado, debemos intentar que sea lo suficientemente grande como para que aparezcan máximos y mínimos absolutos.
dfm = df.cummin()
dfm1 = df.cummax()
dfmin = pd.DataFrame(np.where(df == dfm,1,0))
dfmax = pd.DataFrame(np.where(df == dfm1,1,0))
La idea es ser capaces de señalizar los momentos en los que aparecen máximos y mínimos. Gracias a los métodos cummax()
y cummin()
podemos comparar precios de cierre con su respectivo acumulado, y si coinciden, nos encontramos ante un máximo o mínimo.
Con 1 determinamos la condición "True" y 0 con "False".
returns = df.pct_change()[1:]*100
subenbajan = pd.DataFrame(np.where(returns>0,1,-1))
subenbajan.index = returns.index
Calculamos los retornos en porcentaje y hacemos lo mismo para determinar periodos en los que la variación fue positiva.
a=[]
d=[]
for i in range(len(subenbajan)):
a.append(len(subenbajan.iloc[i][subenbajan.iloc[i]==1]))
d.append(len(subenbajan.iloc[i][subenbajan.iloc[i]==-1]))
l = pd.DataFrame(a,d)
l = l.reset_index()
l.index = subenbajan.index
l.columns = ['Descenso','Avance']
Filtramos para cada activo y periodo la condición de returns>0
y con un bucle lo vamos añadiendo a dos listas, que tendrá el número exacto de filas del DataFrame
con el conteo (len
) de la condición True y False anterior (1 y -1).
l['RANA'] = (l['Avance']-l['Descenso'])/(l['Avance']+l['Descenso'])
l['AvanceEMA19'] = l['Avance'].ewm(span=19).mean()
l['AvanceEMA39'] = l['Avance'].ewm(span=39).mean()
l['Mcclellan'] = l['AvanceEMA19']-l['AvanceEMA39']
l['ad'] = l['Avance']-l['Descenso']
l['ratios/b'] = l['Avance']/l['Descenso']
Ahora es sencillo calcular lo que necesitemos. Generalmente son métricas y ratios relacionados con las que ascienden/descienden.
print("\n"+"=="*20)
print("AMPLITUD DE MERCADO:", len(df.columns) ,"Empresas\n")
print("Suben hoy:", l['Avance'][-1])
print("Bajan hoy:", l['Descenso'][-1])
print("Suben semanal:", l['Avance'][-5:].sum())
print("Bajan semanal:", l['Descenso'][-5:].sum())
print("Suben mensual:", l['Avance'][-22:].sum())
print("Bajan mensual:", l['Descenso'][-22:].sum())
print("\nNuevos máximos hoy:", dfmax.iloc[-1].sum())
print("Nuevos mínimos hoy:", dfmin.iloc[-1].sum())
print("Nuevos máximos semanales:", dfmax.iloc[-5:].sum(axis=1).sum())
print("Nuevos mínimos semanales:", dfmin.iloc[-5:].sum(axis=1).sum())
print("Nuevos máximos mensuales:", dfmax.iloc[-22:].sum(axis=1).sum())
print("Nuevos mínimos mensuales:", dfmin.iloc[-22:].sum(axis=1).sum())
print("\nAvance/Descenso hoy:", l['ad'][-1])
print("Ratio Suben/bajan:", round(l['ratios/b'][-1],3))
print("Oscilador Mcclellan:", round(l['Mcclellan'][-1],3))
print("=="*20)
Procedemos a mostrar por pantalla los últimos periodos o los semanales/mensuales, a discreción.
data = yf.download("^GSPC", start)['Close']
plt.style.use(['dark_background'])
fig,(ax,ax1) = plt.subplots(2, figsize=(20,10))
ax1.get_xaxis().set_visible('False')
data.plot(ax=ax, title="SP500", ylabel="Precios")
l['Mcclellan'].plot(ax=ax1, title="Oscilador Mcclellan", ylabel="0-100").fill_between(l.index,l['Mcclellan'], where=l['Mcclellan']>0, color='g')
l['Mcclellan'].plot(ax=ax1).fill_between(l.index,l['Mcclellan'], where=l['Mcclellan']<0, color='r')
ax1.get_xaxis().set_visible(False)
fig.tight_layout()
plt.show()
Para acabar, podemos graficar el Oscilador Mcclellan junto al SP500 y analizar conjuntamente la situación.
El código entero:
import yfinance as yf
import investpy
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
data = investpy.get_stocks()
data = data[data['country'] == 'united states']
tickers = list(data['symbol'])
start = '2010-01-01'
df = yf.download(tickers, start, progress=True)['Close']
dfm = df.cummin()
dfm1 = df.cummax()
dfmin = pd.DataFrame(np.where(df == dfm,1,0))
dfmax = pd.DataFrame(np.where(df == dfm1,1,0))
returns = df.pct_change()[1:]*100
subenbajan = pd.DataFrame(np.where(returns>0,1,-1))
subenbajan.index = returns.index
subenbajan.columns = returns.columns
a=[]
d=[]
for i in range(len(subenbajan)):
a.append(len(subenbajan.iloc[i][subenbajan.iloc[i]==1]))
d.append(len(subenbajan.iloc[i][subenbajan.iloc[i]==-1]))
l = pd.DataFrame(a,d)
l = l.reset_index()
l.index = subenbajan.index
l.columns = ['Descenso','Avance']
l['RANA'] = (l['Avance']-l['Descenso'])/(l['Avance']+l['Descenso'])
l['AvanceEMA19'] = l['Avance'].ewm(span=19).mean()
l['AvanceEMA39'] = l['Avance'].ewm(span=39).mean()
l['Mcclellan'] = l['AvanceEMA19']-l['AvanceEMA39']
l['ad'] = l['Avance']-l['Descenso']
l['ratios/b'] = l['Avance']/l['Descenso']
print("\n"+"=="*20)
print("AMPLITUD DE MERCADO:", len(df.columns) ,"Empresas\n")
print("Suben hoy:", l['Avance'][-1])
print("Bajan hoy:", l['Descenso'][-1])
print("Suben semanal:", l['Avance'][-5:].sum())
print("Bajan semanal:", l['Descenso'][-5:].sum())
print("Suben mensual:", l['Avance'][-22:].sum())
print("Bajan mensual:", l['Descenso'][-22:].sum())
print("\nNuevos máximos hoy:", dfmax.iloc[-1].sum())
print("Nuevos mínimos hoy:", dfmin.iloc[-1].sum())
print("Nuevos máximos semanales:", dfmax.iloc[-5:].sum(axis=1).sum())
print("Nuevos mínimos semanales:", dfmin.iloc[-5:].sum(axis=1).sum())
print("Nuevos máximos mensuales:", dfmax.iloc[-22:].sum(axis=1).sum())
print("Nuevos mínimos mensuales:", dfmin.iloc[-22:].sum(axis=1).sum())
print("\nAvance/Descenso hoy:", l['ad'][-1])
print("Ratio Suben/bajan:", round(l['ratios/b'][-1],3))
print("Oscilador Mcclellan:", round(l['Mcclellan'][-1],3))
print("=="*20)
data = yf.download("^GSPC", start)['Close']
plt.style.use(['dark_background'])
fig,(ax,ax1) = plt.subplots(2, figsize=(20,10))
ax1.get_xaxis().set_visible('False')
data.plot(ax=ax, title="SP500", ylabel="Precios")
l['Mcclellan'].plot(ax=ax1, title="Oscilador Mcclellan", ylabel="0-100").fill_between(l.index,l['Mcclellan'], where=l['Mcclellan']>0, color='g')
l['Mcclellan'].plot(ax=ax1).fill_between(l.index,l['Mcclellan'], where=l['Mcclellan']<0, color='r')
ax1.get_xaxis().set_visible(False)
fig.tight_layout()
plt.show()