(Español) Detección de cambios direccionales en Python

= np.NaN
valleysBuffer = np.NaN

i = depth

while (i < Bars – depth – 1):

is_upper_fractal = False
is_lower_fractal = False

lower_range_pos = i – depth
upper_range_pos = i + depth + 1
N = lower_range_pos + depth

lower_range_values = data.iloc[lower_range_pos:N][column_mode].values
upper_range_values = data.iloc[N + 1:upper_range_pos][column_mode].values
N_value = data.iloc[N][column_mode]

# Basic Fractal:
# Peaks
if np.append([N_value], lower_range_values).argmax() == 0 and np.append([N_value],
upper_range_values).argmax() == 0:
is_upper_fractal = True
peaksBuffer[N] = N_value

# Valleys
if not is_upper_fractal:
if np.append([N_value], lower_range_values).argmin() == 0 and np.append([N_value],
upper_range_values).argmin() == 0:
is_lower_fractal = True
valleysBuffer[N] = N_value
i += 1

peaksBufferSeries = pd.Series(peaksBuffer, name=”peaks”, index=data.index).dropna()
valleysBufferSeries = pd.Series(valleysBuffer, name=”valleys”, index=data.index).dropna()

self.__data = data
self.__peaks = peaksBufferSeries
self.__valleys = valleysBufferSeries
self.__column_mode = column_mode

return pd.merge(peaksBufferSeries, valleysBufferSeries, left_index=True, right_index=True, how=”outer”)

 

  = np.NaN
valleysBuffer = np.NaN

i = depth

while (i < Bars - depth - 1): is_upper_fractal = False is_lower_fractal = False lower_range_pos = i - depth upper_range_pos = i + depth + 1 N = lower_range_pos + depth lower_range_values = data.iloc[lower_range_pos:N][column_mode].values upper_range_values = data.iloc[N + 1:upper_range_pos][column_mode].values N_value = data.iloc[N][column_mode] # Basic Fractal: # Peaks if np.append([N_value], lower_range_values).argmax() == 0 and np.append([N_value], upper_range_values).argmax() == 0: is_upper_fractal = True peaksBuffer[N] = N_value # Valleys if not is_upper_fractal: if np.append([N_value], lower_range_values).argmin() == 0 and np.append([N_value], upper_range_values).argmin() == 0: is_lower_fractal = True valleysBuffer[N] = N_value i += 1 peaksBufferSeries = pd.Series(peaksBuffer, name="peaks", index=data.index).dropna() valleysBufferSeries = pd.Series(valleysBuffer, name="valleys", index=data.index).dropna() self.__data = data self.__peaks = peaksBufferSeries self.__valleys = valleysBufferSeries self.__column_mode = column_mode return pd.merge(peaksBufferSeries, valleysBufferSeries, left_index=True, right_index=True, how="outer")

¿Cómo se utiliza esto? Hace falta pasarle un DataFrame de pandas cuyo índice sea de tipo datetime y que esté ordenado ascendente.

Los datos

Supongamos que tenemos el siguiente DataTrame:

date open high low close
2003-05-28 08:40:00 1.1782 1.17968 1.17619 1.1795
2003-05-28 08:45:00 1.17967 1.18012 1.17967 1.18004
2003-05-28 08:50:00 1.17996 1.18007 1.17939 1.17939
2003-05-28 08:55:00 1.17932 1.17944 1.17691 1.17695
2003-05-28 09:00:00 1.17702 1.17796 1.17702 1.1779
2003-05-28 09:05:00 1.17795 1.17823 1.17759 1.17759
2003-05-28 09:10:00 1.17768 1.17805 1.17756 1.17802
2003-05-28 09:15:00 1.17791 1.17802 1.1778 1.17787
2003-05-28 09:20:00 1.17796 1.17923 1.17773 1.17923
2003-05-28 09:25:00 1.17933 1.17935 1.17786 1.17789
2003-05-28 09:30:00 1.17793 1.17832 1.17762 1.17793
2003-05-28 09:35:00 1.17794 1.17839 1.17789 1.17802
2003-05-28 09:40:00 1.17808 1.17837 1.177 1.17703
2003-05-28 09:45:00 1.17673 1.17827 1.17666 1.17827
2003-05-28 09:50:00 1.17819 1.1783 1.17779 1.17788
2003-05-28 09:55:00 1.17784 1.17872 1.17784 1.17872
2003-05-28 10:00:00 1.17847 1.17867 1.17833 1.17844
2003-05-28 10:05:00 1.17863 1.17886 1.17852 1.17862
2003-05-28 10:10:00 1.17865 1.17872 1.17771 1.17772
2003-05-28 10:15:00 1.1775 1.1787 1.17744 1.17839
2003-05-28 10:20:00 1.1784 1.17883 1.17837 1.17859
2003-05-28 10:25:00 1.17859 1.17869 1.17842 1.17855
2003-05-28 10:30:00 1.17859 1.1787 1.17839 1.17851
2003-05-28 10:35:00 1.17845 1.17874 1.17831 1.17867
2003-05-28 10:40:00 1.17866 1.17977 1.17818 1.17861
2003-05-28 10:45:00 1.17852 1.17866 1.17822 1.17856
2003-05-28 10:50:00 1.1787 1.17893 1.1786 1.1786
2003-05-28 10:55:00 1.17855 1.17855 1.17786 1.17786
2003-05-28 11:00:00 1.17769 1.17783 1.17426 1.17429
2003-05-28 11:05:00 1.17429 1.17474 1.17411 1.17456
2003-05-28 11:10:00 1.17467 1.17516 1.17462 1.17484
2003-05-28 11:15:00 1.17472 1.17517 1.17217 1.17517
2003-05-28 11:20:00 1.17518 1.17537 1.17493 1.17493
2003-05-28 11:25:00 1.17493 1.17532 1.17469 1.17511
2003-05-28 11:30:00 1.17504 1.17504 1.17436 1.17469
2003-05-28 11:35:00 1.17471 1.17563 1.17471 1.17558
2003-05-28 11:40:00 1.17559 1.17793 1.17528 1.17529
2003-05-28 11:45:00 1.17519 1.17559 1.17506 1.17547
2003-05-28 11:50:00 1.17558 1.17572 1.17533 1.17533
2003-05-28 11:55:00 1.17536 1.17536 1.17307 1.17307
2003-05-28 12:00:00 1.17268 1.17268 1.17166 1.17167
2003-05-28 12:05:00 1.17168 1.17237 1.17147 1.17224
2003-05-28 12:10:00 1.17214 1.17228 1.17194 1.17217
2003-05-28 12:15:00 1.17224 1.17237 1.17187 1.17209
2003-05-28 12:20:00 1.17205 1.17229 1.17153 1.17153
2003-05-28 12:25:00 1.17121 1.17226 1.17047 1.17204
2003-05-28 12:30:00 1.17208 1.1726 1.17181 1.17259
2003-05-28 12:35:00 1.17261 1.17264 1.17182 1.17192
2003-05-28 12:40:00 1.17203 1.17337 1.17189 1.17337
2003-05-28 12:45:00 1.17351 1.1736 1.1714 1.17153

Si el campo “date” es una columna tendríamos y el dataframe se llama “data”, por ejemplo, tendríamos que escribir lo siguiente para poder usarlo como argumento, pues el  índice ha de ser datetime:

data = data.set_index("date")

Por supuesto el código de la clase anterior se puede modificar para funcionar con numpy o cualquier otra biblioteca.

La aplicación

Si queremos obtener los máximos y mínimos locales de los valores “high” de nuestra serie de datos utilizando 6 valores para calcular los picos y valles, es tan simple como hacer esto:

#"data" es un DataFrame que contiene los datos de la tabla de arriba.
fr = fractals()
high_fractals = fr.getFractals(data=data,column_mode="high", depth=3)
<pre>
&nbsp;
 
&nbsp; = np.NaN
        valleysBuffer = np.NaN
 
        i = depth
 
        while (i < Bars - depth - 1):
 
            is_upper_fractal = False
            is_lower_fractal = False
 
            lower_range_pos = i - depth
            upper_range_pos = i + depth + 1
            N = lower_range_pos + depth
 
            lower_range_values = data.iloc[lower_range_pos:N][column_mode].values
            upper_range_values = data.iloc[N + 1:upper_range_pos][column_mode].values
            N_value = data.iloc[N][column_mode]
 
            # Basic Fractal:
            # Peaks
            if np.append([N_value], lower_range_values).argmax() == 0 and\
                    np.append([N_value],upper_range_values).argmax() == 0:
 
                if N_value not in lower_range_values:
                    is_upper_fractal = True
                    peaksBuffer[N] = N_value
 
            # Valleys
            if not is_upper_fractal:
                if np.append([N_value], lower_range_values).argmin() == 0 and \
                        np.append([N_value], upper_range_values).argmin() == 0:
 
                    if N_value not in lower_range_values:
                        is_lower_fractal = True
                        valleysBuffer[N] = N_value
            i += 1
 
            peaksBufferSeries = pd.Series(peaksBuffer, name="peaks", index=data.index).dropna()
            valleysBufferSeries = pd.Series(valleysBuffer, name="valleys", index=data.index).dropna()
 
            self.__data = data
            self.__peaks = peaksBufferSeries
            self.__valleys = valleysBufferSeries
            self.__column_mode = column_mode
 
        return pd.merge(
            peaksBufferSeries, valleysBufferSeries, left_index=True, right_index=True, how="outer", sort=False
        )

¿Cómo se utiliza esto? Hace falta pasarle un DataFrame de pandas cuyo índice sea de tipo datetime y que esté ordenado ascendente.

Los datos

Supongamos que tenemos el siguiente DataTrame:

date open high low close
2003-05-28 08:40:00 1.1782 1.17968 1.17619 1.1795
2003-05-28 08:45:00 1.17967 1.18012 1.17967 1.18004
2003-05-28 08:50:00 1.17996 1.18007 1.17939 1.17939
2003-05-28 08:55:00 1.17932 1.17944 1.17691 1.17695
2003-05-28 09:00:00 1.17702 1.17796 1.17702 1.1779
2003-05-28 09:05:00 1.17795 1.17823 1.17759 1.17759
2003-05-28 09:10:00 1.17768 1.17805 1.17756 1.17802
2003-05-28 09:15:00 1.17791 1.17802 1.1778 1.17787
2003-05-28 09:20:00 1.17796 1.17923 1.17773 1.17923
2003-05-28 09:25:00 1.17933 1.17935 1.17786 1.17789
2003-05-28 09:30:00 1.17793 1.17832 1.17762 1.17793
2003-05-28 09:35:00 1.17794 1.17839 1.17789 1.17802
2003-05-28 09:40:00 1.17808 1.17837 1.177 1.17703
2003-05-28 09:45:00 1.17673 1.17827 1.17666 1.17827
2003-05-28 09:50:00 1.17819 1.1783 1.17779 1.17788
2003-05-28 09:55:00 1.17784 1.17872 1.17784 1.17872
2003-05-28 10:00:00 1.17847 1.17867 1.17833 1.17844
2003-05-28 10:05:00 1.17863 1.17886 1.17852 1.17862
2003-05-28 10:10:00 1.17865 1.17872 1.17771 1.17772
2003-05-28 10:15:00 1.1775 1.1787 1.17744 1.17839
2003-05-28 10:20:00 1.1784 1.17883 1.17837 1.17859
2003-05-28 10:25:00 1.17859 1.17869 1.17842 1.17855
2003-05-28 10:30:00 1.17859 1.1787 1.17839 1.17851
2003-05-28 10:35:00 1.17845 1.17874 1.17831 1.17867
2003-05-28 10:40:00 1.17866 1.17977 1.17818 1.17861
2003-05-28 10:45:00 1.17852 1.17866 1.17822 1.17856
2003-05-28 10:50:00 1.1787 1.17893 1.1786 1.1786
2003-05-28 10:55:00 1.17855 1.17855 1.17786 1.17786
2003-05-28 11:00:00 1.17769 1.17783 1.17426 1.17429
2003-05-28 11:05:00 1.17429 1.17474 1.17411 1.17456
2003-05-28 11:10:00 1.17467 1.17516 1.17462 1.17484
2003-05-28 11:15:00 1.17472 1.17517 1.17217 1.17517
2003-05-28 11:20:00 1.17518 1.17537 1.17493 1.17493
2003-05-28 11:25:00 1.17493 1.17532 1.17469 1.17511
2003-05-28 11:30:00 1.17504 1.17504 1.17436 1.17469
2003-05-28 11:35:00 1.17471 1.17563 1.17471 1.17558
2003-05-28 11:40:00 1.17559 1.17793 1.17528 1.17529
2003-05-28 11:45:00 1.17519 1.17559 1.17506 1.17547
2003-05-28 11:50:00 1.17558 1.17572 1.17533 1.17533
2003-05-28 11:55:00 1.17536 1.17536 1.17307 1.17307
2003-05-28 12:00:00 1.17268 1.17268 1.17166 1.17167
2003-05-28 12:05:00 1.17168 1.17237 1.17147 1.17224
2003-05-28 12:10:00 1.17214 1.17228 1.17194 1.17217
2003-05-28 12:15:00 1.17224 1.17237 1.17187 1.17209
2003-05-28 12:20:00 1.17205 1.17229 1.17153 1.17153
2003-05-28 12:25:00 1.17121 1.17226 1.17047 1.17204
2003-05-28 12:30:00 1.17208 1.1726 1.17181 1.17259
2003-05-28 12:35:00 1.17261 1.17264 1.17182 1.17192
2003-05-28 12:40:00 1.17203 1.17337 1.17189 1.17337
2003-05-28 12:45:00 1.17351 1.1736 1.1714 1.17153

Si el campo “date” es una columna y el dataframe se llama “data”, por ejemplo, tendríamos que escribir lo siguiente para poder usarlo como argumento, pues el  índice ha de ser datetime:

data = data.set_index("date")

Por supuesto el código de la clase anterior se puede modificar para funcionar con numpy o cualquier otra biblioteca.

La aplicación

Si queremos obtener los máximos y mínimos locales de los valores “high” de nuestra serie de datos utilizando 6 valores para calcular los picos y valles, es tan simple como hacer esto:

#"data" es un DataFrame que contiene los datos de la tabla de arriba.
fr = fractals()
high_fractals = fr.getFractals(data=data,column_mode="high", depth=3)

El resultado se cargaría en la variable high_fractals y sería el siguiente:

Y para extraer los máximos, por ejemplo, solo bastaría ejecutar el siguiente código:

Con una profundidad de 2, éste sería el resultado para los datos:

import matplotlib.pyplot as plt
 
fr = fractals() 
#"data" es un DataFrame que contiene los datos de la tabla de arriba. fr = fractals() 
high_fractals = fr.getFractals(data=data,column_mode="high", depth=2)
 
fig, ax = plt.subplots()
fig.set_size_inches(10, 5)
plt.plot(high_fractals["valleys"].dropna(), marker='^', linestyle="none", markersize=7,  color="red")
plt.plot(high_fractals["peaks"].dropna(), marker='v', linestyle="none", markersize=7,  color="green")
plt.plot(data.high, color="black")
plt.show()

El resultado es más que satisfactorio para la mayoría de escenarios.  En mi caso, lo voy a mejorar añadiendo también identificación de movimientos a partir de cierto nivel (porcentaje, pips, etc).

Pero, en general, este sencillo algoritmo cumple con todas las expectativas que personalmente necesito para marcar cambios direccionales en series temporales. Menos es más.

¿Y qué se puede hacer con estos puntos?

Con dos puntos podemos pintar una línea, por ejemplo.

data = high_fractals["peaks"].dropna()
TF_seconds = 300 #timeframe de 5 minutos.
peaks = []
 
for i in range(1, data.shape[0]):
    peaks.append( { "type": "H", 
    "x1": data[i - 1:i].index, "y1": data[i - 1], 
    "x2": data[i:i + 1].index, "y2": data[i], 
    )
 
for point in peaks:
    diference = (point["x2"] - point['x1']).seconds[0]
    units = diference / TF_seconds
    slope = (point["y2"] - point["y1"]) / units
    slope = slope / 0.0001
    peaks[i]["slope"] = slope
    peaks[i]["units"] = units
    i += 1

Y he ahí todos los vectores directores de todas las líneas de todos los picos consecutivos. Con meta-información relativa a su pendiente e incremento de X en formato escalar (aparte de datetime). La imagen de abajo ilustra la estructura de la implementación completa, donde la pendiente está calculada en pips:

La imaginación es el límite.

 

(Español) Principios de negociación armónica. Desde Leonardo hasta Scott.

“, se describe la serie descubierta por Leonardo de Pisa, más conocido por Fibonacci. Pero, ¿por qué estudiar una mera serie exponencial puramente matemática para negociar las variaciones de precios que sufren los activos?

Si vives en Europa y trabajas, tu horario de trabajo coincide con el horario de apertura de los principales mercados financieros. O mejor dicho con el solapamiento de Reino Unido, Europa y Estados Unidos. Si ignoramos los picos de la sesión Asiática, es entre las 10:00AM y las 17:00PM CET cuando vale la pena operar las principales divisas (euro, dólar, yen…). De no ser que tengas un capital inicial, es obligatorio compaginar un buen trabajo con la negociación. En estos casos, la actividad de trading, si operas intradía, la realizas justo en los horarios de finalización y cierre de los principales mercados financieros; por lo que la mayor parte de la actividad financiera ya ha sido realizada y los operadores se retiran.

A priori se podría pensar que esto supone un estancamiento de las negociaciones y una consolidación de los precios hasta la siguiente sesión, un cese de los movimientos de precios. Pero lo que he estado observando en cada ocasión que he estado operando ha sido que había un retroceso de los precios paulatino. En términos prácticos, si la tendencia intra-día era ascendente, de repente ésta se invertía; por lo que si aplicaba la estrategia natural de comprar en mercados ascendentes, mis operaciones se cancelaban sin beneficios.

Mis estrategias funcionaban como un reloj durante la sesión, pero no eran tan fiables al final de las sesiones, dado que en un momento dado el momento se invertía y tenía que replantear toda la estructura de precios e invalidar la preliminar.

Anteriormente había estudiado y puesto en práctica las obras de L.A. Little sobre la determinación de tendencias y de Bob Volman sobre estructuras de precios; intercambiando incluso algún e-mail con éste último. Por ese motivo, recordando algunas de las referencias de mi amigo y trader Carlos Valverde a Richard Demille Wyckoff, en referencia al perfeccionamiento de la teoría de ondas de Ralph Nelson Elliott; empecé un estudio del sistema de Wickoff. Fuera por su caracterización más categórica, fuera por otro motivo; al igual que me sucedió con la teoría de Elliott cuando estaba en la facultad de Empresariales, no encontré de mi gusto la forma de analizar los mercados. El libro “Trades About to Happen”, de David H. Weis sobre el método Wickoff fue el último que leí sobre el tema, el cual por otro lado describía un sistema de trading bastante parecido al que yo ya seguía en ese momento.

Al tener una forma de pensar muy numérica, mis sistemas deben de ser reproducibles mediante codificación algorítmica para que les dé validez. El sistema actual que utilizo tiene menos de un pip de margen de error; esto es, lo cierro si el precio excede en un solo pip mi nivel de entrada. Esto significa que la entrada es extremadamente precisa o se invalida. Veamos un ejemplo:

Compra con stop loss y take profit.