def triple_moving_average(fund: pd.DataFrame, **kwargs) -> dict: """Triple Moving Average 3 simple moving averages of "config" length Arguments: fund {pd.DataFrame} -- fund historical data Optional Args: name {list} -- name of fund, primarily for plotting (default: {''}) plot_output {bool} -- True to render plot in realtime (default: {True}) config {list of ints} -- list of moving average time periods (default: {[12, 50, 200]}) progress_bar {ProgressBar} -- (default: {None}) view {str} -- directory of plots (default: {''}) Returns: tma {dict} -- contains all ma information in "short", "medium", and "long" keys """ name = kwargs.get('name', '') config = kwargs.get('config', [12, 50, 200]) plot_output = kwargs.get('plot_output', True) out_suppress = kwargs.get('out_suppress', False) progress_bar = kwargs.get('progress_bar', None) view = kwargs.get('view', '') tshort = simple_moving_avg(fund, config[0]) if progress_bar is not None: progress_bar.uptick(increment=0.2) tmed = simple_moving_avg(fund, config[1]) if progress_bar is not None: progress_bar.uptick(increment=0.2) tlong = simple_moving_avg(fund, config[2]) if progress_bar is not None: progress_bar.uptick(increment=0.2) mshort = [] mmed = [] mlong = [] for i, close in enumerate(fund['Close']): mshort.append(np.round((close - tshort[i]) / tshort[i] * 100.0, 3)) mmed.append(np.round((close - tmed[i]) / tmed[i] * 100.0, 3)) mlong.append(np.round((close - tlong[i]) / tlong[i] * 100.0, 3)) triple_exp_mov_average(fund, config=[9, 21, 50], plot_output=plot_output, name=name, view=view) tshort_x, tshort2 = adjust_signals(fund, tshort, offset=config[0]) tmed_x, tmed2 = adjust_signals(fund, tmed, offset=config[1]) tlong_x, tlong2 = adjust_signals(fund, tlong, offset=config[2]) plot_short = { "plot": tshort2, "color": "blue", "legend": f"{config[0]}-day MA", "x": tshort_x } plot_med = { "plot": tmed2, "color": "orange", "legend": f"{config[1]}-day MA", "x": tmed_x } plot_long = { "plot": tlong2, "color": "black", "legend": f"{config[2]}-day MA", "x": tlong_x } if not out_suppress: name3 = INDEXES.get(name, name) name2 = name3 + \ ' - Simple Moving Averages [{}, {}, {}]'.format( config[0], config[1], config[2]) # legend = ['Price', f'{config[0]}-SMA', # f'{config[1]}-SMA', f'{config[2]}-SMA'] # plots = [fund['Close'], tshort2, tmed2, tlong2] # x_vals = [fund.index, tshort_x, tmed_x, tlong_x] if plot_output: candlestick_plot(fund, title=name2, additional_plts=[plot_short, plot_med, plot_long]) # generic_plotting(plots, x=x_vals, # legend=legend, title=name2) else: filename = os.path.join(name, view, f"simple_moving_averages_{name}.png") candlestick_plot(fund, title=name2, filename=filename, saveFig=True, additional_plts=[plot_short, plot_med, plot_long]) # generic_plotting(plots, x=x_vals, # legend=legend, title=name2, saveFig=True, filename=filename) tma = dict() tma['short'] = {'period': config[0]} tma['medium'] = {'period': config[1]} tma['long'] = {'period': config[2]} tma['tabular'] = {'short': tshort, 'medium': tmed, 'long': tlong} tma['metrics'] = { f'{config[0]}-d': mshort, f'{config[1]}-d': mmed, f'{config[2]}-d': mlong } if progress_bar is not None: progress_bar.uptick(increment=0.1) tma['type'] = 'trend' tma['length_of_data'] = len(tma['tabular']['short']) tma['signals'] = find_crossovers(tma, fund) return tma
def triple_exp_mov_average(fund: pd.DataFrame, config=[9, 20, 50], **kwargs) -> list: """Triple Exponential Moving Average Arguments: fund {pd.DataFrame} -- fund dataset Optional Args: plot_output {bool} -- (default: {True}) name {str} -- (default: {str}) view {str} -- file directory of plots (default: {''}) p_bar {ProgressBar} -- (default: {None}) Keyword Arguments: config {list} -- look back period (default: {[9, 13, 50]}) Returns: list -- plots and signals """ plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view', '') p_bar = kwargs.get('progress_bar') out_suppress = kwargs.get('out_suppress', False) tema = dict() tshort = exponential_moving_avg(fund, config[0]) if p_bar is not None: p_bar.uptick(increment=0.2) tmed = exponential_moving_avg(fund, config[1]) if p_bar is not None: p_bar.uptick(increment=0.2) tlong = exponential_moving_avg(fund, config[2]) if p_bar is not None: p_bar.uptick(increment=0.2) tema['tabular'] = {'short': tshort, 'medium': tmed, 'long': tlong} tema['short'] = {"period": config[0]} tema['medium'] = {"period": config[1]} tema['long'] = {"period": config[2]} tshort_x, tshort2 = adjust_signals(fund, tshort, offset=config[0]) tmed_x, tmed2 = adjust_signals(fund, tmed, offset=config[1]) tlong_x, tlong2 = adjust_signals(fund, tlong, offset=config[2]) plot_short = { "plot": tshort2, "color": "blue", "legend": f"{config[0]}-day MA", "x": tshort_x } plot_med = { "plot": tmed2, "color": "orange", "legend": f"{config[1]}-day MA", "x": tmed_x } plot_long = { "plot": tlong2, "color": "black", "legend": f"{config[2]}-day MA", "x": tlong_x } mshort = [] mmed = [] mlong = [] for i, close in enumerate(fund['Close']): mshort.append(np.round((close - tshort[i]) / tshort[i] * 100.0, 3)) mmed.append(np.round((close - tmed[i]) / tmed[i] * 100.0, 3)) mlong.append(np.round((close - tlong[i]) / tlong[i] * 100.0, 3)) if p_bar is not None: p_bar.uptick(increment=0.2) tema['metrics'] = { f'{config[0]}-d': mshort, f'{config[1]}-d': mmed, f'{config[2]}-d': mlong } if not out_suppress: name3 = INDEXES.get(name, name) name2 = name3 + \ ' - Exp Moving Averages [{}, {}, {}]'.format( config[0], config[1], config[2]) # legend = ['Price', f'{config[0]}-EMA', # f'{config[1]}-EMA', f'{config[2]}-EMA'] # plots = [fund['Close'], tshort2, tmed2, tlong2] # x_vals = [fund.index, tshort_x, tmed_x, tlong_x] if plot_output: candlestick_plot(fund, title=name2, additional_plts=[plot_short, plot_med, plot_long]) # generic_plotting(plots, x=x_vals, # legend=legend, title=name2) else: filename = os.path.join(name, view, f"exp_moving_averages_{name}.png") candlestick_plot(fund, title=name2, filename=filename, saveFig=True, additional_plts=[plot_short, plot_med, plot_long]) # generic_plotting(plots, x=x_vals, # legend=legend, title=name2, saveFig=True, filename=filename) if p_bar is not None: p_bar.uptick(increment=0.2) tema['type'] = 'trend' tema['length_of_data'] = len(tema['tabular']['short']) tema['signals'] = find_crossovers(tema, fund) return tema
def generate_sar(fund: pd.DataFrame, **kwargs) -> dict: """Generate Stop and Reverse (SAR) Signal Arguments: fund {pd.DataFrame} -- fund dataset Optional Args: af {list} -- acceleration factor (default: {[0.02, 0.01]}) max_factor {float} -- max acceleration factor (default: {0.2}) period_offset {int} -- lookback period to establish a trend in starting (default: {5}) plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- (default: {''}) p_bar {ProgressBar} -- (default: {None}) Returns: dict -- sar data object; signals (different AFs) """ # Note, higher/faster AF should be first of 2 ACC_FACTOR = kwargs.get('af', [0.02, 0.01]) MAX_FACTOR = kwargs.get('max_factor', 0.2) period_offset = kwargs.get('period_offset', 5) plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view', '') p_bar = kwargs.get('pbar') sar_dict = dict() signals = {"fast": [], "slow": []} sig_names = ["fast", "slow"] colors = ["blue", "black"] indicators = [] signal = [0.0] * len(fund['Close']) auto = autotrend(fund['Close'], periods=[4]) # Observe for an 'offset' amount of time before starting ep_high = 0.0 ep_low = float('inf') for i in range(period_offset): signal[i] = fund['Close'][i] if fund['Low'][i] < ep_low: ep_low = fund['Low'][i] if fund['High'][i] > ep_high: ep_high = fund['High'][i] # Determine an initial trend to start trend = 'down' e_p = ep_low signal[period_offset - 1] = ep_high if auto[period_offset - 1] > 0.0: trend = 'up' e_p = ep_high signal[period_offset - 1] = ep_low add_plts = [] if p_bar is not None: p_bar.uptick(increment=0.1) # Begin generating SAR signal, number of signals determined by number of AFs for k, afx in enumerate(ACC_FACTOR): a_f = afx for i in range(period_offset, len(signal)): data = None date = fund.index[i].strftime("%Y-%m-%d") if trend == 'down': sar_i = signal[i - 1] - a_f * (signal[i - 1] - e_p) if fund['High'][i] > sar_i: # Broken downward trend. Stop and reverse! a_f = afx sar_i = e_p e_p = fund['High'][i] trend = 'up' data = { "type": 'bullish', "value": f'downtrend broken-{sig_names[k]}', "index": i, "date": date } else: if fund['Low'][i] < e_p: e_p = fund['Low'][i] a_f += afx if a_f > MAX_FACTOR: a_f = MAX_FACTOR else: sar_i = signal[i - 1] + a_f * (e_p - signal[i - 1]) if fund['Low'][i] < sar_i: # Broken upward trend. Stop and reverse! a_f = afx sar_i = e_p e_p = fund['Low'][i] trend = 'down' data = { "type": 'bearish', "value": f'upward broken-{sig_names[k]}', "index": i, "date": date } else: if fund['High'][i] > e_p: e_p = fund['High'][i] a_f += afx if a_f > MAX_FACTOR: a_f = MAX_FACTOR signal[i] = sar_i if data is not None: indicators.append(data) signals[sig_names[k]] = signal.copy() add_plts.append({ "plot": signals[sig_names[k]], "style": 'scatter', "legend": f'Parabolic SAR-{sig_names[k]}', "color": colors[k] }) if p_bar is not None: p_bar.uptick(increment=0.2) name2 = INDEXES.get(name, name) title = f"{name2} - Parabolic SAR" if plot_output: candlestick_plot(fund, additional_plts=add_plts, title=title) else: filename = os.path.join(name, view, f"parabolic_sar_{name}") candlestick_plot(fund, additional_plts=add_plts, title=title, saveFig=True, filename=filename) if p_bar is not None: p_bar.uptick(increment=0.1) sar_dict['tabular'] = signals sar_dict['signals'] = indicators return sar_dict