mastering - python for trading
Índice de fuerza relativa en pandas python (7)
Soy nuevo en pandas. ¿Cuál es la mejor manera de calcular la parte de fuerza relativa en el indicador RSI en pandas? Hasta ahora he conseguido lo siguiente:
from pylab import *
import pandas as pd
import numpy as np
def Datapull(Stock):
try:
df = (pd.io.data.DataReader(Stock,''yahoo'',start=''01/01/2010''))
return df
print ''Retrieved'', Stock
time.sleep(5)
except Exception, e:
print ''Main Loop'', str(e)
def RSIfun(price, n=14):
delta = price[''Close''].diff()
#-----------
dUp=
dDown=
RolUp=pd.rolling_mean(dUp, n)
RolDown=pd.rolling_mean(dDown, n).abs()
RS = RolUp / RolDown
rsi= 100.0 - (100.0 / (1.0 + RS))
return rsi
Stock=''AAPL''
df=Datapull(Stock)
RSIfun(df)
¿Lo estoy haciendo correctamente hasta ahora? Tengo problemas con la parte de diferencia de la ecuación en la que se separan los cálculos ascendentes y descendentes
Es importante tener en cuenta que hay varias formas de definir el RSI. Por lo general, se define al menos de dos maneras: usando un promedio móvil simple (SMA) como arriba, o usando un promedio móvil exponencial (EMA). Aquí hay un fragmento de código que calcula ambas definiciones de RSI y las traza para comparación. Estoy descartando la primera fila después de tomar la diferencia, ya que siempre es NaN por definición.
Tenga en cuenta que cuando use EMA, debe tener cuidado: ya que incluye una memoria que se remonta al principio de los datos, ¡el resultado depende de dónde comience! Por esta razón, normalmente las personas agregarán algunos datos al principio, digamos 100 pasos de tiempo, y luego cortarán los primeros 100 valores RSI.
En la gráfica a continuación, se puede ver la diferencia entre el RSI calculado utilizando SMA y EMA: el SMA tiende a ser más sensible. Tenga en cuenta que el RSI basado en EMA tiene su primer valor finito en el primer paso (que es el segundo paso del período original, debido a descartar la primera fila), mientras que el RSI basado en SMA tiene su primer valor finito en el Paso 14 de tiempo. Esto se debe a que, de forma predeterminada, rolling_mean () solo devuelve un valor finito una vez que hay suficientes valores para llenar la ventana.
import pandas
import pandas.io.data
import datetime
import matplotlib.pyplot as plt
# Window length for moving average
window_length = 14
# Dates
start = datetime.datetime(2010, 1, 1)
end = datetime.datetime(2013, 1, 27)
# Get data
data = pandas.io.data.DataReader(''AAPL'', ''yahoo'', start, end)
# Get just the close
close = data[''Adj Close'']
# Get the difference in price from previous step
delta = close.diff()
# Get rid of the first row, which is NaN since it did not have a previous
# row to calculate the differences
delta = delta[1:]
# Make the positive gains (up) and negative gains (down) Series
up, down = delta.copy(), delta.copy()
up[up < 0] = 0
down[down > 0] = 0
# Calculate the EWMA
roll_up1 = pandas.stats.moments.ewma(up, window_length)
roll_down1 = pandas.stats.moments.ewma(down.abs(), window_length)
# Calculate the RSI based on EWMA
RS1 = roll_up1 / roll_down1
RSI1 = 100.0 - (100.0 / (1.0 + RS1))
# Calculate the SMA
roll_up2 = pandas.rolling_mean(up, window_length)
roll_down2 = pandas.rolling_mean(down.abs(), window_length)
# Calculate the RSI based on SMA
RS2 = roll_up2 / roll_down2
RSI2 = 100.0 - (100.0 / (1.0 + RS2))
# Compare graphically
plt.figure()
RSI1.plot()
RSI2.plot()
plt.legend([''RSI via EWMA'', ''RSI via SMA''])
plt.show()
Mi respuesta se prueba en los datos de muestra de StockCharts.
[Información del RSI de StockChart] [1] http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:relative_strength_index_rsi
def RSI(series, period):
delta = series.diff().dropna()
u = delta * 0
d = u.copy()
u[delta > 0] = delta[delta > 0]
d[delta < 0] = -delta[delta < 0]
u[u.index[period-1]] = np.mean( u[:period] ) #first value is sum of avg gains
u = u.drop(u.index[:(period-1)])
d[d.index[period-1]] = np.mean( d[:period] ) #first value is sum of avg losses
d = d.drop(d.index[:(period-1)])
rs = pd.stats.moments.ewma(u, com=period-1, adjust=False) / /
pd.stats.moments.ewma(d, com=period-1, adjust=False)
return 100 - 100 / (1 + rs)
#sample data from StockCharts
data = pd.Series( [ 44.34, 44.09, 44.15, 43.61,
44.33, 44.83, 45.10, 45.42,
45.84, 46.08, 45.89, 46.03,
45.61, 46.28, 46.28, 46.00,
46.03, 46.41, 46.22, 45.64 ] )
print RSI( data, 14 )
#output
14 70.464135
15 66.249619
16 66.480942
17 69.346853
18 66.294713
19 57.915021
Puede utilizar rolling_apply en combinación con una subfunción para realizar una función limpia como esta:
def rsi(price, n=14):
'''''' rsi indicator ''''''
gain = (price-price.shift(1)).fillna(0) # calculate price gain with previous day, first row nan is filled with 0
def rsiCalc(p):
# subfunction for calculating rsi for one lookback period
avgGain = p[p>0].sum()/n
avgLoss = -p[p<0].sum()/n
rs = avgGain/avgLoss
return 100 - 100/(1+rs)
# run for all periods with rolling_apply
return pd.rolling_apply(gain,n,rsiCalc)
Yo también tenía esta pregunta y estaba trabajando en el camino de enrollado que Jev tomó. Sin embargo, cuando probé mis resultados, no coincidían con los programas comerciales de gráficos de acciones que utilizo, como StockCharts.com o thinkorswim. Así que hice algunas excavaciones y descubrí que cuando Welles Wilder creó el RSI, usó una técnica de suavizado que ahora se conoce como Wilder Smoothing. Los servicios comerciales anteriores utilizan Wilder Smoothing en lugar de un simple promedio móvil para calcular las ganancias y pérdidas promedio.
Soy nuevo en Python (y Pandas), así que me pregunto si hay alguna manera brillante de refactorizar el bucle for para hacerlo más rápido. Tal vez alguien más pueda comentar sobre esa posibilidad.
Espero que encuentres esto útil.
def get_rsi_timeseries(prices, n=14):
# RSI = 100 - (100 / (1 + RS))
# where RS = (Wilder-smoothed n-period average of gains / Wilder-smoothed n-period average of -losses)
# Note that losses above should be positive values
# Wilder-smoothing = ((previous smoothed avg * (n-1)) + current value to average) / n
# For the very first "previous smoothed avg" (aka the seed value), we start with a straight average.
# Therefore, our first RSI value will be for the n+2nd period:
# 0: first delta is nan
# 1:
# ...
# n: lookback period for first Wilder smoothing seed value
# n+1: first RSI
# First, calculate the gain or loss from one price to the next. The first value is nan so replace with 0.
deltas = (prices-prices.shift(1)).fillna(0)
# Calculate the straight average seed values.
# The first delta is always zero, so we will use a slice of the first n deltas starting at 1,
# and filter only deltas > 0 to get gains and deltas < 0 to get losses
avg_of_gains = deltas[1:n+1][deltas > 0].sum() / n
avg_of_losses = -deltas[1:n+1][deltas < 0].sum() / n
# Set up pd.Series container for RSI values
rsi_series = pd.Series(0.0, deltas.index)
# Now calculate RSI using the Wilder smoothing method, starting with n+1 delta.
up = lambda x: x if x > 0 else 0
down = lambda x: -x if x < 0 else 0
i = n+1
for d in deltas[n+1:]:
avg_of_gains = ((avg_of_gains * (n-1)) + up(d)) / n
avg_of_losses = ((avg_of_losses * (n-1)) + down(d)) / n
if avg_of_losses != 0:
rs = avg_of_gains / avg_of_losses
rsi_series[i] = 100 - (100 / (1 + rs))
else:
rsi_series[i] = 100
i += 1
return rsi_series
# Relative Strength Index
# Avg(PriceUp)/(Avg(PriceUP)+Avg(PriceDown)*100
# Where: PriceUp(t)=1*(Price(t)-Price(t-1)){Price(t)- Price(t-1)>0};
# PriceDown(t)=-1*(Price(t)-Price(t-1)){Price(t)- Price(t-1)<0};
# Change the formula for your own requirement
def rsi(values):
up = values[values>0].mean()
down = -1*values[values<0].mean()
return 100 * up / (up + down)
stock[''RSI_6D''] = stock[''Momentum_1D''].rolling(center=False,window=6).apply(rsi)
stock[''RSI_12D''] = stock[''Momentum_1D''].rolling(center=False,window=12).apply(rsi)
Momentum_1D = Pt - P (t-1) donde P es el precio de cierre y t es la fecha
def RSI(series):
delta = series.diff()
u = delta * 0
d = u.copy()
i_pos = delta > 0
i_neg = delta < 0
u[i_pos] = delta[i_pos]
d[i_neg] = delta[i_neg]
rs = moments.ewma(u, span=27) / moments.ewma(d, span=27)
return 100 - 100 / (1 + rs)
dUp= delta[delta > 0]
dDown= delta[delta < 0]
También necesitas algo como:
RolUp = RolUp.reindex_like(delta, method=''ffill'')
RolDown = RolDown.reindex_like(delta, method=''ffill'')
de lo contrario, RS = RolUp / RolDown
no hará lo que usted desea
Editar: parece que esta es una forma más precisa de cálculo RS:
# dUp= delta[delta > 0]
# dDown= delta[delta < 0]
# dUp = dUp.reindex_like(delta, fill_value=0)
# dDown = dDown.reindex_like(delta, fill_value=0)
dUp, dDown = delta.copy(), delta.copy()
dUp[dUp < 0] = 0
dDown[dDown > 0] = 0
RolUp = pd.rolling_mean(dUp, n)
RolDown = pd.rolling_mean(dDown, n).abs()
RS = RolUp / RolDown