Re: Ehlers Indicators for TradeStation

11
Ultimate Strength Index (USI)

Links: |TradingView|TradeStation|NinjaTrader|MT4|MT5|Python|

TradeStation: November 2024
In “Ultimate Strength Index (USI)” in this issue, John Ehlers introduces an enhanced version of the RSI with significantly reduced lag. The USI retains many benefits of the traditional RSI, while providing faster, more responsive results. It highlights bullish and bearish conditions and allows for adjustments based on different data lengths, typically using more data than the standard RSI to achieve comparable outcomes.

Code: Select all

Function: $UltimateSmoother
{
	UltimateSmoother Function
	(C) 2004-2024 John F. Ehlers
}

inputs:
	Price( numericseries ),
	Period( numericsimple );
	
variables:
	a1( 0 ),
	b1( 0 ),
	c1( 0 ),
	c2( 0 ),
	c3( 0 ),
	US( 0 );
	
a1 = ExpValue(-1.414*3.14159 / Period);
b1 = 2 * a1 * Cosine(1.414*180 / Period);
c2 = b1;
c3 = -a1 * a1;
c1 = (1 + c2 - c3) / 4;

if CurrentBar >= 4 then 
 US = (1 - c1)*Price + (2 * c1 - c2) * Price[1] 
 - (c1 + c3) * Price[2] + c2*US[1] + c3 * US[2];
 
if CurrentBar < 4 then 
	US = Price;

$UltimateSmoother = US;

Indicator: Ultimate Strength Index (USI) 

{
	TASC NOVEMBER 2024
	Ultimate Strength Index (USI)
	(C) 2024 John F. Ehlers
}

inputs:
	Length( 14 );

variables:
	SU( 0 ),
	USU( 0 ),
	SD( 0 ),
	USD( 0 ),
	USI( 0 );

if Close > Close[1] then 
	SU = Close - Close[1] 
else 
	SU = 0;

USU = $UltimateSmoother(Average(SU,4), Length);

if Close < Close[1] then 
	SD = Close[1] - Close 
else 
	SD = 0;

USD = $UltimateSmoother(Average(SD, 4), Length);

If (USU + USD <> 0 and USU > .01 and USD > .01) then 
	USI = (USU - USD) / (USU + USD);

Plot1( USI, "USI" );
Plot2( 0, "Zero Line" );

FIGURE 1: TRADESTATION. A daily chart of the emini S&P 500 continuous futures contract (ES) demonstrates the ultimate strength index indicator applied.
These users thanked the author Banzai for the post (total 2):
Abdi, Krunal Gajjar

Re: Ehlers Indicators for TradeStation

13
Laguerre Filters

Links: |TradingView|TradeStation|NinjaTrader|MT4|MT5|Python|Excel|

TradeStation: July 2025
In “Laguerre Filters” in this issue, John Ehlers presents a trend-trading technique using the Laguerre filter. Since Laguerre filters excel at smoothing long-wavelength components in a data set, this makes them particularly well-suited for identifying trading trends.

Code: Select all

Function: Laguerre Filter
{
	TASC JUL 2025
	Laguerre Filter
	(C) 2002-2025 John F. Ehlers
}

inputs:
	Gama( .8 ),
	Length( 40 );

variables:
	L0( 0 ),
	L1( 0 ),
	L2( 0 ),
	L3( 0 ),
	L4( 0 ),
	Laguerre( 0 );
	
L0 = $UltimateSmoother(Close, Length);
L1 = -Gama * L0[1] + L0[1] + Gama * L1[1];
L2 = -Gama * L1[1] + L1[1] + Gama * L2[1];
L3 = -Gama * L2[1] + L2[1] + Gama * L3[1];
L4 = -Gama * L3[1] + L3[1] + Gama * L4[1];

Laguerre = (L0 + 4*L1 + 6*L2 + 4*L3 + L4) / 16;


Plot1( Laguerre );
Plot2( L0 );


Indicator: Laguerre Oscillator
{
	TASC JUL 2025
	Laguerre Oscillator
	(C) 2002-2025 John F. Ehlers
}

inputs:
	Gama( .5 ),
	Length( 30 );

variables:
	L0( 0 ),
	L1( 0 ),
	RMS( 0 ),
	LaguerreOsc( 0 );

L0 = $UltimateSmoother(Close, Length);
L1 = -Gama * L0 + L0[1] + Gama * L1[1];
RMS = $RMS(L0 - L1, 100);

if RMS <> 0 then 
	LaguerreOsc = (L0 - L1) / RMS;
	
Plot1( LaguerreOsc, "Laguerre Osc" );
Plot2( 0, "Zero Line" );

Function: $RMS
{
	RMS Function
	(C) 2015-2025 John F. Ehlers
}

inputs:
	Price( numericseries ),
	Length( numericsimple );

variables:
	SumSq( 0 ),
	count( 0 );

SumSq = 0;

for count = 0 to Length - 1 
begin
	SumSq = SumSq + Price[count] * Price[count];
end;

If SumSq <> 0 then 
	$RMS = SquareRoot(SumSq / Length);

Function: $SuperSmoother
{
	UltimateSmoother Function
	(C) 2004-2025 John F. Ehlers
}

inputs:
	Price( numericseries ),
	Period( numericsimple );
	
variables:
	a1( 0 ),
	b1( 0 ),
	c1( 0 ),
	c2( 0 ),
	c3( 0 ),
	US( 0 );
	
a1 = ExpValue(-1.414*3.14159 / Period);
b1 = 2 * a1 * Cosine(1.414*180 / Period);
c2 = b1;
c3 = -a1 * a1;
c1 = (1 + c2 - c3) / 4;

if CurrentBar >= 4 then 
 US = (1 - c1)*Price + (2 * c1 - c2) * Price[1] 
 - (c1 + c3) * Price[2] + c2*US[1] + c3 * US[2];
 
if CurrentBar < 4 then 
	US = Price;

$UltimateSmoother = US;
These users thanked the author Banzai for the post:
alexm

Re: Ehlers Indicators for TradeStation

14
A Synthetic Oscillator

Links: |TradingView|TradeStation|NinjaTrader|MT4|MT5|Python|

TradeStation: April 2026
In “A Synthetic Oscillator” in this issue, John Ehlers introduces a nonlinear oscillator designed to reduce lag while maintaining smooth, responsive trading signals. The indicator adapts to changing market conditions by measuring the instantaneous dominant cycle and generating signals through a phase-based sine waveform.

EasyLanguage code for the indicator is shown here and a sample chart plotting the indicator is shown in the code below.

Code: Select all

Function: $HighPass

{
	$HighPass Function
 	(C) 2004-2024 John F. Ehlers
}

inputs:
	Price(numericseries),
	Period(numericsimple);
	
variables:
	a1( 0 ),
	b1( 0 ),
	c1( 0 ),
	c2( 0 ),
	c3( 0 );

a1 = ExpValue(-1.414 * 3.14159 / Period);
b1 = 2 * a1 * Cosine(1.414 * 180 / Period);
c2 = b1;
c3 = -a1 * a1;
c1 = (1 + c2 - c3) / 4;

if CurrentBar >= 4 then 
 	$HighPass = c1*(Price - 2 * Price[1] + Price[2]) +
	 c2 * $HighPass[1] + c3 * $HighPass[2];
if Currentbar < 4 then 
	$HighPass = 0;


Function: $RMS

{
 $RMS Function
 (C) 2025 John F. Ehlers
}

inputs:
	Price( numericseries ),
	Length( numericsimple );

variable:
	SumSq( 0 ),
	Count( 0 );

SumSq = 0;

for Count = 0 to Length - 1
begin
	SumSq = SumSq + Price[count]*Price[count];
end;

if SumSq <> 0 then 
	$RMS = SquareRoot(SumSq / Length);


Function: $SuperSmoother

{
	$SuperSmoother Function
	(C) 2025 John F. Ehlers
}
inputs:
	Price( numericseries ),
	Period( numericsimple );

variables:
	A0( 0 ),
	Q( 0 ),
	C1( 0 ),
	C2( 0 );
	
Q = ExpValue( -1.414*3.14159 / Period );
C1 = 2 * Q * Cosine(1.414*180 / Period);
C2 = Q*Q;
A0 = (1 - c1 + c2) / 2;

if CurrentBar >= 4 Then $SuperSmoother = A0*( Price +
 Price[1]) + C1 * $SuperSmoother[1] - C2 * $SuperSmoother[2];

if Currentbar < 4 then 
	$SuperSmoother = Price;


Function: $Hann

{
	$Hann Windowed Lowpass FIR Filter Function
	(c) 2025 John F. Ehlers
}
inputs:
	Price( numericseries ),
	Length( numericsimple );

variables:
	count(0),
	coef(0),
	Filt(0);
	Filt = 0;
	coef = 0;

for count = 1 to Length 
begin
	Filt = Filt + (1 - Cosine(360*count / (Length +
	 1)))*Price[count - 1];
	coef = coef + (1 - Cosine(360*count / (Length + 1)));
end;

if coef <> 0 then 
	$Hann = Filt / coef;


Function $UltimateSmoother

{
	UltimateSmoother Function
	(C) 2004-2024 John F. Ehlers
}

inputs:
	Price( numericseries ),
	Period( numericsimple );
	
variables:
	a1( 0 ),
	b1( 0 ),
	c1( 0 ),
	c2( 0 ),
	c3( 0 ),
	US( 0 );
	
a1 = ExpValue(-1.414*3.14159 / Period);
b1 = 2 * a1 * Cosine(1.414*180 / Period);
c2 = b1;
c3 = -a1 * a1;
c1 = (1 + c2 - c3) / 4;

if CurrentBar >= 4 then 
 US = (1 - c1)*Price + (2 * c1 - c2) * Price[1] 
 - (c1 + c3) * Price[2] + c2*US[1] + c3 * US[2];
 
if CurrentBar < 4 then 
	US = Price;

$UltimateSmoother = US;


Indicator: Synthetic Oscillator

{
	TASC APR 2026
	Synthetic Oscillator Indicator
	(C) 2025 John F. Ehlers
}

inputs:
	LowerBound( 15 ),
	UpperBound( 25 ),
	Length( 4 );

variables:
	Price( 0 ),
	HP( 0 ),
	LP( 0 ),
	RMS( 0 ),
	Real( 0 ),
	ROC( 0 ),
	QRMS( 0 ),
	Imag( 0 ),
	Denom( 0 ),
	DC( 0 ),
	Count( 0 ),
	Mid( 0 ),
	HP2( 0 ),
	BP( 0 ),
	Phase( 0 ),
	Synth( 0 ),
	Synth2( 0 ),
	ROC2( 0 );

Price = $Hann( Close, 12 );

{ Real component is bandpass filtered and normalized }
HP = $HighPass( Price, UpperBound );
LP = $SuperSmoother( HP, LowerBound );

RMS = $RMS( LP, 100 );
if RMS <> 0 then
	Real = LP / RMS;

{ Imaginary component is rate of change normalized }
ROC = Real - Real[ 1 ];

QRMS = $RMS( ROC, 100 );
if QRMS <> 0 then
	Imag = ROC / QRMS;

{ Solve rate of change of arctangent }
Denom = ( ( Real - Real[ 1 ] ) * Imag ) 
 - ( ( Imag - Imag[ 1 ] ) * Real );
 
if Denom <> 0 then
	DC = 6.28 * ( ( Real * Real ) 
	 + ( Imag * Imag ) ) / Denom;

{ Limit range of measured values }
if DC < LowerBound then
	DC = LowerBound;

if DC > UpperBound then
	DC = UpperBound;

Mid = SquareRoot( LowerBound * UpperBound );

{ Create a bandpass filter at the average dominant cycle period }
HP2 = $HighPass( Close, Mid );
BP  = $UltimateSmoother( HP2, Mid );

{ Cumulate phase and force reset at 0 and 180 degrees }
Phase = Phase + ( 360 / DC );

if BP crosses over 0 then
	Phase = 180 / DC;

if BP crosses under 0 then
	Phase = 180 + ( 180 / DC );

{ Synthetic oscillator is the sine of the cumulative phase angle }
Synth = Sine( Phase );

{ Remove reset glitch if continuity falls in the same quadrant }
if Phase > 0 and Phase < 90 and Synth < Synth[ 1 ] then
	Synth = Synth[ 1 ];

if Phase > 180 and Phase < 270 and Synth > Synth[ 1 ] then
	Synth = Synth[ 1 ];

plot1( Synth );
plot2( 0 );
FIGURE 1: TRADESTATION. This demonstrates a daily chart of the S&P 500 ETF SPY with the indicator applied, showing a portion of 2025 and 2026.

This article is for informational purposes. No type of trading or investment recommendation, advice, or strategy is being made, given, or in any manner provided by TradeStation Securities or its affiliates.
These users thanked the author Banzai for the post (total 2):
RodrigoRT7, alexm

Re: Ehlers Indicators for TradeStation

15
The AutoTune Filter

Links: |TradingView|TradeStation|NinjaTrader|MT4|MT5|Python|

TradeStation: May 2026
In “The AutoTune Filter” in this issue, John Ehlers presents an adaptive filter that measures dominant market cycles using rolling autocorrelation, then tunes a bandpass filter to produce smoother, more consistent mean-reversion signals with reduced phase distortion. The tuned bandpass output highlights peaks and troughs that can help identify potential market turning points. In the EasyLanguage code, plots 3, 4, and 5 of the charts have been commented out but can be enabled to display additional values, including the minimum autocorrelation used in cycle detection, resulting dominant cycle length, and tuned bandpass output.

Code: Select all

Function: $HighPass

{
	$HighPass Function
 	(C) 2004-2024 John F. Ehlers
}

inputs:
	Price(numericseries),
	Period(numericsimple);
	
variables:
	a1( 0 ),
	b1( 0 ),
	c1( 0 ),
	c2( 0 ),
	c3( 0 );

a1 = ExpValue(-1.414 * 3.14159 / Period);
b1 = 2 * a1 * Cosine(1.414 * 180 / Period);
c2 = b1;
c3 = -a1 * a1;
c1 = (1 + c2 - c3) / 4;

if CurrentBar >= 4 then 
 	$HighPass = c1*(Price - 2 * Price[1] + Price[2]) +
	 c2 * $HighPass[1] + c3 * $HighPass[2];
if Currentbar < 4 then 
	$HighPass = 0;


Function: $BandPass

{
	Bandpass Function
	(C) 2005 - 2022 John F. Ehlers
}

inputs:
	Price( NumericSeries ),
	Period( NumericSimple ),
	Bandwidth( NumericSimple );

variables:
	G1( 0 ),
	S1( 0 ),
	L1( 0 ),
	BP( 0 );

L1 = Cosine( 360 / Period );
G1 = Cosine( Bandwidth * 360 / Period );
S1 = 1 / G1 - SquareRoot( 1 / (G1 * G1) - 1 );
BP = .5 * (1 - S1) * (Price - Price[2]) 
 + L1 * (1 + S1) * BP[1] - S1 * BP[2];

if CurrentBar < 3 then
begin
	BP = 0;
end;

$BandPass = BP;

Indicator: AutoTune

{
	TASC MAY 2026
	AutoTune Indicator
	(C) 2025 John F. Ehlers
}

inputs:
	Window( 20 );

variables:
	Filt( 0 ),
	Lag( 0 ),
	J( 0 ),
	Sx( 0 ),
	Sy( 0 ),
	Sxx( 0 ),
	Sxy( 0 ),
	Syy( 0 ),
	X( 0 ),
	Y( 0 ),
	MinCorr( 0 ),
	DC( 0 ),
	BP( 0 );

arrays:
	Corr[100]( 0 );

Filt = $Highpass( Close, Window );
//Cycle test waveform
//Filt = Sine(360*CurrentBar / 20);
 
//>>>>>>>>> Correlation >>>>>>>>>>>>
for Lag = 1 to Window
begin
	Sx = 0;
	Sy = 0;
	Sxx = 0;
	Sxy = 0;
	Syy = 0;

	for J = 0 to Window - 1
	begin
		X = Filt[J];
		Y = Filt[Lag + J];
		Sx = Sx + X;
		Sy = Sy + Y;
		Sxx = Sxx + X * X;
		Sxy = Sxy + X * Y;
		Syy = Syy + Y * Y;
	end;

	if (Window * Sxx - Sx * Sx > 0) and( Window * Syy -
	Sy * Sy > 0) then Corr[Lag] = (Window * Sxy - Sx * Sy) /
	SquareRoot( (Window * Sxx - Sx * Sx)
	* (Window * Syy - Sy * Sy));
end;

//Find minimum correlation and Dominant Cycle
MinCorr = 1;

for Lag = 1 to Window
begin
	if Corr[Lag] < MinCorr then
	begin
		MinCorr = Corr[Lag];
		DC = 2 * Lag;
	end;
end;

if DC > DC[1] + 2 then
begin
	DC = DC[1] + 2;
end;

if DC < DC[1] - 2 then
begin
	DC = DC[1] - 2;
end;

BP = $Bandpass( Close, DC, .25 );

Plot1( 0, "Zero Line" );
Plot2( Filt, "Filt" );

//Plot3(MinCorr, "", blue, 4, 4);
//Plot4(DC, "", blue, 4, 4);
//Plot5(BP, "", blue, 4, 4);

Strategy: AutoTune Pro Forma

{
AutoTune Pro Forma Strategy
(C) 2025 John F. Ehlers
}
inputs:
	BegDate( 1090101 ),
	EndDate( 1251231 ),
	Window( 26 ),
	BW( .22 ),
	Thresh( -.22 ),
	Delay( 0 );

variables:
	Filt( 0 ),
	Lag( 0 ),
	J( 0 ),
	Sx( 0 ),
	Sy( 0 ),
	Sxx( 0 ),
	Sxy( 0 ),
	Syy( 0 ),
	X( 0 ),
	Y( 0 ),
	MinCorr( 0 ),
	DC( 0 ),
	BP( 0 ),
	ROC( 0 );

arrays:
	Corr[100]( 0 );

Filt = $Highpass( Close, Window );

//>>>>>>>>> Correlation >>>>>>>>>>>>
for Lag = 1 to Window
begin
	Sx = 0;
	Sy = 0;
	Sxx = 0;
	Sxy = 0;
	Syy = 0;

	for J = 0 to Window - 1
	begin
		X = Filt[J];
		Y = Filt[Lag + J];
		Sx = Sx + X;
		Sy = Sy + Y;
		Sxx = Sxx + X * X;
		Sxy = Sxy + X * Y;
		Syy = Syy + Y * Y;
	end;

	if (Window * Sxx - Sx * Sx > 0) 
	 and (Window * Syy - Sy * Sy > 0) then 
	 	Corr[Lag] = (Window * Sxy - Sx * Sy) /
	 	 SquareRoot( (Window * Sxx - Sx * Sx) 
	 	 * (Window * Syy - Sy * Sy) );
end;

//Find minimum correlation and Dominant Cycle
MinCorr = 1;

for Lag = 1 to Window
begin
	if Corr[Lag] < MinCorr then
	begin
		MinCorr = Corr[Lag];
		DC = 2 * Lag;
	end;
end;

if DC > DC[1] + 2 then
begin
	DC = DC[1] + 2;
end;

if DC < DC[1] - 2 then
begin
	DC = DC[1] - 2;
end;

BP = $Bandpass( Close, DC, BW );
ROC = BP - BP[2];

if ROC crosses over 0 and MinCorr < Thresh then
	Buy Next Bar On Open;

if ROC crosses under 0 and MinCorr < Thresh and Filt > 0 then 
	Sell Short Next Bar on Open;
A sample chart is shown in Figure 1. FIGURE 1: TRADESTATION. Demonstrated here is a daily chart of the emini S&P 500 continuous futures contract showing a portion of 2020 with the indicator and strategy applied.

This article is for informational purposes. No type of trading or investment recommendation, advice, or strategy is being made, given, or in any manner provided by TradeStation Securities or its affiliates.
These users thanked the author Banzai for the post:
RodrigoRT7