Ehlers Ultimate Smoother For ThinkOrSwim

bigboss

Active member
In the April 2024 issue of TASC, John Ehler's introduces The Ultimate Smoother.


The UltimateSmoother is designed to retain low-frequency swings while reducing high-frequency noise in the input time series. Its critical period parameter determines the shortest wavelength that remains in the filter's pass band. This means it suppresses oscillations shorter than the critical period.

Ehlers highlights that the UltimateSmoother offers zero lag in its pass band and minimal lag in its transition band, setting it apart from traditional digital filters like moving averages. It's versatile and can be applied to different input data series, including other indicators.

Code for various platforms was posted on their website here however it did not have a thinkscript version. I always enjoy examining Ehler's math so I took the opportunity to create a thinkscript version, posted below.

Update 4/13/2024:
Version 3.0
- Added Ehlers Ultimate Bands and Ultimate Channels

Update 3/31/2024:

Version 2.0
- Added option to displace
- Added option to show signals on concavity or direction change
- Added option to color bars based on concavity or direction
- Reworked ultimate smoother into a script
- Changed CompoundValue to default to src instead of Double.NaN

Screenshot 2024-03-17 184925.png



Code:
# FOXHOUND
# Ehler's Ultimate Smoother, Ultimate Channels, and Ultimate Bands
# by bigboss
#
# https://www.traders.com/Documentation/FEEDbk_docs/2024/04/TradersTips.html
# https://www.traders.com/Documentation/FEEDbk_docs/2024/05/TradersTips.html
#
# Version 3.0
#     - Added Ehlers Ultimate Bands and Ultimate Channels
#
# Version 2.0
#     - Added option to displace
#     - Added option to show signals on concavity or direction change
#     - Added option to color bars based on concavity or direction
#     - Reworked ultimate smoother into a script
#     - Changed CompoundValue to default to src instead of Double.NaN
#
# Version 1.0
#     - Initial Release

input src = close;
input length = 20;
input displace = 0;
input showSignals = no;
input signalSource = {default direction, concavity};
input paintbars = no;
input bandMode = {default off, bands, channel};
input bandLength = 20;
input bandWidth = 1.0;
input bandCloud = yes;

script us{
    input src = close;
    input length = 20;
    input displace = 0;
    def a1 = Exp(-Sqrt(2) * Double.Pi / length);
    def c2 = 2.0 * a1 * Cos(Sqrt(2) * Double.Pi / length);
    def c3 = -a1 * a1;
    def c1 = (1.0 + c2 - c3) / 4.0;
    def us =
        (1.0 - c1) * src +
        (2.0 * c1 - c2) * src[1] -
        (c1 + c3) * src[2] +
         c2 * us[1] + c3 * us[2];

    plot ultimate = CompoundValue(4,us[-displace],src);
}

plot ultimate = us(src,length,displace);
ultimate.SetLineWeight(2);

def width =
    if bandMode == bandMode.Channel then
        us(TrueRange(high,close,low),bandLength,displace)
    else if bandMode == bandMode.Bands then
        StDev(src-ultimate,bandLength)
    else
        0;

plot upperBand = if bandMode != bandMode.off then ultimate + width * bandwidth else Double.NaN;
upperBand.SetDefaultColor(Color.LIGHT_GRAY);

plot lowerBand = if bandMode != bandMode.off then ultimate - width * bandwidth else Double.NaN;
lowerBand.SetDefaultColor(Color.LIGHT_GRAY);

AddCloud( if bandCloud then upperBand else Double.NaN, if bandCloud then lowerBand else Double.NaN,Color.Dark_Gray);

def direction = if ultimate[1] < ultimate then 1 else -1;
def slope = reference LinearRegressionSlope(ultimate, 2);
def concavity = if slope  > slope [1] then 1 else -1;

ultimate.AssignValueColor(
                if signalSource == signalSource.concavity then
                      if concavity > 0 and direction > 0 then Color.GREEN else
                      if concavity > 0 and direction < 0 then Color.DARK_GREEN else
                      if concavity < 0 and direction < 0 then Color.RED else
                      Color.DARK_ORANGE
                else
                      if direction > 0 then Color.LIGHT_GREEN
                      else Color.LIGHT_RED
);   

def ss = if signalSource == signalSource.concavity then concavity else direction;

AssignPriceColor(if !paintbars then Color.CURRENT else if ss > 0 then Color.GREEN else Color.RED);

plot buy = if ss[1] < 0 and ss > 0 and showSignals then ultimate else Double.NaN;
buy.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
buy.HideBubble();
buy.HideTitle();
buy.SetDefaultColor(Color.GREEN);
buy.SetLineWeight(3);

plot sell = if ss[1] > 0 and ss < 0 and showSignals then ultimate else Double.NaN;
sell.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
sell.HideBubble();
sell.HideTitle();
sell.SetDefaultColor(Color.RED);
sell.SetLineWeight(3);

Here is code for Ultimate Smoother Convergence Divergence (USCD)

1711926576984.png



Code:
# FOXHOUND
# Ehler's Ultimate Smoother Convergence Divergence (USCD)
# by bigboss
# https://www.traders.com/Documentation/FEEDbk_docs/2024/04/TradersTips.html

declare lower;

input fastLength = 12;
input slowLength = 26;
input USCDLength = 9;
input src = close;
input showBreakoutSignals = no;

script us{
    input src = close;
    input length = 20;
    def a1 = Exp(-Sqrt(2) * Double.Pi / length);
    def c2 = 2.0 * a1 * Cos(Sqrt(2) * Double.Pi / length);
    def c3 = -a1 * a1;
    def c1 = (1.0 + c2 - c3) / 4.0;
    def us =
        (1.0 - c1) * src +
        (2.0 * c1 - c2) * src[1] -
        (c1 + c3) * src[2] +
         c2 * us[1] + c3 * us[2];

    plot ultimate = CompoundValue(4,us,src);
}

plot Value = us(close, fastLength) - us(close, slowLength);
plot Avg = us(Value, USCDLength);

plot Diff = Value - Avg;
plot ZeroLine = 0;

plot UpSignal = if Diff crosses above ZeroLine then ZeroLine else Double.NaN;
plot DownSignal = if Diff crosses below ZeroLine then ZeroLine else Double.NaN;

UpSignal.SetHiding(!showBreakoutSignals);
DownSignal.SetHiding(!showBreakoutSignals);

Value.SetDefaultColor(GetColor(1));
Avg.SetDefaultColor(GetColor(8));
Diff.SetDefaultColor(GetColor(5));
Diff.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
Diff.SetLineWeight(3);
Diff.DefineColor("Positive and Up", Color.GREEN);
Diff.DefineColor("Positive and Down", Color.DARK_GREEN);
Diff.DefineColor("Negative and Down", Color.RED);
Diff.DefineColor("Negative and Up", Color.DARK_RED);
Diff.AssignValueColor(if Diff >= 0 then if Diff > Diff[1] then Diff.color("Positive and Up") else Diff.color("Positive and Down") else if Diff < Diff[1] then Diff.color("Negative and Down") else Diff.color("Negative and Up"));
ZeroLine.SetDefaultColor(GetColor(0));
UpSignal.SetDefaultColor(Color.UPTICK);
UpSignal.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
DownSignal.SetDefaultColor(Color.DOWNTICK);
DownSignal.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
 
Last edited:

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Ehlers Guppy MMA V2.gif
this is a version that allows for a displacement or shift ,
Code:
# FOXHOUND
# Ehler's Ultimate Smoother
# by bigboss
# https://www.traders.com/Documentation/FEEDbk_docs/2024/04/TradersTips.html

input src = close;
input Displace = 0;

def a1a = Exp(-1.414 * Double.Pi / 5);
def c2a = 2.0 * a1a * Cos(Sqrt(2) * Double.Pi / 5);
def c3a = -a1a * a1a;
def c1a = (1.0 + c2a - c3a) / 4.0;
def usa =
    (1.0 - c1a) * src +
    (2.0 * c1a - c2a) * src[1] -
    (c1a + c3a) * src[2] +
     c2a * usa[1] + c3a * usa[2];
def a1b = Exp(-1.414 * Double.Pi / 8);
def c2b = 2.0 * a1b * Cos(Sqrt(2) * Double.Pi / 8);
def c3b = -a1b * a1b;
def c1b = (1.0 + c2b - c3b) / 4.0;
def usb =
    (1.0 - c1b) * src +
    (2.0 * c1b - c2b) * src[1] -
    (c1b + c3b) * src[2] +
     c2b * usb[1] + c3b * usb[2];
def a1c = Exp(-1.414 * Double.Pi / 10);
def c2c = 2.0 * a1c * Cos(Sqrt(2) * Double.Pi / 10);
def c3c = -a1c * a1c;
def c1c = (1.0 + c2c - c3c) / 4.0;
def usc =
    (1.0 - c1c) * src +
    (2.0 * c1c - c2c) * src[1] -
    (c1c + c3c) * src[2] +
     c2c * usc[1] + c3c * usc[2];
def a1d = Exp(-1.414 * Double.Pi / 13);
def c2d = 2.0 * a1d * Cos(Sqrt(2) * Double.Pi / 13);
def c3d = -a1d * a1d;
def c1d = (1.0 + c2d - c3d) / 4.0;
def usd =
    (1.0 - c1d) * src +
    (2.0 * c1d - c2d) * src[1] -
    (c1d + c3d) * src[2] +
     c2d * usd[1] + c3d * usd[2];
def a1e = Exp(-1.414 * Double.Pi / 15);
def c2e = 2.0 * a1e * Cos(Sqrt(2) * Double.Pi / 15);
def c3e = -a1e * a1e;
def c1e = (1.0 + c2e - c3e) / 4.0;
def use =
    (1.0 - c1e) * src +
    (2.0 * c1e - c2e) * src[1] -
    (c1e + c3e) * src[2] +
     c2e * use[1] + c3e * use[2];
def a1f = Exp(-1.414 * Double.Pi / 30);
def c2f = 2.0 * a1f * Cos(Sqrt(2) * Double.Pi / 30);
def c3f = -a1f * a1f;
def c1f = (1.0 + c2f - c3f) / 4.0;
def usf =
    (1.0 - c1f) * src +
    (2.0 * c1f - c2f) * src[1] -
    (c1f + c3f) * src[2] +
     c2f * usf[1] + c3f * usf[2];
def a1g = Exp(-1.414 * Double.Pi / 35);
def c2g = 2.0 * a1g * Cos(Sqrt(2) * Double.Pi / 35);
def c3g = -a1g * a1g;
def c1g = (1.0 + c2g - c3g) / 4.0;
def usg =
    (1.0 - c1g) * src +
    (2.0 * c1g - c2g) * src[1] -
    (c1g + c3g) * src[2] +
     c2g * usg[1] + c3g * usg[2];
def a1h = Exp(-1.414 * Double.Pi / 40);
def c2h = 2.0 * a1h * Cos(Sqrt(2) * Double.Pi / 40);
def c3h = -a1h * a1h;
def c1h = (1.0 + c2h - c3h) / 4.0;
def ush =
    (1.0 - c1h) * src +
    (2.0 * c1h - c2h) * src[1] -
    (c1h + c3h) * src[2] +
     c2h * ush[1] + c3h * ush[2];
def a1i = Exp(-1.414 * Double.Pi / 45);
def c2i = 2.0 * a1i * Cos(Sqrt(2) * Double.Pi / 45);
def c3i = -a1i * a1i;
def c1i = (1.0 + c2i - c3i) / 4.0;
def usi =
    (1.0 - c1i) * src +
    (2.0 * c1i - c2i) * src[1] -
    (c1i + c3i) * src[2] +
     c2i * usi[1] + c3i * usi[2];
def a1j = Exp(-1.414 * Double.Pi / 50);
def c2j = 2.0 * a1j * Cos(Sqrt(2) * Double.Pi / 50);
def c3j = -a1j * a1j;
def c1j = (1.0 + c2j - c3j) / 4.0;
def usj =
    (1.0 - c1j) * src +
    (2.0 * c1j - c2j) * src[1] -
    (c1j + c3j) * src[2] +
     c2j * usj[1] + c3j * usj[2];
plot ultimatea = CompoundValue(4,usa[displace],Double.NaN);
plot ultimateb = CompoundValue(4,usb[displace],Double.NaN);
plot ultimatec = CompoundValue(4,usc[displace],Double.NaN);
plot ultimated = CompoundValue(4,usd[displace],Double.NaN);
plot ultimatee = CompoundValue(4,use[displace],Double.NaN);
plot ultimatef = CompoundValue(4,usf[displace],Double.NaN);
plot ultimateg = CompoundValue(4,usg[displace],Double.NaN);
plot ultimateh = CompoundValue(4,ush[displace],Double.NaN);
plot ultimatei = CompoundValue(4,usi[displace],Double.NaN);
plot ultimatej = CompoundValue(4,usj[displace],Double.NaN);
ultimateA.AssignValueColor(If UltimateA >= UltimateA[1] then Color.Cyan else Color.Blue);
ultimateB.AssignValueColor(If UltimateB >= UltimateB[1] then Color.Cyan else Color.Blue);
ultimateC.AssignValueColor(If UltimateC >= UltimateC[1] then Color.Cyan else Color.Blue);
ultimateD.AssignValueColor(If UltimateD >= UltimateD[1] then Color.Cyan else Color.Blue);
ultimateE.AssignValueColor(If UltimateE >= UltimateE[1] then Color.Cyan else Color.Blue);
AddCloud(UltimateA,UltimateE,Color.Cyan,Color.Blue);
ultimateF.AssignValueColor(If UltimateF >= UltimateF[1] then Color.Magenta else Color.Red);
ultimateG.AssignValueColor(If UltimateG >= UltimateG[1] then Color.Magenta else Color.Red);
ultimateH.AssignValueColor(If UltimateH >= UltimateH[1] then Color.Magenta else Color.Red);
ultimateI.AssignValueColor(If UltimateI >= UltimateI[1] then Color.Magenta else Color.Red);
ultimateJ.AssignValueColor(If UltimateJ >= UltimateJ[1] then Color.Magenta else Color.Red);
AddCloud(UltimateF,UltimateJ,Color.Magenta,Color.Red);
along with multiple Ma's
 
Last edited:
@henry1224 Please help everyone keep up with what iteration this is.
Common practice is to put the bold below the above header as shown. Thank you!
# FOXHOUND
# Ehler's Ultimate Smoother
# by bigboss
# https://www.traders.com/Documentation/FEEDbk_docs/2024/04/TradersTips.html
# V1.0 Henry1224 3-20-2024 - Added MA's & Displacement/Shift.

BTW, nice iteration!

If all would do this, we would know how far we are from the original and additions deletions, etc.
Thanks!

@MerryDay & @BenTen, is this still proper protocol?
 
View attachment 21392this is a version that allows for a displacement or shift ,
Code:
# FOXHOUND
# Ehler's Ultimate Smoother
# by bigboss
# https://www.traders.com/Documentation/FEEDbk_docs/2024/04/TradersTips.html

input src = close;
input Displace = 0;

def a1a = Exp(-1.414 * Double.Pi / 5);
def c2a = 2.0 * a1a * Cos(Sqrt(2) * Double.Pi / 5);
def c3a = -a1a * a1a;
def c1a = (1.0 + c2a - c3a) / 4.0;
def usa =
    (1.0 - c1a) * src +
    (2.0 * c1a - c2a) * src[1] -
    (c1a + c3a) * src[2] +
     c2a * usa[1] + c3a * usa[2];
def a1b = Exp(-1.414 * Double.Pi / 8);
def c2b = 2.0 * a1b * Cos(Sqrt(2) * Double.Pi / 8);
def c3b = -a1b * a1b;
def c1b = (1.0 + c2b - c3b) / 4.0;
def usb =
    (1.0 - c1b) * src +
    (2.0 * c1b - c2b) * src[1] -
    (c1b + c3b) * src[2] +
     c2b * usb[1] + c3b * usb[2];
def a1c = Exp(-1.414 * Double.Pi / 10);
def c2c = 2.0 * a1c * Cos(Sqrt(2) * Double.Pi / 10);
def c3c = -a1c * a1c;
def c1c = (1.0 + c2c - c3c) / 4.0;
def usc =
    (1.0 - c1c) * src +
    (2.0 * c1c - c2c) * src[1] -
    (c1c + c3c) * src[2] +
     c2c * usc[1] + c3c * usc[2];
def a1d = Exp(-1.414 * Double.Pi / 13);
def c2d = 2.0 * a1d * Cos(Sqrt(2) * Double.Pi / 13);
def c3d = -a1d * a1d;
def c1d = (1.0 + c2d - c3d) / 4.0;
def usd =
    (1.0 - c1d) * src +
    (2.0 * c1d - c2d) * src[1] -
    (c1d + c3d) * src[2] +
     c2d * usd[1] + c3d * usd[2];
def a1e = Exp(-1.414 * Double.Pi / 15);
def c2e = 2.0 * a1e * Cos(Sqrt(2) * Double.Pi / 15);
def c3e = -a1e * a1e;
def c1e = (1.0 + c2e - c3e) / 4.0;
def use =
    (1.0 - c1e) * src +
    (2.0 * c1e - c2e) * src[1] -
    (c1e + c3e) * src[2] +
     c2e * use[1] + c3e * use[2];
def a1f = Exp(-1.414 * Double.Pi / 30);
def c2f = 2.0 * a1f * Cos(Sqrt(2) * Double.Pi / 30);
def c3f = -a1f * a1f;
def c1f = (1.0 + c2f - c3f) / 4.0;
def usf =
    (1.0 - c1f) * src +
    (2.0 * c1f - c2f) * src[1] -
    (c1f + c3f) * src[2] +
     c2f * usf[1] + c3f * usf[2];
def a1g = Exp(-1.414 * Double.Pi / 35);
def c2g = 2.0 * a1g * Cos(Sqrt(2) * Double.Pi / 35);
def c3g = -a1g * a1g;
def c1g = (1.0 + c2g - c3g) / 4.0;
def usg =
    (1.0 - c1g) * src +
    (2.0 * c1g - c2g) * src[1] -
    (c1g + c3g) * src[2] +
     c2g * usg[1] + c3g * usg[2];
def a1h = Exp(-1.414 * Double.Pi / 40);
def c2h = 2.0 * a1h * Cos(Sqrt(2) * Double.Pi / 40);
def c3h = -a1h * a1h;
def c1h = (1.0 + c2h - c3h) / 4.0;
def ush =
    (1.0 - c1h) * src +
    (2.0 * c1h - c2h) * src[1] -
    (c1h + c3h) * src[2] +
     c2h * ush[1] + c3h * ush[2];
def a1i = Exp(-1.414 * Double.Pi / 45);
def c2i = 2.0 * a1i * Cos(Sqrt(2) * Double.Pi / 45);
def c3i = -a1i * a1i;
def c1i = (1.0 + c2i - c3i) / 4.0;
def usi =
    (1.0 - c1i) * src +
    (2.0 * c1i - c2i) * src[1] -
    (c1i + c3i) * src[2] +
     c2i * usi[1] + c3i * usi[2];
def a1j = Exp(-1.414 * Double.Pi / 50);
def c2j = 2.0 * a1j * Cos(Sqrt(2) * Double.Pi / 50);
def c3j = -a1j * a1j;
def c1j = (1.0 + c2j - c3j) / 4.0;
def usj =
    (1.0 - c1j) * src +
    (2.0 * c1j - c2j) * src[1] -
    (c1j + c3j) * src[2] +
     c2j * usj[1] + c3j * usj[2];
plot ultimatea = CompoundValue(4,usa[displace],Double.NaN);
plot ultimateb = CompoundValue(4,usb[displace],Double.NaN);
plot ultimatec = CompoundValue(4,usc[displace],Double.NaN);
plot ultimated = CompoundValue(4,usd[displace],Double.NaN);
plot ultimatee = CompoundValue(4,use[displace],Double.NaN);
plot ultimatef = CompoundValue(4,usf[displace],Double.NaN);
plot ultimateg = CompoundValue(4,usg[displace],Double.NaN);
plot ultimateh = CompoundValue(4,ush[displace],Double.NaN);
plot ultimatei = CompoundValue(4,usi[displace],Double.NaN);
plot ultimatej = CompoundValue(4,usj[displace],Double.NaN);
ultimateA.AssignValueColor(If UltimateA >= UltimateA[1] then Color.Cyan else Color.Blue);
ultimateB.AssignValueColor(If UltimateB >= UltimateB[1] then Color.Cyan else Color.Blue);
ultimateC.AssignValueColor(If UltimateC >= UltimateC[1] then Color.Cyan else Color.Blue);
ultimateD.AssignValueColor(If UltimateD >= UltimateD[1] then Color.Cyan else Color.Blue);
ultimateE.AssignValueColor(If UltimateE >= UltimateE[1] then Color.Cyan else Color.Blue);
AddCloud(UltimateA,UltimateE,Color.Cyan,Color.Blue);
ultimateF.AssignValueColor(If UltimateF >= UltimateF[1] then Color.Magenta else Color.Red);
ultimateG.AssignValueColor(If UltimateG >= UltimateG[1] then Color.Magenta else Color.Red);
ultimateH.AssignValueColor(If UltimateH >= UltimateH[1] then Color.Magenta else Color.Red);
ultimateI.AssignValueColor(If UltimateI >= UltimateI[1] then Color.Magenta else Color.Red);
ultimateJ.AssignValueColor(If UltimateJ >= UltimateJ[1] then Color.Magenta else Color.Red);
AddCloud(UltimateF,UltimateJ,Color.Magenta,Color.Red);
along with multiple Ma's
I find very interesting implementation as a Duffy style but as a suggestion, could you create a function for the ma and then just use it along the desired script? it would look more compact and clean. maybe allow for optional lengths?
 
I find very interesting implementation as a Duffy style but as a suggestion, could you create a function for the ma and then just use it along the desired script? it would look more compact and clean. maybe allow for optional lengths?
it's an iteration of the Guppy MMA. Because the first two lines establish the various variables of each line, you can not just create an indicator and apply a different length to that line.

IE: Variables A1A and C2A are different than A1B and C2B, so the rest of the variables are different values than the others,

The formula can't be shortened for the 10 lines!
You can use 4 of the lines Ultimate A, UltimateE, UltimateF, and UltimateJ and color the cloud which is in the formula
 
Ehlers, in its TASC paper, concludes that the Ultimate Smoother could be used as a replacement for the Exponential Moving Averages and maybe also for the Simple.

I am curious to check how a US based MACD would compare to a standard one. The easiest way would be to have US as a function to be called instead of the moving average.

Does someone here please rework the indicator so as the script uses a US function instead of the explicit form?
 
Ehlers, in its TASC paper, concludes that the Ultimate Smoother could be used as a replacement for the Exponential Moving Averages and maybe also for the Simple.

I am curious to check how a US based MACD would compare to a standard one. The easiest way would be to have US as a function to be called instead of the moving average.

Does someone here please rework the indicator so as the script uses a US function instead of the explicit form?
Here is a US based MACD, with the US code reworked as a script. The screenshot shows USCD vs MACD. The lengths probably need adjusting. At equivalent settings you can see USCD signals earlier with the tradeoff of more false positives.

1711925123192.png


Code:
# FOXHOUND
# Ehler's Ultimate Smoother Convergence Divergence (USCD)
# by bigboss
# https://www.traders.com/Documentation/FEEDbk_docs/2024/04/TradersTips.html

declare lower;

input fastLength = 12;
input slowLength = 26;
input USCDLength = 9;
input src = close;
input showBreakoutSignals = no;

script us{
    input src = close;
    input length = 20;
    def a1 = Exp(-Sqrt(2) * Double.Pi / length);
    def c2 = 2.0 * a1 * Cos(Sqrt(2) * Double.Pi / length);
    def c3 = -a1 * a1;
    def c1 = (1.0 + c2 - c3) / 4.0;
    def us =
        (1.0 - c1) * src +
        (2.0 * c1 - c2) * src[1] -
        (c1 + c3) * src[2] +
         c2 * us[1] + c3 * us[2];

    plot ultimate = CompoundValue(4,us,src);
}

plot Value = us(close, fastLength) - us(close, slowLength);
plot Avg = us(Value, USCDLength);

plot Diff = Value - Avg;
plot ZeroLine = 0;

plot UpSignal = if Diff crosses above ZeroLine then ZeroLine else Double.NaN;
plot DownSignal = if Diff crosses below ZeroLine then ZeroLine else Double.NaN;

UpSignal.SetHiding(!showBreakoutSignals);
DownSignal.SetHiding(!showBreakoutSignals);

Value.SetDefaultColor(GetColor(1));
Avg.SetDefaultColor(GetColor(8));
Diff.SetDefaultColor(GetColor(5));
Diff.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
Diff.SetLineWeight(3);
Diff.DefineColor("Positive and Up", Color.GREEN);
Diff.DefineColor("Positive and Down", Color.DARK_GREEN);
Diff.DefineColor("Negative and Down", Color.RED);
Diff.DefineColor("Negative and Up", Color.DARK_RED);
Diff.AssignValueColor(if Diff >= 0 then if Diff > Diff[1] then Diff.color("Positive and Up") else Diff.color("Positive and Down") else if Diff < Diff[1] then Diff.color("Negative and Down") else Diff.color("Negative and Up"));
ZeroLine.SetDefaultColor(GetColor(0));
UpSignal.SetDefaultColor(Color.UPTICK);
UpSignal.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
DownSignal.SetDefaultColor(Color.DOWNTICK);
DownSignal.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
 
Last edited:
Here is a US based MACD, with the US code reworked as a script. The screenshot shows USCD vs MACD. The lengths probably need adjusting. At equivalent settings you can see USCD signals earlier with the tradeoff of more false positives.

View attachment 21477

Code:
# FOXHOUND
# Ehler's Ultimate Smoother Convergence Divergence (USCD)
# by bigboss
# https://www.traders.com/Documentation/FEEDbk_docs/2024/04/TradersTips.html

declare lower;

input fastLength = 12;
input slowLength = 26;
input USCDLength = 9;
input src = close;
input showBreakoutSignals = no;

script us{
    input src = close;
    input length = 20;
    def a1 = Exp(-Sqrt(2) * Double.Pi / length);
    def c2 = 2.0 * a1 * Cos(Sqrt(2) * Double.Pi / length);
    def c3 = -a1 * a1;
    def c1 = (1.0 + c2 - c3) / 4.0;
    def us =
        (1.0 - c1) * src +
        (2.0 * c1 - c2) * src[1] -
        (c1 + c3) * src[2] +
         c2 * us[1] + c3 * us[2];

    plot ultimate = CompoundValue(4,us,src);
}

plot Value = us(close, fastLength) - us(close, slowLength);
plot Avg = us(Value, USCDLength);

plot Diff = Value - Avg;
plot ZeroLine = 0;

plot UpSignal = if Diff crosses above ZeroLine then ZeroLine else Double.NaN;
plot DownSignal = if Diff crosses below ZeroLine then ZeroLine else Double.NaN;

UpSignal.SetHiding(!showBreakoutSignals);
DownSignal.SetHiding(!showBreakoutSignals);

Value.SetDefaultColor(GetColor(1));
Avg.SetDefaultColor(GetColor(8));
Diff.SetDefaultColor(GetColor(5));
Diff.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
Diff.SetLineWeight(3);
Diff.DefineColor("Positive and Up", Color.GREEN);
Diff.DefineColor("Positive and Down", Color.DARK_GREEN);
Diff.DefineColor("Negative and Down", Color.RED);
Diff.DefineColor("Negative and Up", Color.DARK_RED);
Diff.AssignValueColor(if Diff >= 0 then if Diff > Diff[1] then Diff.color("Positive and Up") else Diff.color("Positive and Down") else if Diff < Diff[1] then Diff.color("Negative and Down") else Diff.color("Negative and Up"));
ZeroLine.SetDefaultColor(GetColor(0));
UpSignal.SetDefaultColor(Color.UPTICK);
UpSignal.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
DownSignal.SetDefaultColor(Color.DOWNTICK);
DownSignal.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
@bigboss , thanks a lot for this. sure it looks that using different lengths might be more appropriate to be practical, but this is just the first step in an interesting path to be explored further. thanks again 👋 👋

ps: just as a reminder, the idea on the length used in US is related to the maximum frequency of the signal (price) that will not be excluded from the US output. I presume in the future, an adaptive length US will be created, to define dynamically the length of US depending on the original signal most relevant frequency range. and I will not be surprised if someone would further use AI to guess what is the next period relevant frequency range to which US length needs to be adapted.
 
Last edited:
Ehlers is back in May's issue of TASC with Ehler's Ultimate Bands and Channels. These use the Ultimate Smoother function in creating Bollinger Bands and Keltner Channels . I've updated the code in the OP to add the Bands and Channels. The channel looks workable at the default 1.0 width but I'd up it to 2.0 for the bands.
 
I noticed that you use c2 = 2.0 * a1 * Cos(Sqrt(2) * Double.Pi / length);. However, in TASC, Ehler's lists the proper script as c2 = 2*a1*Cosine(1.414*180 / Period). Why change 180 to double.pi?
 
I noticed that you use c2 = 2.0 * a1 * Cos(Sqrt(2) * Double.Pi / length);. However, in TASC, Ehler's lists the proper script as c2 = 2*a1*Cosine(1.414*180 / Period). Why change 180 to double.pi?
it looks it depends on how each script cosine reads its inputs. from what can be deduced here, thinkscript considers degrees, while the one in TASC radians. it happens even to the best of families.
 
it looks it depends on how each script cosine reads its inputs. from what can be deduced here, thinkscript considers degrees, while the one in TASC radians. it happens even to the best of families.
I believe pinescript uses radians for trig functions. tradestation easylanguage, what Ehlers writes codes for, uses degrees.
 
Last edited:

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
538 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top