The Kalman Hull Supertrend is a sophisticated technical indicator that combines state-of-the-art mathematical filtering with classic trend-following logic. By integrating a Kalman Filter into the Hull Moving Average (HMA) formula and using that as the engine for a Supertrend, this indicator offers a remarkably smooth baseline that remains highly responsive to price action.
Traditional Supertrends often suffer from “whipsaws” in noisy markets. This version addresses that by replacing standard price inputs with a multi-state Kalman filter, significantly reducing market noise while maintaining minimal lag.
Unlike simple moving averages, the Kalman Filter is an iterative mathematical process that estimates the “true” state of a price series by filtering out random noise. It uses a series of measurements observed over time, containing statistical noise and other inaccuracies, and produces estimates of unknown variables. In this code, it is used to pre-filter the price source before any trend calculations occur.
The Hull Moving Average is known for its ability to reduce lag while improving smoothness. By feeding Kalman-filtered data into the HMA calculation, we get a “Kalman HMA” (KalmanHMA). This serves as the central spine of the indicator, providing a cleaner trend bias than a standard average.
The final layer applies the Supertrend algorithm to the KalmanHMA. It uses the Average True Range (ATR) to create dynamic volatility bands. When price closes above the upper band or below the lower band, the trend direction shifts, and a visual signal is generated.
ParameterDefault ValueDescriptionPriceSourceCustomCloseThe price data used for filtering (Open, Close, Typical, etc.).
Measurement Noise3Controls how much the filter “trusts” the incoming price. Higher = Smoother.
Process Noise0.01Controls the filter’s agility. Lower = Smoother.
ATR Period12The look-back period for volatility calculation.
Factor1.7The multiplier for the ATR bands. Higher = Fewer signals.
KalmanHMA line can be used independently to determine the overall market bias before entering a trade.
//--------------------------------------------------------------//
//PRC_Kalman Hull Supertrend
//version = 0
//28.05.24
//Iván González @ www.prorealcode.com
//Sharing ProRealTime knowledge
//--------------------------------------------------------------//
//-----Inputs---------------------------------------------------//
pricesource=customclose
measurementNoise=3
processNoise=0.01
atrPeriod=12
factor=1.7
showkalman=1
paintCandles=1
showlongshort=1
//--------------------------------------------------------------//
//-----Kalman Price Filter Function-----------------------------//
N=5
if not isset($stateEstimate1[0]) then
for j=0 to N-1 do
$stateEstimate1[j]=pricesource
$errorCovariance1[j]=1
next
endif
for i=0 to N-1 do
$predictedStateEstimate1[i]=$stateEstimate1[i]
$predictedErrorCovariance1[i]=$errorCovariance1[i]+processNoise
next
for k=0 to N-1 do
kg1=$predictedErrorCovariance1[k]/($predictedErrorCovariance1[k]+measurementNoise)
$kalmanGain1[k]=kg1
$stateEstimate1[k]=$predictedStateEstimate1[k]+kg1*(pricesource-$predictedStateEstimate1[k])
$errorCovariance1[k]=(1-kg1)*$predictedErrorCovariance1[k]
next
Kalman1=$stateEstimate1[0]
//--------------------------------------------------------------//
//-----Hull Moving Average with Kalman--------------------------//
//Kalman2
if not isset($stateEstimate2[0]) then
for j=0 to N-1 do
$stateEstimate2[j]=pricesource
$errorCovariance2[j]=1
next
endif
for i=0 to N-1 do
$predictedStateEstimate2[i]=$stateEstimate2[i]
$predictedErrorCovariance2[i]=$errorCovariance2[i]+processNoise
next
for k=0 to N-1 do
kg2=$predictedErrorCovariance2[k]/($predictedErrorCovariance2[k]+measurementNoise/2)
$kalmanGain2[k]=kg2
$stateEstimate2[k]=$predictedStateEstimate2[k]+kg2*(pricesource-$predictedStateEstimate2[k])
$errorCovariance2[k]=(1-kg2)*$predictedErrorCovariance2[k]
next
Kalman2=$stateEstimate2[0]
//Kalman Hull MA
src=2*Kalman2-Kalman1
length=round(sqrt(measurementNoise))
if not isset($stateEstimate3[0]) then
for j=0 to N-1 do
$stateEstimate3[j]=src
$errorCovariance3[j]=1
next
endif
for i=0 to N-1 do
$predictedStateEstimate3[i]=$stateEstimate3[i]
$predictedErrorCovariance3[i]=$errorCovariance3[i]+processNoise
next
for k=0 to N-1 do
kg3=$predictedErrorCovariance3[k]/($predictedErrorCovariance3[k]+length)
$kalmanGain3[k]=kg3
$stateEstimate3[k]=$predictedStateEstimate3[k]+kg3*(src-$predictedStateEstimate3[k])
$errorCovariance3[k]=(1-kg3)*$predictedErrorCovariance3[k]
next
KalmanHMA=$stateEstimate3[0]
//--------------------------------------------------------------//
//-----SuperTrend Function--------------------------------------//
if barindex <= atrperiod then
upperband=kalmanHMA
lowerband=kalmanHMA
direction=1
else
atr=averagetruerange[atrPeriod](close)
upperband=kalmanHMA+factor*atr
lowerband=kalmanHMA-factor*atr
prevlowerband=lowerband[1]
prevupperband=upperband[1]
if lowerband>prevlowerband or close[1]<prevlowerband then
lowerband=lowerband
else
lowerband=prevlowerband
endif
if upperband<prevupperband or close[1]>prevupperband then
upperband=upperband
else
upperband=prevupperband
endif
prevST=ST[1]
if prevST=prevupperband then
if close > upperband then
direction=-1
else
direction=1
endif
else
if close < lowerband then
direction=1
else
direction=-1
endif
endif
if direction=-1 then
ST=lowerband
else
ST=upperband
endif
endif
//--------------------------------------------------------------//
//-----Conditional Trend----------------------------------------//
once trend=0
SuperTrendLong=direction crosses under 0
SuperTrendShort=direction crosses over 0
if SupertrendLong and not SuperTrendShort then
trend=1
r=0
g=188
b=212
drawtext("▲",barindex,low)coloured(r,g,b)
elsif SuperTrendShort then
trend=-1
r=255
g=82
b=82
drawtext("▼",barindex,high)coloured(r,g,b)
endif
//--------------------------------------------------------------//
//--------------------------------------------------------------//
return ST as "Kalman Hull SuperTrend" coloured(r,g,b), KalmanHMA