Halftrend is a Trend trend-following indicator that could manage the sideways market better. In this tutorial, we will be building Halftrend using pandas_ta and the Plotly Python library. Halftrend which is considered a better than Supertrend indicator to manage sideways markets with fewer whipsaws

Halftrend comes with an upline (buymode) and a downline (sellmode) and trend (+1 – long direction and -1 is short direction and ATR bands (upper and lower).
Importing the Libraries
import yfinance as yf
import pandas as pd
import pandas_ta as ta
import plotly.graph_objects as go
import numpy as np
Halftrend Function
def halftrend(data, amplitude=2, channel_deviation=4):
data['ATR'] = ta.atr(data['High'], data['Low'], data['Close'], length=50)
atr = data['ATR'] / 2
atr /= 2
dev = channel_deviation * atr
current_high = data['High'].mask(data['High'].shift(-amplitude) > data['High'].rolling(window=amplitude, min_periods=1).max(), np.nan)
current_low = data['Low'].mask(data['Low'].shift(-amplitude) < data['Low'].rolling(window=amplitude, min_periods=1).min(), np.nan)
upper_band = data['High'].rolling(window=amplitude, min_periods=1).mean()
lower_band = data['Low'].rolling(window=amplitude, min_periods=1).mean()
main = pd.Series(index=data.index, dtype='float64')
trend = pd.Series(index=data.index, dtype='float64')
up_line = pd.Series(index=data.index, dtype='float64')
down_line = pd.Series(index=data.index, dtype='float64')
atr_high = pd.Series(index=data.index, dtype='float64')
atr_low = pd.Series(index=data.index, dtype='float64')
for i in range(1, len(data)):
if trend[i-1] > 0:
main[i] = max(main[i-1], current_low[i])
if upper_band[i] < main[i] and data['Close'][i] < data['Low'][i-1]:
trend[i] = -1
main[i] = min(main[i-1], current_high[i])
else:
trend[i] = trend[i-1] + 1
elif trend[i-1] < 0:
main[i] = min(main[i-1], current_high[i])
if lower_band[i] > main[i] and data['Close'][i] > data['High'][i-1]:
trend[i] = 1
main[i] = max(main[i-1], current_low[i])
else:
trend[i] = trend[i-1] - 1
else:
if data['Close'][i] > data['Close'][i-1]:
trend[i] = 1
main[i] = current_low[i]
else:
trend[i] = -1
main[i] = current_high[i]
if trend[i] > 0:
up_line[i] = main[i]
down_line[i] = None
atr_high[i] = up_line[i] + dev[i]
atr_low[i] = up_line[i] - dev[i]
else:
up_line[i] = None
down_line[i] = main[i]
atr_high[i] = down_line[i] + dev[i]
atr_low[i] = down_line[i] - dev[i]
return trend, up_line, down_line, atr_high, atr_low
Download the Data and Compute Halftrend
# Download historical data
ticker = 'RELIANCE.NS'
start_date = '2023-01-01'
end_date = '2024-01-01'
data = yf.download(ticker, start=start_date, end=end_date)
# Compute Halftrend
trend, up_line, down_line, atr_high, atr_low = halftrend(data,amplitude=2,channel_deviation=4)
Plot Candlestick Along with Halftrend Indicator
# Plotting with Plotly
fig = go.Figure(data=[go.Candlestick(x=data.index,
open=data['Open'],
high=data['High'],
low=data['Low'],
close=data['Close'],
name='Candlesticks'),
go.Scatter(x=data.index, y=up_line, mode='lines', name='Up Line', line=dict(color='green')),
go.Scatter(x=data.index, y=down_line, mode='lines', name='Down Line', line=dict(color='red')),
go.Scatter(x=data.index, y=atr_high, mode='lines', name='ATR High', line=dict(color='green', dash='dash')),
go.Scatter(x=data.index, y=atr_low, mode='lines', name='ATR Low', line=dict(color='red', dash='dash'))])
fig.update_layout(title=f'{ticker} Chart with Halftrend',
template='plotly_dark',
xaxis_title='Date',
yaxis_title='Price',
xaxis=dict(type='category', showgrid=False, tickmode='array',
tickvals=data.index[::len(data)//5], # Adjust the frequency of ticks as needed
ticktext=data.index[::len(data)//5].strftime('%b %Y')), # Format ticks as 'Jan 2022'
xaxis_rangeslider_visible=False,
yaxis=dict(showgrid=False))
fig.show()
Output
