Moving Average Research Indicators For ThinkOrSwim

StoneMan

Member
Plus
The following indicators were made by me back in May and June of 2023. They represent some signals I was interested in researching at the time that I now want to share with the community. It has been a while since I made these so there may be holes in my ability to explain everything and answer questions.

The first is the manipulation of the alpha factor in the ema calculation:
Moving Averages1.jpg


I set about doing this by taking the Z score percentile across what I considered to be critical metrics such as measures of volume and volatility. The code for a lower indicator that displays this is attached below. Also attached is the question the community helped answer so I could do those calculations. You could Z-Score percentile any number of signals you wanted to adjust EMA sensitivity. The indicator gives a wide variety of examples.

Alpha Oscillator question: https://usethinkscript.com/threads/fold-function-counter.15604/post-125990
09WKzpK.png

Code:
#Alpha Oscillator
#StoneMan (StoneFelt)
#8.25.2024
#V1.0
#Calculates the Z-Score percentile of multiple metrics and combines them into one signal


declare lower;

input Length = 10;

#Calculating volitility Z-Score percentile, true range used as volitility metric
def TrueRange = TrueRange(high, close, low); #Observed value
def TR_Average = Average(TrueRange, Length); #Mean of sample
def TR_StDev = StDev(TrueRange, Length);     #Standard Deviation of sample

def TR_Z_Score = (TrueRange - TR_Average) / TR_StDev;

def TR_Z_Percent_Count =
    fold i = 0
    to Length
    with S = 0
    do S + if (TR_Z_Score > getValue(TR_Z_Score, i, Length)) then 1 else 0;

def TR_Percentile = TR_Z_Percent_Count / (Length - 1);

#Calculating volume Z-Score percentile
Def V = Volume;                     #Observed value
def V_Average = Average(V, Length); #Mean of sample
def V_StDev = StDev(V, Length);     #Standard Deviation of sample

def V_Z_Score = (V - V_Average) / V_StDev;

def V_Z_Percent_Count =
    fold j = 0
    to Length
    with Q = 0
    do Q + if (V_Z_Score > getValue(V_Z_Score, j, Length)) then 1 else 0;

def V_Percentile = V_Z_Percent_Count / (Length - 1);

#Calculating Close Price Z-Score percentile
#Use absolute value of Z-score to examine distance from price mean
Def C = Close;                      #Observed value
def C_Average = Average(C, Length); #Mean of sample
def C_StDev = StDev(C, Length);     #Standard Deviation of sample

def C_Z_Score = AbsValue((C - C_Average) / C_StDev);

def C_Z_Percent_Count =
    fold n = 0
    to Length
    with R = 0
    do R + if (C_Z_Score > getValue(C_Z_Score, n, Length)) then 1 else 0;

def C_Percentile = C_Z_Percent_Count / (Length - 1);

#Calculating OHLC4 Price Z-Score percentile
#Use absolute value of Z-score to examine distance from price mean
Def OHCL4 = OHLC4;                          #Observed value
def OHCL4_Average = Average(OHCL4, Length); #Mean of sample
def OHCL4_StDev = StDev(OHCL4, Length);     #Standard Deviation of sample

def OHCL4_Z_Score = AbsValue((OHCL4 - OHCL4_Average) / OHCL4_StDev);

def OHCL4_Z_Percent_Count =
    fold t = 0
    to Length
    with U = 0
    do U + if (OHCL4_Z_Score > getValue(OHCL4_Z_Score, t, Length)) then 1 else 0;

def OHCL4_Percentile = OHCL4_Z_Percent_Count / (Length - 1);


#Calculating volitility Z-Score percentile using difference between open and close
def OpenCloseDiff = AbsValue(open - close);      #Observed value
def OCD_Average = Average(OpenCloseDiff, Length); #Mean of sample
def OCD_StDev = StDev(OpenCloseDiff, Length);     #Standard Deviation of sample

def OCD_Z_Score = (OpenCloseDiff - OCD_Average) / OCD_StDev;

def OCD_Z_Percent_Count =
    fold g = 0
    to Length
    with F = 0
    do F + if (OCD_Z_Score > getValue(OCD_Z_Score, g, Length)) then 1 else 0;

def OCD_Percentile = OCD_Z_Percent_Count / (Length - 1);

def Alpha = (TR_Percentile + OCD_Percentile + C_Percentile + OHCL4_Percentile + (2 * V_Percentile)) / 6;
plot AlphaPlot = Alpha;

The next thing I was interested in was designing a decaying sum type function for this signal. Details can be found in the question below. In that process I also wanted to code up the ability to display the average of any number of moving average signals within a set of bounds and apply it to this new signal. For example, averaging every signal between length 6 and 14 then seeing how it compares to the vanilla signal at just 10. There is a very slight difference that interesting to study on a chart. The nested folds required to do this was very difficult to code and I couldn't have done it without the help of folks here.

Fold Function question: https://usethinkscript.com/threads/nested-fold-function-debugging.15685/post-129929

To put all of this together, I made myself a little playground that can yield just about any form of this signal you could imagine. That code will be pasted below. It is a bit complex, but I think there are concepts in it that would be awesome to see other folks run with to craft their own signals.

For the plots here it is important to note that plots labeled with 1 don't use any smoothing, 2 averages a smoothed signal with the unsmoothed one, and 3 is fully smoothed. Together, these make for some interesting ribbons. The code for this is pasted below:
ncUkoDd.png

Code:
#Research Signal Playground
#StoneMan (StoneFelt)
#8.25.2024
#V1.0
#Accumulates to research of some custom signal generation into one indicator for easy use.

input FilterPriceType = {default Close, HLC3, OHLC4, Extreme}; #choose the price you want analyzed
input AlphaPeriod = 10; #Period Z score percentiles are averaged over to get alpha weighting
input Length1 = 10; #Controls the period of the vanilla alpha weighted ma (AWMA)
input SmoothingFactor = 5; #optional smoothing. Plot 1's have no smoothing plot 2's and 3's have some

input SuperAverageLength1 = 10; #Every AWMA between these bounds are calculated, averaged, then displayed as one vslue
input SuperAverageLength2 = 5;  #Code is set up so either bound can be high or low


script A{
input Length = 10;

#Calculating volitility Z-Score percentile, true range used as volitility metric
def TrueRange = TrueRange(high, close, low); #Observed value
def TR_Average = Average(TrueRange, Length); #Mean of sample
def TR_StDev = StDev(TrueRange, Length);     #Standard Deviation of sample

def TR_Z_Score = (TrueRange - TR_Average) / TR_StDev;

def TR_Z_Percent_Count =
    fold i = 0
    to Length
    with S = 0
    do S + if (TR_Z_Score > getValue(TR_Z_Score, i, Length)) then 1 else 0;

def TR_Percentile = TR_Z_Percent_Count / (Length - 1);

#Calculating volume Z-Score percentile
Def V = Volume;                     #Observed value
def V_Average = Average(V, Length); #Mean of sample
def V_StDev = StDev(V, Length);     #Standard Deviation of sample

def V_Z_Score = (V - V_Average) / V_StDev;

def V_Z_Percent_Count =
    fold j = 0
    to Length
    with B = 0
    do B + if (V_Z_Score > getValue(V_Z_Score, j, Length)) then 1 else 0;

def V_Percentile = V_Z_Percent_Count / (Length - 1);

#Calculating Close Price Z-Score percentile
#Use absolute value of Z-score to examine distance from price mean
Def C = Close;                      #Observed value
def C_Average = Average(C, Length); #Mean of sample
def C_StDev = StDev(C, Length);     #Standard Deviation of sample

def C_Z_Score = AbsValue((C - C_Average) / C_StDev);

def C_Z_Percent_Count =
    fold n = 0
    to Length
    with Y = 0
    do Y + if (C_Z_Score > getValue(C_Z_Score, n, Length)) then 1 else 0;

def C_Percentile = C_Z_Percent_Count / (Length - 1);

#Calculating OHLC4 Price Z-Score percentile
#Use absolute value of Z-score to examine distance from price mean
Def OHCL4 = OHLC4;                          #Observed value
def OHCL4_Average = Average(OHCL4, Length); #Mean of sample
def OHCL4_StDev = StDev(OHCL4, Length);     #Standard Deviation of sample

def OHCL4_Z_Score = AbsValue((OHCL4 - OHCL4_Average) / OHCL4_StDev);

def OHCL4_Z_Percent_Count =
    fold t = 0
    to Length
    with M = 0
    do M + if (OHCL4_Z_Score > getValue(OHCL4_Z_Score, t, Length)) then 1 else 0;

def OHCL4_Percentile = OHCL4_Z_Percent_Count / (Length - 1);


#Calculating volitility Z-Score percentile using difference between open and close
def OpenCloseDiff = AbsValue(open - close);      #Observed value
def OCD_Average = Average(OpenCloseDiff, Length); #Mean of sample
def OCD_StDev = StDev(OpenCloseDiff, Length);     #Standard Deviation of sample

def OCD_Z_Score = (OpenCloseDiff - OCD_Average) / OCD_StDev;

def OCD_Z_Percent_Count =
    fold d = 0
    to Length
    with F = 0
    do F + if (OCD_Z_Score > getValue(OCD_Z_Score, d, Length)) then 1 else 0;

def OCD_Percentile = OCD_Z_Percent_Count / (Length - 1);

plot Alpha = (TR_Percentile + OCD_Percentile + C_Percentile + OHCL4_Percentile + (2 * V_Percentile)) / 6;
}

#Price Types to generate base level infinite impulse filters
def CloseEma = A(AlphaPeriod) * close + (1 - A(AlphaPeriod)) * CloseEma[1];
def HighEma = A(AlphaPeriod) * high + (1 - A(AlphaPeriod)) * HighEma[1];
def LowEma = A(AlphaPeriod) * low + (1 - A(AlphaPeriod)) * LowEma[1];
def OpenEma = A(AlphaPeriod) * open + (1 - A(AlphaPeriod)) * OpenEma[1];

def Extreme = if (close > close[1], high, if(close < close[1], low, close));
def HLC3Ema = (CloseEma + HighEma + LowEma) / 3;
def OHLC4Ema = (CloseEma + HighEma + LowEma + OpenEma) / 4;


#Price type selection for filters
def avgEMA;

switch(FilterPriceType){
Case Close:
    avgEMA = CloseEMA;
Case HLC3:
    avgEMA = HLC3Ema;
Case OHLC4:
    avgEMA = OHLC4Ema;
Case Extreme:
    avgEMA = Extreme;
}

#Zero lag smoothing algoritm applied to filters output
def avgEMASmooth = 2 * WMA(avgEMA, SmoothingFactor) - WMA(WMA(avgEMA, SmoothingFactor), SmoothingFactor);

#Averaging the smoothed and unsmoothed data
def NormSmoothAvg = (avgEMA + avgEMASmooth) / 2;


Script F {
Input Length = 10;

Input data = 3.7453;

def FilterData =
    fold a = 0
    to Length
    with K = 0
    do K + data[a]*(Length - a);

def FilterCount =
    fold i = 0
    to Length + 1
    with P = 0
    do P + i;

def FilterAverage = FilterData / FilterCount;

plot Filter = FilterAverage;
}

#Input function F as (length, data)
def Plot1Function = F(Length1, avgEMA);
plot Plot1 = Plot1Function;

#Examining smoothing of the input data to F, I checked and smoothing input same as smoothing output
#So averageing the inputs is the same as average the outputs
def Plot2Function = F(Length1, avgEMASmooth);
plot Plot2 = Plot2Function;

def plot3Function = F(Length1, NormSmoothAvg);
plot Plot3 = Plot3Function;


#For the superaverage function you will be able to input both lengths and a the data
#When function F is calle be sure to set the data to the input of this function

Script S{
input SupAvgLength1 = 10;
input SupAvgLength2 = 5;
input data = 3.5632;

#Number to divide moving averages by
def LengthDiff = AbsValue(SupAvgLength1 - SupAvgLength2) / 2;

def SuperAverage =
    if(SupAvgLength1 == SupAvgLength2, F(SupAvgLength1, data),
            (Fold q = if(SupAvgLength1 < SupAvgLength2, SupAvgLength1, SupAvgLength2)
             to if(SupAvgLength1 > SupAvgLength2, SupAvgLength1, SupAvgLength2)
             with R = 0
             do R + (
                     #Implementing nested fold function because ThinkScript cannot do F(q) but we can recreate function F
                     (Fold u = 0
                     to q
                     with X = 0
                     do X + GetValue(data, u)*(q-u)) /
                     #Now divide this by the filter count function as we do in F
                     (Fold w = 0
                     to q + 1
                     with G = 0
                     do G + q)
                     )
             ) / LengthDiff
      )
;

plot OutPut = SuperAverage;
}

#Input function S as (SuperAverageLength1, SuperAverageLength2, data)
def SuperPlot1Function = S(SuperAverageLength1, SuperAverageLength2, avgEMA);
plot SuperPlot1 = SuperPlot1Function;

def SuperPlot2Function = S(SuperAverageLength1, SuperAverageLength2, avgEMASmooth);
plot SuperPlot2 = SuperPlot2Function;

def SuperPlot3Function = S(SuperAverageLength1, SuperAverageLength2, NormSmoothAvg);
plot SuperPlot3 = SuperPlot3Function;
 
Last edited by a moderator:

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

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
427 Online
Create Post

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