Calculate threshold as multiples of ATR
HumanRupert opened this issue · comments
Something like this feature in NinjaTrader or this script in TradingView.
I've got time to implement it. But I'm not sure how active this repo is. In any case, HMU if you need a maintainer.
Did you get a response @akhtariali ? If not, would you consider forking the repo and adding your High/Low and ATR updates? I would be very interested in the functionality you mention especially if you can code a functional version that uses the candle High/Low.
@flowtrader2016 I ended up implementing this code in Python, using ta-lib to calculate ATR. I'll send @jbn an email to see if he looks for maintainers/collaborators.
@flowtrader2016 I ended up implementing this code in Python, using ta-lib to calculate ATR. I'll send @jbn an email to see if he looks for maintainers/collaborators.
I would love to see your code @akhtariali Is it something you could share please? Thanks
@flowtrader2016 Sure thing.
zigzags = []
def calc_change_since_pivot(row, key):
current = row[key]
last_pivot = zigzags[-1]["Value"]
if(last_pivot == 0): last_pivot = 1 ** (-100) # avoid division by 0
perc_change_since_pivot = (current - last_pivot) / abs(last_pivot)
return perc_change_since_pivot
def get_zigzag(row, taip=None):
if(taip == "Peak"): key = "High"
elif(taip == "Trough"): key = "Low"
else: key = "Close"
return {
"Date": row["Date"],
"Value": row[key],
"Type": taip
}
for ix, row in hist.iterrows():
threshold = row["ATR"] / row["Open"] * ATR_MULTIPILIER
# handle first point
is_starting = ix == 0
if(is_starting):
zigzags.append(get_zigzag(row))
continue
# handle first line
is_first_line = len(zigzags) == 1
if(is_first_line):
perc_change_since_pivot = calc_change_since_pivot(row, "Close")
if(abs(perc_change_since_pivot) >= threshold):
if(perc_change_since_pivot > 0):
zigzags.append(get_zigzag(row, "Peak"))
zigzags[0]["Type"] = "Trough"
else:
zigzags.append(get_zigzag(row, "Trough"))
zigzags[0]["Type"] = "Peak"
continue
# handle other lines
is_trough = zigzags[-2]["Value"] > zigzags[-1]["Value"]
is_ending = ix == len(hist.index) - 1
last_pivot = float(zigzags[-1]["Value"])
# based on last pivot type, look for reversal or continuation
if(is_trough):
perc_change_since_pivot = calc_change_since_pivot(row, "High")
is_reversing = (perc_change_since_pivot >= threshold) or is_ending
is_continuing = row["Low"] <= last_pivot
if (is_continuing): zigzags[-1] = get_zigzag(row, "Trough")
elif (is_reversing): zigzags.append(get_zigzag(row, "Peak"))
else:
perc_change_since_pivot = calc_change_since_pivot(row, "Low")
is_reversing = (perc_change_since_pivot <= -threshold) or is_ending
is_continuing = row["High"] >= last_pivot
if(is_continuing): zigzags[-1] = get_zigzag(row, "Peak")
elif (is_reversing): zigzags.append(get_zigzag(row, "Trough"))
zigzags = pd.DataFrame(zigzags)
zigzags["PrevExt"] = zigzags.Value.shift(2)
print(zigzags.head())
print("\n")
# Note: 2nd index extremum is always compared with the 0th index, which is not actually an extremum, so ignore them.
higher_highs = zigzags.dropna()
higher_highs = higher_highs.loc[(higher_highs["Value"] > higher_highs["PrevExt"]) & (higher_highs["Type"] == "Peak") & (higher_highs.index != 2)]
print(higher_highs.head())
print("\n")
lower_lows = zigzags.dropna()
lower_lows = lower_lows.loc[(lower_lows["Value"] < lower_lows["PrevExt"]) & (lower_lows["Type"] == "Trough") & (lower_lows.index != 2)]
print(lower_lows.head())
With a l'l bit of matplotlib
magic, you'll end up with:
fig, ax = plt.subplots()
candlestick_ohlc(ax, hist.values, width=0.6, colorup='green', colordown='red', alpha=0.8)
ax.set_xlabel('Date')
ax.set_ylabel('Price')
date_format = mpl_dates.DateFormatter('%d-%m-%Y')
ax.xaxis.set_major_formatter(date_format)
fig.autofmt_xdate()
zigzags.plot(ax=ax, x="Date", y="Value", legend=False)
for ix, row in lower_lows.iterrows():
plt.plot(row["Date"], row["Value"], "rv")
for ix, row in higher_highs.iterrows():
plt.plot(row["Date"], row["Value"], "g^")
plt.show()
@flowtrader2016 Sure thing.
Hi @akhtariali ,
I tried to use your code with this test dataframe:
def genMockDataFrame(days,start_price,colName,startDate,seed=None):
periods = days*24
np.random.seed(seed)
steps = np.random.normal(loc=0, scale=0.0018, size=periods)
steps[0]=0
P = start_price+np.cumsum(steps)
P = [round(i,4) for i in P]
fxDF = pd.DataFrame({
'ticker':np.repeat( [colName], periods ),
'date':np.tile( pd.date_range(startDate, periods=periods, freq='H'), 1 ),
'price':(P)})
fxDF.index = pd.to_datetime(fxDF.date)
fxDF = fxDF.price.resample('D').ohlc()
return fxDF
hist = genMockDataFrame(100,1.1904,'eurusd','19/3/2020',seed=1)
I'm not sure what value to use for the MULTIPLIER and receive list index out of range error when trying different values. Would be great to see it working with this/any test data.
Thanks
@flowtrader2016 I use yfinance to fetch data. ATR_MULTIPLIER
is used to calculate the % threshold of divergence since the last pivot to calculate reversals–threshold = ATR / open * MULTIPLIER
.
@apologeticallypervy did you receive a response? If not it would be great to see you create a new repo with your zigzag code.
No luck. I'm having crazy times right now; will take a look at it ASAP.
@HumanRupert thanks for you share,It's beautiful