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 a1 wide range of period values.

# 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.70;

    def cosPart = Cos(0.25 * Bandwidth * 2.0 * Double.Pi / Period);
    def sinPart = Sin(0.25 * Bandwidth * 2.0 * Double.Pi / Period);

    def alpha2 = (cosPart + sinPart - 1) / cosPart;
    def HP = (1 + alpha2 / 2) * (source - source[1]) + (1 - alpha2) * HP[1];

    def beta1 = Cos(2.0 * Double.Pi / Period);
    def gamma1 = 1 / Cos(2.0 * Double.Pi * Bandwidth / Period);

    def alpha1 = gamma1 - Sqrt(gamma1 * gamma1 - 1);

    def BP = if BarNumber() == 1 or BarNumber() == 2 then 0 else 0.5 * (1 - alpha1) * (HP - HP[2]) + beta1 * (1 + alpha1) * BP[1] - alpha1 * BP[2];

    def Peak = if AbsValue(BP) > 0.991 * Peak[1] then AbsValue(BP) else 0.991 * Peak[1];

    def Real = if Peak <> 0 then BP / Peak else Double.NaN;

    def crossAbove = Real crosses above 0;
    def crossBelow = Real crosses below 0;
    def zeroCrossing = crossAbove or crossBelow;

    # Uses a recursive variable to count the bars between zero-crossings
    # If a zero-crossing happens, resets the count to 0.
    # Otherwise, increments the count by 1.
    def barsSinceCross = if zeroCrossing then 0 else barsSinceCross[1] + 1;
    def barsBetweenCrossings = barsSinceCross + 1;
    def val = if barsBetweenCrossings < barsBetweenCrossings[1] then 2 * barsBetweenCrossings[1] else val[1];

    # The following line is a port of Dr Ehlers EasyLanguage program, kept here for reference
    #def DC = If barsBetweenCrossings < 3 then 3 else If 2*(barsBetweenCrossings) > 1.25*DC[1] then 1.25*DC[1] else if 2*(barsBetweenCrossings) < 0.8*DC[1] then 0.8*DC[1] else DC[1];

    plot DC = val;
    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);
 

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

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

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