Saltar al contenido

¿Calcular el indicador RSI de pandas DataFrame?

Esta es la contestación más acertada que encomtrarás brindar, sin embargo estúdiala pausadamente y valora si se puede adaptar a tu proyecto.

Solución:

La ganancia y la pérdida promedio se calculan mediante un fórmula recursivacual no se puede vectorizar con numpy. Sin embargo, podemos tratar de encontrar un analítico (es decir, no recursiva) solución para calcular los elementos individuales. Dicha solución se puede implementar usando numpy.

Denotando la ganancia promedio como y y la ganancia actual como xobtenemos y[i] = a*y[i-1] + b*x[i]donde a = 13/14 y b = 1/14 por n = 14. Desenvolver la recursión conduce a:
ingrese la descripción de la imagen aquí


(Perdón por la imagen, fue demasiado engorroso escribirla)

Esto se puede calcular de manera eficiente en numpy usando cumsum (rma = media móvil móvil):

import pandas as pd
import numpy as np

df = pd.DataFrame('close':[4724.89, 4378.51,6463.00,9838.96,13716.36,10285.10,
                          10326.76,6923.91,9246.01,7485.01,6390.07,7730.93,
                          7011.21,6626.57,6371.93,4041.32,3702.90,3434.10,
                          3813.69,4103.95,5320.81,8555.00,10854.10])
n = 14


def rma(x, n, y0):
    a = (n-1) / n
    ak = a**np.arange(len(x)-1, -1, -1)
    return np.r_[np.full(n, np.nan), y0, np.cumsum(ak * x) / ak / n + y0 * a**np.arange(1, len(x)+1)]

df['change'] = df['close'].diff()
df['gain'] = df.change.mask(df.change < 0, 0.0)
df['loss'] = -df.change.mask(df.change > 0, -0.0)
df['avg_gain'] = rma(df.gain[n+1:].to_numpy(), n, np.nansum(df.gain.to_numpy()[:n+1])/n)
df['avg_loss'] = rma(df.loss[n+1:].to_numpy(), n, np.nansum(df.loss.to_numpy()[:n+1])/n)
df['rs'] = df.avg_gain / df.avg_loss
df['rsi_14'] = 100 - (100 / (1 + df.rs))

salida de df.round(2):

         close   change     gain     loss  avg_gain  avg_loss    rs    rsi  rsi_14
0      4724.89      NaN      NaN      NaN       NaN       NaN   NaN    NaN     NaN
1      4378.51  -346.38     0.00   346.38       NaN       NaN   NaN    NaN     NaN
2      6463.00  2084.49  2084.49     0.00       NaN       NaN   NaN    NaN     NaN
3      9838.96  3375.96  3375.96     0.00       NaN       NaN   NaN    NaN     NaN
4     13716.36  3877.40  3877.40     0.00       NaN       NaN   NaN    NaN     NaN
5     10285.10 -3431.26     0.00  3431.26       NaN       NaN   NaN    NaN     NaN
6     10326.76    41.66    41.66     0.00       NaN       NaN   NaN    NaN     NaN
7      6923.91 -3402.85     0.00  3402.85       NaN       NaN   NaN    NaN     NaN
8      9246.01  2322.10  2322.10     0.00       NaN       NaN   NaN    NaN     NaN
9      7485.01 -1761.00     0.00  1761.00       NaN       NaN   NaN    NaN     NaN
10     6390.07 -1094.94     0.00  1094.94       NaN       NaN   NaN    NaN     NaN
11     7730.93  1340.86  1340.86     0.00       NaN       NaN   NaN    NaN     NaN
12     7011.21  -719.72     0.00   719.72       NaN       NaN   NaN    NaN     NaN
13     6626.57  -384.64     0.00   384.64       NaN       NaN   NaN    NaN     NaN
14     6371.93  -254.64     0.00   254.64    931.61    813.96  1.14  53.37   53.37
15     4041.32 -2330.61     0.00  2330.61    865.06    922.29  0.94  48.40   48.40
16     3702.90  -338.42     0.00   338.42    803.27    880.59  0.91  47.70   47.70
17     3434.10  -268.80     0.00   268.80    745.90    836.89  0.89  47.13   47.13
18     3813.69   379.59   379.59     0.00    719.73    777.11  0.93  48.08   48.08
19     4103.95   290.26   290.26     0.00    689.05    721.60  0.95  48.85   48.85
20     5320.81  1216.86  1216.86     0.00    726.75    670.06  1.08  52.03   52.03
21     8555.00  3234.19  3234.19     0.00    905.86    622.20  1.46  59.28   59.28
22    10854.10  2299.10  2299.10     0.00   1005.37    577.75  1.74  63.51   63.51

Con respecto a su última pregunta sobre el rendimiento: los bucles explícitos en python/pandas son terribles, evítalos siempre que puedas. Si no puedes, prueba con cython o numba.

Para ilustrar esto, hice una pequeña comparación de mi solución numpy con la solución de bucle de dimitris_ps:

import pandas as pd
import numpy as np
import timeit

mult = 1        # length of dataframe = 23 * mult
number = 1000   # number of loop for timeit

df0 = pd.DataFrame('close':[4724.89, 4378.51,6463.00,9838.96,13716.36,10285.10,
                          10326.76,6923.91,9246.01,7485.01,6390.07,7730.93,
                          7011.21,6626.57,6371.93,4041.32,3702.90,3434.10,
                          3813.69,4103.95,5320.81,8555.00,10854.10] * mult )
n = 14

def rsi_np():
    # my numpy solution from above
    return df
    
def rsi_loop():
    # loop solution https://stackoverflow.com/a/57008625/3944322
    # without the wrong alternative calculation of df['avg_gain'][14]
    return df

df = df0.copy()
time_np = timeit.timeit('rsi_np()', globals=globals(), number = number) / 1000 * number

df = df0.copy()
time_loop = timeit.timeit('rsi_loop()', globals=globals(), number = number) / 1000 * number

print(f'rowstnptloopnlen(df0)ttime_np:.1fttime_loop:.1f')

assert np.allclose(rsi_np(), rsi_loop(), equal_nan=True)

Resultados (ms/bucle):

rows    np    loop
23      4.9   9.2
230     5.0   112.3
2300    5.5   1122.7

Entonces, incluso para 8 filas (filas 15 … 22), la solución de bucle toma aproximadamente el doble de tiempo que la solución numpy. Numpy escala bien, mientras que la solución de bucle no es factible para grandes conjuntos de datos.

valoraciones y comentarios

Nos puedes añadir valor a nuestro contenido informacional cooperando tu experiencia en las críticas.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *