def cluster_dates(cluster_list: list, fund: pd.DataFrame) -> list: """ Adds non-zero cluster values with date, price, and index """ dates = [] for i in range(len(cluster_list)): if cluster_list[i] != 0: dates.append([date_extractor(fund.index[i], _format='str'), fund['Close'][i], cluster_list[i], i]) return dates
def add_daterange(original: pd.DataFrame, extrema: dict, num_feature_points: int) -> dict: """ Add Date Range Looks at index ranges of 'extrema' and adds actual dates from 'original' to 'extrema' Arguments: original {pd.DataFrame} -- fund dataset extrema {dict} -- extrema data object num_feature_points {int} -- selectable number of points to add dates Returns: dict -- extrema data object """ for feat in extrema['features']: if feat: first_ind = feat['indexes'][0][0] last_ind = feat['indexes'][num_feature_points - 1][0] start = date_extractor(original.index[first_ind], _format='str') end = date_extractor(original.index[last_ind], _format='str') feat['daterange'] = start + ' : ' + end return extrema
def period_strength(fund_name: str, tickers: dict, periods: list, **kwargs) -> list: """Period Strength Try to provide ratio evaluations of 'fund' vs. market and sector. Update utilizes sectors.json file to pull information. Arguments: fund_name {str} -- 'MSFT', for example tickers {dict} -- entire downloaded data object periods {list} - list of lookback periods Optional Args: sector {str} -- name of sector, 'VGT' for Tech, for example (default: {''}) sector_data {pd.DataFrame} -- data of sector (default: {None}) Returns: list -- list of ratio data objects """ sector = kwargs.get('sector', '') sector_data = kwargs.get('sector_data') ratio = [] hasSP = False hasSector = False sp = get_SP500_df(tickers) if sp is not None: hasSP = True if sector != '': if sector_data is not None: sec = sector_data hasSector = True fund = tickers[fund_name] for period in periods: entry = {} entry['period'] = period entry['dates'] = date_extractor(fund.index[len(fund.index)-period], _format='str') + \ " : " + \ date_extractor(fund.index[len(fund.index)-1], _format='str') if hasSP: entry['sp500'] = {} sp_temp = list(sp['Adj Close']) sp_temp = sp_temp[len(sp_temp) - period:len(sp_temp) + 1] f_temp = list(fund['Adj Close']) f_temp = f_temp[len(f_temp) - period:len(f_temp) + 1] rat = normalized_ratio(f_temp, sp_temp, data_type='list') entry['sp500']['avg'] = np.round(np.mean(rat), 6) entry['sp500']['stdev'] = np.round(np.std(rat), 6) entry['sp500']['min'] = np.min(rat) entry['sp500']['max'] = np.max(rat) entry['sp500']['current'] = rat[-1] if hasSector: entry['sector'] = {} entry['sector']['name'] = sector sp_temp = list(sec['Adj Close']) sp_temp = sp_temp[len(sp_temp) - period:len(sp_temp) + 1] f_temp = list(fund['Adj Close']) f_temp = f_temp[len(f_temp) - period:len(f_temp) + 1] rat = normalized_ratio(f_temp, sp_temp, data_type='list') entry['sector']['avg'] = np.round(np.mean(rat), 6) entry['sector']['stdev'] = np.round(np.std(rat), 6) entry['sector']['min'] = np.min(rat) entry['sector']['max'] = np.max(rat) entry['sector']['current'] = rat[-1] ratio.append(entry) return ratio
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 rsi_divergence(position: pd.DataFrame, rsi_data: dict, **kwargs) -> dict: """RSI Divergence: 1. Cross outside threshold at Price A 2. Cross inside threshold 3. Cross outside threshold at Price B 4. Exit [inside] threshold, where Price B is more extreme than A Arguments: position {pd.DataFrame} -- dataset rsi_data {dict} -- rsi data object Optional Args: plot_output {bool} -- (default: {True}) p_bar {ProgressBar} -- (default: {None}) Returns: dict -- rsi data object """ plot_output = kwargs.get('plot_output', True) p_bar = kwargs.get('p_bar') signal = rsi_data['tabular'] ovb_th = rsi_data['thresholds']['overbought'] ovs_th = rsi_data['thresholds']['oversold'] divs = [0.0] * len(signal) state = 'n' maxima = 0.0 minima = 0.0 prices = [0.0, 0.0] rsi_vals = [0.0, 0.0] for i, sig in enumerate(signal): # Start with bullish divergence if (state == 'n') and (sig <= ovs_th[i]): state = 'u1' rsi_vals[0] = sig elif state == 'u1': if sig <= rsi_vals[0]: rsi_vals[0] = sig prices[0] = position['Close'][i] else: state = 'u2' maxima = sig elif state == 'u2': if sig >= maxima: if sig >= ovb_th[i]: state = 'e1' rsi_vals[0] = sig else: maxima = sig else: state = 'u3' rsi_vals[1] = sig elif state == 'u3': if sig <= rsi_vals[1]: prices[1] = position['Close'][i] rsi_vals[1] = sig else: if rsi_vals[1] <= ovs_th[i]: if (prices[1] < prices[0]) and (rsi_vals[0] < rsi_vals[1]): # Bullish divergence! divs[i] = 1.0 rsi_data['bullish'].append([ date_extractor(position.index[i], _format='str'), position['Close'][i], i, "divergence" ]) state = 'n' else: state = 'n' else: state = 'n' # Start with bearish divergence elif (state == 'n') and (sig >= ovb_th[i]): state = 'e1' rsi_vals[0] = sig elif state == 'e1': if sig >= rsi_vals[0]: rsi_vals[0] = sig prices[0] = position['Close'][i] else: state = 'e2' minima = sig elif state == 'e2': if sig <= minima: if sig <= ovs_th[i]: state = 'u1' rsi_vals[0] = sig else: minima = sig else: state = 'e3' rsi_vals[1] = sig elif state == 'e3': if sig >= rsi_vals[1]: prices[1] = position['Close'][i] rsi_vals[1] = sig else: if rsi_vals[1] >= ovb_th[i]: if (prices[1] > prices[0]) and (rsi_vals[0] > rsi_vals[1]): # Bearish divergence! divs[i] = -1.0 rsi_data['bearish'].append([ date_extractor(position.index[i], _format='str'), position['Close'][i], i, "divergence" ]) state = 'n' else: state = 'n' else: state = 'n' rsi_data['divergence'] = divs if plot_output: dual_plotting(position['Close'], divs, 'Price', 'RSI', title='Divs') if p_bar is not None: p_bar.uptick(increment=0.1) return rsi_data
def determine_rsi_swing_rejection(position: pd.DataFrame, rsi_data: dict, **kwargs) -> dict: """ Find bullish / bearish and RSI indicators 1. go beyond threshold 2. go back within thresholds 3. have local minima/maxima inside thresholds 4. exceed max/min (bull/bear) of previous maxima/minima Arguments: position {pd.DataFrame} -- fund data rsi_signal {list} -- pure RSI signal rsi_data {dict} -- RSI data object Optional Args: p_bar {ProgressBar} -- (default: {None}) Returns: dict -- rsi data object """ p_bar = kwargs.get('p_bar', None) thresholds = rsi_data['thresholds'] rsi_signal = rsi_data['tabular'] if thresholds is None: thresholds = dict() thresholds['oversold'] = [30.0] * len(position['Close']) thresholds['overbought'] = [70.0] * len(position['Close']) LOW_TH = thresholds['oversold'] HIGH_TH = thresholds['overbought'] rsi_data['bullish'] = [] rsi_data['bearish'] = [] indicator = [] increment = 0.2 / float(len(rsi_signal)) state = 0 minima = 0.0 maxima = 0.0 for i in range(len(rsi_signal)): if (state == 0) and (rsi_signal[i] <= LOW_TH[i]): # Start of a bullish signal state = 1 indicator.append(0.0) elif (state == 1) and (rsi_signal[i] > LOW_TH[i]): state = 2 maxima = rsi_signal[i] indicator.append(0.0) elif (state == 2): if rsi_signal[i] >= maxima: if rsi_signal[i] >= HIGH_TH[i]: state = 5 else: maxima = rsi_signal[i] else: minima = rsi_signal[i] state = 3 indicator.append(0.0) elif (state == 3): if rsi_signal[i] <= LOW_TH[i]: # Failed attempted breakout state = 1 elif rsi_signal[i] <= minima: minima = rsi_signal[i] else: state = 4 indicator.append(0.0) elif (state == 4): if rsi_signal[i] > maxima: # Have found a bullish breakout! rsi_data['bullish'].append([ date_extractor(position.index[i], _format='str'), position['Close'][i], i, "swing rejection" ]) state = 0 minima = 0.0 maxima = 0.0 indicator.append(1.0) else: if rsi_signal[i] <= LOW_TH[i]: state = 1 indicator.append(0.0) elif (state == 0) and (rsi_signal[i] >= HIGH_TH[i]): state = 5 indicator.append(0.0) elif (state == 5) and (rsi_signal[i] < HIGH_TH[i]): state = 6 minima = rsi_signal[i] indicator.append(0.0) elif (state == 6): if rsi_signal[i] <= minima: if rsi_signal[i] <= LOW_TH[i]: state = 1 else: minima = rsi_signal[i] else: maxima = rsi_signal[i] state = 7 indicator.append(0.0) elif (state == 7): if rsi_signal[i] >= HIGH_TH[i]: # Failed attempted breakout state = 5 elif rsi_signal[i] >= maxima: maxima = rsi_signal[i] else: state = 8 indicator.append(0.0) elif (state == 8): if rsi_signal[i] < minima: rsi_data['bearish'].append([ date_extractor(position.index[i], _format='str'), position['Close'][i], i, "swing rejection" ]) state = 0 minima = 0.0 maxima = 0.0 indicator.append(-1.0) else: if rsi_signal[i] >= HIGH_TH[i]: state = 5 indicator.append(0.0) else: indicator.append(0.0) if p_bar is not None: p_bar.uptick(increment=increment) rsi_data['indicator'] = indicator return rsi_data
def get_stoch_divergences(position: pd.DataFrame, full_stoch: dict, **kwargs) -> dict: """Get Stoch Divergences Arguments: position {pd.DataFrame} -- dataset full_stoch {dict} -- stoch data object Optional Args: name {str} -- (default: {''}) plot_output {bool} -- (default: {True}) out_suppress {bool} -- (default: {True}) p_bar {ProgressBar} -- (default: {None}) Returns: dict -- stoch data object """ name = kwargs.get('name', '') plot_output = kwargs.get('plot_output', True) out_suppress = kwargs.get('out_suppress', True) p_bar = kwargs.get('p_bar') view = kwargs.get('view') OVER_BOUGHT = 80.0 OVER_SOLD = 20.0 fast_k = full_stoch['tabular']['fast_k'] state = 'n' # Look for divergences + fast_k at 50 prices = [0.0, 0.0] s_vals = [0.0, 0.0] for i, fast in enumerate(fast_k): # Bearish divergences if (state == 'n') and fast >= OVER_BOUGHT: state = 'b1' s_vals[0] = fast elif state == 'b1': if fast < s_vals[0]: prices[0] = position['Close'][i] state = 'b2' else: s_vals[0] = fast elif state == 'b2': if fast < OVER_BOUGHT: state = 'b3' s_vals[1] = fast elif fast > s_vals[0]: s_vals[0] = fast state = 'b1' elif state == 'b3': if fast > s_vals[1]: state = 'b4' s_vals[1] = fast else: s_vals[1] = fast elif state == 'b4': if fast < s_vals[1]: prices[1] = position['Close'][i - 1] if (prices[0] < prices[1]) and (s_vals[0] > s_vals[1]): full_stoch['bearish'].append([ date_extractor(position.index[i], _format='str'), position['Close'][i - 1], i, "divergence" ]) full_stoch['indicator'][i] += -1.5 state = 'n' else: state = 'n' else: s_vals[1] = fast if s_vals[1] > s_vals[0]: s_vals[0] = fast state = 'b1' # Bullish divergences elif (state == 'n') and fast <= OVER_SOLD: state = 's1' s_vals[0] = fast elif state == 's1': if fast > s_vals[0]: prices[0] = position['Close'][i] state = 's2' else: s_vals[0] = fast elif state == 's2': if fast > OVER_SOLD: state = 's3' s_vals[1] = fast elif fast < s_vals[0]: s_vals[0] = fast state = 's1' elif state == 's3': if fast < s_vals[1]: state = 's4' s_vals[1] = fast else: s_vals[1] = fast elif state == 's4': if fast > s_vals[1]: prices[1] = position['Close'][i - 1] if (prices[0] > prices[1]) and (s_vals[0] < s_vals[1]): full_stoch['bullish'].append([ date_extractor(position.index[i], _format='str'), position['Close'][i - 1], i, "divergence" ]) full_stoch['indicator'][i] += 1.5 state = 'n' else: state = 'n' else: s_vals[1] = fast if s_vals[1] < s_vals[0]: s_vals[0] = fast state = 's1' if p_bar is not None: p_bar.uptick(increment=0.2) if not out_suppress: name3 = INDEXES.get(name, name) name2 = name3 + ' - Stochastic' if plot_output: dual_plotting(position['Close'], full_stoch['indicator'], 'Position Price', 'Oscillator Signal', title=name2) else: filename = os.path.join(name, view, f"stochastic_{name}.png") dual_plotting(position['Close'], full_stoch['indicator'], 'Position Price', 'Stochastic Oscillator', x_label='Trading Days', title=name2, saveFig=True, filename=filename) return full_stoch
def get_crossover_features(position: pd.DataFrame, full_stoch: dict, **kwargs) -> dict: """Get Crossover Features Look for a %K cross over %d (fast) then cross inside the 80/20 bars. Look for a slow %K cross over slow %d then cross inside the 80/20 bars. Arguments: position {pd.DataFrame} -- dataset full_stoch {dict} -- full stochastic object Optional Args: p_bar {ProgressBar} -- (default: {None}) Returns: dict -- full stochastic object """ p_bar = kwargs.get('p_bar') OVER_BOUGHT = 80.0 OVER_SOLD = 20.0 fast_k = full_stoch['tabular']['fast_k'] smooth_k = full_stoch['tabular']['smooth_k'] d_sma = full_stoch['tabular']['slow_d'] bearish = [] bullish = [] stochastic = [0.0] * (len(fast_k)) state = 'n' # Look at crossovers and over-X positions of fast_k & smooth_k for i, fast in enumerate(fast_k): # Bearish / overbought signaling if (state == 'n') and (fast >= OVER_BOUGHT): state = 'b1' elif (state == 'b1'): if (fast >= OVER_BOUGHT): if (smooth_k[i] >= OVER_BOUGHT): state = 'b2' else: state = 'n' elif (state == 'b2') and (fast < smooth_k[i]): state = 'b3' elif (state == 'b3'): if (fast < OVER_BOUGHT): bearish.append([ date_extractor(position.index[i], _format='str'), position['Close'][i], i, "crossover: fast-k/smooth-k" ]) stochastic[i] += -1.0 state = 'n' # Bullish / oversold signaling elif (state == 'n') and (fast <= OVER_SOLD): state = 's1' elif (state == 's1'): if (fast <= OVER_SOLD): if (smooth_k[i] <= OVER_SOLD): state = 's2' else: state = 'n' elif (state == 's2') and (fast > smooth_k[i]): state = 's3' elif (state == 's3'): if (fast > OVER_SOLD): bullish.append([ date_extractor(position.index[i], _format='str'), position['Close'][i], i, "crossover: fast-k/smooth-k" ]) stochastic[i] += 1.0 state = 'n' if p_bar is not None: p_bar.uptick(increment=0.1) # Look at crossovers and over-X positions of smooth_k & slow_d state = 'n' for i, slow in enumerate(smooth_k): # Bearish / overbought signaling if (state == 'n') and (slow >= OVER_BOUGHT): state = 'b1' elif (state == 'b1'): if (slow >= OVER_BOUGHT): if (d_sma[i] >= OVER_BOUGHT): state = 'b2' else: state = 'n' elif (state == 'b2') and (slow < d_sma[i]): state = 'b3' elif (state == 'b3'): if (slow < OVER_BOUGHT): bearish.append([ date_extractor(position.index[i], _format='str'), position['Close'][i], i, "crossover: smooth-k/slow-d" ]) stochastic[i] += -1.0 state = 'n' # Bullish / oversold signaling elif (state == 'n') and (slow <= OVER_SOLD): state = 's1' elif (state == 's1'): if (slow <= OVER_SOLD): if (d_sma[i] <= OVER_SOLD): state = 's2' else: state = 'n' elif (state == 's2') and (slow > d_sma[i]): state = 's3' elif (state == 's3'): if (slow > OVER_SOLD): bullish.append([ date_extractor(position.index[i], _format='str'), position['Close'][i], i, "crossover: smooth-k/slow-d" ]) stochastic[i] += 1.0 state = 'n' if p_bar is not None: p_bar.uptick(increment=0.1) full_stoch['indicator'] = stochastic full_stoch['bullish'] = bullish full_stoch['bearish'] = bearish return full_stoch
def find_ult_osc_features(position: pd.DataFrame, ultimate: dict, **kwargs) -> list: """Find Ultimate Oscilator Features Arguments: position {pd.DataFrame} -- dataset ultimate {dict} -- ultimate osc data object Optional Args: thresh_low {int} -- oversold signal threshold (default: {30}) thresh_high {int} -- overbought signal threshold (default: {70}) p_bar {ProgressBar} -- (default: {None}) Returns: dict -- ultimate osc data object """ p_bar = kwargs.get('p_bar') LOW_TH = kwargs.get('thresh_low', 30) HIGH_TH = kwargs.get('thresh_high', 70) ult_osc = ultimate['tabular'] trigger = [] marker_val = 0.0 marker_ind = 0 for i, close in enumerate(position['Close']): # Find bullish signal if ult_osc[i] < LOW_TH: ult1 = ult_osc[i] marker_val = close marker_ind = i lows = lower_low(position['Close'], marker_val, marker_ind) if len(lows) != 0: ult2 = ult_osc[lows[-1][1]] if ult2 > ult1: start_ind = lows[-1][1] interval = np.max(ult_osc[i:start_ind + 1]) start_ind = bull_bear_th(ult_osc, start_ind, interval, bull_bear='bull') if start_ind is not None: trigger.append([ "BULLISH", date_extractor(position.index[start_ind], _format='str'), position['Close'][start_ind], start_ind, "divergence (original)" ]) # Find bearish signal if ult_osc[i] > HIGH_TH: ult1 = ult_osc[i] marker_val = position['Close'][i] marker_ind = i highs = higher_high(position['Close'], marker_val, marker_ind) if len(highs) != 0: ult2 = ult_osc[highs[-1][1]] if ult2 < ult1: start_ind = highs[-1][1] interval = np.min(ult_osc[i:start_ind + 1]) start_ind = bull_bear_th(ult_osc, start_ind, interval, bull_bear='bear') if start_ind is not None: trigger.append([ "BEARISH", date_extractor(position.index[start_ind], _format='str'), position['Close'][start_ind], start_ind, "divergence (original)" ]) if p_bar is not None: p_bar.uptick(increment=0.3) state = 'n' prices = [0.0, 0.0] ults = [0.0, 0.0, 0.0] for i, ult in enumerate(ult_osc): # Find bullish divergence and breakout if (state == 'n') and (ult <= LOW_TH): state = 'u1' ults[0] = ult elif state == 'u1': if ult < ults[0]: ults[0] = ult else: prices[0] = position['Close'][i - 1] state = 'u2' elif (state == 'u2') and (ult > LOW_TH): state = 'u3' ults[1] = ult elif state == 'u3': if ult > ults[1]: ults[1] = ult else: # we think we've found a divergent high state = 'u4' ults[2] = ult elif state == 'u4': if ults[2] >= ult: ults[2] = ult else: # We think we've found the bullish 2nd low prices[1] = position['Close'][i - 1] state == 'u5' elif state == 'u5': if ult > ults[1]: if prices[0] > prices[1]: # Bullish breakout! trigger.append([ "BULLISH", date_extractor(position.index[start_ind], _format='str'), position['Close'][start_ind], start_ind, "divergence" ]) state = 'n' else: # False breakout, see if this is the new max: state = 'u3' ults[1] = ult elif ult < ults[2]: # There may have been a false signal ults[2] = ult state = 'u4' # Find bullish divergence and breakout if (state == 'n') and (ult >= HIGH_TH): state = 'e1' ults[0] = ult elif state == 'e1': if ult > ults[0]: ults[0] = ult else: prices[0] = position['Close'][i - 1] state = 'e2' elif (state == 'e2') and (ult < HIGH_TH): state = 'e3' ults[1] = ult elif state == 'e3': if ult < ults[1]: ults[1] = ult else: # we think we've found a divergent low state = 'e4' ults[2] = ult elif state == 'e4': if ults[2] <= ult: ults[2] = ult else: # We think we've found the bullish 2nd high prices[1] = position['Close'][i - 1] state == 'e5' elif state == 'e5': if ult < ults[1]: if prices[0] < prices[1]: # Bullish breakout! trigger.append([ "BEARISH", date_extractor(position.index[start_ind], _format='str'), position['Close'][start_ind], start_ind, "divergence" ]) state = 'n' else: # False breakout, see if this is the new max: state = 'e3' ults[1] = ult elif ult > ults[2]: # There may have been a false signal ults[2] = ult state = 'e4' elif ult >= HIGH_TH: state = 'e1' ults[0] = ult elif ult <= LOW_TH: state = 'u1' ults[0] = ult if p_bar is not None: p_bar.uptick(increment=0.2) ultimate['indicator'] = trigger return ultimate