Adaptive StochasticFull for ThinkOrSwim

Sesqui

Member
VIP
Here is an Adaptive StochasticFull indicator. It is tuned to the market dominant cycle using the band-pass filter method as described in chapter 5 of Dr Ehlers' "Cycle analytics for Traders". The data is smoothed using the EhlersSuperSmootherFilter before it is fed into the indicator to reduce chatter. The smoothing length is set to the dominant cycle length as well. It is shown below as the indicator in the middle of the screenshot, just above the volume indicator. The length of the yellow FullD line can be set using the properties dialog.

1760774332033.png


Here is the script for the Adaptive StochasticFull indicator:


CSS:
# Adaptive StochasticFull Using Dominant Cycle via Bandpass Filter Method by Sesqui 18OCT2025
# Uses BandPass Filter method from ch 5 of Dr Ehlers, "Cycle Analyticsfor Traders"
# to tune the StochasticFull indicator to the Market Dominant Cycle

# Adaptive StochasticFull Using Dominant Cycle via Bandpass Filter Method

declare lower;
input ShowFullD = yes;
input ShowHMA = no;
input BandPassSource = ohlc4;
input BandPassPeriod = 20;
input BandPassBandwidth = 0.70;

###------------------------------------------------------------------------------------------
input OpenTime = 845;   #hint OpenTime: Time you want backtesting to start the session at
input CloseTime = 1600;  #hint CloseTime: Time you want backtesting to stop the session at

def Begin = SecondsFromTime(OpenTime) ; 
def End = SecondsTillTime(CloseTime);
# Only use market hours when using intraday timeframe
def isIntraDay = if GetAggregationPeriod() > 14400000 or GetAggregationPeriod() == 0 then 0 else 1;
def MarketOpen = if !isIntraDay then 1 else if isIntraDay and  Begin >= 0 and End >= 0 then 1 else 0;
###--------------------------------------------------------------------------------------------------


#===================================================================================
#*** 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 get_Highest {
    input source = high;
    input CycleLength = 10;

    def length = if IsNaN(Floor(CycleLength)) then length[1] else Floor(CycleLength);
    def cur = source[0];
    def val = fold i = 0 to length with max = cur do if max >= GetValue(source, i + 1) then max else GetValue(source, i + 1);
    plot Highest = val;
    Highest.HideBubble();
}# end Script getHighest{}

#--------------
script get_Lowest {
    input source = low;
    input CycleLength = 10;
    
    def length = if IsNaN(Floor(CycleLength)) then length[1] else Floor(CycleLength);
    def cur = source[0];
    def val = fold i = 0 to length with min = cur do if min <= GetValue(source, i + 1) then min else GetValue(source, i + 1);
    plot Lowest = val;
    Lowest.HideBubble();
}# end Script getLowest{}
#------------------------------------------------------------------------------
Script AdaptSMA{
    input source = close;
    input CycleLength = 10;
    
    def length = if IsNaN(Floor(CycleLength)) then length[1] else Floor(CycleLength);
    def sum = fold index = 0 to Length with s = 0 do s + GetValue(source,index);
    plot SMA = sum / CycleLength;
    SMA.HideBubble();
}# End Script AdaptSMA{}
#-------------------------------------------------------------------------------
Script SmoothTheSignal{
    # EhlersSuperSmootherFilter{}
    input price = close;
    input Length = 10;

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

    plot Smoothed = if !IsNaN(price) then filt else Double.NaN;
    Smoothed.HideBubble();
} #End Script SmoothTheSignal{}
#==============================================================================================
# StochasticFull
#========================================================================================

input over_bought = 80;
input over_sold = 20;
input H = high;
input L = low;
input C = close;
input slowing_period = 3;

input showBreakoutSignals = {default "No", "On FullK", "On FullD", "On FullK & FullD"};

def Cycle = GetCycleViaBandPassFilter(BandPassSource, BandPassPeriod, BandPassBandwidth).DC;
def CycleLength = if IsNaN(Floor(Cycle)) then CycleLength[1] else Floor(Cycle);
def KPeriod = CycleLength;
input DPeriod = 9;

def priceH = SmoothTheSignal(H,CycleLength);
def priceL = SmoothTheSignal(L,CycleLength);
def priceC = SmoothTheSignal(C,CycleLength);

def lowest_k = get_Lowest(priceL,CycleLength);
def c1 = priceC - lowest_k;
def c2 = get_Highest(priceH, KPeriod) - lowest_k;
def FastK = if c2 != 0 then c1 / c2 * 100 else 0;

plot FullK = If MarketOpen then AdaptSMA(FastK, slowing_period) else Double.NaN;
plot FullD = if MarketOpen and ShowFullD then AdaptSMA(FullK, DPeriod) else Double.NaN;
FullD.AssignValueColor(Color.YELLOW);
FullD.SetLineWeight(2);

# *** Displays an HMA(34) for cross overs ***
input HMALength = 34;

plot HMA = If MarketOpen and ShowHMA then MovingAverage(AverageType.HULL, FullK, HMALength) else Double.NaN;
HMA.AssignValueColor(Color.YELLOW);
HMA.SetLineWeight(2);

plot OverBought = If MarketOpen then over_bought else Double.NaN;
plot OverSold = If MarketOpen then over_sold else Double.NaN;

FullK.SetDefaultColor(Color.CYAN);
FullD.SetDefaultColor(Color.YELLOW);
FullK.AssignValueColor(if FullK-FullK[1] >= 0 then Color.Green else Color.RED);
FullK.SetLineWeight(3);
FullD.SetlineWeight(2);

def Diff = FullK - FullK[1];
FullK.DefineColor("Positive and Up", Color.GREEN);
FullK.DefineColor("Positive and Down", Color.DARK_GREEN);
FullK.DefineColor("Negative and Down", Color.RED);
FullK.DefineColor("Negative and Up", Color.DARK_RED);
FullK.AssignValueColor(if Diff >= 0 then if Diff > Diff[1] then FullK.color("Positive and Up") else FullK.color("Positive and Down") else if Diff < Diff[1] then FullK.color("Negative and Down") else FullK.color("Negative and Up"));

def top = 100;
def bottom = 0;

OverBought.SetStyle(Curve.SHORT_DASH);
OverBought.SetDefaultColor(Color.Red);

OverSold.SetStyle(Curve.SHORT_DASH);
OverSold.SetDefaultColor(Color.Green);

addCloud(top,OverBought, Color.LIGHT_RED, Color.Current);
addCloud(OverSold,bottom, Color.LIGHT_GREEN, Color.Current);

plot center = If MarketOpen then 50 else Double.NaN;
center.SetStyle(Curve.SHORT_DASH);
center.AssignValueColor(Color.Gray);



Enjoy!
 

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