¿Podemos encontrar tops de mercado con Python simple? | de Danny Groves | enero, 2023

Market timing es realmente el santo grial de la inversión, pero ¿es posible?

Para ser honesto, soy muy escéptico acerca de todo esto y no creo que nadie pueda llevarlo a cabo a la perfección. Pero, ¿podemos al menos tener una idea/indicación aproximada al respecto? Incluso si nos acercamos un poco más, eso es suficiente, ¿verdad?

Últimamente he estado leyendo How to Make Money in Stocks de William O’Neil, un libro fantástico lleno de gran conocimiento (¡altamente recomendado!). Hay una sección completa sobre la sincronización del mercado y, en pocas palabras, la forma principal de hacerlo es leer los movimientos de precios y volúmenes de los principales índices.

Afortunadamente, este proceso es algo algorítmico. – ¿Y sabes lo que eso significa? ¡Podemos poner a nuestro geek a prueba y probar esta hipótesis con un sencillo código Python! De hecho, este es el resultado final de las condiciones del mercado hasta enero de 2023:

Las partes rojas son básicamente señales de advertencia de que hay muchas ventas y podría ser una buena idea salir del mercado y retirar dinero. De un vistazo rápido, ¡el enfoque no funciona tan mal para sincronizar los repuntes alcistas y del mercado! Bastante genial, ¿verdad?

⚠️ Tenga en cuenta que esto es solo para fines educativos y de entretenimiento y no es una sugerencia sobre cuándo entrar/salir del mercado. ¡Investigue antes de tomar cualquier decisión de inversión!⚠️

No se desanime, si se pregunta cómo se creó esta figura, ¡siga leyendo!

El enfoque básico aquí es lo que a O’Neill le gusta llamar “días de distribución” — mi interpretación es que estos días hay más montos de ventas de lo habitual. Algunos de estos días en el transcurso de unas pocas semanas pueden indicar más ventas por venir.

Pero, ¿cómo se determinan? Bueno, aquí vamos a ver una versión realmente simple:

  1. El cambio porcentual entre bajo y alto es < -0.2%.
  2. El volumen de negociación diario es mayor que el día anterior.

¡Si eso es! Fácil y algorítmico, ¿verdad?

Complicaciones

A veces, el índice puede tener una tendencia alcista durante todo el día y luego liquidarse masivamente en la última hora, dejando el cierre marginalmente por encima de la apertura pero con un volumen mayor que antes.

O’Neil dice que es “Volumen pesado sin más acción del precio”.

El problema es que es relativo a la actividad comercial del día anterior, pero diría que eso lo hace un poco arbitrario (¡y por supuesto más difícil de codificar!).

¿Que haremos?

Me gusta vivir mi vida con el lema “Mantenlo simple, estúpido” — Eso es porque soy estúpido y las cosas tienen que ser simples. Después de todo, no soy un experto en inversiones (¡todavía!) y quiero hacer las cosas lo más algorítmicas posible mientras aprendo todas las complejidades del mercado.

Es muy fácil comenzar simple y luego agregar complejidad, créanme. La complejidad es definitivamente más difícil de eliminar; casi como tratar de sacar las hierbas y especias de la mortadela después de haberlas agregado.

Así que mantengamos las cosas simples y tengamos el enfoque de dos partes mencionado anteriormente ?

Así es, sé que algunas personas no querrán codificar esto desde cero, así que siéntase libre de tomar mi trabajo, probarlo y luego mejorarlo (¡no lo juzgaré, por supuesto!)

Pero si tengo algunos fanáticos en la audiencia, aquí hay una fórmula aproximada de lo que debemos hacer:

  1. Descargar datos SPY (podemos usar yfinance biblioteca para esto).
  2. Encuentre todas las ubicaciones donde 100*(cerrar/abrir-1) < -0.2 y volumen de hoy > volumen de ayer.
  3. Para cada día, cuente cuántos de esos días hubo durante los últimos n días de negociación (yo uso n=15). Podemos hacer con esto funcionalidad de rodadura de pandas.
  4. Encuentre todos los días en los que el número de rollos supera un cierto umbral. Uso 5 días de 15 días como límite.
  5. Cree un gráfico de velas con un aspecto impresionante y resalte las áreas en las que esto sucede. Después de todo, tengo que mantenerlo simple y ¿qué podría ser más simple que un gráfico resaltado?

Por cierto, William O’Neil dice “cinco o seis días de distribución en cualquier período de cuatro o cinco semanas”. Lo acorté a tres, ya que produjo resultados ligeramente mejores (¡pero te dejaré ser el juez!).

Aquí está la traducción al código Python, ¡bienvenido!

import numpy as np
import pandas as pd
import yfinance as yf

import plotly.io as pio
pio.renderers.default='svg' # Change to browser for an interactive version
import plotly.graph_objects as go
from plotly.subplots import make_subplots

MAX_PERC = -0.2 # Maximum percentage between open and close to count as a distribution day
ROLLING_PERIOD = 15 # Period to check over (days)
HIGHLIGHT_COUNT = 5 # Highlight on the chart if the count exceeds this
TICKER = 'SPY'

# Upper and lower limits for plotting
LOWER_DATE = '2021-01-01'
UPPER_DATE = '2023-02-01'

if __name__ == '__main__':

df = yf.download(TICKER).reset_index()

# Find all locations where the percentage difference between the open and
# the close is below the maximum allowed percentage
df.loc[:, 'below_max_perc'] = np.where(
100*(df['Close']/df['Open'] - 1) <= MAX_PERC,
True,
False,
)

# We classify a distribution day as any day where we have larger volume
# than the previousj day and we are below the maximum percentage difference
df.loc[:, 'is_distribution'] = np.where(
df['below_max_perc'] & (df['Volume'] > df['Volume'].shift(1)),
True,
False,
)

# This counts up the number of distribution days over the last
# ROLLING_PERIOD days. Note the closed='both' keyword argument allows the
# most recent day to be in the calculation
df.loc[:, 'distribution_count'] = (
df['is_distribution']
.rolling(ROLLING_PERIOD, closed='both')
.sum()
)

# This finds all days where we are above the rolling distribution count
df.loc[:, 'above_count'] = df['distribution_count'] >= HIGHLIGHT_COUNT

# Filter between the lower and upper dates for plotting, this prevents
# having a chart that's too 'busy' and hard to understand
df = df[
(df['Date'] >= LOWER_DATE)
& (df['Date'] <= UPPER_DATE)
]

fig = make_subplots(
rows = 2,
cols = 1,
shared_xaxes = True,
vertical_spacing = 0.1,
subplot_titles = (f'{TICKER} Chart', 'Number of Distribution Days'),
row_width = [0.3, 0.7]
)

fig.add_trace(
go.Candlestick(
x = df['Date'],
open = df['Open'],
high = df['High'],
low = df['Low'],
close = df['Close'],
showlegend = False
),
row=1, col=1,
)

# This code plots highlighted sections where we are above the max count
df_counts = (
df[df['above_count']]
.groupby((~df['above_count']).cumsum())
['Date']
.agg(['first', 'last'])
)

for idx, row in df_counts.iterrows():
fig.add_vrect(
x0 = row['first'],
x1 = row['last'],
line_width = 0,
fillcolor = 'red',
opacity = 0.2,
)

fig.add_trace(
go.Line(
x = df['Date'],
y = df['distribution_count'],
showlegend=False,
),
row = 2,
col = 1,
)

fig.update_xaxes(
rangebreaks = [{'bounds': ['sat', 'mon']}],
rangeslider_visible = False,
)

fig.update_layout(
yaxis = {
'range': [df['Low'].min(), df['High'].max()],
'title': 'Price ($)'
},
margin = {'l': 50, 'r': 50, 'b': 50, 't': 25},
width = 800,
height = 800,
)

fig.show()

Así que ya mostré el mercado bajista de 2022 (y parecía bastante decente), ¿qué tal un poco?

Choque de COVID de 2020

El enfoque aquí parece funcionar bien tanto durante la salida (antes de que ocurran grandes pérdidas) como durante el reingreso. Definitivamente hay una señal falsa en el lado derecho del gráfico, ¡pero ningún método es absolutamente perfecto!

crisis financiera de 2008

Aún así, ¡no está tan mal! La mayoría de los repuntes contratendencia están bien sincronizados, pero un ciclo muy grande de ventas cerca del fondo no está bien sincronizado.

Sin embargo, ¡te ahorraría un colapso monumental! ¿O elegir vender en corto en este punto le dio una gran ganancia?

La burbuja de las puntocom

¡Messi sin duda! Nuevamente, como en 2008, se perdió un gran período de ventas cerca del fondo (también a mediados de 2001). Pero definitivamente hay una buena parte de la tendencia bajista, que está bastante bien sincronizada.

Así que ahí lo tiene: una manera simple de usar Python para implementar algo de la sabiduría de O’Neil. Obviamente, para hacerlo más efectivo (como el mismo Sr. O’Neill), necesitamos agregar algunas complejidades más a las descritas anteriormente, ¡pero este enfoque simple es un buen lugar para comenzar y construir!

Permítanme abordar una cosa: No soy un expertoes solo un estudiante que quiere compartir las cosas geniales que ha encontrado (¡con un código documentado para intentar probarlas usted mismo!). Mis artículos no son sugerencias sobre qué comprar/vender. Son solo artículos útiles para ayudarlo a aprender (¡esperemos que sean muy útiles!).

¡Feliz inversión!

Leave a Reply

Your email address will not be published. Required fields are marked *