stockstats
stockstats copied to clipboard
Supertrend indicator seems to incorrectly change orientation
Hello,
I have switched my supertrend calculations from pandas_ta
and I observe a inconsistency in supertrend values. The issue is (probably) caused by specific candle wicks.
I will continously work the description of this issue, since it's a bit difficult to be to debug or even describe.
Current behaviour
The issue occurs at 08:45
or 08:46
. The extreme weird looking candles happens and the supertrend flips for a period of one candle.
- Dataset: Binance OHCL BTCUSDT
- Time range: from 2020-09-14 to 2020-09-15 (UTC)
- ST Multiplier: 2
- ST Window length: 25
- Python
3.10
- Stockstats
0.4.1
Note: I am using an web UI which is displaying only "active" supertrend. This is why is the other line missing from the chart. However I am going to provide charts with both lines, but it's a bit confusing a bit since it's not clear which one is "active".
Code snippet
import pandas as pd
import stockstats
filename = 'binance_btcusdt_ohcl_1m.parquet'
ohcl = pd.read_parquet(filename, engine="fastparquet")
st_a = stockstats.wrap(ohcl.copy())
st_a.SUPERTREND_MUL = 4
st_a.SUPERTREND_WINDOW = 25
st_a = st_a[['supertrend', 'supertrend_ub', 'supertrend_lb']]
# Here I merge the st_a with datetime column generated before the ST calculation
# st_a.insert(1, "datetime", date)
# And renaming columns in order to increase readability
# st_b.rename(columns={'supertrend_ub': 'st_upper', 'supertrend_lb': 'st_lower'}, inplace=True)
Supertrend values (raw)
id datetime st value st upper st lower
611587 2020-09-14 08:40:00 10447.284218 10447.284218 10388.507576
611588 2020-09-14 08:41:00 10447.284218 10447.284218 10388.507576
611589 2020-09-14 08:42:00 10447.284218 10447.284218 10388.507576
611590 2020-09-14 08:43:00 10447.025543 10447.025543 10388.507576
611591 2020-09-14 08:44:00 10447.025543 10447.025543 10388.507576
611592 2020-09-14 08:45:00 10388.507576 10393.494348 10388.507576
611593 2020-09-14 08:46:00 10399.716374 10399.716374 10388.507576
611594 2020-09-14 08:47:00 10399.716374 10399.716374 10293.768281
611595 2020-09-14 08:48:00 10399.716374 10399.716374 10293.768281
611596 2020-09-14 08:49:00 10399.716374 10399.716374 10293.768281
611597 2020-09-14 08:50:00 10399.716374 10399.716374 10293.768281

OHCL with supertrend (stockstats
)
OHCL with supertrend (pandas_ta
)
OHCL with supertrend and both [upper and lowes] values displayed (stockstats
)
Used data source - binance_btcusdt_ohcl_1m.parquet
I am using Pandas to work with data, loading parquet is very easy - guide - however in case of any issues, I can export data in different format (JSON etc.)
binance_btcusdt_ohcl_1m.parquet.zip
Expected behaviour
I do not think that the supertrend - trend should end (change). I think it should continue since the bull (positive/long) candle did not broke it.
I think, I was able to find a solution.
I have adjusted the trend switching logic to watch candle close and last supetrend (lower/upper) value, instead of current. See lines 587+
It's the same logic used in pandas-ta, lines 32+
.
Original code
if last_st == last_ub:
if curr_close <= ub[i]:
st[i] = ub[i]
else:
st[i] = lb[i]
elif last_st == last_lb:
if curr_close > lb[i]:
st[i] = lb[i]
else:
st[i] = ub[i]
After my adjustment
if last_st == last_ub:
if curr_close <= last_ub:
st[i] = ub[i]
else:
st[i] = lb[i]
elif last_st == last_lb:
if curr_close > last_lb:
st[i] = lb[i]
else:
st[i] = ub[i]
Okay, I have opened a PR 126 which is implementing the suggested fix.
On the other hand, I am still unsure about this. The algorithm mentioned in comment inside the current codebase refers to current candle close, not previous.
# IF PREV.ST > PREV.CLOSE AND CUR.ST < CUR.CLOSE ==> BUY SIGNAL
# IF PREV.ST < PREV.CLOSE AND CUR.ST > CUR.CLOSE ==> SELL SIGNAL
for i in range(len(supertrend)):
if close[i] > supertrend.iloc[i, 0]:
upt.append(supertrend.iloc[i, 0])
dt.append(np.nan)
elif close[i] < supertrend.iloc[i, 0]:
upt.append(np.nan)
dt.append(supertrend.iloc[i, 0])
else:
upt.append(np.nan)
dt.append(np.nan)
Also this code computes is in the similar way.
#SUPERTREND = IF((Previous SUPERTREND = Previous FINAL UPPERBAND) and (Current Close <= Current FINAL UPPERBAND)) THEN...
# Line 175 -> df[ohlc[3]]
df[st].iat[i] = df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df[ohlc[3]].iat[
Also this strategy on Tradingview uses current price...
Note: Tradingview uses pinescript and when they refer to previous value, they do it like close[1]
otherwise than in python close[-1]
. So in the snippet below they use current close price and trend values.
# Line 20
trend := trend == -1 and close > dn1 ? 1 : trend == 1 and close < up1 ? -1 : trend
I think, I am missing something....
Thanks for opening this issue. I am not sure about this, either. Maybe both of them are correct. There are just 2 ways to calculate them. Please let me know if you have more clues.