
Author Message:
Concepts
Introducing the innovative PercentX Oscillator, a representation of Bollinger PercentB and Keltner Percent K. This powerful tool offers users the flexibility to customize their PercentK oscillator, including options for the type of moving average and length.
The Oscillator Range is derived dynamically, utilizing two lengths - inner and outer. The inner length initiates the calculation of the oscillator's highest and lowest range, while the outer length is used for further calculations, involving either a moving average or the opposite side of the highest/lowest range, to obtain the oscillator ranges.
Next, the Oscillator Boundaries are derived by applying another round of high/low or moving average calculations on the oscillator range values.
Breakouts occur when the close price crosses above the upper boundary or below the lower boundary, signaling potential trading opportunities.
More Details : https://www.tradingview.com/v/WKgRAD0V/
CODE:
CSS:
#// https://www.tradingview.com/v/WKgRAD0V/
#// © Trendoscope Pty Ltd
#strategy('PercentX Trend Follower [Trendoscope]', shorttitle='pX-TF [Trendoscope]'
# Converted by Sam4Cok@Samer800 - 09/2023
declare lower;
input bandType = {default "Keltner Channel", "Bollinger Bands"}; # 'Select the band type'
input maType = {"EMA", "SMA", default "HMA", "RMA", "VWMA", "WMA"};
input maLength = 40;
input useTrueRange = yes;# 'Use True Range (KC)','Use true range - applicable only for Keltner Channel'
input rangeMethod = {default "High/Low", "SMA", "EMA", "RMA", "HMA", "WMA", "VWMA", "SWMA"}; #'Range Calculation'
input loopbackPeriod = 80; # 'Range Calculation'
input loopbackPeriodLast = 20;# tooltip = 'Range Calculation method, length and range length')
input stickyBorders = yes;# 'Sticky Borders'
input outerRangeMethod = {default "High/Low", "SMA", "EMA", "RMA", "HMA", "WMA", "VWMA", "SWMA"};
input outerLoopback = 80; # tooltip='Method and length for calculating outer range. Outer range is extreme of oversold and overbought ranges')
input doubleSided = yes;# 'Double Sided'
input useInitialStop = yes; #'Enable Stop'
input atrLength = 14;
input trendMultiplier = 1;
input reverseMultiplier = 3;#'Enable Stop and Configure ATR length, Trend and reverse Multiplier for the stop'
def na = Double.NaN;
def last = isNaN(close);
#getStickyRange(highsource, lowsource, upper, lower, sticky=false)=>
script getStickyRange {
input source = high;
input upper = high;
input lower = low;
input sticky = no;
def newUpper;
def newLower;
def newUpperPrev = if (isNaN(newUpper[1]) or !newUpper[1]) then upper else newUpper[1];
def newLowerPrev = if (isNaN(newLower[1]) or !newLower[1]) then lower else newLower[1];
def highBreakout = source[1] >= newUpperPrev;
def lowBreakout = source[1] <= newLowerPrev;
def breakout = (highBreakout or lowBreakout);
newUpper = if !sticky then upper else
if breakout then upper else newUpperPrev;
newLower = if !sticky then lower else
if breakout then lower else newLowerPrev;
plot UpBand = newUpper;
plot LoBand = newLower;
}
script customseries {
input source = close;
input type = "SMA";
input length = 14;
def hh = Highest(high, length);
def ll = Lowest(low, length);
def voAvg = Average(volume, length);
def srcVolAvg = Average(source * volume, length);
def VWMA = srcVolAvg / voAvg;
def swma = source[3] * 1 / 6 + source[2] * 2 / 6 + source[1] * 2 / 6 + source[0] * 1 / 6;
def ma = if type == "EMA" then ExpAverage(source, length) else
if type == "SMA" then Average(source, length) else
if type == "RMA" then WildersAverage(source, length) else
if type == "HMA" then HullMovingAvg(source, length) else
if type == "WMA" then WMA(source, length) else
if type == "VWMA" then VWMA else
if type == "SWMA" then swma else Double.NaN;#(hh + ll) / 2;
plot out = ma;
}
def MovAvgMid = customseries(close, maType, maLength);
def bbMiddle = MovAvgMid;
def bbUpper = bbMiddle + StDev(close, maLength) * 0.01;
def bbLower = bbMiddle - StDev(close, maLength) * 0.01;
def tr = TrueRange(high, close, low);
def kcMiddle = MovAvgMid;
def span = if (useTrueRange) then tr else (high - low);
def rangeMa = customseries(span, maType, maLength);
def kcUpper = kcMiddle + rangeMa * 0.01;
def kcLower = kcMiddle - rangeMa * 0.01;
def middle;
def upper;
def lower;
switch (bandType) {
case "Keltner Channel" :
middle = kcMiddle;
upper = kcUpper;
lower = kcLower;
case "Bollinger Bands" :
middle = bbMiddle;
upper = bbUpper;
lower = bbLower;
}
def distance = close - middle;
def midist = middle - lower;
def oscillator = distance / midist;
def oscHighest = Highest(oscillator, loopbackPeriod);
def oscLowest = Lowest(oscillator, loopbackPeriod);
def methodHiLo = rangeMethod == rangeMethod."High/Low";
def oscOverbought = if methodHiLo then Lowest(oscHighest, loopbackPeriodLast) else
customseries(oscHighest, rangeMethod, loopbackPeriodLast);
def oscOversold = if methodHiLo then Highest(oscLowest, loopbackPeriodLast) else
customseries(oscLowest, rangeMethod, loopbackPeriodLast);
def ob = getStickyRange(oscillator, oscOverbought, oscOversold, stickyBorders).UpBand;
def os = getStickyRange(oscillator, oscOverbought, oscOversold, stickyBorders).LoBand;
def overbought = ob;
def oversold = os;
def highOverboughtMa = customseries(overbought, outerRangeMethod, outerLoopback);
def highestOverbought = highest(overbought, outerLoopback);
def lowOversoldMa = customseries(oversold, outerRangeMethod, outerLoopback);
def lowestOversold = lowest(oversold, outerLoopback);
def outRangeHiLo = outerRangeMethod == outerRangeMethod."High/Low";
def upperRange = if outRangeHiLo then highestOverbought else highOverboughtMa;
def lowerRange = if outRangeHiLo then lowestOversold else lowOversoldMa;
def longSignal = (oscillator > upperRange) and (oscillator[1] <= upperRange[1]);
def shortSignal = (oscillator < lowerRange) and (oscillator[1] >= lowerRange[1]);
def upOverflow = if isNaN(upOverflow[1]) then 0 else
longSignal or (upOverflow[1] and oscillator > overbought);
def downOverflow = if isNaN(downOverflow[1]) then 0 else
shortSignal or (downOverflow[1] and oscillator < oversold);
plot STATE = oscillator;#, title='PercentX Oscillator'
STATE.SetLineWeight(2);
STATE.AssignValueColor( if upOverflow then Color.GREEN else
if downOverflow then Color.RED else CreateColor(33,150,243));
plot lowerBandFill = if downOverflow then oversold else na;#, "Lower Overflow"
lowerBandFill.SetDefaultColor(Color.RED);
plot lowerBand = if last then na else oversold;#, "Lower"
lowerBand.AssignValueColor(if downOverflow then Color.RED else Color.DARK_RED);
lowerBand.SetStyle(Curve.SHORT_DASH);
plot llowerBand = if last then na else lowerRange;#, "Extreme Lower"
llowerBand.AssignValueColor(if downOverflow then Color.RED else Color.DARK_RED);
plot upperBandFill = if upOverflow then overbought else na;#, "Upper Overflow"
upperBandFill.SetDefaultColor(Color.GREEN);
plot upperBand = if last then na else overbought;#, "Upper"
upperBand.AssignValueColor(if upOverflow then Color.GREEN else Color.DARK_GREEN);
upperBand.SetStyle(Curve.SHORT_DASH);
plot uupperBand = if last then na else upperRange;#, "Extreme Upper"
uupperBand.AssignValueColor(if upOverflow then Color.GREEN else Color.DARK_GREEN);
AddCloud(lowerBandFill, STATE, color.RED);
AddCloud(STATE, upperBandFill, color.GREEN);
#-- END of CODE