Adaptive True Momentum Oscillator for ThinkOrSwim

Sesqui

Member
VIP
I took the liberty of modifying the popular True Momentum Oscillator by Mobius by making it adaptive to the dominant market cycle. The dominant cycle in this variant is obtained using the band-pass filter method per Chapter 5 of Dr Ehlers' book, "Cycle Analytics for Traders". Here is a screen shot showing the adaptive TMO as the bottom indicator immediately above the volume indicator. Another modification involves replacing the EMA used to smooth the main and signal lines with EhlersSuperSmootherFilter. The SmootherLength is currently set to 6, but can be changed in the properties dialog. Hopefully this adaptive variant will be the game changer you are looking for. Enjoy!

PS - This variant has a property that lets you set the aggregation to a higher time frame. You might consider placing two instances of the indicator on top of each other and setting one to a higher time frame in order to see confluence in the True Momentum Oscillator between the time frames.

1760827295139.png



Here is the script for the adaptive True Momentum Oscillator (ATMO):

CSS:
# TMO ((T)rue (M)omentum (O)scilator)
# Mobius
# V01.05.2018
# 
# [18OCT2025]: Modified by Sesqui to make it adaptive to Market Dominant Cycle via BandpassFilter Filter method per CH 5 Dr Ehlers' book, "Cycle Analytics for Traders" The band-pass filter method obtained precise dominant cycle values when tested # against sinewaves across a wide range of period values.
#
# [26OCT2025]: Modified the bar counting method to enable to to also work on Tick charts. Also inserted a series High=Pass
#filter per CH 5 method to prevent spectral dilation. 

# hint: TMO calculates momentum using the delta of price. Giving a much better picture of trend, tend reversals and divergence than momentum oscillators using price.

declare lower;
input SuperSmootherLength = 6;
input BandPassSource = ohlc4;
input BandPassPeriod = 20;
input BandPassBandwidth = 0.70;

#===================================================================================
#*** Script to measure dominant cycle length using band-pass 
#*** filter zero crossings method per Dr Ehlers
#*** Chapter 5 of "Cycle Analytics for Traders".  
Script GetCycleViaBandPassFilter{
    input source = ohlc4;
    input period = 20;
    input bandwidth = 0.7;
    
    #*** HPF implemented in this section
    def alpha2 = (cos(0.25*bandwidth*2*Double.Pi/Period) +     
                 sin(0.25*bandwidth*2*Double.Pi/Period)-1)
                 /cos(0.25*bandwidth*2*Double.Pi/Period);
   
    def HP = if IsNaN(HP[1]) then (1+alpha2/2)*(Close-Close[1]) else (1+alpha2/2)*(Close-Close[1]) + (1-alpha2)*HP[1];
    def beta1 = cos(2*Double.Pi/Period);
    def gamma1 = 1/ cos(2*Double.Pi*bandwidth/Period);
    def alpha1 = gamma1 - sqrt(gamma1*gamma1-1);
   
    #***The BPF is implemented in this section
    def BP = If IsNaN(BP[1]+ BP[2]) then 0.5*(1-alpha1)*(HP-HP[2]) else 0.5*(1-alpha1)*(HP-HP[2]) + beta1*(1+alpha1)*BP[1] - alpha1*BP[2]; 
    def Peak = If IsNaN(Peak[1]) then 0 else if absvalue(BP) > 0.991*Peak[1] then AbsValue(BP) else 0.991*Peak[1];
    def Signal = If Peak <>0 then BP/Peak else 0;
   
    plot BPF = Signal; # This is the Band-Pass Filter value

    #*** Computes a leading trigger signal in this section
    def alpha3 = (cos(1.5*bandwidth*2*Double.Pi/Period) +     
                 sin(1.5*bandwidth*2*Double.Pi/Period)-1)
                 /cos(1.5*bandwidth*2*Double.Pi/Period);

    def trig = If IsNaN(trig[1]) then (1-alpha3/2)*(Signal-Signal[1]) else (1-alpha3/2)*(Signal-Signal[1])+(1-alpha3)*trig[1];
    plot Trigger = trig; # This is the leading trigger
    
    #*** Computes the dominant cycle (DC) in this section by counting the 
    # number of bars between zero crossings. it counts bars between two 
    # consecutive crossings, which is half a dominant cycle then multiplies
    # by two
    def bn = barnumber();
    def zc = if BPF crosses 0 then bn else zc[1];

    # The "<3" part handles cases where the band-pass filter barely crosses
    # over the zero line and then crosses back over.
    def cnt_candles = if zc-zc[1] < 3 then cnt_candles[1] else zc-zc[1];
    def CycleLength = if cnt_candles ==0 then CycleLength[1] else 2*cnt_candles;
    
    plot DC = CycleLength;
    DC.HideBubble(); 

} # End Script GetCycleViaBandPassFilter{}
#=============================================================================
#----------------------------------------------------
script SuperSmoother {
    input price = close;
    input Cycle = 10;

    def CutOffLength = if IsNaN(Floor(Cycle)) then CutOffLength[1] else Floor(Cycle);
    def a1 = Exp(-Double.Pi * Sqrt(2) / cutoffLength);
    def coeff2 = 2 * a1 * Cos(Sqrt(2) * Double.Pi / cutoffLength);
    def coeff3 = - Sqr(a1);
    def coeff1 = 1 - coeff2 - coeff3;
    def filt = if IsNaN(filt[1]) or IsNaN(filt[2]) then 0 else if IsNaN(price + price[1]) then filt[1] else coeff1 * (price + price[1]) / 2 + coeff2 * filt[1] + coeff3 * filt[2];

    plot SuperSmootherFilter = if !IsNaN(price) then filt else Double.NaN;
    SuperSmootherFilter.HideBubble();
} # End Script SuperSmoother{}
#=============================================================================
# This section computes the indicator

def Cycle = GetCycleViaBandPassFilter(BandPassSource, BandPassPeriod, BandPassBandwidth).DC;
def length = if IsNaN(Floor(Cycle)) then length[1] else Floor(Cycle);
input calcLength = 5;
input smoothLength = 3;
input agg = AggregationPeriod.MIN;

def o = open(period = agg);
def c = close(period = agg);

def data = fold i = 0 to length
           with s
           do s + (if c > GetValue(o, i)
                   then 1
                   else if c < GetValue(o, i)
                        then - 1
                        else 0);

plot Main =  SuperSmoother(data, SuperSmootherLength); 
plot Signal = SuperSmoother(Main, SuperSmootherLength); 

Main.AssignValueColor(if Main > Signal
                           then Color.GREEN
                           else Color.RED);
Signal.AssignValueColor(if Main > Signal
                             then Color.GREEN
                             else Color.RED);
Signal.HideBubble();
Signal.HideTitle();
AddCloud(Main, Signal, Color.GREEN, Color.RED);
plot zero = if IsNaN(c) then Double.NaN else 0;
zero.SetDefaultColor(Color.GRAY);
zero.HideBubble();
zero.HideTitle();

plot ob = if IsNaN(c) then Double.NaN else Round(length * .7);
ob.SetDefaultColor(Color.GRAY);
ob.HideBubble();
ob.HideTitle();

plot os = if IsNaN(c) then Double.NaN else -Round(length * .7);
os.SetDefaultColor(Color.GRAY);
os.HideBubble();
os.HideTitle();

def b1 = 0.6 * length;
def b2 = -0.8 * length;
AddCloud(ob, b1, Color.LIGHT_RED, Color.LIGHT_RED, no);
AddCloud(b2, os, Color.LIGHT_GREEN, Color.LIGHT_GREEN);
# End Code TMO

#---------------RSI to help gage OB and OS levels---------------------------
input RSIlength = 14;
input over_Bought = 70;
input over_Sold = 30;
input RSIprice = close;
input averageType = AverageType.WILDERS;

def NetChgAvg = MovingAverage(averageType, RSIprice - RSIprice[1], RSIlength);
def TotChgAvg = MovingAverage(averageType, AbsValue(RSIprice - RSIprice[1]), RSIlength);
def ChgRatio = if TotChgAvg != 0 then NetChgAvg / TotChgAvg else 0;

def RSI = 50 * (ChgRatio + 1);

AddLabel(1, if RSI > over_Bought then "RSI OB: " + Round(RSI, 1) else if RSI < over_Sold then "RSI OS: " + Round(RSI, 1) else "RSI: " + Round(RSI, 1), if RSI > over_Bought then Color.RED else if Round(RSI, 2) < over_Sold then Color.GREEN else Color.WHITE , location = Location.TOP_RIGHT, size = FontSize.SMALL);
 
Last edited:

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

@Kluthy
I am sorry the script did not work for you. It may have been an issue with what I will call the initialization of one or more variables where for instance I found that, "def BP = If IsNaN(BP[1]+ BP[2]) ..." helps.

I have revised the script posted on this page above Adaptive True Momentum Oscillator to include handlers for that trouble spot and it should run now. Also in this revision I inserted a series High-Pass Filter per Dr Ehlers text, Ch5, "Cycle Analytics for Traders" in order to prevent the band-pass filter output from experiencing spectral dilation (makes the output more accurate and such that it is generally centered about the zero line).
Please try the code above and feel free to let me know if it still doesn't work.
 

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
710 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