import pandas as pd import pandas_datareader.data as web import numpy as np import math import matplotlib.pyplot as plt from scipy.interpolate import UnivariateSpline import datetime as dt import os import matplotlib as mpl mpl.rc('figure', max_open_warning = 0) # Stock Investors Financial Math # https://www.fmlabs.com/reference/default.htm?url=SimpleMA.htm # Alpha Vantage # https://www.alphavantage.co/documentation/ # Pandas # https://pandas.pydata.org/pandas-docs/stable/index.html # Knowledge # https://www.investopedia.com/articles/technical/02/050602.asp # https://ntguardian.wordpress.com/2016/09/19/introduction-stock-market-data-python-1 key = '0UO7Z2MVZ2YSQSVE' q_days = 20 thresh_macd = 1.5 show_range_days = 40 show_symbols = ['OHB.DE', 'ITMPF', 'PLUG', 'MOR.DE', 'CSCO', 'ERCA.DE', 'AVGO', 'DIS', 'UBSFF', 'DHER.DE', 'AIR'] #show_symbols = ['CSCO'] #show_symbols = ['OHB.DE'] #show_symbols = ['UBSFF'] #show_symbols = ['DHER.DE'] show_symbols = [] k_euro = 1 / 1.11 N_poly = 7 ema_alpha = 0.75 sma_days = 10 fetch_on_outdated = True symbols = { 'WDI.DE' : {'name' : 'WireCard', 'currency' : '€'}, 'AAPL' : {'name' : 'Apple', 'currency' : '$'}, 'XLNX' : {'name' : 'Xilinx', 'currency' : '$'}, 'QCOM' : {'name' : 'Qualcomm', 'currency' : '$'}, 'DPW.DE' : {'name' : 'Deutsche Post', 'currency' : '€'}, 'CSCO' : {'name' : 'Cisco', 'currency' : '$'}, 'AIR' : {'name' : 'Airbus', 'currency' : '$'}, 'BA' : {'name' : 'Boeing', 'currency' : '$'}, 'NVDA' : {'name' : 'Nvidia', 'currency' : '$'}, 'MSFT' : {'name' : 'Microsoft', 'currency' : '$'}, 'DIS' : {'name' : 'Disney', 'currency' : '$'}, 'NFLX' : {'name' : 'Netflix', 'currency' : '$'}, 'OHB.DE' : {'name' : 'OHB', 'currency' : '€'}, 'ERCA.DE' : {'name' : 'Ericsson', 'currency' : '€'}, 'VAR1.DE' : {'name' : 'Varta', 'currency' : '€'}, 'HD' : {'name' : 'Home Depot', 'currency' : '$'}, 'AMZN' : {'name' : 'Amazon', 'currency' : '$'}, 'GOOGL' : {'name' : 'Google', 'currency' : '$'}, 'FB2A.DE' : {'currency' : '€'}, 'ZIL2.DE' : {'currency' : '€'}, 'SIS.DE' : {'currency' : '€'}, 'SIE.DE' : {'name': 'Siemens', 'currency' : '€'}, 'GFT.DE' : {'currency' : '€'}, 'AMD.DE' : {'name': 'Advanced Micro Devices', 'currency' : '€'}, 'CAP.DE' : {'name': 'Encavis', 'currency' : '€'}, 'ADBE' : {'name': 'Adobe', 'currency' : '$'}, 'PANW' : {'name': 'Palo Alto Networks', 'currency' : '$'}, 'AMAT' : {'name': 'Applied Materials', 'currency' : '$'}, 'RIB.DE' : {'name': 'RIB Software', 'currency' : '€'}, 'WAF.DE' : {'name': 'Siltronic', 'currency' : '€'}, 'EVT.DE' : {'name': 'Evotec', 'currency' : '€'}, 'VOW.DE' : {'name': 'Volkswagen', 'currency' : '€'}, 'BMW.DE' : {'name': 'BMW', 'currency' : '€'}, 'NSU.DE' : {'name': 'Audi', 'currency' : '€'}, 'DAI.DE' : {'name': 'Daimler', 'currency' : '€'}, 'SHA.DE' : {'name': 'Schaeffler', 'currency' : '€'}, 'CON.DE' : {'name': 'Continental', 'currency' : '€'}, 'DHER.DE' : {'name' : 'Delivery Hero', 'currency' : '€'}, 'BSL.DE' : {'name': 'Basler', 'currency' : '€'}, 'D6H.DE' : {'currency' : '€'}, 'TC1.DE' : {'currency' : '€'}, 'VODI.DE' : {'currency' : '€'}, 'ATVI' : {'currency' : '$'}, 'GME' : {'currency' : '$'}, 'NTO.F' : {'name': 'Nintendo', 'currency' : '$'}, 'UBSFF' : {'name': 'UBI Soft', 'currency' : '$'}, 'NXPRF' : {'name': 'Nexans', 'currency' : '$'}, 'IMPUF' : {'name': 'Impala Platinum Holdings', 'currency' : '$'}, 'NMPNF' : {'name': 'Northam Platinum', 'currency' : '$'}, 'ALSMY' : {'name': 'Alstom', 'currency' : '$'}, 'ARRD.F' : {'name': 'Arcelor Mittal', 'currency' : '$'}, 'PHG' : {'name': 'Philips', 'currency' : '$'}, 'KEYS' : {'name': 'KeySight', 'currency' : '$'}, 'ORCL' : {'name': 'Oracle', 'currency' : '$'}, 'UBER' : {'name': 'Uber', 'currency' : '$'}, 'VMW' : {'name': 'VMWare', 'currency' : '$'}, 'AVGO' : {'name' : 'Avago', 'currency' : '$'}, 'CY' : {'name': 'Cypress Semiconductor', 'currency' : '$'}, 'QABSY' : {'name': 'Qanta Airways', 'currency' : '$'}, 'BLDP' : {'name': 'Ballard Power', 'currency' : '$'}, 'D7G.F' : {'name': 'Nel ASA', 'currency' : '$'}, 'ITMPF' : {'name': 'ITM Power', 'currency' : '$'}, 'PLUG' : {'name': 'PlugPower', 'currency' : '$'}, 'PCELF' : {'name': 'PowerCell', 'currency' : '$'}, 'SU.PA' : {'currency' : '$'}, # 'SON1.DE' : {'currency' : '€'}, 'STM.DE' : {'name': 'STM Micro', 'currency' : '€'}, 'BC8.DE' : {'name': 'Bechtle', 'currency' : '€'}, 'MOR.DE' : {'name' : 'Morphosys', 'currency' : '€'}, 'BAYN.DE' : {'currency' : '€'}, 'BEI.DE' : {'currency' : '€'}, 'EUZ.DE' : {'currency' : '€'}, 'SLAB' : {'name': 'Silicon Laboratories', 'currency' : '$'}, 'SPLK' : {'name' : 'Splunk', 'currency' : '$'}, 'IAG' : {'name' : 'IAG', 'currency' : '$'}, 'INTC' : {'currency' : '$'}, 'I' : {'name': 'IntelSat', 'currency' : '$'}} def exponential_moving_average(data, key, alpha=0.5, ic=None): result = [] if ic is None: r = data[key][0] else: r = ic for v in data[key]: r = alpha*r + (1-alpha)*v result.append(r) return np.array(result) def moving_average(data, key, window_days, ic=0): mem = [ic] * window_days result = [] k=0 cumsum = ic for v in data[key]: cumsum += (v - mem[k]) mem[k] = v k += 1 if k >= window_days: k=0 result.append(cumsum/window_days) return result def moving_variance(data, key, window_days, ic=0): mem = [ic] * window_days result = [] k=0 cumsum = ic data_mean = data[key] - moving_average(data, key, window_days, ic) for v in data_mean: v2 = v * v cumsum += (v2 - mem[k]) mem[k] = v2 k += 1 if k >= window_days: k=0 var = max(0, cumsum) / window_days result.append(math.sqrt(var)) return result def moving_max(data, key, window_days, ic=-1e9): mem = [ic] * window_days result = [] k=0 for v in data[key]: mem[k] = v k += 1 if k >= window_days: k=0 _max = np.max(mem) result.append(_max) return result def moving_min(data, key, window_days, ic=1e9): mem = [ic] * window_days result = [] k=0 for v in data[key]: mem[k] = v k += 1 if k >= window_days: k=0 _min = np.min(mem) result.append(_min) return result def normalize(data, key, ic=None): result = [] if ic is None: prev = data[key][0] else: prev = ic for v in data[key]: v_n = (v - prev)/prev result.append(100*v_n) return result def colsum(data, keys): result = np.zeros(data.shape[0]) for key in keys: result += data[key] return result def macd(data, key): ema_short = exponential_moving_average(data, key, alpha=0.85) ema_long = exponential_moving_average(data, key, alpha=0.925) return ema_short - ema_long def bollinger(data, window_days, f=2): tp_ser = colsum(data, keys=['high', 'low', 'close']) / 3 tp_df = tp_ser.to_frame(name='tp') stddev = np.array(moving_variance(tp_df, 'tp', window_days)) mid = np.array(moving_average(tp_df, key='tp', window_days=window_days)) upper = mid + f * stddev lower = mid - f * stddev result = pd.DataFrame(upper, index=data.index, columns=['upper']) result['lower'] = lower result['mid'] = mid return result def agent_buy(name, data, thresh_max=-10, thresh_min=1): buy_list = [] cand = None for n in range(0, len(data.index)): vmax = data['Qmax'][n] vmin = data['Qmin'][n] trend = data['macd_fd'][n] index = data.index[n] if vmin <= thresh_min: if vmax <= thresh_max: cand = n if trend >= 0: print ("{}: Buy on {}".format(name, index)) buy_list.append({'name': name, 'date': index}) cand = None if cand is not None: if n - cand >= 5: if trend >= 0: print("{}: Delayed buy on {}".format(name, data.index[cand])) buy_list.append({'name': name, 'date': data.index[cand]}) cand = None return buy_list class Title(object): def __init__(self, symbol, params): self.symbol = symbol self.params = params try: self.name = params['name'] except: self.name = symbol self.ax = None self.fig = None self.currency = params['currency'] self.currency_corr = 1 if '$' in self.currency: self.currency_corr = k_euro def fetch(self): filename = self.symbol.replace('.', '_') + '.h5' fetch = False today = dt.date.today() try: hdf = pd.HDFStore(filename, 'r') hdf.close() except: fetch = True try: lastmodified = dt.datetime.fromtimestamp(os.stat(filename).st_mtime).date() if lastmodified != today: fetch = fetch_on_outdated except: fetch = True if fetch: print ("Fetching \"{}\"".format(self.symbol)) # Get stock price via data reader start = dt.datetime(today.year,1,1) end = dt.date.today() data = web.DataReader(self.symbol, "av-daily", start, end, api_key=key) hdf = pd.HDFStore(filename) hdf[self.symbol] = data else: hdf = pd.HDFStore(filename, 'r') self.data_full = hdf[self.symbol] * self.currency_corr hdf.close() def __plot(self, d, keys): dates = d.index for key in keys: data = d[key] data.plot() def hover(event): if event.inaxes == self.ax: x_idx = int(event.xdata) self.ax.format_xdata = lambda x: dates[x_idx] self.ax.format_ydata = lambda y: '€ {:.2f}'.format(data[x_idx]) self.fig.canvas.mpl_connect("motion_notify_event", hover) def analyze(self): N = len(self.data_full['close']) self.data_full['ema'] = exponential_moving_average(self.data_full, key='close', alpha=ema_alpha) self.data_full['macd'] = macd(self.data_full, key='close') self.data_full['sma'] = moving_average(self.data_full, key='close', window_days=sma_days) self.data_full['close_n'] = normalize(self.data_full, key='close') self.data_full['min'] = moving_min(self.data_full, key='close_n', window_days=q_days) self.data_full['max'] = moving_max(self.data_full, key='close_n', window_days=q_days) self.data_full['Qmin'] = self.data_full['close_n'] - self.data_full['min'] self.data_full['Qmax'] = self.data_full['close_n'] - self.data_full['max'] self.boll_full = bollinger(self.data_full, window_days=30) x_r = np.linspace(0, N, N) y_r = self.data_full['macd'].to_numpy() spl = UnivariateSpline(x_r, y_r) spl.set_smoothing_factor(0.5) yf = spl(x_r) yf_d = spl.derivative()(x_r) self.data_full['macd_f'] = pd.DataFrame(np.transpose([yf]), index=self.data_full['macd'].index, columns=['macd_fitted']) self.data_full['macd_fd'] = pd.DataFrame(np.transpose([yf_d]), index=self.data_full['macd'].index, columns=['macd_fitted_d']) _macd_f_curr = self.data_full['macd_f'][N-1] _macd_fd_curr = self.data_full['macd_fd'][N-1] self.data = self.data_full[len(self.data_full) - show_range_days:] self.boll = self.boll_full[len(self.boll_full) - show_range_days:] return agent_buy(self.symbol, self.data) def show(self, figNum=1): self.fig = plt.figure(figNum) self.ax = plt.subplot(311) self.__plot(self.data, ['min', 'max', 'close_n']) plt.title('{} ({})'.format(self.name, self.symbol)) plt.legend() plt.grid() plt.subplot(312) # self.boll['upper'].plot() # self.boll['mid'].plot() # self.boll['lower'].plot() self.data['Qmin'].plot() self.data['Qmax'].plot() plt.legend() plt.grid() plt.subplot(313) self.data['macd_f'].plot() self.data['macd_fd'].plot() plt.legend() plt.grid() figNum = 1 if len(show_symbols) > 0: for symbol in show_symbols: if symbol in symbols: title = Title(symbol, symbols[symbol]) title.fetch() title.analyze() title.show(figNum) figNum += 1 else: for symbol in symbols: print('-----------------------------------------------') title = Title(symbol, symbols[symbol]) title.fetch() if len(title.analyze()) > 0: title.show(figNum) figNum += 1 plt.show()