Technical analysis Indicators without Talib (code)

I found the base somewhere on the web and extended it where needed. It really helped me to understand the indicators itself instead of blindly using Talib. Have fun with it

Peter

@author: Bruno Franca
@author: Peter Bakker



import numpy
import pandas as pd
import math as m

#Moving Average
def MA(df, n):
MA = pd.Series(pd.rolling_mean(df['Close'], n), name = 'MA_' + str(n))
df = df.join(MA)
return df

#Exponential Moving Average
def EMA(df, n):
EMA = pd.Series(pd.ewma(df['Close'], span = n, min_periods = n - 1), name = 'EMA_' + str(n))
df = df.join(EMA)
return df

#Momentum
def MOM(df, n):
M = pd.Series(df['Close'].diff(n), name = 'Momentum_' + str(n))
df = df.join(M)
return df

#Rate of Change
def ROC(df, n):
M = df['Close'].diff(n - 1)
N = df['Close'].shift(n - 1)
ROC = pd.Series(M / N, name = 'ROC_' + str(n))
df = df.join(ROC)
return df

#Average True Range
def ATR(df, n):
i = 0
TR_l = [0]
while i < df.index[-1]:
TR = max(df.get_value(i + 1, 'High'), df.get_value(i, 'Close')) - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))
TR_l.append(TR)
i = i + 1
TR_s = pd.Series(TR_l)
ATR = pd.Series(pd.ewma(TR_s, span = n, min_periods = n), name = 'ATR_' + str(n))
df = df.join(ATR)
return df

#Bollinger Bands
def BBANDS(df, n):
MA = pd.Series(pd.rolling_mean(df['Close'], n))
MSD = pd.Series(pd.rolling_std(df['Close'], n))
b1 = 4 * MSD / MA
B1 = pd.Series(b1, name = 'BollingerB_' + str(n))
df = df.join(B1)
b2 = (df['Close'] - MA + 2 * MSD) / (4 * MSD)
B2 = pd.Series(b2, name = 'Bollinger%b_' + str(n))
df = df.join(B2)
return df

#Pivot Points, Supports and Resistances
def PPSR(df):
PP = pd.Series((df['High'] + df['Low'] + df['Close']) / 3)
R1 = pd.Series(2 * PP - df['Low'])
S1 = pd.Series(2 * PP - df['High'])
R2 = pd.Series(PP + df['High'] - df['Low'])
S2 = pd.Series(PP - df['High'] + df['Low'])
R3 = pd.Series(df['High'] + 2 * (PP - df['Low']))
S3 = pd.Series(df['Low'] - 2 * (df['High'] - PP))
psr = {'PP':PP, 'R1':R1, 'S1':S1, 'R2':R2, 'S2':S2, 'R3':R3, 'S3':S3}
PSR = pd.DataFrame(psr)
df = df.join(PSR)
return df

#Stochastic oscillator %K
def STOK(df):
SOk = pd.Series((df['Close'] - df['Low']) / (df['High'] - df['Low']), name = 'SO%k')
df = df.join(SOk)
return df

# Stochastic Oscillator, EMA smoothing, nS = slowing (1 if no slowing)
def STO(df,  nK, nD, nS=1):
SOk = pd.Series((df['Close'] - df['Low'].rolling(nK).min()) / (df['High'].rolling(nK).max() - df['Low'].rolling(nK).min()), name = 'SO%k'+str(nK))
SOd = pd.Series(SOk.ewm(ignore_na=False, span=nD, min_periods=nD-1, adjust=True).mean(), name = 'SO%d'+str(nD))
SOk = SOk.ewm(ignore_na=False, span=nS, min_periods=nS-1, adjust=True).mean()
SOd = SOd.ewm(ignore_na=False, span=nS, min_periods=nS-1, adjust=True).mean()
df = df.join(SOk)
df = df.join(SOd)
return df
# Stochastic Oscillator, SMA smoothing, nS = slowing (1 if no slowing)
def STO(df, nK, nD,  nS=1):
SOk = pd.Series((df['Close'] - df['Low'].rolling(nK).min()) / (df['High'].rolling(nK).max() - df['Low'].rolling(nK).min()), name = 'SO%k'+str(nK))
SOd = pd.Series(SOk.rolling(window=nD, center=False).mean(), name = 'SO%d'+str(nD))
SOk = SOk.rolling(window=nS, center=False).mean()
SOd = SOd.rolling(window=nS, center=False).mean()
df = df.join(SOk)
df = df.join(SOd)
return df
#Trix
def TRIX(df, n):
EX1 = pd.ewma(df['Close'], span = n, min_periods = n - 1)
EX2 = pd.ewma(EX1, span = n, min_periods = n - 1)
EX3 = pd.ewma(EX2, span = n, min_periods = n - 1)
i = 0
ROC_l = [0]
while i + 1 <= df.index[-1]:
ROC = (EX3[i + 1] - EX3[i]) / EX3[i]
ROC_l.append(ROC)
i = i + 1
Trix = pd.Series(ROC_l, name = 'Trix_' + str(n))
df = df.join(Trix)
return df

#Average Directional Movement Index
i = 0
UpI = []
DoI = []
while i + 1 <= df.index[-1]:
UpMove = df.get_value(i + 1, 'High') - df.get_value(i, 'High')
DoMove = df.get_value(i, 'Low') - df.get_value(i + 1, 'Low')
if UpMove > DoMove and UpMove > 0:
UpD = UpMove
else: UpD = 0
UpI.append(UpD)
if DoMove > UpMove and DoMove > 0:
DoD = DoMove
else: DoD = 0
DoI.append(DoD)
i = i + 1
i = 0
TR_l = [0]
while i < df.index[-1]:
TR = max(df.get_value(i + 1, 'High'), df.get_value(i, 'Close')) - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))
TR_l.append(TR)
i = i + 1
TR_s = pd.Series(TR_l)
ATR = pd.Series(pd.ewma(TR_s, span = n, min_periods = n))
UpI = pd.Series(UpI)
DoI = pd.Series(DoI)
PosDI = pd.Series(pd.ewma(UpI, span = n, min_periods = n - 1) / ATR)
NegDI = pd.Series(pd.ewma(DoI, span = n, min_periods = n - 1) / ATR)
return df

#MACD, MACD Signal and MACD difference
def MACD(df, n_fast, n_slow):
EMAfast = pd.Series(pd.ewma(df['Close'], span = n_fast, min_periods = n_slow - 1))
EMAslow = pd.Series(pd.ewma(df['Close'], span = n_slow, min_periods = n_slow - 1))
MACD = pd.Series(EMAfast - EMAslow, name = 'MACD_' + str(n_fast) + '_' + str(n_slow))
MACDsign = pd.Series(pd.ewma(MACD, span = 9, min_periods = 8), name = 'MACDsign_' + str(n_fast) + '_' + str(n_slow))
MACDdiff = pd.Series(MACD - MACDsign, name = 'MACDdiff_' + str(n_fast) + '_' + str(n_slow))
df = df.join(MACD)
df = df.join(MACDsign)
df = df.join(MACDdiff)
return df

#Mass Index
def MassI(df):
Range = df['High'] - df['Low']
EX1 = pd.ewma(Range, span = 9, min_periods = 8)
EX2 = pd.ewma(EX1, span = 9, min_periods = 8)
Mass = EX1 / EX2
MassI = pd.Series(pd.rolling_sum(Mass, 25), name = 'Mass Index')
df = df.join(MassI)
return df

#Vortex Indicator: http://www.vortexindicator.com/VFX_VORTEX.PDF
def Vortex(df, n):
i = 0
TR = [0]
while i < df.index[-1]:
Range = max(df.get_value(i + 1, 'High'), df.get_value(i, 'Close')) - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))
TR.append(Range)
i = i + 1
i = 0
VM = [0]
while i < df.index[-1]:
Range = abs(df.get_value(i + 1, 'High') - df.get_value(i, 'Low')) - abs(df.get_value(i + 1, 'Low') - df.get_value(i, 'High'))
VM.append(Range)
i = i + 1
VI = pd.Series(pd.rolling_sum(pd.Series(VM), n) / pd.rolling_sum(pd.Series(TR), n), name = 'Vortex_' + str(n))
df = df.join(VI)
return df

#KST Oscillator
def KST(df, r1, r2, r3, r4, n1, n2, n3, n4):
M = df['Close'].diff(r1 - 1)
N = df['Close'].shift(r1 - 1)
ROC1 = M / N
M = df['Close'].diff(r2 - 1)
N = df['Close'].shift(r2 - 1)
ROC2 = M / N
M = df['Close'].diff(r3 - 1)
N = df['Close'].shift(r3 - 1)
ROC3 = M / N
M = df['Close'].diff(r4 - 1)
N = df['Close'].shift(r4 - 1)
ROC4 = M / N
KST = pd.Series(pd.rolling_sum(ROC1, n1) + pd.rolling_sum(ROC2, n2) * 2 + pd.rolling_sum(ROC3, n3) * 3 + pd.rolling_sum(ROC4, n4) * 4, name = 'KST_' + str(r1) + '_' + str(r2) + '_' + str(r3) + '_' + str(r4) + '_' + str(n1) + '_' + str(n2) + '_' + str(n3) + '_' + str(n4))
df = df.join(KST)
return df

#Relative Strength Index
def RSI(df, n):
i = 0
UpI = [0]
DoI = [0]
while i + 1 <= df.index[-1]:
UpMove = df.get_value(i + 1, 'High') - df.get_value(i, 'High')
DoMove = df.get_value(i, 'Low') - df.get_value(i + 1, 'Low')
if UpMove > DoMove and UpMove > 0:
UpD = UpMove
else: UpD = 0
UpI.append(UpD)
if DoMove > UpMove and DoMove > 0:
DoD = DoMove
else: DoD = 0
DoI.append(DoD)
i = i + 1
UpI = pd.Series(UpI)
DoI = pd.Series(DoI)
PosDI = pd.Series(pd.ewma(UpI, span = n, min_periods = n - 1))
NegDI = pd.Series(pd.ewma(DoI, span = n, min_periods = n - 1))
RSI = pd.Series(PosDI / (PosDI + NegDI), name = 'RSI_' + str(n))
df = df.join(RSI)
return df

#True Strength Index
def TSI(df, r, s):
M = pd.Series(df['Close'].diff(1))
aM = abs(M)
EMA1 = pd.Series(pd.ewma(M, span = r, min_periods = r - 1))
aEMA1 = pd.Series(pd.ewma(aM, span = r, min_periods = r - 1))
EMA2 = pd.Series(pd.ewma(EMA1, span = s, min_periods = s - 1))
aEMA2 = pd.Series(pd.ewma(aEMA1, span = s, min_periods = s - 1))
TSI = pd.Series(EMA2 / aEMA2, name = 'TSI_' + str(r) + '_' + str(s))
df = df.join(TSI)
return df

#Accumulation/Distribution
def ACCDIST(df, n):
ad = (2 * df['Close'] - df['High'] - df['Low']) / (df['High'] - df['Low']) * df['Volume']
ROC = M / N
AD = pd.Series(ROC, name = 'Acc/Dist_ROC_' + str(n))
return df

#Chaikin Oscillator
def Chaikin(df):
ad = (2 * df['Close'] - df['High'] - df['Low']) / (df['High'] - df['Low']) * df['Volume']
Chaikin = pd.Series(pd.ewma(ad, span = 3, min_periods = 2) - pd.ewma(ad, span = 10, min_periods = 9), name = 'Chaikin')
df = df.join(Chaikin)
return df

#Money Flow Index and Ratio
def MFI(df, n):
PP = (df['High'] + df['Low'] + df['Close']) / 3
i = 0
PosMF = [0]
while i < df.index[-1]:
if PP[i + 1] > PP[i]:
PosMF.append(PP[i + 1] * df.get_value(i + 1, 'Volume'))
else:
PosMF.append(0)
i = i + 1
PosMF = pd.Series(PosMF)
TotMF = PP * df['Volume']
MFR = pd.Series(PosMF / TotMF)
MFI = pd.Series(pd.rolling_mean(MFR, n), name = 'MFI_' + str(n))
df = df.join(MFI)
return df

#On-balance Volume
def OBV(df, n):
i = 0
OBV = [0]
while i < df.index[-1]:
if df.get_value(i + 1, 'Close') - df.get_value(i, 'Close') > 0:
OBV.append(df.get_value(i + 1, 'Volume'))
if df.get_value(i + 1, 'Close') - df.get_value(i, 'Close') == 0:
OBV.append(0)
if df.get_value(i + 1, 'Close') - df.get_value(i, 'Close') < 0:
OBV.append(-df.get_value(i + 1, 'Volume'))
i = i + 1
OBV = pd.Series(OBV)
OBV_ma = pd.Series(pd.rolling_mean(OBV, n), name = 'OBV_' + str(n))
df = df.join(OBV_ma)
return df

#Force Index
def FORCE(df, n):
F = pd.Series(df['Close'].diff(n) * df['Volume'].diff(n), name = 'Force_' + str(n))
df = df.join(F)
return df

#Ease of Movement
def EOM(df, n):
EoM = (df['High'].diff(1) + df['Low'].diff(1)) * (df['High'] - df['Low']) / (2 * df['Volume'])
Eom_ma = pd.Series(pd.rolling_mean(EoM, n), name = 'EoM_' + str(n))
df = df.join(Eom_ma)
return df

#Commodity Channel Index
def CCI(df, n):
PP = (df['High'] + df['Low'] + df['Close']) / 3
CCI = pd.Series((PP - pd.rolling_mean(PP, n)) / pd.rolling_std(PP, n), name = 'CCI_' + str(n))
df = df.join(CCI)
return df

#Coppock Curve
def COPP(df, n):
M = df['Close'].diff(int(n * 11 / 10) - 1)
N = df['Close'].shift(int(n * 11 / 10) - 1)
ROC1 = M / N
M = df['Close'].diff(int(n * 14 / 10) - 1)
N = df['Close'].shift(int(n * 14 / 10) - 1)
ROC2 = M / N
Copp = pd.Series(pd.ewma(ROC1 + ROC2, span = n, min_periods = n), name = 'Copp_' + str(n))
df = df.join(Copp)
return df

#Keltner Channel
def KELCH(df, n):
KelChM = pd.Series(pd.rolling_mean((df['High'] + df['Low'] + df['Close']) / 3, n), name = 'KelChM_' + str(n))
KelChU = pd.Series(pd.rolling_mean((4 * df['High'] - 2 * df['Low'] + df['Close']) / 3, n), name = 'KelChU_' + str(n))
KelChD = pd.Series(pd.rolling_mean((-2 * df['High'] + 4 * df['Low'] + df['Close']) / 3, n), name = 'KelChD_' + str(n))
df = df.join(KelChM)
df = df.join(KelChU)
df = df.join(KelChD)
return df

#Ultimate Oscillator
def ULTOSC(df):
i = 0
TR_l = [0]
BP_l = [0]
while i < df.index[-1]:
TR = max(df.get_value(i + 1, 'High'), df.get_value(i, 'Close')) - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))
TR_l.append(TR)
BP = df.get_value(i + 1, 'Close') - min(df.get_value(i + 1, 'Low'), df.get_value(i, 'Close'))
BP_l.append(BP)
i = i + 1
UltO = pd.Series((4 * pd.rolling_sum(pd.Series(BP_l), 7) / pd.rolling_sum(pd.Series(TR_l), 7)) + (2 * pd.rolling_sum(pd.Series(BP_l), 14) / pd.rolling_sum(pd.Series(TR_l), 14)) + (pd.rolling_sum(pd.Series(BP_l), 28) / pd.rolling_sum(pd.Series(TR_l), 28)), name = 'Ultimate_Osc')
df = df.join(UltO)
return df

#Donchian Channel
def DONCH(df, n):
i = 0
DC_l = []
while i < n - 1:
DC_l.append(0)
i = i + 1
i = 0
while i + n - 1 < df.index[-1]:
DC = max(df['High'].ix[i:i + n - 1]) - min(df['Low'].ix[i:i + n - 1])
DC_l.append(DC)
i = i + 1
DonCh = pd.Series(DC_l, name = 'Donchian_' + str(n))
DonCh = DonCh.shift(n - 1)
df = df.join(DonCh)
return df

#Standard Deviation
def STDDEV(df, n):
df = df.join(pd.Series(pd.rolling_std(df['Close'], n), name = 'STD_' + str(n)))
return df

44 responses

This is great , thank you for sharing this :)

Lionel.

this seems great

There's little need to see an indicator implementation, the whole point of using external libraries is delegating implementation details to someone else, and ta-lib is well tested in that regard. Moreover by using a python implementation you're possibly not using acceleration on numpy or panda's side.

It is IMHO better to understand and indicator's rationale and proper usage by reading the author's note or some specific article about it than looking at the code.

Also those are just methods from the pandas implemented module of pyTaLib with packages import rather than wildcards, I'd put the author's name back in the body (due to copyright) and remove your name from it since the file is essentially unmodified.

Your opinion granted. For me it actually helps to look at what happens and as I don't think I'm unique so that's why I put the code up.

The author Bruno is mentioned so I don't think that's an issue

The code is changed here and there and I added a few functions but I omitted to record what I added. As in my humble opinion it's useful I'll keep the post live.

In addition to that. Above code has quite a few indicators that talib does not have so only for that reason it's useful to have it around.

This is sick. I have spent way too much time trying to python talib functions for custom oscillators. Thanks.

edit: What is the good/standard way to build a dataframe with high/low/open/close/volume? I typically have/use individual history dataframes for each but I would like to know how to use the code as it is in the original post.

Thanks again

Thank you!

Robby, here is some code from one of my algos I use to build an OHLC dataframe.. It works but I don't like the method. If anyone has a more streamlined approach I would be grateful to see it.
EDIT: you may want to modify it, I made it in such a way that it appends securities in the same data columns, this was useful for the way I was using the securities data

#Define Window
trail = 200
#store OHLCV data
open_hist = history(trail,'1d','open_price',ffill=False)
close_hist = history(trail,'1d','close_price',ffill=False)
high_hist = history(trail,'1d','high',ffill=False)
low_hist = history(trail,'1d','low',ffill=False)
volume_hist = history(trail,'1d','volume',ffill=False)
opencol = []
closecol = []
highcol = []
lowcol = []
volumecol = []
#trinsmit OHLCV to continuous numpy arrays
for sec in context.secs:
opencol = np.concatenate((opencol, open_hist[sec][:]))
closecol = np.concatenate((closecol, close_hist[sec][:]))
highcol = np.concatenate((highcol, high_hist[sec][:]))
lowcol = np.concatenate((lowcol, low_hist[sec][:]))
volumecol = np.concatenate((volumecol, volume_hist[sec][:]))
print("putting in pandas")
#Combine arrays into dataframe
df = pd.DataFrame({'O': opencol,
'H': highcol,
'L': lowcol,
'C': closecol,
'V': volumecol})


Thank you for this expansive list of indicators. This is really great.

However, I am having a hard time linking up the indicators with my algo order logic. For example, when I reference S1 for a pivot point under def handle_data, I get the following error: NameError: global name 'S1' is not defined

I start my code with the following, which I thought would define S1:

import numpy
import pandas as pd
import math as m

def initialize(context):
set_universe(universe.DollarVolumeUniverse(floor_percentile=99.9, ceiling_percentile=100))

def PPSR(df):
PP = pd.Series((df['High'] + df['Low'] + df['Close']) / 3)
R1 = pd.Series(2 * PP - df['Low'])
S1 = pd.Series(2 * PP - df['High'])
R2 = pd.Series(PP + df['High'] - df['Low'])
S2 = pd.Series(PP - df['High'] + df['Low'])
R3 = pd.Series(df['High'] + 2 * (PP - df['Low']))
S3 = pd.Series(df['Low'] - 2 * (df['High'] - PP))
psr = {'PP':PP, 'R1':R1, 'S1':S1, 'R2':R2, 'S2':S2, 'R3':R3, 'S3':S3}
PSR = pd.DataFrame(psr)
df = df.join(PSR)
return df



Can someone please tell me what I'm missing? I'm new to coding so I wouldn't be shocked if it was something very simple. Thanks very much.

can you share the rest of the code so I can look into it? you can share with me personally through collaborate ( peter.bakker AT alumni.insead.edu ) if you dont want to throw your code in the open

Hi,

I'm working on this Python Pandas implementation of technical indicators.
I've made a standalone project (with a pip package) for this.
Feel free to help. This is still is "work in progress" state.
I've add some unit tests and I'm facing some errors

    ERROR: tests.test_pandas_talib.test_indicator_ATR
----------------------------------------------------------------------
Traceback (most recent call last):
File "//anaconda/lib/python3.4/site-packages/nose/case.py", line 198, in runTest
self.test(*self.arg)
File "/Users/femto/pandas_talib/tests/test_pandas_talib.py", line 45, in test_indicator_ATR
result = ATR(df, n)
File "/Users/femto/pandas_talib/pandas_talib/__init__.py", line 75, in ATR
while i < df.index[-1]:
TypeError: unorderable types: int() < datetime.datetime()


I think that's because I'm using index as datetime (and not int)

Kind regards

Dears,

Can anyone nominate a good trading Algo for me? I need to buy a good one with monthly ROI 10%

Thank you

Hi Ahmed,

sorry your question is out of topic here. Maybe you should try to make you own post.

About Pandas implementation of technical indicators.

I think

 while i < df.index[-1]:


should be replaced by

 while i < len(df) -1:


I also noticed some errors (KeyError: 1) with:

df.get_value(..., ...)


maybe

.iloc[...]


@Peter... maybe you can provide.. a sample... a working backtest for these... on how it is implemented.... ;)

I also noticed that ROC Pandas implementation doesn't return same values that TA-Lib's one

x and y nan location mismatch:
x: array([            nan,             nan,  -1.6632517e-02,  -1.7376779e-03,
-8.6422326e-04,  -6.5179861e-03,  -1.9357306e-02,  -1.4724111e-03,
2.5237212e-02,   4.0374865e-02,   1.4385414e-02,  -2.9944823e-02,...
y: array([            nan,             nan,             nan,  -2.3665359e+00,
4.5841804e-01,  -1.3623165e+00,  -1.3147031e+00,  -1.4223587e+00,
1.8396280e+00,   4.5821285e+00,   3.4569644e+00,  -1.0468660e+00,...



Hi guys, I created a repository of technical indicators on github. The library works similarly to the famous ta-lib library, and contains some indicators that I wanted to use in my strategies and are not included in ta-lib. Enjoy, and even better- contribute!
talibextensions

That seems nice, but this thread was aimed at having python indicators without the use ot TAlib, your package is the opposite of that concept since it relies on it.

That apart I suggest to add a little info in the README; at the very least add docstrings in the functions, people should know the functions' purpose without having to read the code (and even then sometimes it's not obvious).

The package hierarchy and code look quite strange for a python package, they got "java-ish" feeling. Lose the semicolons and the src/ folder.
I'd also switch from package to a module, since you're already using the single file approach.

Hi Guys, newbie here.
I am using the function code by Peter to anlayze a stock. Unfortunately being new to python, I have issue using the functions in my code as it did not run properly.

import numpy
import pandas as pd

#Moving Average
def MA(df, n):
MA = pd.Series(pd.rolling_mean(df['Close'], n), name = 'MA_' + str(n))
df = df.join(MA)
return df

#MACD, MACD Signal and MACD difference
def MACD(df, n_fast, n_slow):
EMAfast = pd.Series(pd.ewma(df['Close'], span = n_fast, min_periods = n_slow - 1))
EMAslow = pd.Series(pd.ewma(df['Close'], span = n_slow, min_periods = n_slow - 1))
MACD = pd.Series(EMAfast - EMAslow, name = 'MACD_' + str(n_fast) + '_' + str(n_slow))
MACDsign = pd.Series(pd.ewma(MACD, span = 9, min_periods = 8), name = 'MACDsign_' + str(n_fast) + '_' + str(n_slow))
MACDdiff = pd.Series(MACD - MACDsign, name = 'MACDdiff_' + str(n_fast) + '_' + str(n_slow))
df = df.join(MACD)
df = df.join(MACDsign)
df = df.join(MACDdiff)
return df

data = pd.read_csv("NAIM.csv", index_col='Stock', usecols =[0,6])

vol = data['Close']
print vol
print MA(data,5)
print MACD(data,12,26)


But the MA result was not correct and MACD has ValueError: negative dimensions are not allowed.
The csv file is as below:

Stock,Date,Time,Open,High,Low,Close,Volume
NAIM,2015-01-02,00:00:00,2.9,3.0,2.9,3.0,46900
NAIM,2015-01-05,00:00:00,2.95,3.05,2.92,3.05,225900
NAIM,2015-01-06,00:00:00,2.95,2.96,2.9,2.9,682000
NAIM,2015-01-07,00:00:00,2.88,2.95,2.88,2.9,160900
.
.
.
NAIM,2016-01-06,00:00:00,2.48,2.61,2.47,2.6,3260900
NAIM,2016-01-07,00:00:00,2.64,2.74,2.6,2.65,3906100
NAIM,2016-01-08,00:00:00,2.65,2.71,2.62,2.64,1875000
NAIM,2016-01-11,00:00:00,2.65,2.7,2.65,2.68,1089400
NAIM,2016-01-12,00:00:00,2.68,2.71,2.65,2.69,965200
NAIM,2016-01-13,00:00:00,2.69,2.74,2.69,2.73,2091500
NAIM,2016-01-14,00:00:00,2.71,2.71,2.66,2.66,1206000
NAIM,2016-01-15,00:00:00,2.66,2.67,2.62,2.62,738600

Hi Ching, As Andrea wrote in an earlier the post, I suggest that instead of using the code in the script, use ta-lib library for technical analysis. (installation guide included in the link). Most of the famous and widely used indicators are implemented and the library's api is very friendly. If there's an indicator that you want to use and is not included in ta-lib, only then should you bother to implement it yourself. I shared a github repository link for implementing more indicators and you are invited to contribute :)

Hi Tom,
I try to install ta-lib(same link that you provided) but fail to do so on my windows Python2.7. I extract the zip file at the site-packages folder, but unable to import the ta-lib library (ImportError: No module named talib)
So using function code from Peter is my second choice since ta-lib was not working for me.

Try to install ta-lib from the binaries here (one of the cp27 should do - as you are using python2.7)

@tom
Thanks. I did manage to install with anaconda.
However, I am unable to use with my above script as I encounter an error when I apply talib.MA(data,12)
TypeError: Argument 'real' has incorrect type (expected numpy.ndarray, got Series)
any idea?

For help with Zipline, the open-source backtest engine that powers Quantopian, you should post in the Zipline Google group. That community has deep Zipline expereience. This community is generally more for financial or algorithmic topics related to Quantopian.

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

@tom The problem with ta-lib is it depends on the C ta-lib. A pure Python TA library would be great.

Speaking of which, @peter do you plan on making this into a proper Python library and putting it up on github?

For pure Python TA lib implementation library you might have a look at https://github.com/femtotrader/pandas_talib
This is base on Bruno Franca / Peter Bakker code
You might also have a look at https://github.com/davidastephens/pandas-finance/issues/1 and https://github.com/bpsmith/tia

Thanks!

Hi
in :
EMA = pd.Series(pd.ewma(df['Close'], span = n, min_periods = n - 1), name = 'EMA_' + str(n))
I don't understand why min_periods=n-1.
Not that it makes any difference at all, but shouldn't it be min_periods = n ?

I am looking for a python code for the indicator McGinley Dynamic. Kindly share.

Thanks Peter. Great code, for newbies like me to learn!

I tried implementing your code. However, I get the following error : TypeError: list indices must be integers, not str EMA = pd.Series(pd.ewma(df['Close'], span = n, min_periods = n - 1), name = 'EMA_' + str(n))

I enter the data via Excel which feeds into Python xlwings.

Any idea what's going on?

@xw.func
def EMA(df, n):
EMA = pd.Series(pd.ewma(df['Close'], span = n, min_periods = n - 1), name = 'EMA_' + str(n))
df = df.join(EMA)
return df

Kind regards

*****I am on the quantopian algo page, not the notebook. I don't understand the notebook for the life of me.

I have been using talib for indicators but I cannot figure out what the input is for the ADOSC - the Chaikin Oscillator. My question can be answered one of two ways with the first preferable.

1) What goes into the ADOSC arguments/parameters for the function to work? I get the error "function requires four arguments (2 given)" with the below code. No idea if these are even close to the correct inputs.

import talib as ta

price_history_hlc_20 = data.history(context.security, ['high', 'low', 'close'], 30, '1d')
volume_history_20 = data.history(context.security, 'volume', 20, '1d')

2) How do I use the indicator if I decide to use the code from this post that puts it into its own def? The way I developed my algo, I am doing all the calculations/defining indicators in the same def that I run the logic to trade.

Chaikin Oscillator

def Chaikin(df):
ad = (2 * df['Close'] - df['High'] - df['Low']) / (df['High'] - df['Low']) * df['Volume']
Chaikin = pd.Series(pd.ewma(ad, span = 3, min_periods = 2) - pd.ewma(ad, span = 10, min_periods = 9), name = 'Chaikin')
df = df.join(Chaikin)
return df

def rebalance(context, data):

Taylor,

Try this:

import talib

def initialize(context):
stock, ma_f, ma_s = symbol('SPY'), 3, 10
H = data.history(stock, 'high', ma_s, '1d')
L = data.history(stock, 'low', ma_s, '1d')
C = data.history(stock, 'close', ma_s, '1d')
V = data.history(stock, 'volume', ma_s, '1d')

record(Chaikin_Osc = Chaikin_Osc_talib)


Thank you so much for the quick reply! I tried to keep the same formatting I have for the rest of the algo so I didn't quite write it like that but you definitely helped me solve it. I used the below code. I must say though, it's disappointing how much variation there is between the one here and the one on stockcharts.com. I presume the stockcharts one is the most accurate. =/ (btw, I know I can do the '[-1]' on the end of the thing but I like to do it separately to keep the logic separated out =)

price_history_hlcv_20 = data.history(context.security, ['high', 'low', 'close', 'volume'], 20, '1d')

adosc = ta.ADOSC(price_history_hlcv_20['high'], price_history_hlcv_20['low'], price_history_hlcv_20['close'], price_history_hlcv_20['volume'], fastperiod = 3, slowperiod = 10)

adosc = adosc[-1]


Do you know what the input is for ADX? It requests 3 arguments. I gave it high, low, close, and its very similar to how it looks on stockcharts but some of the numbers are much higher/lower, more off than the adosc above was.

price_history_hlc_20 = data.history(context.security, ['high', 'low', 'close'], 20, '1d')

adx = adx[-1]


Same thing for Aroon Oscillator. It's similar but not taking the same shape. Any ideas on that one?

aroonosc = ta.AROONOSC(price_history_hlc_20['high'], price_history_hlc_20['low'], timeperiod = 14)

aroonosc = aroonosc[-1]

# ADX talib indicator

import talib

def initialize(context):
stock, period = symbol('SPY'), 10
bars = period*2
H = data.history(stock, 'high', bars, '1d')
L = data.history(stock, 'low', bars, '1d')
C = data.history(stock, 'close', bars, '1d')

# AROON_OSC talib indicator

import talib

def initialize(context):
stock, period = symbol('SPY'), 10
bars = period + 1
H = data.history(stock, 'high', bars, '1d')
L = data.history(stock, 'low', bars, '1d')
AROON_OSC = talib.AROONOSC(H,L,period)
record(AROON_OSC = AROON_OSC[-1])


Yeah I set them up that way but they are off compared to stockcharts. Lame. Thanks for the reply!

Last one Vlad. xD What do you input for the STOCHRSI ta lib indicator?

Try this:

    stoch_rsi = talib.STOCHRSI(C,period,fk,fd,fdma )[1][-1]

# where fdma --> fast D moving average type = 0 --> SMA


Worked great! Thanks bro! What does the [1][-1] on the end mean?! I know [-1] gets you the last value

thank you for this

Stochastic Oscillator formula is wrong. Here is correct code.

# Stochastic Oscillator, EMA smoothing, nS = slowing (1 if no slowing)
def STO(df,  nK, nD, nS=1):
SOk = pd.Series((df['Close'] - df['Low'].rolling(nK).min()) / (df['High'].rolling(nK).max() - df['Low'].rolling(nK).min()), name = 'SO%k'+str(nK))
SOd = pd.Series(SOk.ewm(ignore_na=False, span=nD, min_periods=nD-1, adjust=True).mean(), name = 'SO%d'+str(nD))
SOk = SOk.ewm(ignore_na=False, span=nS, min_periods=nS-1, adjust=True).mean()
SOd = SOd.ewm(ignore_na=False, span=nS, min_periods=nS-1, adjust=True).mean()
df = df.join(SOk)
df = df.join(SOd)
return df
# Stochastic Oscillator, SMA smoothing, nS = slowing (1 if no slowing)
def STO(df, nK, nD,  nS=1):
SOk = pd.Series((df['Close'] - df['Low'].rolling(nK).min()) / (df['High'].rolling(nK).max() - df['Low'].rolling(nK).min()), name = 'SO%k'+str(nK))
SOd = pd.Series(SOk.rolling(window=nD, center=False).mean(), name = 'SO%d'+str(nD))
SOk = SOk.rolling(window=nS, center=False).mean()
SOd = SOd.rolling(window=nS, center=False).mean()
df = df.join(SOk)
df = df.join(SOd)
return df


Just one little thing...this code has been written a long time ago (from now). So maybe would be interesting to look for possible "depracated" warnings in different functions of pandas and update it!

Thanks for sharing

@Alex Brunt, according to this source:

The standard settings for the full version would look like "14, 3, 3",
which is nothing but the slow version of stochastic with the settings
"14,3". Change the parameter in middle to anything else, say 7, and
you are smoothening out the main line by it's SMA of 7 periods and
then taking out a 3 period SMA of that to have your trigger line.

This suggests that D% should be the average of post-smoothed K% instead of pre-smoothed K%. Furthermore, I don't think D% needs to be smoothed again because it's already an EMA/SMA of an EMA/SMA. I've also multiplied K% by 100 to get the more common chart number, removed default arguments, changed the argument names to be more intuitive coming from popular charting software, and renamed columns to simply D% and K%. My final code looks like this:

# Stochastic Oscillator (EMA method)
def StochEMA(df, period, fast, slow=3):
K = pd.Series(((df['close']-df['low'].rolling(period).min()) /
(df['high'].rolling(period).max()-df['low'].rolling(period).min()))*100, name='K%')
K = K.ewm(span=slow, min_periods=slow-1).mean()
D = pd.Series(K.ewm(span=fast, min_periods=fast-1).mean(), name = 'D%')
df = df.join(D)
df = df.join(K)
return df

# Stochastic Oscillator (SMA method)
def StochSMA(df, period, fast, slow=3):
K = pd.Series(((df['close']-df['low'].rolling(period).min()) /
(df['high'].rolling(period).max()-df['low'].rolling(period).min()))*100, name='K%')
K = K.rolling(window=slow).mean()
D = pd.Series(K.rolling(window=fast).mean(), name = 'D%')
df = df.join(D)
df = df.join(K)
return df


This was great to come across! I am currently trying to run a python script in 64-bit Windows and installing TA-Lib on x64 doesn't work, so I needed a way to do my program's talib-dependent calculations without it. I created a Talib class for only the functions I need (EMA, ATR, RSI, and BBANDS).

Rather than change the input lines that were using the talib library, I modified the functions above to work exactly like running a talib line would. For example, the input for many of the talib functions are multiple arrays, not single dataframes; the "n"s used above are referred to as "timeperiod" in talib, etc.

I also noticed the RSI in the original post above is wrong. RSI only takes a Close as its input, not Highs or Lows (The RSI function above looks more like what you'd need for an ADX calculation).

I tried to simplify the 4 functions below (some of the pandas functions used above, like pd.rolling_mean, are scheduled to be deprecated, so I swapped those out) and keep the logic more in line with what steps a user would see if they looked up how to calculate these indicators online.

Perhaps this code may be useful to someone trying to emulate the talib library more in line with how the library itself works, using the same input datatypes and returning the same output.

(I like to instantiate this with Talib = TalibClass() so I only have to capitalize the "T" in "talib" throughout my code to shift from using the talib library to using this custom class)

class TalibClass():
#Exponential Moving Average
def EMA(self, array, timeperiod):
EMA = pd.Series(array).ewm(span = timeperiod, min_periods = timeperiod - 1).mean().rename('EMA_' + str(timeperiod))
return EMA.values
#Average True Range
def ATR(self, highArray, lowArray, closeArray, timeperiod):
TRL = [highArray[0] - lowArray[0]]
ATRL = [np.nan]
i = 1
while i < len(highArray):
TR = np.nanmax([(highArray[i] - lowArray[i]), abs(highArray[i] - closeArray[i]), abs(lowArray[i] - closeArray[i])])
TRL.append(TR)
if i < timeperiod:
ATRL.append(np.nan)
elif i == timeperiod:
ATR = np.nanmean(TRL)
ATRL.append(ATR)
else:
ATR = ((ATRL[-1] * (timeperiod - 1)) + TR) / timeperiod
ATRL.append(ATR)
i += 1
return pd.Series(ATRL).values
#Relative Strength Index
def RSI(self, closeArray, timeperiod):
changeL = [np.nan]
avgGainL = [np.nan]
avgLossL = [np.nan]
RSIL = [np.nan]
i = 1
while i < len(closeArray):
changeL.append(closeArray[i] - closeArray[i - 1])
if i < timeperiod:
avgGainL.append(np.nan)
avgLossL.append(np.nan)
RSIL.append(np.nan)
elif i == timeperiod:
avgGainL.append(abs(np.nansum([x for x in changeL[-timeperiod:] if x >= 0]) / timeperiod))
avgLossL.append(abs(np.nansum([x for x in changeL[-timeperiod:] if x < 0]) / timeperiod))
RS = avgGainL[-1] / avgLossL[-1]
RSI = 100 - (100 / (1 + RS))
RSIL.append(RSI)
else:
if changeL[-1] >= 0:
currGain = changeL[-1]
currLoss = 0
else:
currGain = 0
currLoss = abs(changeL[-1])
avgGain = (avgGainL[-1] * (timeperiod - 1) + currGain) / timeperiod
avgLoss = (avgLossL[-1] * (timeperiod - 1) + currLoss) / timeperiod
avgGainL.append(avgGain)
avgLossL.append(avgLoss)
RS = avgGain / avgLoss
RSI = 100 - (100 / (1 + RS))
RSIL.append(RSI)
i += 1
return pd.Series(RSIL).values

#Bollinger Bands
def BBANDS(self, closeArray, timeperiod, nbdevup, nbdevdn):
middle = pd.Series(closeArray).rolling(window=timeperiod).mean().values
stdev = pd.Series(closeArray).rolling(window=timeperiod).std().values
upper = middle + (stdev * nbdevup)
lower = middle - (stdev * nbdevdn)
return upper, middle, lower


Thank you for sharing this, it is appreciated. This is exactly what I was searching for (and for the very same reasons that motivated you to share it in the first place). It even prompted me to sign up to Quantopian, just so that I could pass on my thanks via this thread! Anyway, thanks and good luck.