def kst_signal(fund: pd.DataFrame, **kwargs) -> list: """Know Sure Thing - Signal Also known as the "Summed Rate of Change" Oscillator Arguments: fund {pd.DataFrame} -- fund dataset Optional Args: periods {list} -- ROC periods (default: {[10, 15, 20, 30]}) sma_intervals {list} -- sma intervals corresponding to the ROC periods (default: {[10, 10, 10, 15]}) plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) views {str} -- (default: {''}) p_bar {ProgressBar} -- (default: {None}) Returns: list -- kst signal and its 9d sma signal line """ periods = kwargs.get('periods', [10, 15, 20, 30]) sma_intervals = kwargs.get('sma_intervals', [10, 10, 10, 15]) plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') views = kwargs.get('views', '') p_bar = kwargs.get('p_bar') increment = 0.7 / float(len(periods) * 3) signal = [0.0] * len(fund['Close']) for i, period in enumerate(periods): roc = roc_signal(fund, period) if p_bar: p_bar.uptick(increment=increment) sma = simple_moving_avg(roc, sma_intervals[i], data_type='list') if p_bar: p_bar.uptick(increment=increment) for j in range(len(signal)): signal[j] += float(i + 1) * sma[j] if p_bar: p_bar.uptick(increment=increment) signal_line = simple_moving_avg(signal, 9, data_type='list') if p_bar: p_bar.uptick(increment=0.1) name2 = INDEXES.get(name, name) title = f"{name2} - Know Sure Thing" if plot_output: dual_plotting(fund['Close'], [signal, signal_line], 'Price', 'KST', title=title) else: filename = os.path.join(name, views, f"kst_oscillator_{name}") dual_plotting(fund['Close'], [signal, signal_line], 'Price', 'KST', title=title, saveFig=True, filename=filename) return signal, signal_line
def generate_macd_signal(fund: pd.DataFrame, **kwargs) -> dict: """Generate MACD Signal macd = ema(12) - ema(26) 'signal' = macd(ema(9)) Arguments: fund {pd.DataFrame} -- fund dataset Keyword Arguments: plotting {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- directory of plots (default: {''}) Returns: dict -- macd data object """ plotting = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view') macd = dict() emaTw = exponential_moving_avg(fund, interval=12) emaTs = exponential_moving_avg(fund, interval=26) macd_val = [] for i in range(len(emaTw)): if i < 26: macd_val.append(0.0) else: macd_val.append(emaTw[i] - emaTs[i]) macd_sig = exponential_moving_avg(macd_val, interval=9, data_type='list') # Actual MACD vs. its signal line m_bar = [] for i, sig in enumerate(macd_sig): m_bar.append(macd_val[i] - sig) macd['tabular'] = {'macd': macd_val, 'signal_line': macd_sig, 'bar': m_bar} x = dates_extractor_list(fund) name3 = INDEXES.get(name, name) name2 = name3 + ' - MACD' if plotting: bar_chart(m_bar, position=fund, x=x, title=name2) else: filename = os.path.join(name, view, f"macd_bar_{name}.png") bar_chart(m_bar, position=fund, x=x, title=name2, saveFig=True, filename=filename) return macd
def slide_title_header(slide, fund: str, include_time=True, price_details=''): """Slide Title Header Generates a consistent title header for fund slides. Arguments: slide {pptx-slide} -- slide object fund {str} -- fund name Keyword Arguments: include_time {bool} -- will include time of pptx creation (default: {True}) price_details {str} -- price content for latest period (default: {''}) Returns: pptx-slide -- updated slide with title content """ fund = INDEXES.get(fund, fund) left = Inches(0) # Inches(3.86) top = Inches(0) width = height = Inches(0.5) txbox = slide.shapes.add_textbox(left, top, width, height) tf = txbox.text_frame p = tf.paragraphs[0] p.text = fund p.font.size = Pt(36) p.font.name = 'Arial' p.font.bold = True if include_time: p = tf.add_paragraph() p.font.size = Pt(14) p.font.bold = False p.font.name = 'Arial' p.text = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) if price_details != '': left = Inches(2) top = Inches(0.1) width = height = Inches(0.5) txbox = slide.shapes.add_textbox(left, top, width, height) tf = txbox.text_frame p = tf.paragraphs[0] p.text = fund p.font.size = Pt(24) p.font.name = 'Arial' p.font.bold = True p.text = price_details deets = price_details.split(' ') if '+' in deets[1]: p.font.color.rgb = color_to_RGB('green') else: p.font.color.rgb = color_to_RGB('red') return slide
def cci_metrics(position: pd.DataFrame, cci: dict, **kwargs) -> list: plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view', '') metrics = [0.0] * len(position['Close']) features = cci['signals'] signals = cci['tabular'] periods = cci['periods'] for feat in features: if 'zero' in feat['value']: if feat['type'] == 'bullish': metrics[feat['index']] += 0.6 else: metrics[feat['index']] -= 0.6 elif '100' in feat['value']: if feat['type'] == 'bullish': metrics[feat['index']] += 1.0 else: metrics[feat['index']] -= 1.0 for period in signals: if period == str(periods['short']): point = 0.2 elif period == str(periods['medium']): point = 0.2 else: point = 0.2 for i, value in enumerate(signals[period]): if value > 100.0: metrics[i] += point if value < 100.0: metrics[i] -= point metrics = exponential_moving_avg(metrics, 7, data_type='list') norm = normalize_signals([metrics]) metrics = norm[0] name2 = INDEXES.get(name, name) title = f"{name2} - Commodity Channel Index Metrics" if plot_output: dual_plotting(position['Close'], metrics, 'Price', 'Metrics', title=title) else: filename = filename = os.path.join( name, view, f"commodity_metrics_{name}.png") dual_plotting(position['Close'], metrics, 'Price', 'Metrics', title=title, saveFig=True, filename=filename) return metrics
def generate_momentum_signal(position: pd.DataFrame, **kwargs) -> list: """Generate Momentum Signal https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cmo Arguments: position {pd.DataFrame} -- fund dataset Optional Args: interval {int} -- lookback period (default: {20}) plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- (default: {''}) Returns: list -- momentum signal """ interval = kwargs.get('interval', 20) plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view', '') signal = [] for i in range(interval - 1): signal.append(0.0) for i in range(interval - 1, len(position['Close'])): sum_up = 0.0 sum_down = 0.0 for j in range(i - (interval - 2), i): if position['Close'][j] > position['Close'][j - 1]: sum_up += position['Close'][j] - position['Close'][j - 1] else: sum_down += np.abs(position['Close'][j] - position['Close'][j - 1]) cmo = 100.0 * (sum_up - sum_down) / (sum_up + sum_down) signal.append(cmo) name2 = INDEXES.get(name, name) if plot_output: dual_plotting(position['Close'], signal, 'Price', 'CMO', title=f'{name2} - (Chande) Momentum Oscillator') else: filename = os.path.join(name, view, f"momentum_oscillator_{name}.png") dual_plotting(position['Close'], signal, 'Price', 'CMO', title='(Chande) Momentum Oscillator', filename=filename, saveFig=True) return signal
def get_atr_signal(fund: pd.DataFrame, **kwargs) -> list: """Get ATR Signal Arguments: fund {pd.DataFrame} -- fund dataset Optional Args: period {int} -- lookback period of atr signal (default: {14}) plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) views {str} -- (default: {''}) out_suppress {bool} -- (default: {False}) Returns: list -- atr signal """ period = kwargs.get('period', 14) plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') views = kwargs.get('views', '') out_suppress = kwargs.get('out_suppress', False) signal = [0.0] * len(fund['Close']) atr = [0.0] for i in range(1, len(fund['Close'])): trues = [ fund['High'][i] - fund['Low'][i], abs(fund['High'][i] - fund['Close'][i - 1]), abs(fund['Low'][i] - fund['Close'][i - 1]) ] _max = max(trues) atr.append(_max) for i in range(period - 1, len(fund['Close'])): atr_val = sum(atr[i - (period - 1):i + 1]) / float(period) signal[i] = atr_val if not out_suppress: name2 = INDEXES.get(name, name) title = f"{name2} - Average True Range" if plot_output: dual_plotting(fund['Close'], signal, 'Price', 'ATR', title=title) else: filename = os.path.join(name, views, f"atr_{name}.png") dual_plotting(fund['Close'], signal, 'Price', 'ATR', title=title, saveFig=True, filename=filename) return signal
def mov_avg_convergence_divergence(fund: pd.DataFrame, **kwargs) -> dict: """Moving Average Convergence Divergence (MACD) Arguments: fund {pd.DataFrame} -- fund historical data Optional Args: name {str} -- name of fund, primarily for plotting (default: {''}) plot_output {bool} -- True to render plot in realtime (default: {True}) progress_bar {ProgressBar} -- (default: {None}) view {str} -- directory of plots (default: {''}) Returns: dict -- contains all macd information in regarding macd """ name = kwargs.get('name', '') plot_output = kwargs.get('plot_output', True) progress_bar = kwargs.get('progress_bar', None) view = kwargs.get('view', '') macd = generate_macd_signal( fund, plot_output=plot_output, name=name, view=view) if progress_bar is not None: progress_bar.uptick(increment=0.3) macd = get_macd_statistics(macd, progress_bar=progress_bar) macd = macd_metrics(fund, macd, p_bar=progress_bar, name=name, plot_output=plot_output, view=view) macd_sig = macd['tabular']['macd'] sig_line = macd['tabular']['signal_line'] name3 = INDEXES.get(name, name) name2 = name3 + ' - MACD' if plot_output: dual_plotting(fund['Close'], [macd_sig, sig_line], 'Position Price', ['MACD', 'Signal Line'], title=name2) print_macd_statistics(macd) else: filename = os.path.join(name, view, f"macd_{name}.png") dual_plotting(fund['Close'], [macd_sig, sig_line], 'Position Price', ['MACD', 'Signal Line'], title=name2, saveFig=True, filename=filename) if progress_bar is not None: progress_bar.uptick(increment=0.3) macd['type'] = 'oscillator' macd['signals'] = get_macd_features(macd, fund) macd['length_of_data'] = len(macd['tabular']['macd']) return macd
def kst_metrics(fund: pd.DataFrame, kst_dict: dict, **kwargs) -> dict: """KST Metrics Arguments: fund {pd.DataFrame} -- fund dataset kst_dict {dict} -- kst data object Optional Args: plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) views {str} -- (default: {''}) Returns: dict -- kst data object """ plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') views = kwargs.get('views', '') metrics = [0.0] * len(kst_dict['tabular']['signal']) for sig in kst_dict['signals']: if sig['type'] == 'bullish': multiply = 1.0 else: multiply = -1.0 if 'crossover' in sig['value']: metrics[sig['index']] += 0.2 * multiply if 'crossed' in sig['value']: metrics[sig['index']] += 1.0 * multiply metrics = exponential_moving_avg(metrics, 7, data_type='list') metrics = normalize_signals([metrics])[0] kst_dict['metrics'] = metrics name2 = INDEXES.get(name, name) title = f"{name2} - KST Metrics" if plot_output: dual_plotting(fund['Close'], kst_dict['metrics'], 'Price', 'Metrics', title=title) else: filename = os.path.join(name, views, f"kst_metrics_{name}") dual_plotting(fund['Close'], kst_dict['metrics'], 'Price', 'Metrics', title=title, saveFig=True, filename=filename) return kst_dict
def generate_bear_bull_signal(position: pd.DataFrame, **kwargs) -> dict: """Generate Bear Bull Signal Arguments: position {pd.DataFrame} -- dataset Optional Args: interval {int} -- size of exponential moving average window (default: {13}) plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) p_bar {ProgressBar} -- (default: {None}) Returns: dict -- [description] """ interval = kwargs.get('interval', 13) plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') p_bar = kwargs.get('p_bar') bb_signal = {'bulls': [], 'bears': []} ema = exponential_moving_avg(position, interval) for i, high in enumerate(position['High']): bb_signal['bulls'].append(high - ema[i]) if p_bar is not None: p_bar.uptick(increment=0.1) for i, low in enumerate(position['Low']): bb_signal['bears'].append(low - ema[i]) if p_bar is not None: p_bar.uptick(increment=0.1) x = dates_extractor_list(position) if plot_output: name3 = INDEXES.get(name, name) name2 = name3 + ' - Bull Power' bar_chart(bb_signal['bulls'], position=position, title=name2, x=x) name2 = name3 + ' - Bear Power' bar_chart(bb_signal['bears'], position=position, title=name2, x=x) return bb_signal
def output_to_terminal(synopsis: dict, print_out=False, **kwargs): """Output to Terminal Mapping function to show fund information in terminal Arguments: synopsis {dict} -- synopsis object to output Keyword Arguments: print_out {bool} -- (default: {False}) Optional Args: name {str} -- (default: {''}) """ name = kwargs.get('name', '') name2 = INDEXES.get(name, name) if print_out: for period in synopsis: if period not in EXEMPT_METRICS: print("\r\n") print(f"Time period: {period} for {name2}") print("") tabs = strings_to_tabs("Tabular:") tabs2 = strings_to_tabs("Current", style='percent') print(f"\r\nTabular:{tabs}Current{tabs2}Previous\r\n") for tab in synopsis[period]['tabular']: custom_print(tab, synopsis[period]['tabular'][tab], prev=synopsis[period]['tabular_delta'][tab], thr=synopsis[period]['tabular_delta'][tab]) tabs = strings_to_tabs("Metrics:") tabs2 = strings_to_tabs("Current", style='percent') print(f"\r\n\r\nMetrics:{tabs}Current{tabs2}Previous\r\n") for met in synopsis[period]['metrics']: custom_print(met, synopsis[period]['metrics'][met], prev=synopsis[period]['metrics_delta'][met])
def fund_pdf_pages(pdf, analysis: dict, **kwargs): """Fund PDF Pages Arguments: pdf {FPDF} -- pdf object analysis {dict} -- data run data set Optional Args: views {str} -- (default: {None}) Returns: FPDF -- pdf object """ views = kwargs.get('views') has_view = False if views is None: for fund in analysis: for period in analysis[fund]: views = period has_view = True break if has_view: break for fund in analysis: if fund != '_METRICS_': name = INDEXES.get(fund, fund) fund_data = analysis[fund] pdf.add_page() pdf = fund_title(pdf, name) pdf = fund_statistics(pdf, fund_data, sample_view=views) pdf = fund_volatility(pdf, fund_data) pdf = beta_rsq(pdf, fund_data) for period in fund_data['synopsis']: pdf = metrics_tables(pdf, fund_data, period) pdf = latest_signals(pdf, fund_data, period) return pdf
def feature_plotter(fund: pd.DataFrame, shapes: list, **kwargs): """Feature Plotter Plots a rectangle of where the feature was detected overlayed on the ticker signal. Arguments: fund {pd.DataFrame} -- fund dataset shapes {list} -- list of shape objects Optional Args: plot_output {bool} -- (default: {True}) feature {str} -- to control content (default: {'head_and_shoulders'}) name {str} -- name of fund (default: {''}) view {str} -- period of viewing of dataset (default: {''}) """ plot_output = kwargs.get('plot_output', True) feature = kwargs.get('feature', 'head_and_shoulders') name = kwargs.get('name', '') view = kwargs.get('view', '') name2 = INDEXES.get(name, name) filename = name + f"/{view}" + f'/{feature}_{name}.png' title = f'{name2} Feature Detection: ' if feature == 'head_and_shoulders': title += 'Head and Shoulders' elif feature == 'price_gaps': title += 'Price Gaps' saveFig = not plot_output shape_plotting(fund['Close'], shapeXY=shapes, feature=feature, saveFig=saveFig, title=title, filename=filename)
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 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 assemble_last_signals(meta_sub: dict, fund: pd.DataFrame = None, lookback: int = 10, **kwargs) -> dict: """assemble_last signals Look through all indicators of lookback time and list them Arguments: meta_sub {dict} -- metadata subset "metadata[fund][view]" Keyword Arguments: lookback {int} -- number of trading periods into past to find signals (default: {5}) fund {pd.DataFrame} -- fund dataset Optional Args: standalone {bool} -- if run as a function, fetch all metadata info (default: {False}) print_out {bool} -- print in terminal (default: {False}) name {str} -- (default: {''}) pbar {ProgressBar} -- (default: {None}) Returns: dict -- last signals data object """ standalone = kwargs.get('standalone', False) print_out = kwargs.get('print_out', False) name = kwargs.get('name', '') pbar = kwargs.get('progress_bar') plot_output = kwargs.get('plot_output', True) metadata = [] meta_keys = [] name2 = INDEXES.get(name, name) if fund is not None: fund = fund['Close'] if standalone: for key in meta_sub: if key not in EXEMPT_METRICS: metadata.append(meta_sub[key]) meta_keys.append(key) else: metadata = [meta_sub] meta_keys.append('') increment = 1.0 / float(len(metadata)) content = {"signals": [], "metrics": []} for a, sub in enumerate(metadata): content['signals'] = [] for key in sub: if key in INDICATOR_NAMES: if SIGNAL_KEY in sub[key] and SIZE_KEY in sub[key]: start_period = sub[key][SIZE_KEY] - lookback - 1 for signal in sub[key][SIGNAL_KEY]: if signal['index'] >= start_period: data = { "type": signal['type'], "indicator": key, "value": signal['value'], "date": signal['date'], "days_ago": (sub[key][SIZE_KEY] - 1 - signal['index']) } content["signals"].append(data) if METRICS_KEY in sub[key] and TYPE_KEY in sub[key]: if sub[key][TYPE_KEY] == 'oscillator': if len(content["metrics"]) == 0: content["metrics"] = sub[key][METRICS_KEY] else: for i, met in enumerate(sub[key][METRICS_KEY]): content["metrics"][i] += met else: for trend in sub[key][METRICS_KEY]: if trend != 'metrics': if len(content["metrics"]) == 0: content["metrics"] = [ x / 10.0 for x in sub[key][METRICS_KEY][trend] ] else: for i, met in enumerate( sub[key][METRICS_KEY][trend]): content["metrics"][i] += met / 10.0 content["signals"].sort(key=lambda x: x['days_ago']) if pbar is not None: pbar.uptick(increment=increment) if fund is None: fund = sub.get(STATS_KEY, {}).get('tabular') if print_out: content_printer(content, meta_keys[a], name=name2) if fund is not None: title = f"NATA Metrics - {name2}" upper = 0.3 * max(content["metrics"]) upper = [upper] * len(content["metrics"]) lower = 0.3 * min(content["metrics"]) lower = [lower] * len(content["metrics"]) if plot_output: dual_plotting(fund, [content["metrics"], upper, lower], 'Price', 'Metrics', title=title) else: filename = os.path.join(name, meta_keys[a], f"overall_metrics_{name}.png") dual_plotting(fund, [content["metrics"], upper, lower], 'Price', 'Metrics', title=title, saveFig=True, filename=filename) return content
def run_dev(script: list): """Run Development Script Script that is for implementing new content Arguments: script {list} -- dataset, funds, periods, config Returns: dict -- analysis object of fund data """ dataset = script[0] funds = script[1] periods = script[2] config = script[3] # Start of automated process analysis = {} clock = start_clock() for fund_name in funds: if fund_name in SKIP_INDEXES: continue fund_print = INDEXES.get(fund_name, fund_name) print("") print(f"~~{fund_print}~~") create_sub_temp_dir(fund_name, sub_periods=config['period']) analysis[fund_name] = {} analysis[fund_name]['metadata'] = get_api_metadata( fund_name, max_close=max(dataset[periods[0]][fund_name]['Close']), data=dataset[periods[0]][fund_name]) ###################### START OF PERIOD LOOPING ############################# for i, period in enumerate(periods): fund_data = {} fund = dataset[period][fund_name] start = date_extractor(fund.index[0], _format='str') end = date_extractor(fund.index[-1], _format='str') fund_data['dates_covered'] = {'start': str(start), 'end': str(end)} fund_data['name'] = fund_name fund_print2 = fund_print + f" ({period}) " p = ProgressBar(config['process_steps'], name=fund_print2, offset=clock) p.start() fund_data['statistics'] = get_high_level_stats(fund) fund_data['clustered_osc'] = cluster_oscs(fund, function='all', filter_thresh=3, name=fund_name, plot_output=False, progress_bar=p, view=period) fund_data['full_stochastic'] = full_stochastic(fund, name=fund_name, plot_output=False, out_suppress=False, progress_bar=p, view=period) fund_data['rsi'] = RSI(fund, name=fund_name, plot_output=False, out_suppress=False, progress_bar=p, view=period) fund_data['ultimate'] = ultimate_oscillator(fund, name=fund_name, plot_output=False, out_suppress=False, progress_bar=p, view=period) fund_data['awesome'] = awesome_oscillator(fund, name=fund_name, plot_output=False, progress_bar=p, view=period) fund_data['momentum_oscillator'] = momentum_oscillator( fund, name=fund_name, plot_output=False, progress_bar=p, view=period) fund_data['on_balance_volume'] = on_balance_volume( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['simple_moving_average'] = triple_moving_average( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['exp_moving_average'] = triple_exp_mov_average( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['sma_swing_trade'] = moving_average_swing_trade( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['ema_swing_trade'] = moving_average_swing_trade( fund, function='ema', plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['hull_moving_average'] = hull_moving_average( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['macd'] = mov_avg_convergence_divergence( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['bear_bull_power'] = bear_bull_power(fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['total_power'] = total_power(fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['bollinger_bands'] = bollinger_bands(fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['commodity_channels'] = commodity_channel_index( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['rate_of_change'] = rate_of_change_oscillator( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['know_sure_thing'] = know_sure_thing(fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['average_true_range'] = average_true_range( fund, plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['adx'] = average_directional_index( fund, atr=fund_data['average_true_range']['tabular'], plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['parabolic_sar'] = parabolic_sar( fund, adx_tabular=fund_data['adx']['tabular'], plot_output=False, name=fund_name, progress_bar=p, view=period) fund_data['demand_index'] = demand_index(fund, plot_output=False, name=fund_name, progress_bar=p, view=period) if 'no_index' not in config['state']: strength, match_data = relative_strength( fund_name, full_data_dict=dataset[period], config=config, plot_output=False, meta=analysis[fund_name]['metadata'], progress_bar=p, period=period, interval=config['interval'][i], view=period) fund_data['relative_strength'] = strength fund_data['statistics']['risk_ratios'] = risk_comparison( fund, dataset[period]['^GSPC'], dataset[period]['^IRX'], sector_data=match_data) p.uptick() # Support and Resistance Analysis fund_data['support_resistance'] = find_resistance_support_lines( fund, name=fund_name, plot_output=False, progress_bar=p, view=period) # Feature Detection Block fund_data['features'] = {} fund_data['features'][ 'head_shoulders'] = feature_detection_head_and_shoulders( fund, name=fund_name, plot_output=False, progress_bar=p, view=period) fund_data['candlesticks'] = candlesticks(fund, name=fund_name, plot_output=False, view=period, progress_bar=p) fund_data['price_gaps'] = analyze_price_gaps(fund, name=fund_name, plot_output=False, progress_bar=p, view=period) # Get Trendlines fund_data['trendlines'] = get_trendlines( fund, name=fund_name, plot_output=False, progress_bar=p, view=period, meta=analysis[fund_name]['metadata']) # Various Fund-specific Metrics fund_data['futures'] = future_returns(fund, progress_bar=p) # Parse through indicators and pull out latest signals (must be last) fund_data['last_signals'] = assemble_last_signals( fund_data, fund=fund, name=fund_name, view=period, progress_bar=p, plot_output=False) p.end() analysis[fund_name][period] = fund_data analysis[fund_name]['synopsis'] = generate_synopsis(analysis, name=fund_name) return analysis, clock
def get_bollinger_signals(position: pd.DataFrame, period: int, stdev: float, **kwargs) -> dict: """Get Bollinger Band Signals Arguments: position {pd.DataFrame} -- dataset period {int} -- time frame for moving average stdev {float} -- multiplier for band range Optional Args: plot_output {bool} -- (default: {True}) filter_type {str} -- type of moving average (default: {'simple'}) name {str} -- (default: {''}) view {str} -- (default: {None}) Returns: dict -- bollinger band data object """ filter_type = kwargs.get('filter_type', 'simple') plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view') typical_price = [] for i, close in enumerate(position['Close']): typical_price.append( (close + position['Low'][i] + position['High'][i]) / 3.0) if filter_type == 'exponential': ma = exponential_moving_avg(typical_price, period, data_type='list') else: ma = simple_moving_avg(typical_price, period, data_type='list') upper = ma.copy() lower = ma.copy() std_list = [0.0] * len(ma) for i in range(period, len(ma)): std = np.std(typical_price[i - (period):i]) std_list[i] = std upper[i] = ma[i] + (stdev * std) lower[i] = ma[i] - (stdev * std) signals = {'upper_band': upper, 'lower_band': lower, 'middle_band': ma} name3 = INDEXES.get(name, name) name2 = name3 + ' - Bollinger Bands' if plot_output: generic_plotting( [position['Close'], ma, upper, lower], title=name2, x=position.index, legend=['Price', 'Moving Avg', 'Upper Band', 'Lower Band']) else: filename = os.path.join(name, view, f"bollinger_bands_{name}.png") generic_plotting( [position['Close'], ma, upper, lower], title=name2, x=position.index, legend=['Price', 'Moving Avg', 'Upper Band', 'Lower Band'], saveFig=True, filename=filename) return signals
def relative_strength(primary_name: str, full_data_dict: dict, **kwargs) -> list: """Relative Strength Compare a fund vs. market, sector, and/or other fund Arguments: primary_name {str} -- primary fund to compare against full_data_dict {dict} -- all retrieved funds by fund name Optional Args: secondary_fund_names {list} -- fund names to compare against (default: {[]}) meta {dict} -- "metadata" from api calls (default: {None}) config {dict} -- control config dictionary of software package (default: {None}) sector {str} -- sector fund (if in full_data_dict) for comparison (default: {''}) plot_output {bool} -- True to render plot in realtime (default: {True}) progress_bar {ProgressBar} -- (default: {None}) view {str} -- Directory of plots (default: {''}) Returns: list -- dict containing all relative strength information, sector match data """ secondary_fund_names = kwargs.get('secondary_fund_names', []) config = kwargs.get('config', None) sector = kwargs.get('sector', '') plot_output = kwargs.get('plot_output', True) progress_bar = kwargs.get('progress_bar', None) meta = kwargs.get('meta', None) sector_data = kwargs.get('sector_data', {}) view = kwargs.get('view', '') period = kwargs.get('period', '2y') interval = kwargs.get('interval', '1d') sector_bench = None comp_funds = [] comp_data = {} if meta is not None: match = meta.get('info', {}).get('sector') if match is not None: fund_len = { 'length': len(full_data_dict[primary_name]['Close']), 'start': full_data_dict[primary_name].index[0], 'end': full_data_dict[primary_name].index[ len(full_data_dict[primary_name]['Close']) - 1], 'dates': full_data_dict[primary_name].index } match_fund, match_data = api_sector_match(match, config, fund_len=fund_len, period=period, interval=interval) if match_fund is not None: comp_funds, comp_data = api_sector_funds(match_fund, config, fund_len=fund_len, period=period, interval=interval) if match_data is None: match_data = full_data_dict sector = match_fund sector_data = match_data sector_bench = match_data[match_fund] if progress_bar is not None: progress_bar.uptick(increment=0.3) r_strength = dict() rat_sp = [] rat_sector = [] rat_secondaries = [] secondary_names = [] secondary_fund_names.extend(comp_funds) for key in comp_data: if sector_data.get(key) is None: sector_data[key] = comp_data[key] sp = get_SP500_df(full_data_dict) if sp is not None: rat_sp = normalized_ratio(full_data_dict[primary_name], sp) if progress_bar is not None: progress_bar.uptick(increment=0.1) if len(sector_data) > 0: rat_sector = normalized_ratio(full_data_dict[primary_name], sector_data[sector]) if progress_bar is not None: progress_bar.uptick(increment=0.1) if len(secondary_fund_names) > 0: for sfund in secondary_fund_names: if full_data_dict.get(sfund) is not None: rat_secondaries.append( normalized_ratio(full_data_dict[primary_name], full_data_dict[sfund])) secondary_names.append(sfund) elif sector_data.get(sfund) is not None: rat_secondaries.append( normalized_ratio(full_data_dict[primary_name], sector_data[sfund])) secondary_names.append(sfund) if progress_bar is not None: progress_bar.uptick(increment=0.2) st = period_strength(primary_name, full_data_dict, config=config, periods=[20, 50, 100], sector=sector, sector_data=sector_data.get(sector, None)) r_strength['market'] = {'tabular': rat_sp, 'comparison': 'S&P500'} r_strength['sector'] = {'tabular': rat_sector, 'comparison': sector} r_strength['period'] = st r_strength['secondary'] = [{ 'tabular': second, 'comparison': secondary_names[i] } for i, second in enumerate(rat_secondaries)] dates = dates_extractor_list(full_data_dict[list( full_data_dict.keys())[0]]) if len(rat_sp) < len(dates): dates = dates[0:len(rat_sp)] output_data = [] legend = [] if len(rat_sp) > 0: output_data.append(rat_sp) legend.append("vs. S&P 500") if len(rat_sector) > 0: output_data.append(rat_sector) legend.append(f"vs. Sector ({sector})") if len(rat_secondaries) > 0: for i, rat in enumerate(rat_secondaries): output_data.append(rat) legend.append(f"vs. {secondary_names[i]}") r_strength['tabular'] = {} for i, out_data in enumerate(output_data): r_strength['tabular'][str(legend[i])] = out_data primary_name2 = INDEXES.get(primary_name, primary_name) title = f"Relative Strength of {primary_name2}" if progress_bar is not None: progress_bar.uptick(increment=0.1) if plot_output: generic_plotting(output_data, x=dates, title=title, legend=legend, ylabel='Difference Ratio') else: filename = os.path.join(primary_name, view, f"relative_strength_{primary_name}.png") generic_plotting(output_data, x=dates, title=title, saveFig=True, filename=filename, legend=legend, ylabel='Difference Ratio') if progress_bar is not None: progress_bar.uptick(increment=0.2) return r_strength, sector_bench
def rate_of_change_oscillator(fund: pd.DataFrame, periods: list = [10, 20, 40], **kwargs) -> dict: """Rate of Change Oscillator Arguments: fund {pd.DataFrame} -- fund dataset Keyword Arguments: periods {list} -- lookback periods for ROC (default: {[10, 20, 40]}) Optional Args: plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) views {str} -- (default: {''}) progress_bar {ProgressBar} -- (default: {None}) Returns: dict -- roc data object """ plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') views = kwargs.get('views', '') p_bar = kwargs.get('progress_bar') roc = dict() tshort = roc_signal(fund, periods[0]) if p_bar is not None: p_bar.uptick(increment=0.1) tmed = roc_signal(fund, periods[1]) if p_bar is not None: p_bar.uptick(increment=0.1) tlong = roc_signal(fund, periods[2]) if p_bar is not None: p_bar.uptick(increment=0.1) roc['tabular'] = {'short': tshort, 'medium': tmed, 'long': tlong} roc['short'] = periods[0] roc['medium'] = periods[1] roc['long'] = periods[2] tsx, ts2 = adjust_signals(fund, tshort, offset=periods[0]) tmx, tm2 = adjust_signals(fund, tmed, offset=periods[1]) tlx, tl2 = adjust_signals(fund, tlong, offset=periods[2]) if p_bar is not None: p_bar.uptick(increment=0.2) name2 = INDEXES.get(name, name) title = f"{name2} - Rate of Change Oscillator" plots = [ts2, tm2, tl2] xs = [tsx, tmx, tlx] if plot_output: dual_plotting(fund['Close'], [tshort, tmed, tlong], 'Price', 'Rate of Change', title=title, legend=[ f'ROC-{periods[0]}', f'ROC-{periods[1]}', f'ROC-{periods[2]}' ]) generic_plotting(plots, x=xs, title=title, legend=[ f'ROC-{periods[0]}', f'ROC-{periods[1]}', f'ROC-{periods[2]}' ]) else: filename = os.path.join(name, views, f"rate_of_change_{name}") dual_plotting(fund['Close'], [tshort, tmed, tlong], 'Price', 'Rate of Change', title=title, legend=[ f'ROC-{periods[0]}', f'ROC-{periods[1]}', f'ROC-{periods[2]}' ], saveFig=True, filename=filename) if p_bar is not None: p_bar.uptick(increment=0.1) roc = roc_metrics(fund, roc, plot_output=plot_output, name=name, views=views, p_bar=p_bar) roc['length_of_data'] = len(roc['tabular']['short']) roc['type'] = 'oscillator' if p_bar is not None: p_bar.uptick(increment=0.1) return roc
def moving_average_swing_trade(fund: pd.DataFrame, **kwargs): """Triple Moving Average 3 simple moving averages of "config" length Arguments: fund {pd.DataFrame} -- fund historical data Optional Args: function {str} -- type of filtering scheme (default: {'sma'}) name {str} -- name of fund, primarily for plotting (default: {''}) plot_output {bool} -- True to render plot in realtime (default: {True}) config {list} -- list of moving average time periods (default: {[4, 9, 18]}) progress_bar {ProgressBar} -- (default: {None}) view {str} -- directory for plot (default: {''}) Returns: mast {dict} -- contains all ma information in "short", "medium", "long", and "swing" keys """ function = kwargs.get('function', 'sma') name = kwargs.get('name', '') plot_output = kwargs.get('plot_output', True) config = kwargs.get('config', [4, 9, 18]) progress_bar = kwargs.get('progress_bar', None) view = kwargs.get('view', '') if plot_output: out_suppress = False else: out_suppress = True if function == 'sma': if config == []: tma = triple_moving_average(fund, plot_output=plot_output, name=name, out_suppress=out_suppress, view=view) else: tma = triple_moving_average(fund, config=config, plot_output=plot_output, name=name, out_suppress=out_suppress, view=view) sh = tma['tabular']['short'] me = tma['tabular']['medium'] ln = tma['tabular']['long'] elif function == 'ema': if config == []: tma = triple_exp_mov_average(fund, plot_output=plot_output, name=name, out_suppress=out_suppress, view=view) else: tma = triple_exp_mov_average(fund, config=config, plot_output=plot_output, name=name, out_suppress=out_suppress, view=view) sh = tma['tabular']['short'] me = tma['tabular']['medium'] ln = tma['tabular']['long'] else: return {} if progress_bar is not None: progress_bar.uptick(increment=0.4) mast = dict() mast['tabular'] = {} mast['tabular']['short'] = sh mast['tabular']['medium'] = me mast['tabular']['long'] = ln mast = generate_swing_signal(fund, mast, max_period=config[2], config=config) mast = swing_trade_metrics(fund, mast) swings = mast['metrics'] if progress_bar is not None: progress_bar.uptick(increment=0.4) name3 = INDEXES.get(name, name) funct_name = function.upper() name2 = name3 + f' - Swing Trade {funct_name}s' legend = ['Price', 'Short-SMA', 'Medium-SMA', 'Long-SMA', 'Swing Signal'] if plot_output: specialty_plotting([fund['Close'], sh, me, ln, swings], alt_ax_index=[4], legend=legend, title=name2) else: filename = os.path.join(name, view, f"swing_trades_{function}_{name}.png") specialty_plotting([fund['Close'], sh, me, ln, swings], alt_ax_index=[4], legend=['Swing Signal'], title=name2, saveFig=True, filename=filename) if progress_bar is not None: progress_bar.uptick(increment=0.2) mast['type'] = 'oscillator' return mast
def demand_index_metrics(fund: pd.DataFrame, dmx: dict, **kwargs) -> list: """Demand Index Metrics Arguments: fund {pd.DataFrame} -- fund dataset dmx {dict} -- demand index data object Optional Args: plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- (default: {''}) progress_bar {ProgressBar} -- (default: {None}) Returns: list -- demand index data object """ plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view', '') pbar = kwargs.get('pbar') metrics = [0.0] * len(dmx['tabular']) weight_map = { "zero crossover": [1.2, 0.95, 0.6, 0.1], "dual-signal-line": [1.0, 0.85, 0.55, 0.1], "signal-line zone": [0.8, 0.65, 0.4, 0.1] } for sig in dmx['signals']: ind = sig['index'] s = 1.0 if sig['type'] == 'bearish': s = -1.0 if 'dual-signal-line' in sig['value']: weights = weight_map['dual-signal-line'] elif 'signal-line zone' in sig['value']: weights = weight_map['signal-line zone'] else: weights = weight_map['zero crossover'] metrics[ind] = s # Smooth the curves if ind - 1 >= 0: metrics[ind-1] += s * weights[1] if ind + 1 < len(metrics): metrics[ind+1] += s * weights[1] if ind - 2 >= 0: metrics[ind-2] += s * weights[2] if ind + 2 < len(metrics): metrics[ind+2] += s * weights[2] if ind - 3 >= 0: metrics[ind-3] += s * weights[3] if ind + 3 < len(metrics): metrics[ind+3] += s * weights[3] if pbar is not None: pbar.uptick(increment=0.1) metrics = exponential_moving_avg(metrics, 7, data_type='list') norm = normalize_signals([metrics]) metrics = norm[0] if pbar is not None: pbar.uptick(increment=0.1) name2 = INDEXES.get(name, name) title = f"{name2} - Demand Index" if plot_output: dual_plotting(fund['Close'], metrics, 'Price', 'Demand Index Metrics') else: filename = os.path.join(name, view, f"di_metrics_{name}") dual_plotting(fund['Close'], metrics, 'Price', 'Demand Index Metrics', title=title, saveFig=True, filename=filename) return metrics
def generate_di_signal(fund: pd.DataFrame, **kwargs) -> list: """Generate Demand Index Signal Arguments: fund {pd.DataFrame} -- fund dataset Optional Args: plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- (default: {''}) progress_bar {ProgressBar} -- (default: {None}) Returns: list -- demand index signal """ plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view', '') pbar = kwargs.get('pbar') signal = [] # First, generate the "2d H-L volatility" signal. vol_hl = [0.0] * len(fund['Close']) for i in range(1, len(vol_hl)): high = max([fund['High'][i], fund['High'][i-1]]) low = min([fund['Low'][i], fund['Low'][i-1]]) vol_hl[i] = high - low if pbar is not None: pbar.uptick(increment=0.05) vol_av = simple_moving_avg(vol_hl, 10, data_type='list') if pbar is not None: pbar.uptick(increment=0.05) # Calculate the constant 'K'. const_K = [0.0] * len(vol_av) for i, vol in enumerate(vol_av): if vol == 0.0: const_K[i] = 0.0 else: const_K[i] = 3.0 * fund['Close'][i] / vol if pbar is not None: pbar.uptick(increment=0.05) # Calculate daily percent change of open-close. percent = [0.0] * len(vol_av) for i, ope in enumerate(fund['Open']): percent[i] = (fund['Close'][i] - ope) / ope * const_K[i] if pbar is not None: pbar.uptick(increment=0.05) # Calculate BP and SP, so we can get DI = BP/SP | SP/BP B_P = [] S_P = [] for i, vol in enumerate(fund['Volume']): if percent[i] == 0.0: B_P.append(0.0) S_P.append(0.0) elif percent[i] > 0.0: B_P.append(vol) S_P.append(vol / percent[i]) else: B_P.append(vol / percent[i]) S_P.append(vol) if pbar is not None: pbar.uptick(increment=0.05) for i, bpx in enumerate(B_P): if abs(bpx) > abs(S_P[i]): signal.append(S_P[i] / bpx) else: if abs(bpx) == 0.0 and abs(S_P[i]) == 0.0: signal.append(0.0) else: signal.append(bpx / S_P[i]) if pbar is not None: pbar.uptick(increment=0.1) signal = exponential_moving_avg(signal, 10, data_type='list') if pbar is not None: pbar.uptick(increment=0.05) name2 = INDEXES.get(name, name) title = f"{name2} - Demand Index" if plot_output: dual_plotting(fund['Close'], signal, 'Price', 'Demand Index', title=title) else: filename = os.path.join(name, view, f"demand_index_{name}") dual_plotting(fund['Close'], signal, 'Price', 'Demand Index', title=title, saveFig=True, filename=filename) if pbar is not None: pbar.uptick(increment=0.1) return signal
def find_resistance_support_lines(data: pd.DataFrame, **kwargs) -> dict: """Find Resistance / Support Lines Arguments: data {pd.DataFrame} -- fund historical data Optional Args: name {str} -- name of fund, primarily for plotting (default: {''}) plot_output {bool} -- True to render plot in realtime (default: {True}) timeframes {list} -- time windows for feature discovery (default: {[13, 21, 34, 55]}) progress_bar {ProgressBar} -- (default: {None}) view {str} -- directory of plots (default: {''}) Returns: dict -- contains all trendline information """ name = kwargs.get('name', '') plot_output = kwargs.get('plot_output', True) timeframes = kwargs.get('timeframes', [13, 21, 34, 55]) progress_bar = kwargs.get('progress_bar', None) view = kwargs.get('view', '') resist_support_lines = {} resist_support_lines['support'] = {} resist_support_lines['resistance'] = {} increment = 0.5 / (float(len(timeframes))) support = {} resistance = {} for time in timeframes: support[str(time)] = {} x, y = find_points(data, line_type='support', timeframe=time, filter_type='windowed') support[str(time)]['x'] = x support[str(time)]['y'] = y sorted_support = sort_and_group(support) resist_support_lines['support'][str( time)] = cluster_notables(sorted_support, data) resistance[str(time)] = {} x2, y2 = find_points(data, line_type='resistance', timeframe=time) resistance[str(time)]['x'] = x2 resistance[str(time)]['y'] = y2 sorted_resistance = sort_and_group(resistance) resist_support_lines['resistance'][str( time)] = cluster_notables(sorted_resistance, data) if progress_bar is not None: progress_bar.uptick(increment=increment) Xs, Ys, Xr, Yr = get_plot_content( data, resist_support_lines, selected_timeframe=str(timeframes[len(timeframes)-1])) if progress_bar is not None: progress_bar.uptick(increment=0.2) Xc, Yc = res_sup_unions(Yr, Xr, Ys, Xs) # Odd list behavior when no res/sup lines drawn on appends, so if-else to fix if len(Yc) > 0: Xp = Xc.copy() Xp2 = dates_convert_from_index(data, Xp) Yp = Yc.copy() Xp2.append(data.index) Yp.append(remove_dates_from_close(data)) else: Xp2 = data.index Yp = [remove_dates_from_close(data)] c = colorize_plots(len(Yp), primary_plot_index=len(Yp)-1) if progress_bar is not None: progress_bar.uptick(increment=0.1) name2 = INDEXES.get(name, name) if plot_output: generic_plotting(Yp, x=Xp2, colors=c, title=f'{name2} Major Resistance & Support') else: filename = f"{name}/{view}/resist_support_{name}.png" generic_plotting( Yp, x=Xp2, colors=c, title=f'{name2} Major Resistance & Support', saveFig=True, filename=filename) if progress_bar is not None: progress_bar.uptick(increment=0.1) analysis = detailed_analysis([Yr, Ys, Yc], data, key_args={'Colors': c}) if progress_bar is not None: progress_bar.uptick(increment=0.1) analysis['type'] = 'trend' return analysis
def cluster_oscs(position: pd.DataFrame, **kwargs): """ 2-3-5-8 multiplier comparing several different osc lengths Arguments: position {pd.DataFrame} -- list of y-value datasets to be plotted (multiple) Optional Args: name {str} -- name of fund, primarily for plotting (default: {''}) plot_output {bool} -- True to render plot in realtime (default: {True}) function {str} -- type of oscillator (default: {'full_stochastic'}) (others: ultimate, rsi, all, market) wma {bool} -- output signal is filtered by windowed moving average (default: {True}) progress_bar {ProgressBar} -- (default: {None}) view {str} -- file directory of plots (default: {''}) Returns: list -- dict of all clustered oscillator info, list of clustered osc signal """ name = kwargs.get('name', '') plot_output = kwargs.get('plot_output', True) function = kwargs.get('function', 'full_stochastic') wma = kwargs.get('wma', True) prog_bar = kwargs.get('progress_bar', None) view = kwargs.get('view', '') cluster_oscs = {} clusters = generate_cluster(position, function, p_bar=prog_bar) cluster_oscs['tabular'] = clusters cluster_oscs['length_of_data'] = len(clusters) #clusters_filtered = cluster_filtering(clusters, filter_thresh) clusters_wma = exponential_moving_avg(clusters, interval=3, data_type='list') if prog_bar is not None: prog_bar.uptick(increment=0.1) dates = cluster_dates(clusters_wma, position) if prog_bar is not None: prog_bar.uptick(increment=0.1) signals = clustered_signals(dates) if prog_bar is not None: prog_bar.uptick(increment=0.1) cluster_oscs['clustered type'] = function cluster_oscs[function] = dates cluster_oscs['signals'] = signals cluster_oscs = clustered_metrics(position, cluster_oscs, plot_output=plot_output, name=name, view=view) name3 = INDEXES.get(name, name) name2 = name3 + ' - Clustering: ' + function if plot_output: dual_plotting(position['Close'], clusters, 'Position Price', 'Clustered Oscillator', x_label='Trading Days', title=name2) else: filename = os.path.join(name, view, f"clustering_{name}_{function}") dual_plotting(y1=position['Close'], y2=clusters, y1_label='Price', y2_label='Clustered Oscillator', x_label='Trading Days', title=name2, saveFig=True, filename=filename) if prog_bar is not None: prog_bar.uptick(increment=0.2) if wma: cluster_oscs['tabular'] = clusters_wma cluster_oscs['type'] = 'oscillator' return cluster_oscs
def roc_metrics(fund: pd.DataFrame, roc_dict: dict, **kwargs) -> dict: """Rate of Change Metrics Arguments: fund {pd.DataFrame} -- fund dataset roc_dict {dict} -- roc data object Optional Args: plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) views {str} -- (default: {''}) p_bar {ProgressBar} -- (default: {None}) Returns: dict -- roc data object """ plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') views = kwargs.get('views', '') p_bar = kwargs.get('p_bar') MAP_TABULAR = {'short': 0.65, 'medium': 1.0, 'long': 0.4} roc_dict['metrics'] = [0.0] * len(fund['Close']) roc_dict['signals'] = [] # Look at zero crossovers first roc_dict = roc_zero_crossovers(fund, roc_dict, MAP_TABULAR) if p_bar is not None: p_bar.uptick(increment=0.1) roc_dict = roc_over_threshold_crossovers(fund, roc_dict, MAP_TABULAR) if p_bar is not None: p_bar.uptick(increment=0.1) roc_dict['metrics'] = exponential_moving_avg(roc_dict['metrics'], 7, data_type='list') roc_dict['metrics'] = normalize_signals([roc_dict['metrics']])[0] if p_bar is not None: p_bar.uptick(increment=0.1) name2 = INDEXES.get(name, name) title = f"{name2} - Rate of Change Metrics" if plot_output: dual_plotting(fund['Close'], roc_dict['metrics'], 'Price', 'ROC Metrics', title=title) else: filename = os.path.join(name, views, f"roc_metrics_{name}") dual_plotting(fund['Close'], roc_dict['metrics'], 'Price', 'ROC Metrics', title=title, saveFig=True, filename=filename) return roc_dict
def clustered_metrics(position: pd.DataFrame, cluster_oscs: dict, **kwargs) -> dict: """Clustered Metrics Arguments: position {pd.DataFrame} -- dataset cluster_oscs {dict} -- clustered osc data object Optional Args: plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- file directory of plots (default: {''}) Returns: dict -- clustered osc data object """ plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view') ults = cluster_oscs['tabular'] # Take indicator set: weight, filter, normalize weights = [1.0, 0.85, 0.55, 0.1] state2 = [0.0] * len(ults) for ind, s in enumerate(ults): if s != 0.0: state2[ind] += s # Smooth the curves if ind - 1 >= 0: state2[ind - 1] += s * weights[1] if ind + 1 < len(ults): state2[ind + 1] += s * weights[1] if ind - 2 >= 0: state2[ind - 2] += s * weights[2] if ind + 2 < len(ults): state2[ind + 2] += s * weights[2] if ind - 3 >= 0: state2[ind - 3] += s * weights[3] if ind + 3 < len(ults): state2[ind + 3] += s * weights[3] metrics = exponential_moving_avg(state2, 7, data_type='list') norm = normalize_signals([metrics]) metrics = norm[0] cluster_oscs['metrics'] = metrics name3 = INDEXES.get(name, name) name2 = name3 + " - Clustered Oscillator Metrics" if plot_output: dual_plotting(position['Close'], metrics, 'Price', 'Metrics', title=name2) else: filename = os.path.join(name, view, f"clustered_osc_metrics_{name}.png") dual_plotting(position['Close'], metrics, 'Price', 'Metrics', title=name2, filename=filename, saveFig=True) return cluster_oscs
def generate_obv_content(fund: pd.DataFrame, **kwargs) -> dict: """Generate On Balance Signal Content Arguments: fund {pd.DataFrame} Optional Args: plot_output {bool} -- (default: {True}) filter_factor {float} -- threshold divisor (x/filter_factor) for "significant" OBVs (default: {2.5}) sma_interval {int} -- interval for simple moving average (default: {20}) name {str} -- (default: {''}) progress_bar {ProgressBar} -- (default: {None}) view {'str'} -- period (default: {None}) Returns: dict -- obv data object """ plot_output = kwargs.get('plot_output', True) filter_factor = kwargs.get('filter_factor', 2.5) sma_interval = kwargs.get('sma_interval', 20) name = kwargs.get('name', '') progress_bar = kwargs.get('progress_bar') view = kwargs.get('view') obv_dict = dict() obv = generate_obv_signal(fund) obv_dict['obv'] = obv if progress_bar is not None: progress_bar.uptick(increment=0.125) ofilter, features = obv_feature_detection(obv, fund, sma_interval=sma_interval, filter_factor=filter_factor, progress_bar=progress_bar, plot_output=plot_output) obv_dict['tabular'] = ofilter obv_dict['signals'] = features if progress_bar is not None: progress_bar.uptick(increment=0.125) volume = [] volume.append(fund['Volume'][0]) for i in range(1, len(fund['Volume'])): if fund['Close'][i] - fund['Close'][i - 1] < 0: volume.append(-1.0 * fund['Volume'][i]) else: volume.append(fund['Volume'][i]) x = dates_extractor_list(fund) name3 = INDEXES.get(name, name) name2 = name3 + ' - On Balance Volume (OBV)' name4 = name3 + ' - Significant OBV Changes' name5 = name3 + ' - Volume' if plot_output: dual_plotting(fund['Close'], obv, x=x, y1_label='Position Price', y2_label='On Balance Volume', x_label='Trading Days', title=name2, subplot=True) dual_plotting(fund['Close'], ofilter, x=x, y1_label='Position Price', y2_label='OBV-DIFF', x_label='Trading Days', title=name4, subplot=True) bar_chart(volume, x=x, position=fund, title=name5, all_positive=True) else: filename = os.path.join(name, view, f"obv_diff_{name}.png") filename2 = os.path.join(name, view, f"obv_standard_{name}.png") filename3 = os.path.join(name, view, f"volume_{name}.png") bar_chart(volume, x=x, position=fund, title=name5, saveFig=True, filename=filename3, all_positive=True) bar_chart(ofilter, x=x, position=fund, title=name4, saveFig=True, filename=filename) dual_plotting(fund['Close'], obv, x=x, y1_label='Position Price', y2_label='On Balance Volume', x_label='Trading Days', title=name2, saveFig=True, filename=filename2) if progress_bar is not None: progress_bar.uptick(increment=0.125) obv_dict['length_of_data'] = len(obv_dict['tabular']) return obv_dict
def adx_metrics(fund: pd.DataFrame, adx: dict, **kwargs) -> dict: """ADX Metricx Arguments: fund {pd.DataFrame} -- fund dataset adx {dict} -- adx data object Optional Args: plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- (default: {''}) pbar {ProgressBar} -- (default: {None}) Returns: dict -- adx data object """ plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view', '') pbar = kwargs.get('pbar') adx['metrics'] = [0.0] * len(fund['Close']) adx['signals'] = [] auto = autotrend(fund['Close'], periods=[14]) no_trend = adx['tabular']['no_trend'][0] strong_trend = adx['tabular']['strong_trend'][0] over_trend = adx['tabular']['high_trend'][0] signal = adx['tabular']['adx'] for i in range(1, len(signal)): data = None date = fund.index[i].strftime("%Y-%m-%d") trend = 0.0 state = 'none' if auto[i] > 0.0: trend = 1.0 state = 'bull' elif auto[i] < 0.0: trend = -1.0 state = 'bear' if signal[i - 1] < no_trend and signal[i] > no_trend: adx['metrics'][i] += 1.0 * trend if state != 'none': data = { "type": state, "value": '20-crossover: trend start', "index": i, "date": date } if signal[i - 1] < strong_trend and signal[i] > strong_trend: adx['metrics'][i] += 0.6 * trend if state != 'none': data = { "type": state, "value": '25-crossover: STRONG trend', "index": i, "date": date } if signal[i] > strong_trend: adx['metrics'][i] += 0.2 * trend if signal[i - 1] > over_trend and signal[i] < over_trend: adx['metrics'][i] += -1.0 * trend if state != 'none': if state == 'bull': state = 'bear' else: state = 'bull' data = { "type": state, "value": '40-crossunder: weakening trend', "index": i, "date": date } if signal[i - 1] > no_trend and signal[i] < no_trend: adx['metrics'][i] += 0.0 if state != 'none': if state == 'bull': state = 'bear' else: state = 'bull' data = { "type": state, "value": '20-crossunder: WEAK/NO trend', "index": i, "date": date } if data is not None: adx['signals'].append(data) if pbar is not None: pbar.uptick(increment=0.1) metrics = exponential_moving_avg(adx['metrics'], 7, data_type='list') if pbar is not None: pbar.uptick(increment=0.1) adx['metrics'] = normalize_signals([metrics])[0] name2 = INDEXES.get(name, name) title = f"{name2} - ADX Metrics" if plot_output: dual_plotting(fund['Close'], adx['metrics'], 'Price', 'Metrics', title=title) else: filename = os.path.join(name, view, f"adx_metrics_{name}.png") dual_plotting(fund['Close'], adx['metrics'], 'Price', 'Metrics', title=title, saveFig=True, filename=filename) if pbar is not None: pbar.uptick(increment=0.2) return adx
def bollinger_metrics(position: pd.DataFrame, bol_bands: dict, **kwargs) -> dict: """Bollinger Metrics Arguments: position {pd.DataFrame} -- dataset of fund bol_bands {dict} -- bollinger band data object Optional Args: period {int} -- look back period for Stdev (default: {20}) plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- (default: {''}) Returns: dict -- bollinger band data object """ period = kwargs.get('period', 20) plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view') weights = [1.0, 0.6, 0.25, 0.0] tot_len = len(position['Close']) metrics = [0.0] * tot_len for feat in bol_bands['indicators']: ind = feat['index'] if feat['type'] == 'bullish': val = 1.0 else: val = -1.0 metrics[ind] += val * weights[0] # Smooth the curves if ind - 1 >= 0: metrics[ind - 1] += val * weights[1] if ind + 1 < tot_len: metrics[ind + 1] += val * weights[1] if ind - 2 >= 0: metrics[ind - 2] += val * weights[2] if ind + 2 < tot_len: metrics[ind + 2] += val * weights[2] if ind - 3 >= 0: metrics[ind - 3] += val * weights[3] if ind + 3 < tot_len: metrics[ind + 3] += val * weights[3] close = position['Close'] bb = bol_bands['tabular'] for i in range(period, len(close)): s_range = bb['middle_band'][i] - bb['lower_band'][i] if metrics[i] <= 0.6 and metrics[i] >= -0.6: c = close[i] - bb['middle_band'][i] c = np.round(c / s_range, 4) metrics[i] = c metrics = exponential_moving_avg(metrics, 7, data_type='list') norm_signal = normalize_signals([metrics])[0] bol_bands['metrics'] = norm_signal name3 = INDEXES.get(name, name) name2 = name3 + f" - Bollinger Band Metrics" if plot_output: dual_plotting(position['Close'], norm_signal, 'Price', 'Indicators', title=name2) else: filename = os.path.join(name, view, f"bollinger_band_metrics_{name}.png") # filename = name + f"/{view}" + f"/bollinger_band_metrics_{name}.png" dual_plotting(position['Close'], norm_signal, 'Price', 'Metrics', title=name2, saveFig=True, filename=filename) return bol_bands
def get_adx_signal(fund: pd.DataFrame, atr: list, **kwargs) -> dict: """Get ADX Signal Arguments: fund {pd.DataFrame} -- fund dataset atr {list} -- atr signal Optional Args: interval {int} -- lookback period (default: {14}) adx_default {float} -- default adx signal value before calculations appear (default: {20.0}) no_trend_value {float} -- threshold of ADX where signal is deemed to not have a trend (default: {20.0}) strong_trend_value {float} -- threshold of ADX where signal is deemed to have a strong trend (default: {25.0}) high_trend_value {float} -- threshold of ADX where signal drops below as trend weakens (default: {40.0}) plot_output {bool} -- (default: {True}) name {str} -- (default: {''}) view {str} -- (default: {''}) pbar {ProgressBar} -- (default: {None}) Returns: dict -- tabular adx and DI signals """ interval = kwargs.get('interval', 14) ADX_DEFAULT = kwargs.get('adx_default', 20.0) NO_TREND = kwargs.get('no_trend_value', 20.0) STRONG_TREND = kwargs.get('strong_trend_value', 25.0) HIGH_TREND = kwargs.get('high_trend_value', 40.0) plot_output = kwargs.get('plot_output', True) name = kwargs.get('name', '') view = kwargs.get('view', '') pbar = kwargs.get('pbar') signal = dict() # Calculate the directional movement signals dmp = [0.0] * len(fund['Close']) dmn = [0.0] * len(fund['Close']) for i in range(1, len(fund['Close'])): dmp[i] = fund['High'][i] - fund['High'][i - 1] dmn[i] = fund['Low'][i - 1] - fund['Low'][i] if dmp[i] > dmn[i]: dmn[i] = 0.0 else: dmp[i] = 0.0 if dmp[i] < 0.0: dmp[i] = 0.0 if dmn[i] < 0.0: dmn[i] = 0.0 if pbar is not None: pbar.uptick(increment=0.1) # Calculate the dm signals, di signals, dx signal with 'interval' averages dma_p = [0.0] * len(fund['Close']) dma_n = [0.0] * len(fund['Close']) di_p = [0.0] * len(fund['Close']) di_n = [0.0] * len(fund['Close']) dx_signal = [0.0] * len(fund['Close']) dma_p[interval - 1] = sum(dmp[0:interval]) dma_n[interval - 1] = sum(dmn[0:interval]) for i in range(interval, len(fund['Close'])): dma_p[i] = dma_p[i - 1] - (dma_p[i - 1] / float(interval)) + dmp[i] dma_n[i] = dma_n[i - 1] - (dma_n[i - 1] / float(interval)) + dmn[i] di_p[i] = dma_p[i] / atr[i] * 100.0 di_n[i] = dma_n[i] / atr[i] * 100.0 dx_signal[i] = abs(di_p[i] - di_n[i]) / (di_p[i] + di_n[i]) * 100.0 if pbar is not None: pbar.uptick(increment=0.3) # Finally, calculate the adx signal as an 'interval' average of dx adx_signal = [ADX_DEFAULT] * len(fund['Close']) adx_signal[interval - 1] = sum(dx_signal[0:interval]) / float(interval) for i in range(interval, len(adx_signal)): adx_signal[i] = ( (adx_signal[i - 1] * 13) + dx_signal[i]) / float(interval) if pbar is not None: pbar.uptick(increment=0.1) signal['di_+'] = di_p signal['di_-'] = di_n signal['adx'] = adx_signal signal['high_trend'] = [HIGH_TREND] * len(adx_signal) signal['no_trend'] = [NO_TREND] * len(adx_signal) signal['strong_trend'] = [STRONG_TREND] * len(adx_signal) plots = [ signal['no_trend'], signal['high_trend'], signal['strong_trend'], signal['adx'] ] name2 = INDEXES.get(name, name) title = f"{name2} - Average Directional Index (ADX)" if plot_output: dual_plotting(fund['Close'], plots, 'Price', ['No Trend', 'Over Trend', 'Strong Trend', 'ADX'], title=title) else: filename = os.path.join(name, view, f"adx_tabular_{name}.png") dual_plotting(fund['Close'], plots, 'Price', ['No Trend', 'Over Trend', 'Strong Trend', 'ADX'], title=title, saveFig=True, filename=filename) if pbar is not None: pbar.uptick(increment=0.1) return signal