diff --git a/pandas_eval.py b/pandas_eval.py index fae7edd..722ade9 100644 --- a/pandas_eval.py +++ b/pandas_eval.py @@ -6,9 +6,7 @@ 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 @@ -24,21 +22,21 @@ mpl.rc('figure', max_open_warning = 0) # 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 = ['CSCO'] #show_symbols = ['OHB.DE'] #show_symbols = ['UBSFF'] #show_symbols = ['DHER.DE'] +show_symbols = ['WDI.DE'] show_symbols = [] k_euro = 1 / 1.11 -N_poly = 7 ema_alpha = 0.75 sma_days = 10 +q_days = 10 fetch_on_outdated = True +mpl.rc('figure', max_open_warning = 0) symbols = { 'WDI.DE' : {'name' : 'WireCard', 'currency' : '€'}, @@ -120,22 +118,25 @@ symbols = { 'I' : {'name': 'IntelSat', 'currency' : '$'}} def exponential_moving_average(data, key, alpha=0.5, ic=None): - result = [] + result = np.zeros_like(data[key]) if ic is None: r = data[key][0] else: r = ic + n = 0 for v in data[key]: r = alpha*r + (1-alpha)*v - result.append(r) + result[n] = r + n += 1 return np.array(result) def moving_average(data, key, window_days, ic=0): mem = [ic] * window_days - result = [] + result = np.zeros_like(data[key]) k=0 cumsum = ic + n = 0 for v in data[key]: cumsum += (v - mem[k]) mem[k] = v @@ -143,18 +144,23 @@ def moving_average(data, key, window_days, ic=0): if k >= window_days: k=0 - result.append(cumsum/window_days) + result[n] = cumsum/window_days + n += 1 return result def moving_variance(data, key, window_days, ic=0): mem = [ic] * window_days - result = [] + result = np.zeros_like(data[key]) k=0 cumsum = ic data_mean = data[key] - moving_average(data, key, window_days, ic) + n = 0 for v in data_mean: - v2 = v * v + try: + v2 = v * v + except: + print ('v', v) cumsum += (v2 - mem[k]) mem[k] = v2 k += 1 @@ -162,55 +168,60 @@ def moving_variance(data, key, window_days, ic=0): k=0 var = max(0, cumsum) / window_days - result.append(math.sqrt(var)) + result[n] = math.sqrt(var) + n += 1 return result def moving_max(data, key, window_days, ic=-1e9): mem = [ic] * window_days - result = [] + result = np.zeros_like(data[key]) k=0 + n = 0 for v in data[key]: mem[k] = v k += 1 if k >= window_days: k=0 - _max = np.max(mem) - result.append(_max) + result[n] = np.max(mem) + n += 1 return result def moving_min(data, key, window_days, ic=1e9): mem = [ic] * window_days - result = [] + result = np.zeros_like(data[key]) k=0 + n = 0 for v in data[key]: mem[k] = v k += 1 if k >= window_days: k=0 - _min = np.min(mem) - result.append(_min) + result[n] = np.min(mem) + n += 1 return result def normalize(data, key, ic=None): - result = [] + result = np.zeros_like(data[key]) if ic is None: prev = data[key][0] else: prev = ic + n = 0 for v in data[key]: v_n = (v - prev)/prev - result.append(100*v_n) + result[n] = 100*v_n + n += 1 return result def colsum(data, keys): - result = np.zeros(data.shape[0]) + result = np.zeros(data['index'].shape) for key in keys: result += data[key] @@ -223,39 +234,39 @@ def macd(data, key): 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)) + tp = colsum(data, keys=['high', 'low', 'close']) / 3 + tp_dict = {'tp': tp} + stddev = np.array(moving_variance(tp_dict, 'tp', window_days)) - mid = np.array(moving_average(tp_df, key='tp', window_days=window_days)) + mid = np.array(moving_average(tp_dict, 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 + result = {'index' : data['index'], 'upper' : upper, 'mid' : mid, 'lower' : lower} return result -def agent_buy(name, data, thresh_max=-10, thresh_min=1): - buy_list = [] +def agent_buy(name, data, cand_window, thresh_max=-10, thresh_min=1): + N = len(data['index']) + buy_list = np.array([None]*N) cand = None - for n in range(0, len(data.index)): + 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}) + print ("{}: Buy on {} at {:0.2f}%".format(name, data['index'][cand], data['close_n'][cand])) + y = data['close_n'][n] + buy_list[n] = y cand = None if cand is not None: - if n - cand >= 5: + if n - cand <= cand_window: if trend >= 0: - print("{}: Delayed buy on {}".format(name, data.index[cand])) - buy_list.append({'name': name, 'date': data.index[cand]}) + print("{}: Delayed buy on {}".format(name, data['index'][n])) + y = data['close_n'][n] + buy_list[n] = y cand = None return buy_list @@ -275,6 +286,9 @@ class Title(object): if '$' in self.currency: self.currency_corr = k_euro + self.data = {} + self.boll = {} + self.indicators = {} def fetch(self): filename = self.symbol.replace('.', '_') + '.h5' @@ -304,79 +318,103 @@ class Title(object): else: hdf = pd.HDFStore(filename, 'r') - self.data_full = hdf[self.symbol] * self.currency_corr + data = hdf[self.symbol] * self.currency_corr + self.data['index'] = np.array(data.index) + self.data['close'] = np.array(data['close']) + self.data['high'] = np.array(data['high']) + self.data['low'] = np.array(data['low']) + 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) + N = len(self.data['index']) + self.data['ema'] = exponential_moving_average(self.data, key='close', alpha=ema_alpha) + self.data['sma'] = moving_average(self.data, key='close', window_days=sma_days) + self.data['close_n'] = normalize(self.data, key='close') + self.data['macd'] = macd(self.data, key='close_n') + self.data['min'] = moving_min(self.data, key='close_n', window_days=q_days) + self.data['max'] = moving_max(self.data, 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) + self.data['Qmin'] = self.data['close_n'] - self.data['min'] + self.data['Qmax'] = self.data['close_n'] - self.data['max'] + self.boll = bollinger(self.data, window_days=30) x_r = np.linspace(0, N, N) - y_r = self.data_full['macd'].to_numpy() + y_r = self.data['macd'] spl = UnivariateSpline(x_r, y_r) - spl.set_smoothing_factor(0.5) + spl.set_smoothing_factor(0.25) 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']) + self.data['macd_f'] = np.transpose(yf) + self.data['macd_fd'] = np.transpose(yf_d) - _macd_f_curr = self.data_full['macd_f'][N-1] - _macd_fd_curr = self.data_full['macd_fd'][N-1] + self.data['Buy'] = agent_buy(self.symbol, self.data, cand_window=q_days) - self.data = self.data_full[len(self.data_full) - show_range_days:] - self.boll = self.boll_full[len(self.boll_full) - show_range_days:] + @staticmethod + def slice(data, start, stop): + result = {} + for key in iter(data): + result[key] = data[key][start:stop] + + return result + + def __plot(self, data, keys): + N = len(data['index']) + start = max(0, N - show_range_days) + stop = N + xr = list(range(start, stop)) + sliced = Title.slice(data, start, stop) + + for key in keys: + plt.plot(xr, sliced[key], label=key) - return agent_buy(self.symbol, self.data) + def hover(event): + if event.inaxes == self.ax: + x_idx = min(N-1, int(event.xdata)) + self.ax.format_xdata = lambda x: self.data['index'][x_idx] + self.ax.format_ydata = lambda y: '{:.2f}%'.format(self.data['close_n'][x_idx]) + self.fig.canvas.mpl_connect("motion_notify_event", hover) + + def __ind(self, data, keys): + N = len(data['index']) + start = max(0, N - show_range_days) + stop = N + xr = list(range(start, stop)) + sliced = Title.slice(data, start, stop) + + for key in keys: + plt.plot(xr, sliced[key], 'go', label=key) + + def has_candidate(self, key, range_days): + N = len(self.data['index']) + start = max(0, N - range_days) + stop = N + sliced = Title.slice(self.data, start, stop) + result = np.count_nonzero(sliced[key] != None) + return result > 0 def show(self, figNum=1): self.fig = plt.figure(figNum) self.ax = plt.subplot(311) self.__plot(self.data, ['min', 'max', 'close_n']) + self.__ind(self.data, ['Buy']) 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() +# self.__plot(self.boll, ['lower', 'mid', 'upper']) + self.__plot(self.data, ['Qmin', 'Qmax']) plt.legend() plt.grid() plt.subplot(313) - self.data['macd_f'].plot() - self.data['macd_fd'].plot() + self.__plot(self.data, ['macd', 'macd_f', 'macd_fd']) plt.legend() plt.grid() @@ -390,15 +428,16 @@ if len(show_symbols) > 0: title = Title(symbol, symbols[symbol]) title.fetch() title.analyze() - title.show(figNum) + title.show(figNum=figNum) figNum += 1 else: for symbol in symbols: print('-----------------------------------------------') title = Title(symbol, symbols[symbol]) title.fetch() - if len(title.analyze()) > 0: - title.show(figNum) + title.analyze() + if title.has_candidate(key='Buy', range_days=show_range_days): + title.show(figNum=figNum) figNum += 1 plt.show()