Iván González

Kalman Hull Supertrend

Category: Indicators By: Iván González Created: February 4, 2026, 11:31 AM
February 4, 2026, 11:31 AM
Indicators
7 Comments
Kalman Hull Supertrend

Introduction

 

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.

 

Core Concepts

 

1. The Kalman Filter

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.

 

2. The Hull MA Integration

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.

 

3. Supertrend Logic

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.

 

Key Features

 

  • Recursive Filtering: Uses three distinct Kalman states to refine price data and the final HMA curve.
  • Volatility-Adjusted Bands: Adapts to market conditions using ATR to widen or tighten the trend-following stops.
  • Visual Signals: Automatically plots cyan (bullish) and red (bearish) arrows on the chart when a trend reversal is detected.

 

Parameter Configuration

 

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.

 

Trading Utility

 

  • Long Signals: Indicated by a cyan “▲” below the low of the bar. The Supertrend line will turn cyan, acting as a trailing support.
  • Short Signals: Indicated by a red “▼” above the high of the bar. The Supertrend line will turn red, acting as a trailing resistance.
  • Trend Filter: The KalmanHMA line can be used independently to determine the overall market bias before entering a trade.

 

ProBuilder Code

 

//--------------------------------------------------------------//
//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

Download
Filename: PRC_Kalman-Hull-Supertrend.itf
Downloads: 93
Iván González
Iván González Legend
Currently debugging life, so my bio is on hold. Check back after the next commit for an update.
Author’s Profile

Comments

jth82
6 months ago
#

Here is a modified version that is stable in live trading:

 

//--------------------------------------------------------------//
// PRC Kalman Hull SuperTrend (Stable Live Version)
//--------------------------------------------------------------//

//----- Inputs -------------------------------------------------//
pricesource = customclose
measurementNoise = 3
processNoise = 0.01
atrPeriod = 12
factor = 1.7

//--------------------------------------------------------------//
//----- Kalman Filter 1 ----------------------------------------//
once state1 = pricesource
once cov1 = 1

predState1 = state1
predCov1 = cov1 + processNoise

kg1 = predCov1 / (predCov1 + measurementNoise)
state1 = predState1 + kg1 * (pricesource - predState1)
cov1 = (1 - kg1) * predCov1

Kalman1 = state1

//--------------------------------------------------------------//
//----- Kalman Filter 2 ----------------------------------------//
once state2 = pricesource
once cov2 = 1

predState2 = state2
predCov2 = cov2 + processNoise

kg2 = predCov2 / (predCov2 + measurementNoise / 2)
state2 = predState2 + kg2 * (pricesource - predState2)
cov2 = (1 - kg2) * predCov2

Kalman2 = state2

//--------------------------------------------------------------//
//----- Kalman Hull MA -----------------------------------------//
src = 2 * Kalman2 - Kalman1
length = round(sqrt(measurementNoise))

once state3 = src
once cov3 = 1

predState3 = state3
predCov3 = cov3 + processNoise

kg3 = predCov3 / (predCov3 + length)
state3 = predState3 + kg3 * (src - predState3)
cov3 = (1 - kg3) * predCov3

KalmanHMA = state3

//--------------------------------------------------------------//
//----- SuperTrend ---------------------------------------------//
if barindex <= atrPeriod then
ST = KalmanHMA
direction = 1
else
atr = averagetruerange[atrPeriod](close)

upperband = KalmanHMA + factor * atr
lowerband = KalmanHMA - factor * atr

prevUpper = upperband[1]
prevLower = lowerband[1]

if lowerband <= prevLower AND close[1] >= prevLower then
lowerband = prevLower
endif

if upperband >= prevUpper AND close[1] <= prevUpper then
upperband = prevUpper
endif

prevST = ST[1]

// Direction logic (fixed)
if prevST = prevUpper then
if close > upperband then
direction = -1
else
direction = 1
endif
else
if close < lowerband then
direction = 1
else
direction = -1
endif
endif

// ST assignment (fixed)
if direction = -1 then
ST = lowerband
else
ST = upperband
endif
endif

//--------------------------------------------------------------//
//----- Signals ------------------------------------------------//
once trend = 0
once r = 0
once g = 188
once b = 212

longSignal = direction crosses under 0
shortSignal = direction crosses over 0

if longSignal then
trend = 1
r = 0
g = 188
b = 212
drawtext("▲", barindex, low) coloured(r,g,b)
elsif shortSignal then
trend = -1
r = 255
g = 82
b = 82
drawtext("▼", barindex, high) coloured(r,g,b)
endif

//--------------------------------------------------------------//
//----- Return -------------------------------------------------//
return ST as "Kalman Hull SuperTrend" coloured(r,g,b), KalmanHMA


Iván González
6 months ago
#

good job! thanks a lot!!

jth82
6 months ago
#

Hi Ivan, this looks like a great indicator. It draws as expected on historic bars, but not on new bars after the chart is loaded - instead the moving average line starts to follow exactly the close price, and the supertrend lines move much closer to the bars. Is there a way to fix this so it works as well on new bars formed after the chart is opened? Thanks

Nicolas
6 months ago
#

Thanks a lot jth82! Greatly appreciated!

Hayri Or
8 months ago
#

hi

which program code this indicator. i use mt5

Iván González
8 months ago
#

prorealtime...

Pedro Herrero
8 months ago
#

Gracias, vamos a probarlo

ProRealCode ProRealCode
Loading...