Files
Stock/pandas_eval.py
T
jens a34c4d1d2f - avoid pandas for data arrays
-  use more numpy consequently

git-svn-id: http://moon:8086/svn/projects/Stock@324 fda53097-d464-4ada-af97-ba876c37ca34
2019-12-22 22:38:58 +00:00

445 lines
12 KiB
Python

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
# 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'
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 = ['WDI.DE']
show_symbols = []
k_euro = 1 / 1.11
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' : '€'},
'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 = 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[n] = r
n += 1
return np.array(result)
def moving_average(data, key, window_days, ic=0):
mem = [ic] * window_days
result = np.zeros_like(data[key])
k=0
cumsum = ic
n = 0
for v in data[key]:
cumsum += (v - mem[k])
mem[k] = v
k += 1
if k >= window_days:
k=0
result[n] = cumsum/window_days
n += 1
return result
def moving_variance(data, key, window_days, ic=0):
mem = [ic] * window_days
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:
try:
v2 = v * v
except:
print ('v', v)
cumsum += (v2 - mem[k])
mem[k] = v2
k += 1
if k >= window_days:
k=0
var = max(0, cumsum) / window_days
result[n] = math.sqrt(var)
n += 1
return result
def moving_max(data, key, window_days, ic=-1e9):
mem = [ic] * window_days
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
result[n] = np.max(mem)
n += 1
return result
def moving_min(data, key, window_days, ic=1e9):
mem = [ic] * window_days
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
result[n] = np.min(mem)
n += 1
return result
def normalize(data, key, ic=None):
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[n] = 100*v_n
n += 1
return result
def colsum(data, keys):
result = np.zeros(data['index'].shape)
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 = 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_dict, key='tp', window_days=window_days))
upper = mid + f * stddev
lower = mid - f * stddev
result = {'index' : data['index'], 'upper' : upper, 'mid' : mid, 'lower' : lower}
return result
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'])):
vmax = data['Qmax'][n]
vmin = data['Qmin'][n]
trend = data['macd_fd'][n]
if vmin <= thresh_min:
if vmax <= thresh_max:
cand = n
if trend >= 0:
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 <= cand_window:
if trend >= 0:
print("{}: Delayed buy on {}".format(name, data['index'][n]))
y = data['close_n'][n]
buy_list[n] = y
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
self.data = {}
self.boll = {}
self.indicators = {}
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')
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 analyze(self):
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['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['macd']
spl = UnivariateSpline(x_r, y_r)
spl.set_smoothing_factor(0.25)
yf = spl(x_r)
yf_d = spl.derivative()(x_r)
self.data['macd_f'] = np.transpose(yf)
self.data['macd_fd'] = np.transpose(yf_d)
self.data['Buy'] = agent_buy(self.symbol, self.data, cand_window=q_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)
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.__plot(self.boll, ['lower', 'mid', 'upper'])
self.__plot(self.data, ['Qmin', 'Qmax'])
plt.legend()
plt.grid()
plt.subplot(313)
self.__plot(self.data, ['macd', 'macd_f', 'macd_fd'])
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)
figNum += 1
else:
for symbol in symbols:
print('-----------------------------------------------')
title = Title(symbol, symbols[symbol])
title.fetch()
title.analyze()
if title.has_candidate(key='Buy', range_days=show_range_days):
title.show(figNum=figNum)
figNum += 1
plt.show()