Detect double top in stocks with Python

<!DOCTYPE html>



detect_double_top_in_stocks







In [68]:
#!pip3 install fix_yahoo_finance
#!pip3 install pandas_datareader

from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))

# ___library_import_statements___
import pandas as pd

# for pandas_datareader, otherwise it might have issues
pd.core.common.is_list_like = pd.api.types.is_list_like
import pandas_datareader.data as web
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import datetime
import time

#yahoo API data source correction
import fix_yahoo_finance as yahoo_finance
yahoo_finance.pdr_override()

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
In [69]:
# ___variables___
ticker = 'VZ'
start_time = datetime.datetime(2017, 10, 1)
#end_time = datetime.datetime(2019, 1, 20)
end_time = datetime.datetime.now().date().isoformat()         # today
In [70]:
# yahoo gives only daily historical data
connected = False
while not connected:
    try:
        ticker_df = web.get_data_yahoo(ticker, start=start_time, end=end_time)
        connected = True
        print('connected to yahoo')
    except Exception as e:
        print("type error: " + str(e))
        time.sleep( 5 )
        pass   

# use numerical integer index instead of date    
ticker_df = ticker_df.reset_index()
print(ticker_df.head(5))
[*********************100%***********************]  1 of 1 downloaded
connected to yahoo
        Date       Open       High        Low      Close  Adj Close    Volume
0 2017-10-02  49.389999  49.700001  49.250000  49.360001  45.613998   9525800
1 2017-10-03  49.450001  50.020000  49.430000  49.849998  46.066799  10218600
2 2017-10-04  49.639999  50.080002  49.380001  49.900002  46.113014   9887500
3 2017-10-05  49.959999  50.029999  49.680000  49.770000  45.992878  12230600
4 2017-10-06  49.040001  49.200001  48.660000  48.810001  45.646858  13239600
In [71]:
# discrete dataset
x_data = ticker_df.index.tolist()      # the index will be our x axis, not date
y_data = ticker_df['High']

# x values for the polynomial fit, 200 points
x = np.linspace(0, max(ticker_df.index.tolist()), max(ticker_df.index.tolist()) + 1)

# polynomial fit of degree xxx
pol = np.polyfit(x_data, y_data, 15)
y_pol = np.polyval(pol, x)

# ___ plotting ___
plt.figure(figsize=(20, 10), dpi= 120, facecolor='w', edgecolor='k')
# plot stock data
plt.plot(x_data, y_data, 'o', markersize=3.5, color='grey', alpha=0.7)
# plot polynomial fit
plt.plot(x, y_pol, '-', markersize=1.0, color='black', alpha=0.9)
plt.legend(['stock data', 'polynomial fit'])
plt.show()
In [72]:
data = y_pol

#                         ___ detection of local minimums and maximums ___

min_max = np.diff(np.sign(np.diff(data))).nonzero()[0] + 1          # local min & max
l_min = (np.diff(np.sign(np.diff(data))) > 0).nonzero()[0] + 1      # local min
l_max = (np.diff(np.sign(np.diff(data))) < 0).nonzero()[0] + 1      # local max
# +1 due to the fact that diff reduces the original index number

# plot
plt.figure(figsize=(20, 5))
plt.plot(x, data, color='grey')
plt.plot(x[l_min], data[l_min], "o", label="min", color='r')        # minima
plt.plot(x[l_max], data[l_max], "o", label="max", color='b')        # maxima
plt.title('Local minima and maxima')
plt.show()
In [73]:
print('corresponding HIGH values for suspected indices: ')
print(ticker_df.High.iloc[l_max])

#extend the suspected x range:
delta = 5                                        # how many ticks to the left and to the right from local maximum on x axis

dict_i = dict()

for element in l_max:
    l_bound = element - delta                    # lower bound
    u_bound = element + delta                    # upper bound
    x_range = range(l_bound, u_bound + 1)

    y_loc_list = list()
    for x_element in x_range:
        y_loc_list.append(ticker_df.High.iloc[x_element]) 
        #print(y_loc_list)   
    dict_i[element] = y_loc_list
print('DICTIONARY for l_max: ', dict_i)
corresponding HIGH values for suspected indices: 
7      49.320000
66     52.880001
138    48.619999
294    60.369999
377    59.070000
Name: High, dtype: float64
DICTIONARY for l_max:  {7: [50.080002, 50.029999, 49.200001, 49.09, 49.490002000000004, 49.32, 48.849998, 48.48, 48.349998, 48.490002000000004, 48.849998], 66: [53.549999, 53.459998999999996, 53.57, 53.34, 52.889998999999996, 52.880001, 52.75, 52.439999, 51.73, 52.18, 52.240002000000004], 138: [47.98, 48.459998999999996, 49.040001000000004, 49.060001, 48.959998999999996, 48.619999, 48.720001, 50.389998999999996, 50.16, 50.040001000000004, 51.689999], 294: [59.0, 59.509997999999996, 60.700001, 60.73, 59.790001000000004, 60.369999, 59.75, 59.400002, 58.639998999999996, 58.43, 58.369999], 377: [61.189999, 60.889998999999996, 59.27, 59.41, 59.23, 59.07, 59.099998, 59.099998, 59.130001, 58.560001, 58.700001]}
In [79]:
y_delta = 0.05                              # percentage distance between average highs
threshold = max(ticker_df['High']) * 0.90   # setting threshold lower than the global high

y_dict = dict()
maxi = list()
suspected_tops = list()

for key in dict_i.keys():
    mn = sum(dict_i[key])/len(dict_i[key])
    maxi.append(max(dict_i[key]))
    l_y = mn * (1.0 - y_delta)
    u_y = mn * (1.0 + y_delta)
    y_dict[key] = [l_y, u_y, mn]

print('SCREENING FOR DOUBLE TOP:')    

for key_i in y_dict.keys():    
    for key_j in y_dict.keys():    
        if (key_i != key_j) and (y_dict[key_i][2] > threshold):

            if (y_dict[key_i][2] < y_dict[key_j][1]) and (y_dict[key_i][2] > y_dict[key_j][0]):
                print('----------------------- ')
                print('--- Topping pattern found for x index pair: ', key_i, ',', key_j)
                suspected_tops.append(key_i)
                print('----------------------- ')
            else:
                print('Not found yet')
SCREENING FOR DOUBLE TOP:
Not found yet
Not found yet
Not found yet
----------------------- 
--- Topping pattern found for x index pair:  294 , 377
----------------------- 
Not found yet
Not found yet
Not found yet
----------------------- 
--- Topping pattern found for x index pair:  377 , 294
----------------------- 
In [82]:
print(y_dict)

# ___ plotting ___
plt.figure(figsize=(20, 10), dpi= 120, facecolor='w', edgecolor='k')
# plot stock data
plt.plot(x_data, y_data, 'o', markersize=3.5, color='grey', alpha=0.7)
# plot polynomial fit
plt.plot(x, y_pol, '-', markersize=1.0, color='black', alpha=0.9)
plt.legend(['stock data', 'polynomial fit'])

for position in suspected_tops:
    plt.axvline(x=position)

plt.axhline(threshold)    

plt.show()
{7: [46.65622727272728, 51.56740909090911, 49.111818181818194], 66: [50.17986355, 55.46195445, 52.820909], 138: [46.90581809545454, 51.84327263181819, 49.374545363636365], 294: [56.54140900454544, 62.493136268181814, 59.51727263636363], 377: [56.45159065000001, 62.39386335000001, 59.42272700000001]}