Recursive Moving Average Difference for ThinkOrSwim

samer800

Moderator - Expert
VIP
Lifetime
tONi2q1.png

Message from Author:
The relative difference between a moving average and the price can be a useful tool for interpreting trend direction and identifying pullbacks or breakdowns. This indicator recursively finds all the relative moving average differences between two simple moving averages of your choosing and weighs them by their lengths. It then returns a value that represents the weighted average of all the moving average differences. This can represent the gradient of motion between moving averages, or the path of least resistance, which the price may revert to in certain situations.

For the settings: minimum MA represents the minimum simple moving average to consider for the total weighted average. Maximum MA represents the maximum simple moving average to consider. "Move By" is the increment that you want to move between these two moving averages.

A positive moving average difference indicates that the price is above the moving average difference (i.e. the weighted average of all moving average differences between your two selected moving averages). A negative moving average difference indicates that the price is below the moving average difference. I have added a signal with a configurable length input as well to smooth out trends.
CODE:

CSS:
#//@wbburgin
#indicator("Recursive Moving Average Difference [wbburgin]", overlay=false, timeframe="")
# converted by Sam4Cok@Samer800    - 05/2023
declare lower;
input ColorBars = yes;
input minMaLength = 100;        # "Minimum Moving Average Length"
input maxMaLength = 200;        # "Maximum Moving Average Length"
input MoveBy = 5;               # "Move By"
input signalLength = 50;        # "Signal Length"
input ShowSignalLine = yes;     # "Show Signal"

def na = Double.NaN;
def last = isNaN(close);
#--- Color
DefineGlobalColor("grow_above" , CreateColor(38,166,154));
DefineGlobalColor("fall_above" , CreateColor(178,223,219));
DefineGlobalColor("grow_below" , CreateColor(255,205,210));
DefineGlobalColor("fall_below" , CreateColor(255,82,82));
DefineGlobalColor("Signal"     , CreateColor(255,235,59));

def masTotalLength = fold i = minMALength to maxMALength with p do
                     if  (i % MoveBy) ==0 then p + i else p;

def weightedAverage = fold i1 = minMALength to maxMALength with p1 do
                     if (i1 % MoveBy) ==0 then p1 +
                         (((close - ((fold j=0 to i1 with q do
                      q +  GetValue(close,j)) / i1)) /  close) * i1) else p1;

def wa = weightedAverage / masTotalLength;
def signal = ExpAverage(wa, signalLength);

def col = if wa > 0 then if wa > wa[1] then 2 else 1 else
          if wa > wa[1] then -1 else -2;

plot sigLine = if ShowSignalLine then signal else na;
sigLine.SetDefaultColor(GlobalColor("Signal"));
sigLine.SetLineWeight(2);

plot maDiff = wa;
maDiff.SetLineWeight(3);
maDiff.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
maDiff.AssignValueColor(if col==2 then GlobalColor("grow_above") else
                        if col==1 then GlobalColor("fall_above") else
                        if col==-1 then GlobalColor("grow_below") else GlobalColor("fall_below"));
plot zero = if last then na else 0;
zero.SetDefaultColor(Color.GRAY);
AssignPriceColor(if !ColorBars then Color.CURRENT else
                 if col==2 then GlobalColor("grow_above") else
                 if col==1 then GlobalColor("fall_above") else
                 if col==-1 then GlobalColor("grow_below") else GlobalColor("fall_below"));
#-- END of CODE
 
Really impressive! Thanks for posting, I'll trial it for a while, but first glance seems to be very useful :)

Update: This is useful for automatically finding axis lines and also useful in a strong trending market (or security) to show when a good exit might be.
As with most averages, it isn't that good to use for entry points in a choppy market.

Update: This does take a little bit of calculation time whenever I add it to a chart, but my charts usually have 1-2k bars on them.
I edited the code to be able to use the price color change without having any graphs on the lower. I keep this on my chart and use it to point out places of friction (axis line mentioned before). It doesn't tell exactly what is going to happen, but it usually does a good job of changing the bar colors when something is likely to happen.
I am using it with tape reading and a simple retracement level scheme (of roughly 38.2%).

Code:
#//@wbburgin
#indicator("Recursive Moving Average Difference [wbburgin]", overlay=false, timeframe="")
# converted by Sam4Cok@Samer800    - 05/2023

input ColorBars = yes;
input minMaLength = 100;        # "Minimum Moving Average Length"
input maxMaLength = 200;        # "Maximum Moving Average Length"
input MoveBy = 5;               # "Move By"
input signalLength = 50;        # "Signal Length"
#input ShowSignalLine = yes;     # "Show Signal"

def na = Double.NaN;
def last = isNaN(close);
#--- Color
DefineGlobalColor("grow_above" , CreateColor(38,166,154));
DefineGlobalColor("fall_above" , CreateColor(178,223,219));
DefineGlobalColor("grow_below" , CreateColor(255,205,210));
DefineGlobalColor("fall_below" , CreateColor(255,82,82));
DefineGlobalColor("Signal"     , CreateColor(255,235,59));

def masTotalLength = fold i = minMALength to maxMALength with p do
                     if  (i % MoveBy) ==0 then p + i else p;

def weightedAverage = fold i1 = minMALength to maxMALength with p1 do
                     if (i1 % MoveBy) ==0 then p1 +
                         (((close - ((fold j=0 to i1 with q do
                      q +  GetValue(close,j)) / i1)) /  close) * i1) else p1;

def wa = weightedAverage / masTotalLength;
def signal = ExpAverage(wa, signalLength);

def col = if wa > 0 then if wa > wa[1] then 2 else 1 else
          if wa > wa[1] then -1 else -2;

#plot sigLine = if ShowSignalLine then signal else na;
#sigLine.SetDefaultColor(GlobalColor("Signal"));
#sigLine.SetLineWeight(2);

#plot maDiff = wa;
#maDiff.SetLineWeight(3);
#maDiff.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
#maDiff.AssignValueColor(if col==2 then GlobalColor("grow_above") else
#                        if col==1 then GlobalColor("fall_above") else
#                        if col==-1 then GlobalColor("grow_below") else GlobalColor("fall_below"));
#plot zero = if last then na else 0;
#zero.SetDefaultColor(Color.GRAY);
AssignPriceColor(if !ColorBars then Color.CURRENT else
                 if col==2 then GlobalColor("grow_above") else
                 if col==1 then GlobalColor("fall_above") else
                 if col==-1 then GlobalColor("grow_below") else GlobalColor("fall_below"));
#-- END of CODE

For reference what most of my screen looks like:
The bright green dashed line is a retracement line from the premarket low, showing where a reversal is likely.
The yellow lined channel has 2 retracement levels on it (38.2 and 61.8). You can see how at the end of the yellow channel lines the price begins to rest on top of the upper retrace level (61.8).
Notice how the recursive moving avg changes the color of the candles very close to the retracement line, and then a brief pause down at the end of the channel before the big jump up in price.
 

Attachments

  • Screenshot 2023-06-06 at 12.55.49.png
    Screenshot 2023-06-06 at 12.55.49.png
    244 KB · Views: 393
Last edited:
tONi2q1.png

Message from Author:
The relative difference between a moving average and the price can be a useful tool for interpreting trend direction and identifying pullbacks or breakdowns. This indicator recursively finds all the relative moving average differences between two simple moving averages of your choosing and weighs them by their lengths. It then returns a value that represents the weighted average of all the moving average differences. This can represent the gradient of motion between moving averages, or the path of least resistance, which the price may revert to in certain situations.

For the settings: minimum MA represents the minimum simple moving average to consider for the total weighted average. Maximum MA represents the maximum simple moving average to consider. "Move By" is the increment that you want to move between these two moving averages.

A positive moving average difference indicates that the price is above the moving average difference (i.e. the weighted average of all moving average differences between your two selected moving averages). A negative moving average difference indicates that the price is below the moving average difference. I have added a signal with a configurable length input as well to smooth out trends.
CODE:

CSS:
#//@wbburgin
#indicator("Recursive Moving Average Difference [wbburgin]", overlay=false, timeframe="")
# converted by Sam4Cok@Samer800    - 05/2023
declare lower;
input ColorBars = yes;
input minMaLength = 100;        # "Minimum Moving Average Length"
input maxMaLength = 200;        # "Maximum Moving Average Length"
input MoveBy = 5;               # "Move By"
input signalLength = 50;        # "Signal Length"
input ShowSignalLine = yes;     # "Show Signal"

def na = Double.NaN;
def last = isNaN(close);
#--- Color
DefineGlobalColor("grow_above" , CreateColor(38,166,154));
DefineGlobalColor("fall_above" , CreateColor(178,223,219));
DefineGlobalColor("grow_below" , CreateColor(255,205,210));
DefineGlobalColor("fall_below" , CreateColor(255,82,82));
DefineGlobalColor("Signal"     , CreateColor(255,235,59));

def masTotalLength = fold i = minMALength to maxMALength with p do
                     if  (i % MoveBy) ==0 then p + i else p;

def weightedAverage = fold i1 = minMALength to maxMALength with p1 do
                     if (i1 % MoveBy) ==0 then p1 +
                         (((close - ((fold j=0 to i1 with q do
                      q +  GetValue(close,j)) / i1)) /  close) * i1) else p1;

def wa = weightedAverage / masTotalLength;
def signal = ExpAverage(wa, signalLength);

def col = if wa > 0 then if wa > wa[1] then 2 else 1 else
          if wa > wa[1] then -1 else -2;

plot sigLine = if ShowSignalLine then signal else na;
sigLine.SetDefaultColor(GlobalColor("Signal"));
sigLine.SetLineWeight(2);

plot maDiff = wa;
maDiff.SetLineWeight(3);
maDiff.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
maDiff.AssignValueColor(if col==2 then GlobalColor("grow_above") else
                        if col==1 then GlobalColor("fall_above") else
                        if col==-1 then GlobalColor("grow_below") else GlobalColor("fall_below"));
plot zero = if last then na else 0;
zero.SetDefaultColor(Color.GRAY);
AssignPriceColor(if !ColorBars then Color.CURRENT else
                 if col==2 then GlobalColor("grow_above") else
                 if col==1 then GlobalColor("fall_above") else
                 if col==-1 then GlobalColor("grow_below") else GlobalColor("fall_below"));
#-- END of CODE
@samer800 Is there a way to make that trigger line appear on the upper chart as a moving average?
 
This is very interesting. I've ultimately decided not to add it to my chart, but I'm going to keep it and study it a while. My decision was made based on asking myself the following: "Would this indicator have helped me enter any trades I would not have taken otherwise, or kept me out of any trades I shouldn't have taken?" I resolved the answer to be no, but I bet for newer traders or those who have difficulty spotting the trend this could be an amazingly great tool since there are all kinds of problems with standard moving averages. The recursive aspect to it makes it very impressive compared to a standard MA approach.

I've often thought about creating a recursive bar (yes, a whole new bar type) where you could recursively evaluate all bar intervals from 10 ticks to 1,000 ticks and do some kind of weighting to determine what the final 1,000 tick bar would look like (I.E. something like a recursively weighted / modified open, high, low, close). Clearly not possible in TOS, but again, the recursive aspect is a very cool thing to think about since the market does, in fact, operate on nearly every timeframe simultaneously.

@samer800, it would be interesting to see if you could put this kind of recursive process to use developing something that would approximate a recursive "bar" by looking at all the OHLC values over the past ___ bars and determining market strength based on something like close - open, or similar. Just a thought.

Again, very interesting. Thanks for posting.
 
Last edited:
Doesn't work as an avg on the upper chart but here is an interesting visualization for the lower chart:
Code:
#//@wbburgin
#indicator("Recursive Moving Average Difference [wbburgin]", overlay=false, timeframe="")
# converted by Sam4Cok@Samer800    - 05/2023
declare lower;
input ColorBars = yes;
input minMaLength = 100;        # "Minimum Moving Average Length"
input maxMaLength = 200;        # "Maximum Moving Average Length"
input MoveBy = 5;               # "Move By"
input signalLength = 50;        # "Signal Length"
input ShowSignalLine = yes;     # "Show Signal"

def na = Double.NaN;
def last = isNaN(close);

def masTotalLength = fold i = minMALength to maxMALength with p do
                     if  (i % MoveBy) ==0 then p + i else p;

def weightedAverage = fold i1 = minMALength to maxMALength with p1 do
                     if (i1 % MoveBy) ==0 then p1 +
                         (((close - ((fold j=0 to i1 with q do
                      q +  GetValue(close,j)) / i1)) /  close) * i1) else p1;

def wa = weightedAverage / masTotalLength;
def signal = ExpAverage(wa, signalLength);

def col = if wa > 0 then if wa > wa[1] then 2 else 1 else
          if wa > wa[1] then -1 else -2;


plot t = movingaverage(averagetype.exponential, signal, 13);

def k = signal;
def d = t;

#heikin ashi pricing
def hao = (k[1] + d[1]) / 2;
def hac = (k + d + d + k) / 4;
def highestH = if k > hao && hac then k else if
    hao > k && hao > hac then hao else if
    hac > k && hac > hao then hac else Double.NaN;
def lowestL = if k < hao && hac then k else if
    hao < k && hao < hac then hao else if
    hac < k && hac < hao then hac else Double.NaN;

#------------------------------------------addchart up---------------------------------------------------------------------------#
def haoup = if hao > hao[1] then hao else Double.NaN;
def hacup = if hac > hac[1] then hac else Double.NaN;
AddChart(growColor = Color.CYAN, neutralColor = Color.CURRENT,  high = highestH, low = lowestL, open = haoup, close = hacup, type = ChartType.CANDLE);
#------------------------------------------addchart up---------------------------------------------------------------------------#
def haodown = if hao < hao[1] then hao else Double.NaN;
def hacdown = if hac < hac[1] then hac else Double.NaN;
AddChart(growColor = Color.DARK_ORANGE, neutralColor = Color.CURRENT,  high = highestH, low = lowestL, open = haodown, close = hacdown, type = ChartType.CANDLE);

#plot haoplot = hao;
plot hacplot = hac;
hacplot.SetPaintingStrategy(PaintingStrategy.DASHES);
hacplot.AssignValueColor(if hac > hac[1] then Color.CYAN
         else Color.DARK_ORANGE);
hacplot.SetLineWeight(2);
 

Attachments

  • Screenshot 2023-06-13 at 13.18.18.png
    Screenshot 2023-06-13 at 13.18.18.png
    316.6 KB · Views: 334
Just a question on how you use it. Would you enter long when the vertical bars first start changing to green, or do you wait for the yellow line to rise above the 0 Level?
 
Just a question on how you use it. Would you enter long when the vertical bars first start changing to green, or do you wait for the yellow line to rise above the 0 Level?
There are three conditions to observe when using Oscillators.
  1. Zero-crossing (Delta of Price): the histogram is considered bullish when it crosses above zero.
  2. Signal Line Crossing (Variance in Price): histogram is considered bullish when it crosses above the oscillator's moving average (yellow line) .
  3. Direction Change: bullish when below midpoint and starts ascending,
read more: https://usethinkscript.com/threads/how-to-read-an-oscillator-in-thinkorswim.11497/

It should be noted that moving averages are lagging indicators. The extreme lengths in this indicator makes it an extreme lagging indicator. So while this study provides valuable information, it is not meant to be used as an entry indicator.
Read more: https://usethinkscript.com/threads/moving-average-accuracy-for-thinkorswim.15624/#post-126015

FYI, no indicator can be used in isolation or reviewed on just one timeframe:
https://usethinkscript.com/threads/basics-for-developing-a-good-strategy.8058/

@bdbald
 
Last edited:
I added a standard deviation to this to get an idea of how high or low it is compared to average. I also added a secondary color bar that is based on if the level is above or below the trigger line.
1686882370014.png


Code:
#//@wbburgin
#indicator("Recursive Moving Average Difference [wbburgin]", overlay=false, timeframe="")
# converted by Sam4Cok@Samer800    - 05/2023
# chewie added standard deviation and color bar based on trigger line.  -06/2023

declare lower;
input ColorBars = no;
input ColorBarsTrigger = no;
input minMaLength = 20;         # (100 default) "Minimum Moving Average Length"
input maxMaLength = 200;        # "Maximum Moving Average Length"
input MoveBy = 5;               # "Move By"
input signalLength = 50;        # "Signal Length"
input ShowSignalLine = yes;     # "Show Signal"

def na = Double.NaN;
def last = IsNaN(close);
#--- Color
DefineGlobalColor("grow_above" , CreateColor(38, 166, 154));
DefineGlobalColor("fall_above" , CreateColor(178, 223, 219));
DefineGlobalColor("grow_below" , CreateColor(255, 205, 210));
DefineGlobalColor("fall_below" , CreateColor(255, 82, 82));
DefineGlobalColor("Signal"     , CreateColor(255, 235, 59));

def masTotalLength = fold i = minMaLength to maxMaLength with p do
                     if  (i % MoveBy) == 0 then p + i else p;

def weightedAverage = fold i1 = minMaLength to maxMaLength with p1 do
                     if (i1 % MoveBy) == 0 then p1 +
                         (((close - ((fold j = 0 to i1 with q do
                      q +  GetValue(close, j)) / i1)) /  close) * i1) else p1;

def wa = weightedAverage / masTotalLength;
def signal = ExpAverage(wa, signalLength);

def col = if wa > 0 then if wa > wa[1] then 2 else 1 else
          if wa > wa[1] then -1 else -2;

plot sigLine = if ShowSignalLine then signal else na;
sigLine.SetDefaultColor(GlobalColor("Signal"));
sigLine.SetLineWeight(2);

plot maDiff = wa;
maDiff.SetLineWeight(3);
maDiff.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
maDiff.AssignValueColor(if col == 2 then GlobalColor("grow_above") else
                        if col == 1 then GlobalColor("fall_above") else
                        if col == -1 then GlobalColor("grow_below") else GlobalColor("fall_below"));
plot zero = if last then na else 0;
zero.SetDefaultColor(Color.GRAY);
ZERO.HideBubble();
ZERO.Hidetitle();
AssignPriceColor(if !ColorBars then Color.CURRENT else
                 if col == 2 then GlobalColor("grow_above") else
                 if col == 1 then GlobalColor("fall_above") else
                 if col == -1 then GlobalColor("grow_below") else GlobalColor("fall_below"));

AssignPriceColor(if !ColorBarsTrigger then Color.CURRENT else
                 if signal > maDiff then color.red else
                 if signal < maDiff then color.green else
                 Color.CURRENT);


# 2 Standard Deviation Full

input deviations = 2.0;
input deviations2 = 2.5;
input fullRange = Yes;
input length = 21;

def regression;
def stdDeviation;
if (fullRange) {
    regression = InertiaAll(maDiff);
    stdDeviation = StDevAll(maDiff);
} else {
    regression = InertiaAll(maDiff, length);
    stdDeviation = StDevAll(maDiff, length);
}

plot UpperLine = regression + deviations * stdDeviation;
#plot MiddleLine = regression;
plot LowerLine = regression - deviations * stdDeviation;

UpperLine.SetDefaultColor(Color.RED);
LowerLine.SetDefaultColor(Color.GREEN);
UpperLine.SetLineWeight(1);
LowerLine.SetLineWeight(1);
Upperline.HideBubble();
Upperline.Hidetitle();
Lowerline.HideBubble();
Lowerline.Hidetitle();


plot UpperLine2 = regression + deviations2 * stdDeviation;
plot LowerLine2 = regression - deviations2 * stdDeviation;

UpperLine2.SetDefaultColor(Color.RED);
LowerLine2.SetDefaultColor(Color.GREEN);
UpperLine2.SetLineWeight(1);
LowerLine2.SetLineWeight(1);
UpperLine2.HideBubble();
UpperLine2.Hidetitle();
LowerLine2.HideBubble();
LowerLine2.Hidetitle();

AddCloud(UpperLine2, UpperLine, Color.RED, Color.CURRENT);
AddCloud(LowerLine, LowerLine2, color.green, Color.CURRENT);
#-- END of CODE
 
I tried scanning for when the MA bars crosses sig line but not working anyone have a scan for this?

This script utilizes TWO fold operations.
This requires an extreme use of resources.
TDA throttles scans that are a drain on resources.

Therefore, any attempts to utilize this script in a scan will fail with the error message: Error: Script execution timeout
 

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