How to plot a study as a normalized index that has 0-100% values (e.g. like MoneyFlowIndex)?

L

lmk99

Member
VIP
Hi all,

I can't figure out how this could be done: I want to create an index plot that does not include negative values, and works on a scale of 0% to 100% like MoneyFlowIndex. I want the plot to be for the study value (but I'd also like to try doing this for the rate of change of the study value).

The rate of change is normally calculated this way:

Code:
(price / price[length] - 1) * 100

The standard code that I found to normalize a plot is this:

Code:
script normalizePlot {
    input data = close;
    input newRngMin =  -1;
    input newRngMax = 1;
    def hhData = HighestAll( data );
    def llData = LowestAll( data );
    plot nr = ((( newRngMax - newRngMin ) * ( data - llData )) / ( hhData - llData )) + newRngMin;
}

I am plotting normalized FreedomofMovement rate of change like so:

Code:
declare lower;

def length = 6;

#Rate of change formula: (price / price[length] - 1) * 100

#index example formula: input movingAvgLength = 1; plot MoneyFlowIndex = Average(moneyflow(high, close, low, volume, length), movingAvgLength);

script normalizePlot {
    input data = close;
    input newRngMin =  -1;
    input newRngMax = 1;
    def hhData = HighestAll( data );
    def llData = LowestAll( data );
    plot nr = ((( newRngMax - newRngMin ) * ( data - llData )) / ( hhData - llData )) + newRngMin;
}

#FreedomofMovement

def fomlength = 20;
def numDev = 2.0;
def allowNegativeValues = yes;

def mov = AbsValue(close / close[1] - 1);
def minMov = Lowest(mov, fomlength);
def maxMov = Highest(mov, fomlength);
def nMov = 1 + (mov - minMov) / (maxMov - minMov) * 9;
def vol = (volume - Average(volume, fomlength)) / StDev(volume, fomlength);
def minVol = Lowest(vol, fomlength);
def maxVol = Highest(vol, fomlength);
def nVol = 1 + (vol - minVol) / (maxVol - minVol) * 9;
def vByM = nVol / nMov;

def rawFoM = (vByM - Average(vByM, fomlength)) / StDev(vByM, fomlength);
def FoM = if allowNegativeValues then rawFoM else Max(0, rawFoM);
def StDevLevel = numDev;

def FOMNormalized = normalizePlot(FoM);

plot FOMROC = normalizePlot((FOMNormalized/FOMNormalized[length]-1)*100);

FOMROC.SetDefaultColor(Color.Yellow);

What happens when plotting though is that I'm getting an absolute value in some range of oscillations, like -.326 to .145 for example. So even with the normalization script being used, (1) negative values still occur and (2) the scaling will make the plot unusably shrunken in view if also plotting something else normalized that will be in a higher or lower range (such as in a range of -.9 to -.8 or .7 to .9, etc.).

I see that in the case where two studies don't have a scaling problem fitting in a single lower section that uses a y-axis from 0 to 100%, both use average or movingaverage as the function for generating the study value (such as the case for MoneyFlowIndex and SchaffTrendCycle):



Code:
plot MoneyFlowIndex = Average(moneyflow(high, close, low, volume, length), movingAvgLength);

Code:
plot STC = MovingAverage(averageType, fastK2, DPeriod);

Is there some generic way to convert the FreedomofMovement plot, or any other such plot that can contain negative values normally, into this kind of 0-100% index format? Maybe this is a dumb question, please excuse that, as I don't understand the nuts and bolts of how these calculations work, I just know the format that I want the FreedomofMovement study (and/or a plot for FreedomofMovement rate of change) to use -- i.e. the same format as MoneyFlowIndex. Hoping someone can educate me about whether this is actually possible or has an idea of how it could be done.
 
netarchitech

netarchitech

Well-known member
VIP
Hi @lmk99,

It just so happens that I am working on a conversion myself and saw your post...Let me see if I can walk through an example and hopefully help you out...

First, the normalizePlot script...In order to get the desired range you're seeking, you need to change two values:

Code:
declare lower;
declare zerobase;

script normalizePlot {
    input data = close;
    input newRngMin =  -1;
    input newRngMax = 1;
    def hhData = HighestAll( data );
    def llData = LowestAll( data );
    plot nr = ((( newRngMax - newRngMin ) * ( data - llData )) / ( hhData - llData )) + newRngMin;
}

So, newRngMin should be 0 and newRngMax should be 100...

Second, the upper portion of the FreedomOfMovement indicator code needs to be separated from the bottom portion and put in a script:

Code:
script FoMScr {
input length = 60;
input allowNegativeValues = no;

def mov = AbsValue(close / close[1] - 1);
def minMov = Lowest(mov, length);
def maxMov = Highest(mov, length);
def nMov = 1 + (mov - minMov) / (maxMov - minMov) * 9;
def vol = (volume - Average(volume, length)) / StDev(volume, length);
def minVol = Lowest(vol, length);
def maxVol = Highest(vol, length);
def nVol = 1 + (vol - minVol) / (maxVol - minVol) * 9;
def vByM = nVol / nMov;

def rawFoM = (vByM - Average(vByM, length)) / StDev(vByM, length);
plot FoM = if allowNegativeValues then rawFoM else Max(0, rawFoM);
}

Third, the bottom (formatting) portion of the FoM code needs to be set up as shown below...For the purposes of this demonstration I chose "FoMScrX" for the variable name, but you can choose anything you want...

Code:
plot FoMScrX = normalizePlot(FoMScr(), 0, 100);
FoMScrX.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
FoMScrX.SetLineWeight(3);
FoMScrX.DefineColor("Above", GetColor(0));
FoMScrX.DefineColor("Below", GetColor(2));
FoMScrX.AssignValueColor(if FoMScrX >= numDev then FoMScrX.Color("Above") else FoMScrX.Color("Below"));

Fourth, there was code included in the FoM source that wasn't directly related to FoM:

Code:
input numDev = 20;
plot StDevLevel = numDev;
StDevLevel.SetDefaultColor(GetColor(7));
StDevLevel.SetStyle(Curve.SHORT_DASH);

This code should go right above the formatting portion of the FoM code...

Finally, if you put it all together, it should hopefully work and look like this:

Code:
declare lower;
declare zerobase;

script normalizePlot {
    input data = close;
    input newRngMin =  0;
    input newRngMax = 100;
    def hhData = HighestAll( data );
    def llData = LowestAll( data );
    plot nr = ((( newRngMax - newRngMin ) * ( data - llData )) / ( hhData - llData )) + newRngMin;
}

script FoMScr {
    input length = 60;
    input allowNegativeValues = no;

    def mov = AbsValue(close / close[1] - 1);
    def minMov = Lowest(mov, length);
    def maxMov = Highest(mov, length);
    def nMov = 1 + (mov - minMov) / (maxMov - minMov) * 9;
    def vol = (volume - Average(volume, length)) / StDev(volume, length);
    def minVol = Lowest(vol, length);
    def maxVol = Highest(vol, length);
    def nVol = 1 + (vol - minVol) / (maxVol - minVol) * 9;
    def vByM = nVol / nMov;

    def rawFoM = (vByM - Average(vByM, length)) / StDev(vByM, length);
    plot FoM = if allowNegativeValues then rawFoM else Max(0, rawFoM);
}

input numDev = 20;
plot StDevLevel = numDev;
StDevLevel.SetDefaultColor(GetColor(7));
StDevLevel.SetStyle(Curve.SHORT_DASH);

plot FoMScrX = normalizePlot(FoMScr(), 0, 100);
FoMScrX.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
FoMScrX.SetLineWeight(3);
FoMScrX.DefineColor("Above", GetColor(0));
FoMScrX.DefineColor("Below", GetColor(2));
FoMScrX.AssignValueColor(if FoMScrX >= numDev then FoMScrX.Color("Above") else FoMScrX.Color("Below"));

I hope this helps...

Good Luck and Good Trading :)
 
L

lmk99

Member
VIP
I want to check my understanding: because the normalization code is plotting highest and lowest of values to the specified range, does that mean that a plot created with this normalization will repaint but only when a new high or new low occurs?

I had created some signal plots such as arrows based on the position of lines that were normalized after it appeared that the lines were not repainting. But then going back a few days later, it seems like they repainted if I'm not mistaken (however, I'm not completely sure if my memory of the original plot is right).
 
wtf_dude

wtf_dude

Active member
@netarchitech Awesome explanation, man. How the hell did I not know about the zerobase declaration!?. Thanks
 
netarchitech

netarchitech

Well-known member
VIP
I want to check my understanding: because the normalization code is plotting highest and lowest of values to the specified range, does that mean that a plot created with this normalization will repaint but only when a new high or new low occurs?

@lmk99 Good question...I'm currently trying to figure that out...Given, we know that "mashing" indicators together yields scaling/repainting issues...The solution to that problem was supposed to be normalization...With that said, I to am getting the sneaking suspicion that isn't the case...

I had created some signal plots such as arrows based on the position of lines that were normalized after it appeared that the lines were not repainting. But then going back a few days later, it seems like they repainted if I'm not mistaken (however, I'm not completely sure if my memory of the original plot is right).

I need to investigate this issue further...I'm going to do so over the weekend...I'll report back my findings to this thread...Maybe you could do the same, if you have some time to do so? I'd really like to get a definitive answer to this issue, if possible...
 
netarchitech

netarchitech

Well-known member
VIP
Awesome explanation, man. How the hell did I not know about the zerobase declaration!?. Thanks

You're welcome @wtf_dude :) Thanks for the positive affirmation with respect to the "Normalization" tutorial...Just trying to "pay it forward"...
 
netarchitech

netarchitech

Well-known member
VIP
@lmk99 Looks like the current "Normalization" process isn't the answer...Yes, it fixes the scaling, but it looks like the data gets skewed in the process :(
 
L

lmk99

Member
VIP
@lmk99 Looks like the current "Normalization" process isn't the answer...Yes, it fixes the scaling, but it looks like the data gets skewed in the process :(

Thanks for reporting back. I have been working on this today and have reached the same conclusion, in that I believe what's happening is the values are repainting according to new highs or new lows entering the data set of the plot. I'm not strong in mathematical analysis so I'm not positive if this is the actual reason but it's what I believe is happening.
 
MattATM

MattATM

Active member
VIP
I did a normalization code as well. It does kind of eat up resources because you must keep enough chart open to show some history... For instance you would not want to just use just a current day chart or it will count a low as extreme low and a high as extreme high
 
netarchitech

netarchitech

Well-known member
VIP
@MattATM Thanks for the tip...Were you successful with your normalization code? The following is as far as I got:

Code:
declare upper;

script normalizePlot {
    input data = close;
    input newRngMin =  0;
    input newRngMax = 100;
    def hhData = HighestAll( data );
    def llData = LowestAll( data );
    plot nr = ((( newRngMax - newRngMin ) * ( data - llData )) / ( hhData - llData )) + newRngMin;
}

script AccDistByPrScr {
def data1 = if close > close[1] then close - Min(close[1], low) else if close < close[1] then close - Max(close[1], high) else 0;

plot AccDist = TotalSum(data1);
}

def AccDistByPrX = normalizePlot(AccDistByPrScr(), 0, 100);

AddVerticalLine(AccDistByPrX == 0, "Buy Now!", Color.MAGENTA, Curve.SHORT_DASH);
AddVerticalLine(AccDistByPrX == 100, "Sell Now!", Color.MAGENTA, Curve.SHORT_DASH);

The more history I threw at it, the more skewed the data got...I would certainly welcome any additional tips you might have...

Thanks in advance :)
 
MattATM

MattATM

Active member
VIP
@netarchitech
Try this out my friend
Code:
######Begin CMFV######
#def buying=(C-L);
#def selling=(H-C);
#def rangespan = (H - L);
#What about UNFILLED GAPS?! That Would ruin the volume calculation!
#Use a correction equivalent
def equivHi = if h < c[1] then c[1] else h;
def equivLo = if l > c[1] then c[1] else l;
def buying = (c - equivLo);
def selling = (equivHi - c);
def rangespan = (equivHi - equivLo);
def MinValue = 0;
def MaxValue = 1;
#Current Money Flow Volume as used by accumulationDistribution CMFV
plot CMFV = (V * ((buying - selling) / rangespan));
CMFV.assignValueColor(If CMFV>=CMFV[1] then color.white else color.pink);
CMFV.setPaintingStrategy(paintingStrategy.LINE);
CMFV.setlineWeight(2);
def PlotToNormalize = CMFV;

plot NormalizedCMFV = ((MaxValue - MinValue) * (PlotToNormalize - LowestAll(PlotToNormalize)) / (HighestAll(PlotToNormalize) - LowestAll(PlotToNormalize)) + MinValue) * 100;
NormalizedCMFV.assignValueColor(If CMFV>=CMFV[1] then color.white else color.pink);
NormalizedCMFV.setPaintingStrategy(paintingStrategy.LINE);
NormalizedCMFV.setlineWeight(2);
input ShowCurrentMoneyFlowVolume = yes;

AddLabel(ShowCurrentMoneyFlowVolume, "CMFV(Acc/Dist) %=" + NormalizedCMFV, (if NormalizedCMFV > NormalizedCMFV[1] then Color.LIGHT_GREEN else if NormalizedCMFV < NormalizedCMFV[1] then Color.PINK else Color.YELLOW));

plot AccumulationDistribution = simpleMovingAvg(CMFV,AccumulationDistributionLength);
 
L

lmk99

Member
VIP
@MattATM Thanks for sharing this! I'd be very curious to find out if the repainting / refitting is the same or not with this normalization code as it is with netarchitech's normalization code. I think it would be approximately the same because the "LowestAll" and "HighestAll" functions will necessarily cause refitting to occur as new highs and new lows occur. I think this is an unavoidable feature of any normalization. But I'm not sure.

With MoneyFlowIndex (the default one that comes pre-installed with TOS), does the line actually ever repaint or not? I wonder if the repainting issue is affected by the length used, e.g. if you used a length of 1 for a normalized moving average, for example, it seems to me like maybe the plot would never repaint because the highest of 1 period will just appear as the maximum value (so it would be like a t/f oscillator). But again I'm not sure, I'm just speculating based on what makes sense to me, I could be totally wrong.
 
MattATM

MattATM

Active member
VIP
So it cannot repaint but it can re-scale...For instance let's say the highest value BIG_X=10,000 happened 2 weeks ago, it gets normalized to 1 and the day you are looking at has a high of Medium_X=5,000 and that gets normalize to 0.5. Well suppose you drop your chart time to 5 days... Then BIG_X will be gone and you values will change around the high that your chart shows...

This will not Re-Paint as you say because the values are still at the same highs and lows it will just re-scale...

@netarchitech @lmk99 this would not be a problem to you boys as long as you keep charts open more than just a few days and especially if you use the Covariance ; Correlation scripts so you relate a Acc/Dist (normalized to say -1, or 0.5, or 1 ...etc) to a price move or whatever you want...
Then you can read what you are saying as "The greatest increase in Acc/Dist cause a +20% price increase" OR "Slacking in Acc/Dist lead a 10% price retracement and a 3 days consolidation period" ... Do you agree with this statement?
 
L

lmk99

Member
VIP
@MattATM Interesting. So if I understand you correctly, I think that the code we were using also does not repaint, it only rescales. So it may function essentially the same as your normalization code (I'm not sure). However, the reason I was calling the rescaling "repainting" was because I was using multiple line plots in the same lower section, all normalized together. They are normalized plots of the "rate of change" for various things. I was using "this plot line is above that plot line" or "that plot line crosses below this plot line" as signals. The way the re-scaling works, I think that these kinds of signals using the relative positions of the plot lines will essentially behave as if they're repainting, because since each plot line is normalized individually, you can have a situation where a new high or new low makes one line rescale in such a way that it is no longer above or below the same lines it used to be above or below. As such, these kinds of signals on a normalized multi-plot line study can still be useful but may only appear in real time so they're hard to backtest, or check for reliability by looking back over the history of the chart. Does this make sense?

As for the correlation function, it's an interesting thought that maybe it could define the relationships between the plot lines in a manner that was capturing the same information as their relative positions and crossovers/crossunders, but in a way that was agnostic to their scaling to a particular level on the normalized range at a particular moment. I'm not sure. I think it's probably beyond my mathematical ability to figure that one out.
 
MattATM

MattATM

Active member
VIP
I think that these kinds of signals using the relative positions of the plot lines will essentially behave as if they're repainting,
No problem sir, as long as you remember you cannot compare the Normalized function to anything except another normalized function.
If you were to say multiple price Oscillator x Normalized CMFV and you change timelines your study which was always doubtful will be ruined completely. But if you say look for a pattern between normalized Price Oscillator in proportion to Normalized CMFV then you may achieve a powerful clarity. You will need to leave a good number of Bars on your chart. If you only had a chart with 5 bars well 1 bar will be the 100%MAX and another will be -0% MIN (not useable)
I'm already aiming for trying to figure out if there is an inverse correlation target for implied volatility and downward moves.
@wtf_dude sounds like you have a use already... Yeah I know volatility counts against calls that run up in value to quickly! Folks assume volatility means market crash but really it's the frantic buying & selling during market crash.

That is a wonderful application @wtf_dude and that would give us information to compare Imp Volatility that Historic Volatility doesn't have which I think that @YungTraderFromMontana was interested in.
 
MattATM

MattATM

Active member
VIP
I'm already aiming for trying to figure out if there is an inverse correlation target for implied volatility and downward moves. Nothing new, but Ive already got a crossover pattern that seems to be pretty reliable
@wtf_dude & @netarchitech & @Welkin
It may be buried in the group chat but I made some headway in relating the change in movement of a continuous VWAP to the Volume required to move into a resistance.
In the models we consider we are constantly thwarted (or at least limited) by not immediately knowing why a volume of traders is present.
https://usethinkscript.com/threads/accessing-seconds-from-1min-chart.3516/post-34560

Our traditional way of saying volume that enters when price increases will frankly never cut it because we will never know if the volume is truly bullish or bearish or if the volume is resisting the price move to hold S&R... (At least not until it is hindsight will we ever know)

As the group chat is messy I will send a group email out later when I work on it.
 
Last edited:

Similar threads

Top