diff --git a/pyti/accumulation_distribution.py b/pyti/accumulation_distribution.py index def38d5..f4bf9cc 100644 --- a/pyti/accumulation_distribution.py +++ b/pyti/accumulation_distribution.py @@ -17,11 +17,18 @@ def accumulation_distribution(close_data, high_data, low_data, volume): ad = np.zeros(len(close_data)) for idx in range(1, len(close_data)): - ad[idx] = ( - (((close_data[idx] - low_data[idx]) - - (high_data[idx] - close_data[idx])) / - (high_data[idx] - low_data[idx]) * - volume[idx]) + - ad[idx-1] - ) + candle = high_data[idx] - low_data[idx] + if candle == 0: + if high_data[idx] != close_data[idx]: + raise RuntimeError("High and low are equals but close is not.") + else: + ad[idx] = ad[idx - 1] + else: + ad[idx] = ( + (((close_data[idx] - low_data[idx]) - + (high_data[idx] - close_data[idx])) / + (high_data[idx] - low_data[idx]) * + volume[idx]) + + ad[idx-1] + ) return ad diff --git a/pyti/aroon.py b/pyti/aroon.py index 70b4f9f..ebf0522 100644 --- a/pyti/aroon.py +++ b/pyti/aroon.py @@ -37,3 +37,14 @@ def aroon_down(data, period): float(period)) * 100 for idx in range(period-1, len(data))] a_down = fill_for_noncomputable_vals(data, a_down) return a_down + + +def aroon_oscillator(data, period): + """ + Aroon Oscillator. + Formula: + AO = AROON_UP(PERIOD) - AROON_DOWN(PERIOD) + """ + catch_errors.check_for_period_error(data, period) + period = int(period) + return aroon_up(data, period) - aroon_down(data, period) diff --git a/pyti/chaikin_money_flow.py b/pyti/chaikin_money_flow.py index d8bf07e..af772ec 100644 --- a/pyti/chaikin_money_flow.py +++ b/pyti/chaikin_money_flow.py @@ -1,6 +1,8 @@ from __future__ import absolute_import + import numpy as np from pyti import catch_errors +from pyti.exponential_moving_average import exponential_moving_average as ema from pyti.function_helper import fill_for_noncomputable_vals from six.moves import range @@ -20,9 +22,32 @@ def chaikin_money_flow(close_data, high_data, low_data, volume, period): high_data = np.array(high_data) low_data = np.array(low_data) volume = np.array(volume) - cmf = [sum((((close_data[idx+1-period:idx+1] - low_data[idx+1-period:idx+1]) - - (high_data[idx+1-period:idx+1] - close_data[idx+1-period:idx+1])) / - (high_data[idx+1-period:idx+1] - low_data[idx+1-period:idx+1])) * - volume[idx+1-period:idx+1]) / sum(volume[idx+1-period:idx+1]) for idx in range(period-1, len(close_data))] + cmf = [sum((((close_data[idx + 1 - period:idx + 1] - low_data[idx + 1 - period:idx + 1]) - + (high_data[idx + 1 - period:idx + 1] - close_data[idx + 1 - period:idx + 1])) / + (high_data[idx + 1 - period:idx + 1] - low_data[idx + 1 - period:idx + 1])) * + volume[idx + 1 - period:idx + 1]) / sum(volume[idx + 1 - period:idx + 1]) for idx in + range(period - 1, len(close_data))] cmf = fill_for_noncomputable_vals(close_data, cmf) return cmf + + +def chaikin_oscillator(close_data, high_data, low_data, volume, short_period=3, long_period=10): + """ + Chaikin Oscillator (CHO). + Chaikin Accumulation Distribution Line (ADL). + + Formula: + ADL = M(Period-1) + M(Period) + CHO = ADL = 3day EMA(ADL) - 10day EMA(ADL) + """ + ac = [] + val_last = 0 + for index in range(0, len(close_data)): + if high_data[index] != low_data[index]: + val = val_last + ((close_data[index] - low_data[index]) - (high_data[index] - close_data[index])) / ( + high_data[index] - low_data[index]) * volume[index] + else: + val = val_last + ac.append(val) + cho = ema(ac, short_period) - ema(ac, long_period) + return cho diff --git a/pyti/relative_strength_index.py b/pyti/relative_strength_index.py index 8eb40d8..e574941 100644 --- a/pyti/relative_strength_index.py +++ b/pyti/relative_strength_index.py @@ -18,11 +18,8 @@ def relative_strength_index(data, period): period = int(period) changes = [data_tup[1] - data_tup[0] for data_tup in zip(data[::1], data[1::1])] - filtered_gain = [val < 0 for val in changes] - gains = [0 if filtered_gain[idx] is True else changes[idx] for idx in range(0, len(filtered_gain))] - - filtered_loss = [val > 0 for val in changes] - losses = [0 if filtered_loss[idx] is True else abs(changes[idx]) for idx in range(0, len(filtered_loss))] + gains = [0 if val < 0 else val for val in changes] + losses = [0 if val > 0 else abs(val) for val in changes] avg_gain = np.mean(gains[:period]) avg_loss = np.mean(losses[:period]) diff --git a/pyti/simple_moving_average.py b/pyti/simple_moving_average.py index c2b7163..01b5a57 100644 --- a/pyti/simple_moving_average.py +++ b/pyti/simple_moving_average.py @@ -11,7 +11,7 @@ def simple_moving_average(data, period): Simple Moving Average. Formula: - SUM(data / N) + SUM(data) / N """ catch_errors.check_for_period_error(data, period) # Mean of Empty Slice RuntimeWarning doesn't affect output so it is diff --git a/pyti/stochastic.py b/pyti/stochastic.py index 8c67304..bbb38ca 100644 --- a/pyti/stochastic.py +++ b/pyti/stochastic.py @@ -13,7 +13,7 @@ def percent_k(data, period): %K. Formula: - %k = data(t) - low(n) / (high(n) - low(n)) + %k = (data(t) - low(n)) / (high(n) - low(n)) """ catch_errors.check_for_period_error(data, period) percent_k = [((data[idx] - np.min(data[idx+1-period:idx+1])) /