def generate_analysis(fund: pd.DataFrame, x_list: list, y_list: list, len_list: list, color_list: list) -> list: """Generate Analysis Arguments: fund {pd.DataFrame} -- fund dataset x_list {list} -- list of x-value lists y_list {list} -- list of y-value lists len_list {list} -- list of trendline lengths color_list {list} -- list of colors, associated with each trendline Returns: list -- list of analysis data objects """ analysis = [] for i, x in enumerate(x_list): sub = {} sub['length'] = len(x) sub['color'] = color_list[i] reg = linregress(x[0:3], y=y_list[i][0:3]) sub['slope'] = reg[0] sub['intercept'] = reg[1] sub['start'] = {} sub['start']['index'] = x[0] sub['start']['date'] = fund.index[x[0]].strftime("%Y-%m-%d") sub['end'] = {} sub['end']['index'] = x[len(x) - 1] sub['end']['date'] = fund.index[x[len(x) - 1]].strftime("%Y-%m-%d") sub['term'] = len_list[i] if sub['slope'] < 0: sub['type'] = 'bear' else: sub['type'] = 'bull' sub['x'] = {} sub['x']['by_date'] = dates_convert_from_index(fund, [x], to_str=True) sub['x']['by_index'] = x if sub['end']['index'] == len(fund['Close']) - 1: sub['current'] = True else: sub['current'] = False sub = attribute_analysis(fund, x, y_list[i], sub) analysis.append(sub) return analysis
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 get_trendlines(fund: pd.DataFrame, **kwargs) -> dict: """Get Trendlines 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}) interval {list} -- list of windowed filter time periods (default: {[4, 8, 16, 32]}) progress_bar {ProgressBar} -- (default: {None}) sub_name {str} -- file extension within 'name' directory (default: {name}) view {str} -- directory of plots (default: {''}) meta {dict} -- 'metadata' object for fund (default: {None}) out_suppress {bool} -- if True, skips plotting (default: {False}) trend_window {list} -- line time windows (default: {[163, 91, 56, 27]}) Returns: trends {dict} -- contains all trend lines determined by algorithm """ name = kwargs.get('name', '') plot_output = kwargs.get('plot_output', True) interval = kwargs.get('interval', [4, 8, 16, 32]) progress_bar = kwargs.get('progress_bar', None) sub_name = kwargs.get('sub_name', f"trendline_{name}") view = kwargs.get('view', '') meta = kwargs.get('meta') out_suppress = kwargs.get('out_suppress', False) trend_window = kwargs.get('trend_window', [163, 91, 56, 27]) # Not ideal to ignore warnings, but these are handled already by scipy/numpy so... eh... warnings.filterwarnings("ignore", category=RuntimeWarning) trends = dict() mins_y = [] mins_x = [] maxes_y = [] maxes_x = [] all_x = [] vq = 0.06 if meta is not None: vol = meta.get('volatility', {}).get('VQ') if vol is not None: vq = vol / 100.0 increment = 0.7 / (float(len(interval)) * 3) for i, period in enumerate(interval): ma_size = period # ma = windowed_ma_list(fund['Close'], interval=ma_size) weight_strength = 2.0 + (0.1 * float(i)) ma = windowed_moving_avg(fund['Close'], interval=ma_size, weight_strength=weight_strength, data_type='list', filter_type='exponential') ex = find_filtered_local_extrema(ma) r = reconstruct_extrema(fund, extrema=ex, ma_size=ma_size, ma_type='windowed') # Cleanse data sample for duplicates and errors r = remove_duplicates(r, method='point') for y in r['min']: if y[0] not in mins_x: mins_x.append(y[0]) mins_y.append(y[1]) if y[0] not in all_x: all_x.append(y[0]) for y in r['max']: if y[0] not in maxes_x: maxes_x.append(y[0]) maxes_y.append(y[1]) if y[0] not in all_x: all_x.append(y[0]) if progress_bar is not None: progress_bar.uptick(increment=increment) zipped_min = list(zip(mins_x, mins_y)) zipped_min.sort(key=lambda x: x[0]) mins_x = [x[0] for x in zipped_min] mins_y = [y[1] for y in zipped_min] zipped_max = list(zip(maxes_x, maxes_y)) zipped_max.sort(key=lambda x: x[0]) maxes_x = [x[0] for x in zipped_max] maxes_y = [y[1] for y in zipped_max] # mins_xd = [fund.index[x] for x in mins_x] # maxes_xd = [fund.index[x] for x in maxes_x] long_term = trend_window[0] intermediate_term = trend_window[1] short_term = trend_window[2] near_term = trend_window[3] X0, Y0 = get_lines_from_period(fund, [mins_x, mins_y, maxes_x, maxes_y, all_x], interval=long_term, vq=vq) X1, Y1 = get_lines_from_period(fund, [mins_x, mins_y, maxes_x, maxes_y, all_x], interval=intermediate_term, vq=vq) X2, Y2 = get_lines_from_period(fund, [mins_x, mins_y, maxes_x, maxes_y, all_x], interval=short_term, vq=vq) X3, Y3 = get_lines_from_period(fund, [mins_x, mins_y, maxes_x, maxes_y, all_x], interval=near_term, vq=vq) if progress_bar is not None: progress_bar.uptick(increment=increment * 4.0) X = [] Y = [] C = [] L = [] for i, x in enumerate(X0): X.append(x) Y.append(Y0[i]) C.append('blue') L.append('long') for i, x in enumerate(X1): X.append(x) Y.append(Y1[i]) C.append('green') L.append('intermediate') for i, x in enumerate(X2): X.append(x) Y.append(Y2[i]) C.append('orange') L.append('short') for i, x in enumerate(X3): X.append(x) Y.append(Y3[i]) C.append('red') L.append('near') if progress_bar is not None: progress_bar.uptick(increment=increment * 4.0) analysis_list = generate_analysis(fund, x_list=X, y_list=Y, len_list=L, color_list=C) if progress_bar is not None: progress_bar.uptick(increment=0.1) X = dates_convert_from_index(fund, X) X.append(fund.index) Y.append(fund['Close']) C.append('black') name2 = INDEXES.get(name, name) if not out_suppress: try: title = f"{name2} Trend Lines for {near_term}, {short_term}, " + \ f"{intermediate_term}, and {long_term} Periods" if plot_output: generic_plotting(Y, x=X, colors=C, title=title) else: filename = os.path.join(name, view, f"{sub_name}.png") generic_plotting(Y, x=X, colors=C, title=title, saveFig=True, filename=filename) except: print( f"{WARNING}Warning: plot failed to generate in trends.get_trendlines.{NORMAL}" ) if progress_bar is not None: progress_bar.uptick(increment=0.2) trends['trendlines'] = analysis_list current = [] metrics = [] for trend in analysis_list: if trend['current']: current.append(trend) met = {f"{trend.get('term')} term": trend.get('type')} met['periods'] = trend.get('length') metrics.append(met) trends['current'] = current trends['metrics'] = metrics trends['type'] = 'trend' return trends