IBD Distribution Days Study for ThinkOrSwim

There is a R1 indication on the chart for today's price action. I added additional conditions to qualify for the 1st rally day so that we don't show more than 1 R1 day in a confirmed rally.

Replace existing code with the following new code to support it.
Code:
def realRday = cls > cls[1] and
               (prLo <= lowest(prLo[1],rDayInterval) or
                prLo[1] <= lowest(prLo[1],rDayInterval)) and
               (prLo <= lowest(futureLow, rDayInterval) or
                prLo[1] <= lowest(futureLow, rDayInterval)) and
               sum(realRday[1], rDayInterval) == 0 and
               sum(pinkRday[1], rDayInterval) == 0;
Thank You.....
 

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

Sharing: If anyone is interested in upper study, the code plots an arrow on Rally. Enjoy

declare upper;
def length = 20;
def prHi = high;
def prLo = low;
def cls = close;
def vol = volume;
def rDayInterval = Round(25 / 2, 0);

def futureLow = if IsNaN(prLo[-rDayInterval]) then futureLow[1]
else prLo[-rDayInterval];
def futureCls = if IsNaN(cls[-rDayInterval]) then futureCls[1]
else cls[-rDayInterval];

# market correction is currently defined as down 8% from top
def mktCr = prLo <= Highest(high, 25) * .92;
def prRng = TrueRange(prHi, cls, prLo); #prHi - prLo;
def pinkRday = cls > (prLo + (prHi - prLo) / 2) and cls < cls[1] and
prLo <= Lowest(prLo[1], rDayInterval) and
prLo <= Lowest(futureLow, rDayInterval);
def realRday = cls > cls[1] and
cls <= Lowest(futureCls, rDayInterval) and
cls[1] <= Lowest(cls[1], rDayInterval) and
Sum(pinkRday[1], rDayInterval) == 0;

def RallyDay1 = (mktCr or mktCr[1]) and (pinkRday or realRday);
AddChartBubble(RallyDay1, vol, "R1", Color.LIGHT_GREEN);
plot R1 = (mktCr or mktCr[1]) and (pinkRday or realRday);
 
Sharing: If anyone is interested in upper study, the code plots an arrow on Rally. Enjoy

declare upper;
def length = 20;
def prHi = high;
def prLo = low;
def cls = close;
def vol = volume;
def rDayInterval = Round(25 / 2, 0);

def futureLow = if IsNaN(prLo[-rDayInterval]) then futureLow[1]
else prLo[-rDayInterval];
def futureCls = if IsNaN(cls[-rDayInterval]) then futureCls[1]
else cls[-rDayInterval];

# market correction is currently defined as down 8% from top
def mktCr = prLo <= Highest(high, 25) * .92;
def prRng = TrueRange(prHi, cls, prLo); #prHi - prLo;
def pinkRday = cls > (prLo + (prHi - prLo) / 2) and cls < cls[1] and
prLo <= Lowest(prLo[1], rDayInterval) and
prLo <= Lowest(futureLow, rDayInterval);
def realRday = cls > cls[1] and
cls <= Lowest(futureCls, rDayInterval) and
cls[1] <= Lowest(cls[1], rDayInterval) and
Sum(pinkRday[1], rDayInterval) == 0;

def RallyDay1 = (mktCr or mktCr[1]) and (pinkRday or realRday);
AddChartBubble(RallyDay1, vol, "R1", Color.LIGHT_GREEN);
plot R1 = (mktCr or mktCr[1]) and (pinkRday or realRday);
I tried it to just check it out and never seen any arrows. I must be doing something wrong.
 
Dear all, I have modified the IBD Distribution Days Study for TOS shared from SMO_MktVolumesDaily
with buy/sell signals
I hope you will find my work helpful on your trading journey.

Ruby:
# SMO_MktVolumesDaily.ts
# For daily charts only.
# Nasdaq total volume: $TVOL/Q ;   NYSE total volume: $TVOL
# Distribution day count tracking by IBD:
# https://www.investors.com/how-to-invest/investors-corner/
# tracking-distribution-days-a-crucial-habit/
# Stalling daysFromDate tracking by IBD:
# https://www.investors.com/how-to-invest/investors-corner/
# can-slim-market-tops-stalling-distribution/
#BuySell Signal added by dag 2022-03-27

# For daily charts only.
######################################################################################
declare once_per_bar;
input ShowChartBubbleDescription = no;
Input ShowDistrubutionLabels=no;


DefineGlobalColor("B1", Color.GREEN);
DefineGlobalColor("B2", Color.LIGHT_GREEN);
DefineGlobalColor("B3", Color.LIGHT_GREEN);
DefineGlobalColor("B4", Color.LIGHT_GREEN);
DefineGlobalColor("B5", Color.LIGHT_GREEN);
DefineGlobalColor("B6", Color.LIME);
DefineGlobalColor("B7", Color.LIGHT_GREEN);
DefineGlobalColor("B8", Color.LIGHT_GREEN);
DefineGlobalColor("B9", Color.LIGHT_GREEN);
DefineGlobalColor("B10", Color.LIGHT_GREEN);

DefineGlobalColor("S1", Color.LIGHT_RED);
DefineGlobalColor("S2", Color.RED);
DefineGlobalColor("S2ml", Color.DOWNTICK);
DefineGlobalColor("S3", Color.LIGHT_RED);
DefineGlobalColor("S4", Color.LIGHT_RED);
DefineGlobalColor("S5", Color.LIGHT_RED);
DefineGlobalColor("S6", Color.LIGHT_RED);
DefineGlobalColor("S7", Color.LIGHT_RED);
DefineGlobalColor("S8", Color.LIGHT_RED);
DefineGlobalColor("S9", Color.PLUM);
DefineGlobalColor("S10", Color.LIGHT_RED);
DefineGlobalColor("S11", Color.LIGHT_RED);
DefineGlobalColor("S12", Color.LIGHT_RED);
DefineGlobalColor("S13", Color.LIGHT_RED);
DefineGlobalColor("S14", Color.YELLOW);

DefineGlobalColor("Circuit Breaker", Color.RED);
DefineGlobalColor("BuySwitch On", Color.GREEN);
DefineGlobalColor("BuySwitch Off", Color.WHITE);




#############################################################################################

#SWING HIGH LOW
def LookbackPeriod = 9; #
input ShowHLLevels= no;

#--------------------------------------------------------------
def _highInPeriod1 = Highest(high, LookbackPeriod);
def _lowInPeriod1 = Lowest(low, LookbackPeriod);
#--------------------------------------------------------------
def marketLow1 = if _lowInPeriod1 < _lowInPeriod1[-LookbackPeriod] then _lowInPeriod1 else _lowInPeriod1[-LookbackPeriod];
def _markedLow1 = low == marketLow1;

rec _lastMarkedLow1 = CompoundValue(1, if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else _lastMarkedLow1[1], low);
#--------------------------------------------------------------
def marketHigh1 = if _highInPeriod1 > _highInPeriod1[-LookbackPeriod] then _highInPeriod1 else _highInPeriod1[-LookbackPeriod];
def _markedHigh1 = high == marketHigh1;

rec _lastMarkedHigh1 = CompoundValue(1, if IsNaN(_markedHigh1) then _lastMarkedHigh1[1] else if _markedHigh1 then high else _lastMarkedHigh1[1], high);
#--------------------------------------------------------------
plot Resistance1 = _lastMarkedHigh1;
plot Support1 = _lastMarkedLow1 ;

#--------------------------------------------------------------
Resistance1.SetPaintingStrategy(PaintingStrategy.DASHES);
Resistance1.SetDefaultColor(Color.GREEN);
Resistance1.SetHiding(!ShowHLLevels);
#--------------------------------------------------------------
Support1.SetPaintingStrategy(PaintingStrategy.DASHES);
Support1.SetDefaultColor(Color.RED);
Support1.SetHiding(!ShowHLLevels);
#--------------------------------------------------------------

input ShowHighLowBuble = yes;
AddchartBubble(ShowHighLowBuble and _markedHigh1,high, round(high,2),color.white);
AddchartBubble(ShowHighLowBuble and _markedLow1,low, round(Low,2),color.white, up = No);

########################################################################################


def length = 50;        # volume moving average lenth in days

input volumeSymbol = {default NASDAQ, NYSE, SPX, CurrentSymbol};
# Reset distribution day counts on FTD.
input distributionRstDay = 20191010;

def volCl;
def volHi;
def findSymbol;

# To make volume differences more visible, use a base volume number
# The subtracted volume number is then magnified to present a bigger difference
def volMin;    # base number for volume
def dropThreshold;

switch (volumeSymbol) {
# It was found there may be erratic volume data on close values
# On 2/19/2020, NYSE volume close values were 0 on 2/18 & 2/12
case NYSE:
    volCl = if close("$TVOL") == 0 then high("$TVOL") else close("$TVOL");
    #volCl = close("$TVOL");
    volHi = high("$TVOL");
# use SPX volume change percentage to replace erratic NYSE volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOLSPC") - close("$TVOLSPC")[1]) / close("$TVOLSPC")[1]) else volCl;
    volMin = 40000;
    dropThreshold = .942;
case NASDAQ:
    volCl = if close("$TVOLC/Q") == 0 then high("$TVOLC/Q") else close("$TVOLC/Q");
    #volCl = close("$TVOL/Q");
    volHi = high("$TVOLC/Q");
# use SPX volume change percentage to replace erratic NASDAQ volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOLSPC") - close("$TVOLSPC")[1]) / close("$TVOLSPC")[1]) else volCl;
    volMin = 30000;
    dropThreshold = .931;
case SPX:
    volCl = if close("$TVOLSPC") == 0 then high("$TVOLSPC") else close("$TVOLSPC");
    #volCl = close("$TVOLSPC");
    volHi = high("$TVOLSPC");
# use NYSE volume change percentage to replace erratic SPX volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOL") - close("$TVOL")[1]) / close("$TVOL")[1]) else volCl;
    volMin = 0;
    dropThreshold = 1;

case CurrentSymbol:
    volCl = if close == 0 then high else close;
    volHi = high;
 
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close - close[1]) / close[1]) else volCl;
    volMin = 10000;
    dropThreshold = .942;

}

def cls = close;

def lastBar = HighestAll(If (IsNaN(cls), Double.NaN, BarNumber()));
def volumes = if IsNaN(findSymbol) and BarNumber() == lastBar then volumes[1] else findSymbol;

def Vol = 3 * (volumes - volMin);
def VolAvg = 3 * (Average(volumes, length) - volMin);


# Display useful texts starting at upper left corner
# End of Day volume change
def VolChangePercentDay = If (IsNaN(volumes[1]), 0,
                              100 * (volumes - volumes[1]) / volumes[1]);

# InvalidDay was added since volume on 2019/11/29 (after Thanksgiving) was N/A.
AddLabel( ShowDistrubutionLabels, if VolChangePercentDay == 0 then "InvalidDay" else "" +
               "VolmChg=" + Concat("", Round(VolChangePercentDay)) +
               "%", if VolChangePercentDay < 0 then
               Color.DARK_GRAY else if cls > cls[1] then Color.DARK_GREEN
               else Color.DARK_RED);

# Count distributionDay only if market price drops 0.2% or more
def downDay = cls <= (cls[1] * 0.998);

def volIncrease = volumes > volumes[1];

#
# After 25 sessions, a distribution day expires
# Use 25 bar numbers to represent 25 live sessions. GetDay or alike includes weekends.
#

def lastDays = if (BarNumber() > lastBar - 25) then 1 else 0;

# a distribution day can fall off the count if the index rises 6% or more,
# on an intraday basis, from its close on the day the higher-volume loss appears.
# Remove distribution days after prices increase 6% WHEN the market is in an uptrend.
# Need to fix:
# During the market bottomed on 2-28-2020, the stock price rose to 9.8% with the market still in
# correction. The high volume selloff on 2-28 would still be counted as a distribution.
# The highest date should be after the distribution day

# Get proper high for future 25 days
def prHi = high;
def prLo = low;

def futureHigh = if IsNaN(prHi[-25]) then futureHigh[1] else prHi[-25];
def prHighest = Highest(futureHigh, 25);
# Note: This condition disqualifies D-Days after large bear rally
# This is acceptable for now since D-Days in bear market are not really useful
def priceInRange = (cls * 1.06 >= prHighest);

def distributionDay = downDay and volIncrease and lastDays and priceInRange;

# Count valid distribution days in last 25 days
def distDayCount = Sum(distributionDay, 25);


# A broad market correction makes the distribution day count irrelevant
# reset distribution count to 0
# Distribution day count should reset after 2nd confirmation day
# input distributionRstDay = 20191010;   a prior 2nd confirmation day
# input distributionRstDay = 20200402;   a prior 2nd confirmation day
#input distributionRstDay = 20191010;

def newDistributionCycle = GetYYYYMMDD() > distributionRstDay;
# Need to use above variable to restart d-day count
def newDistDays = Sum(distributionDay and newDistributionCycle, 25);

# Display bubble red is count > 5, yellow >3, else while
AddChartBubble(distributionDay and !newDistributionCycle, Vol, Concat("", distDayCount),
                if distDayCount < 3 then Color.WHITE
                else if distDayCount < 5 then Color.LIGHT_ORANGE
                else Color.RED);


# Show D-Day reset line at the reset date input by user
# It appears at the left side of the volume bar
AddVerticalLine( if (GetYYYYMMDD() == distributionRstDay ) then yes
                 else no,
                "                   2ndCnfm",
                Color.GREEN, Curve.MEDIUM_DASH);



# to do: Comparison of preholiday data may be invalid.

#------------------------------------------------------------
# Stalling day counts
# 1. market has been rising and price is within 3% of 25 day high
# 2. Price making a high
#      current close >= prior 2 day close, or
#      current close >= prior day high
# 3. volume >= 95% of prior day volume
# 4. close in lower half of daily range
# 5. small gain within 0.4% for SPX & NASDAQ
# 6. The above IBD criteria disclosed in one article generates too many stalling days
#    Additional rules from IBD book are used to further reduce stalling counts
#    6.1 close up smaller than prior 2 days
#    6.2 low is lower than high of prior day (No unfilled gap-up)
#    6.3 there is at least one decent gain in prior 2 days
#    6.4 daily trading range should be similar to last 2 days
# 7. stalling counts are reduced due to time (25 days) and significantly upward
#    movement (6%) of the index
# Ex. 2019/11/12 was a stalling day on SPX, 2019/12/18 was stalling for Nasdaq

def priceIsHigh = cls >= cls[2] or cls >= prHi[1];
def priceLowHalf = cls < (prHi - prLo) / 2 + prLo;
def priceGainSmall = cls - cls[1] > 0 and
                     ((cls - cls[1] < (cls[1] - cls[2])) or
                     ((cls - cls[1] < cls[2] - cls[3])));

# Added a 0.2% gap from prior day high to allow 2020/05/26 to count
# as a stalling day for NASDAQ
def priceGapFill = prLo < prHi[1] * 1.002;
def priceGainOk = (cls[1] - cls[2] > 0.002 * cls[2]) or
                   (cls[2] - cls[3] > 0.002 * cls[3]);
# price trading range is the high - low plus the gap up if any
def priceRange = if prLo > prHi[1] then prHi - prHi[1] else prHi - prLo;
def priceRangeBig = priceGainOk and priceRange > 0.8 * Min(priceRange[1], priceRange[2]);

def stallDay = cls - cls[25] > 0 and
               cls >= 0.97 * Highest( prHi, 25) and
               volumes > 0.95 * volumes[1] and
               cls - cls[1] > 0 and
               cls - cls[1] < 1.004 * cls[1] and
               priceIsHigh and priceLowHalf and priceGainSmall and priceGapFill and
               priceRangeBig and lastDays;

input ShowStallingDays = yes;
input ShowDistributionDays = yes;
#AddChartBubble(ShowStallingDays and stallDay , high, "Stalling Days", Color.MAGENTA);

# Count stalling days
def stallDayCount = Sum(stallDay, 25);

# calculate new stalling days after the reset day (e.g. follow-up date)
def newStallDays = Sum(stallDay and newDistributionCycle, 25);

# Display final distribution count (incl. stall days)
# red if >= 5, >3: yellow, else green
def totalDdays = distDayCount + stallDayCount;
def totalNdDays = newDistDays + newStallDays;

AddChartBubble(ShowDistributionDays and distributionDay and newDistributionCycle, high,
                if volCl == 0 then Concat("?", newDistDays) else Concat("", newDistDays),
                if totalNdDays < 3 then Color.WHITE
                else if totalNdDays < 5 then Color.LIGHT_ORANGE
                else Color.RED);


AddChartBubble(ShowStallingDays and stallDay and lastDays, high,
                if volCl == 0 then "?Stalling:" + Concat("", stallDayCount)
                              else "Stalling:" + Concat("", stallDayCount),
                if totalNdDays < 3 then Color.WHITE
                else if totalNdDays < 5 then Color.LIGHT_ORANGE
                else Color.RED);
AddChartBubble(volCl == 0 and !stallDay and !distributionDay, high, "?", Color.LIGHT_GRAY);


AddLabel(ShowDistrubutionLabels and totalDdays != totalNdDays, "AllDdays =" + Concat("", totalDdays), Color.GRAY);

#-------------------------------------------------------------------------------------
# Follow-through signals (FTD) are more likely to fail if distribution days
# occur in the first few days of a new uptrend. This is one key red flag.
# Quantification in script is implemented with a concept of critical score (critScore):
#   critScore = 3 for the 1st 5 days after FTD
#   critScore = 2 on the 6th, critScore = 1 on 7th day, critScore = 0 after 7th day
#   Total Distribution days = critScore + regular D-day count
#   critScore is used only if there is at least one D-day in the 1st 5th day after FTD
#-------------------------------------------------------------------------------------

def ftdBar = if GetYYYYMMDD() == distributionRstDay then BarNumber() else 0;
def lastFtdBar = HighestAll(ftdBar);
def daysAfterFTD = lastBar - lastFtdBar;

def critScore = if daysAfterFTD <= 0 then 0 else
                if daysAfterFTD <= 5 then 3 else
                if daysAfterFTD <= 6 then 2 else
                if daysAfterFTD <= 7 then 1 else 0;

def totalNdDaysC = totalNdDays + if totalNdDays > 0 then critScore else 0;

# Actual distribution day count is shown but color depends on totalNdDaysC
AddLabel(ShowDistrubutionLabels, "NewDdays =" + Concat("", totalNdDays ),
         if totalNdDaysC <= 2 then Color.GREEN
         else if totalNdDaysC <= 4 then Color.ORANGE
         else Color.RED);


# Add an indication of 1st rally day to start FTD count
# in a market correction period
# pink rally day is a day satisfying the following conditions:
# 1). Close above ½ of daily TRUE range and below prior day close
# 2). Low is the lowest during the market correction,
#     including future lows if available

# resolution of each 1st rally day is set to about 2 weeks

def rDayInterval = Round(25 / 2, 0);

def futureLow = if IsNaN(prLo[-rDayInterval]) then futureLow[1]
                else prLo[-rDayInterval];
def futureCls = if IsNaN(cls[-rDayInterval]) then futureCls[1]
                else cls[-rDayInterval];

# market correction is currently defined as down about 8% from top
# need to be refined so that it will work in a bear market that is forming a bottom.
# In this case, the 8% drop may not be required.
def mktCr    = prLo[1] <= Highest(high, 25) * dropThreshold; #.931; #.92;
def prRng    = TrueRange(prHi, cls, prLo); #prHi - prLo;


def pinkRday = cls > (prLo + prRng / 2) and cls < cls[1] and
               prLo <= Lowest(prLo[1], rDayInterval) and
               prLo <= Lowest(futureLow, rDayInterval);
 

# The real rally day has its close higher than prior close
# A rally day is invalidated if the low is broken in subsequent days
def realRday = cls > cls[1] and
               (prLo <= Lowest(prLo[1], rDayInterval) or
                prLo[1] <= Lowest(prLo[1], rDayInterval)) and
               (prLo <= Lowest(futureLow, rDayInterval) or
                prLo[1] <= Lowest(futureLow, rDayInterval)) and
               Sum(realRday[1], rDayInterval) == 0 and
               Sum(pinkRday[1], rDayInterval) == 0;

def RallyDay1 = (mktCr or mktCr[1]) and (pinkRday or realRday);

AddChartBubble(RallyDay1, high, "R1", Color.LIGHT_GREEN);



######################################################################
#                            FTD Requirements                        #
######################################################################
#1.Heavier volume than the previous daysAfterFTD  -> volIncrease

#2. Volatility is defined as the percentage gain of the up days over the past 200 days

def IBDlengthVolatility = 200;

def PercentageGreenBars = Round((close - close[1]) / close * 100, 2);

def upday = if close > close[1] then 1 else 0;
def SumUpdays = Sum(upday, IBDlengthVolatility);

def UpdaysPercentage = if upday then PercentageGreenBars else 0;
def SumUpdaysPercentage = Sum(UpdaysPercentage, IBDlengthVolatility);

def volatilityIBD = Round(SumUpdaysPercentage / SumUpdays, 2);

#Market Volatility and FTD price change requirements as percentageGreenBars IBD Market School.

def FTDPriceRequirement = if volatilityIBD < 0.4 then 1.007
else if volatilityIBD >= 0.4 and  volatilityIBD < 0.55 then 1.0085
else if  volatilityIBD >= 0.55 and volatilityIBD < 1 then 1.01
else 1.01245;


######################################################################
#                           FTD Requirements                         #
#--------------------------END-----END----END------------------------#
######################################################################



# Currently (April, 2020) a daily price (cls) increase of 1.25% minimum is
# the price requirement by either SPX or NASDAQ for a FTD.
# Must be day 4 after 1st rally attempt /w an exception (1st 3 days are strong).
# Must have higher volume on the FTD day

def redDay = if close < close[1] then 1 else 0;

def lastR1Bar  = If (RallyDay1, BarNumber(), lastR1Bar[1]);
def daysAfterR1 = BarNumber() - lastR1Bar;


#The Rally days is counting as day number 1  meaning daysAfterR1 = 0 so 4 days and more is daysAfterR1>=3

def isFTD = if daysAfterR1 >= 3  and BarNumber() > lastR1Bar and
               BarNumber() < lastR1Bar + 25 and lastR1Bar != 0 and
               cls >= (cls[1] * FTDPriceRequirement) and
               volIncrease then 1 else 0 ;


def totalR1toFTD = if RallyDay1 then totalR1toFTD[1] + 1  else if !RallyDay1 and !isFTD then totalR1toFTD[1] else 0;

def oneFTD = totalR1toFTD < totalR1toFTD[1];
AddChartBubble(oneFTD, high, "FTD", Color.LIGHT_ORANGE);

def lastOneFTDBar  = If (oneFTD, BarNumber(), lastOneFTDBar[1]);
def daysAfterOneFTD = BarNumber() - lastOneFTDBar;


######################################################################
#                    Distribution Days And Stalling days           #
######################################################################
#Distribution Day
DefineGlobalColor("DistibutionDay", Color.DARK_ORANGE);
DefineGlobalColor("StallingDay", Color.DARK_RED);

input ShowAllDistributionDays = no;
input ShowAllStallingDays = no;



def distributionDays = downDay and volIncrease and priceInRange;

def stallDays = cls - cls[25] > 0 and
               cls >= 0.97 * Highest( prHi, 25) and
               volumes > 0.95 * volumes[1] and
               cls - cls[1] > 0 and
               cls - cls[1] < 1.004 * cls[1] and
               priceIsHigh and priceLowHalf and priceGainSmall and priceGapFill and
               priceRangeBig;

def totalDS =  if stallDayCount >= distDayCount then distDayCount*2-1 else totalDdays;

def distributionDaysCount = sum(distributionDays,25); #check the last 25 days for the S3 and S4 signal.
def distributionDaysCount26 = sum(distributionDays,26); #check the last 26 days for the B10 Signal.
def distributionDaysCount8 = sum(distributionDays,8); #check the last 8 days for the S13 signal.

def stallDaysCount = sum(stallDays,25);
def stallDaysCount26 = sum(stallDays,26);
def stallDaysCount8 = sum(stallDays,8);

def totalDSdays = distributionDaysCount + stallDaysCount;
def totalDSdays26 = distributionDaysCount26 + stallDaysCount26;
def totalDSdays8 = distributionDaysCount8 + stallDaysCount8;

def totalAllDS =  if stallDaysCount >= distributionDaysCount then distributionDaysCount*2-1 else totalDSdays;
def totalAllDS26 =  if stallDaysCount26 >= distributionDaysCount26 then distributionDaysCount26*2-1 else totalDSdays26;


AddChartBubble("time condition" = ShowAllDistributionDays and distributionDays, "price location" = high, text =  if ShowChartBubbleDescription then "Distribution Day (" + PercentageGreenBars + "%)" else "DD", GlobalColor("DistibutionDay"));

AddChartBubble("time condition" = ShowAllStallingDays and stallDays, "price location" = high, text =  if ShowChartBubbleDescription then "Stalling Day (" + PercentageGreenBars + "%)" else "SD", GlobalColor("StallingDay"));



######################################################################
#                       Moving Averages EMA21 MA50                   #
######################################################################

def ema21 = MovAvgExponential("length" = 21)."AvgExp";
def ma50 = MovingAverage(data = close, "length" = 50 );


######################################################################
#                            FTD Signals     (B1)                    #
######################################################################
#Buy on initial follow-through day.
input showB1FTD = yes;
def B1= oneFTD;

AddChartBubble(showB1FTD and B1, high, if ShowChartBubbleDescription then "(B1) " + (daysAfterR1 + 1) + "days from R1 (+ " + PercentageGreenBars + "%)" else "B1", Color.GREEN);

######################################################################
#                         Additional FT Days    (B2)                 #
######################################################################
#Buy on all additional follow-through days within 25-days from a rally day that closes above the low of the initial follow-through day.
input showB2 = yes;
def LowOneFTD = if oneFTD then low else LowOneFTD[1];
def B2 = isFTD and !oneFTD and close > LowOneFTD and daysAfterR1 < 25;
def CountB2 = if B2 then CountB2[1] + 1  else if !B2 and daysAfterR1 < 25 then CountB2[1] else 0;

AddChartBubble("time condition" = showB2 and B2, "price location" = high, text =  if showChartBubbleDescription then "B2-" + CountB2 + " FTD (+ " + PercentageGreenBars + "%)" else "B2", color = GlobalColor("B2"));



######################################################################
#                            Low Above Ema21    (B3)                 #
######################################################################

#S5Reset for resetting the buy signals B3, B4Signal & B5
#S5 Break below ema21
#Sell if index closes 0.2% or greater below ema21
#B3, B4 & B5 Reset by S5. Once you have B3 or B4, you can't have another until is reset by an S5

def S5ResetbelowEma21 = cls <= (ema21 * 0.998);


#Buy on and up or flat day when then intra-day low is at or above then ema21
input showB3 = yes;
def B3 =  low >= ema21 and upday; # and !oneFTD;

def totalB3toS5 = if B3 then totalB3toS5[1] + 1 else if !B3 and !S5ResetbelowEma21 then totalB3toS5[1] else 0;
def totalS5toB3 = if S5ResetbelowEma21 then totalS5toB3[1] + 1 else if !B3 and !S5ResetbelowEma21 then totalS5toB3[1] else 0;

def oneB3 = totalS5toB3 < totalS5toB3[1];
def S5Reset = totalB3toS5 < totalB3toS5[1];

def lastoneB3Bar  = If (oneB3, BarNumber(), lastoneB3Bar[1]);
def daysAfterB3 = BarNumber() - lastoneB3Bar;


AddChartBubble("time condition" = showB3 and oneB3, "price location" = high, text = if showChartBubbleDescription then  "B3 low>ema21. Reset S5, S6, S7 & S8 (" + PercentageGreenBars + "%)" else "B3", color = GlobalColor("B3"));



######################################################################
#                    Trending Above Ema21    (B4)                    #
######################################################################
#Buy after a B3 signal has triggered, buy on the 5th consecutive day that the low is at or above the ema21. The B4 signal is only triggered when the index closes up or flat for the day. If the index close down on the 5th day from the B3, then wait for the next up day to trigger B4.

input showB4 = yes;

# signal for reset the B4
def S5B4ResetSignal = !S5Reset;

def B4Signal = if !oneB3 and daysAfterB3 >= 4 and B3 and S5B4ResetSignal then 1 else 0;

def SumB3toB4 = if oneB3 then SumB3toB4[1] + 1 else if !B4Signal and !oneB3 then SumB3toB4[1] else 0;
def oneB4 = SumB3toB4 < SumB3toB4[1];

AddChartBubble("time condition" = showB4 and oneB4 , "price location" = high, text =  if ShowChartBubbleDescription then "B4 Trending Above ema21. " + (daysAfterB3 + 1) + "days from (B3) (" + PercentageGreenBars + "%)" else "B4", GlobalColor("B4"));


######################################################################
#                        Living Above Ema21  (B5)                    #
######################################################################
#Buy after a B3 signal has triggered, buy on the 10th and every 5th consecutive day after that (15th, 20th, 25th, etc.) day's low is at or above the ema21. The B5 signal is only triggered when the index closes up or flat for the day. If the index close down on the 5th day from the B3, then wait for the next up day to trigger B5.


input showB5 = yes;
def fiveDaysB3 = (daysAfterB3+1) % 5 == 0;
# signal for on off the B5
def S5B5ResetSignal = !S5Reset; # !oneFTD  and daysAfterB3 < daysAfterS5Reset; # and !oneS5Reset

#B5 signal
def B5Signal = daysAfterB3 >= 9 and B3 and S5B5ResetSignal;

def B5_5 =  B5Signal and fiveDaysB3;
def B5_6 = if !B5_5[1] then B5Signal and fiveDaysB3[1] else 0;
def B5_7 = if !B5_6[1] and !B5_5[2] then B5Signal and fiveDaysB3[2] else 0;
def B5_8 = if !B5_7[1] and !B5_6[2] and !B5_5[3] then B5Signal and fiveDaysB3[3] else 0;
def B5_9 = if !B5_8[1]  and !B5_7[2] and !B5_6[3] and !B5_5[4] then B5Signal and fiveDaysB3[4] else 0;

def B5 = B5_5 or B5_6 or B5_7 or B5_8 or B5_9;

AddChartBubble("time condition" = showB5 and B5, "price location" = high, text =  if ShowChartBubbleDescription then "B5 Living above ema21 (" + PercentageGreenBars + "%) " + (daysAfterB3+1) + "days from B3. Low >= ema21" else "B5", GlobalColor("B5"));



######################################################################
#                          Low Above MA50      (B6)                  #
######################################################################
#Close Above ma50
#Buy on an up or flat day when then intraday low is at or above an uptreding ma50.
#Uptreding ma50 -> the day that the ma50 is up from then prior day.
#B6 Reset by S9. Once you have B6, you can't have another until is reset by an S9


#Sell if index closes below 50ma.
#Only triggers if a corresponding buy signal related to that moving average B6 triggered previously
#Sell S9 is reset by B6
#Exception: -> Index closes in the upper half of range ("Shakeouk"), and Index closes within 1% of the 50ma

def S9 = cls < (ma50 * 0.99) and close < hl2;
def B6 =  low >= ma50 and ma50 > ma50[1] and upday;
def SumB6toS9 = if B6 then SumB6toS9[1] + 1 else if !B6 and !S9 then SumB6toS9[1] else 0;
def oneS9 = SumB6toS9 < SumB6toS9[1];


#Close Above ma50
#Buy on and up or flat day when then intra-day low is at or above then ema21

input showB6 = yes;

def SumS9toB6 = if S9 then SumS9toB6[1] + 1 else if !B6 and !S9 then SumS9toB6[1] else 0;
def oneB6 = SumS9toB6 < SumS9toB6[1];


def lastoneB6Bar  = If (oneB6, BarNumber(), lastoneB6Bar[1]);
def daysAfterB6 = BarNumber() - lastoneB6Bar;


AddChartBubble("time condition" = showB6 and oneB6, "price location" = high, text =  if ShowChartBubbleDescription then "B6 Low Above ma50 in uptrending ma50, reset S9 (+" + PercentageGreenBars + "%)" else "B6", color = GlobalColor("B6"));


######################################################################
#                       Accumulation Day     (B7)                    #
######################################################################
#Accumulation Buy Signal is B2 but after the 25 days of rally attempt.
#Index closes up the percentage required for a followthrogh day. Low volatility FTD would apply as well
# Close in upper 25% of the range
#close must be greater than ema21
#Cannot coincide with a FTD or subsequent FTD (daysAfterR1 >= 25)
#For GapUp the range calculation is as following (Close-Previous day's close)/(high-previous day's close)

def PriceRangeIBD = if low > high[1] then (close - close[1]) / (high - close[1]) else if high<low[1] then (close - low)/(close[1]-low) else (close - low) / (high - low);

input showB7 = yes;
def B7 =  lastR1Bar != 0 and cls >= (cls[1] * FTDPriceRequirement) and
               volIncrease and !oneFTD and close > LowOneFTD and daysAfterR1 >= 25 and PriceRangeIBD >= 0.75 and close > ema21;


AddChartBubble("time condition" = showB7 and B7, "price location" = high, text =  if ShowChartBubbleDescription then "B7 Accumulation Day (+ " + PercentageGreenBars + "%)" else "B7", color = GlobalColor("B7"));


######################################################################
#                            Higher High     (B8)                    #
######################################################################
#Buy after closing above the last marked high (Last high before Rally1)
input showB8 = yes;

def HH = if IsNaN(_markedHigh1) then _lastMarkedHigh1[1]  else if _MarkedHigh1 then high else if _MarkedHigh1[1] then if high >hh[1] then high else hh[1] else hh[1];
def B8 = high > hh;



def SumB8Back = if _MarkedHigh1[LookbackPeriod] then SumB8Back[1] + 1 else if !_MarkedHigh1[LookbackPeriod] and !B8 then SumB8Back[1] else 0;
def oneB8 = SumB8Back < SumB8Back[1];

AddchartBubble(showB8 and oneb8,high,  if ShowChartBubbleDescription then "B8 Higher High" else "B8",Color.LIME);


######################################################################
#                    Downside Reversal Buyback    (B9)               #
######################################################################
#After selling on a downside reversal, buy back if the index closes above the intraday high of the downside reversal within two trading days.

################ S11 code ################
#Sell after downside reversal day
#New Intra-day high within prior 13 days. On seminar was noted as weeks but on simulator was days.
#close in bottom quartile of range
#Close lower than the previous day (redday)
#Spread of 1.75% or greater. Spread is calculated as the (Intraday High-Intraday Low)/ (Intraday Low)

def spreadIntraday = ((high-low)/low)*100;

def S11 = high == highest(high,13) and PriceRangeIBD <=0.25 and redday and spreadIntraday >= 1.75;

def lastoneS11Bar  = If (S11, BarNumber(), lastoneS11Bar[1]);
def daysAfterS11 = BarNumber() - lastoneS11Bar;

################ B9 code ################

input showB9 = yes;

def S11High = if(S11,high,S11High[1]);

def B9 = close > S11High and daysAfterS11<=2;

def SumB9S11 = if S11 then SumB9S11[1] + 1 else if !S11 and !b9  then SumB9S11[1] else 0;
def oneB9 = SumB9S11 < SumB9S11[1];

AddChartBubble("time condition" = showB9 and oneB9, "price location" = high, text =  if ShowChartBubbleDescription then "B9 Downside Reversal Buyback (" + PercentageGreenBars + "%)" else "B9", color = GlobalColor("B9"));


######################################################################
#                      Distribution Day Fall Off    (B10)            #
######################################################################
#Distribution Day
#Indicative of institutional Selling
#Price down greater than or equal to 0.2% on volume greater than or equal to prior day's volume.

#Stall Day (Stalling)
#Stalling is a form of heavy volume without further upsideGapThreeMethods price progress.
#Stall days occur on updays when the market is at or near new highs and close in the lower 25% range on heavier volume of  previous day.
#After the first stallDay, you do not consider additional stall days in the distribution count unitl you have more distribution days than stall days.
 
# full Distribution counts (Distribution and stall days.)
#Before 1992    DC:4 (Distribution Count)
#1991-2004      DC: 5
#2005-Present   DC:6

#B10
#Buy on the day that the distribution count falls back to four (Full distribution count minus two) as long as the close is above the ema21 and Buy Switch is on.

#totalDdays = distDayCount + stallDayCount
#def totalNdDays = newDistDays + newStallDays;

input showB10 = yes;


def B10 = totalAllDS==4 and totalAllDS26==5 and close > ema21; #Need fix entry for BSON



######################################################################
#                   Follow Through Day Undercut    (S1)              #
######################################################################
#Follow Through Day Undercut (S1)
#Sell if index closes below the Low of the initial follow through day.
#-Does not apply to additional FTDs
input showS1 = yes;

def S1 =  close < LowOneFTD ;
def SumOneS1 = if oneFTD then SumOneS1[1] + 1 else if !S1 and !oneFTD then SumOneS1[1] else 0;
def oneS1 = SumOneS1 < SumOneS1[1];

AddChartBubble("time condition" = showS1 and oneS1, "price location" = high, text =  if ShowChartBubbleDescription then "S1 FTD undercut (" + PercentageGreenBars + "%)" else "S1", color = GlobalColor("S1"));


######################################################################
#                  Failed Rally Attempt         Major Rule (S2)      #
######################################################################
#Sell if index undercuts the "major low" of rally attempt.
#-Market exposure is reduced to zero
#-Buy Switch is turned off.
input showS2 = yes;
input showS2ml = yes;
def MajorLow =  if RallyDay1 and low < low[1] then low else if RallyDay1 and low[1] < low then low[1] else MajorLow[1];
def S2 = low < MajorLow; # and MajorLow == MajorLow[1];

def SumoneS2 = if RallyDay1 then SumoneS2[1] + 1 else if !rallyDay1 and !s2  then SumoneS2[1] else 0;
def oneS2 = SumoneS2 < SumoneS2[1];

#def OneS2 = Sum(S2) == 1 and S2;

def minorLow = if RallyDay1[1] then low else if !rallyDay1 then if low < minorLow[1] then low else minorLow[1] else minorLow[1];



#Sell if index undercuts the "minor low" of rally attempt
#-Market exposure is reduced by two
#-This does not turn off the Buy Switch
#The "minor low" of a rally is defined as a low that occurs when the buy switch is on (oneFTD)
def minorLowS2 = !RallyDay1 and minorLow<minorLow[1];




AddChartBubble("time condition" = showS2 and OneS2, "price location" = high, text =  if ShowChartBubbleDescription then "S2 Failed Rally attempt (" + PercentageGreenBars + "%)" else "S2", GlobalColor("S2"));

#######################################################
# Go to Buy Switch for the bubble signal of minor low #
#######################################################
def BSOnforS2ml = if oneFTD then BSOnforS2ml[1] + 1 else if !oneS2 and !oneFTD then BSOnforS2ml[1] else 0;




######################################################################
#                   Full Distribution minus one   (S3)               #
######################################################################
# Sell after distribution count increases to five (Full distribution is currently six days).
#This rule is applied each new time that it occurs.

input showS3 = yes;


Def S3 = if((distributionDays or stallDays) and totalAllDS==5,1,0);


AddChartBubble("time condition" = showS3 and S3, "price location" = high, text =  if ShowChartBubbleDescription then "S3 Full Distribution minus one (" + PercentageGreenBars + "%)" else "S3", GlobalColor("S3"));


######################################################################
#                         Full Distribution       (S4)               #
######################################################################
#Sell after full distribution count (full distribution is currently six days).
#This rule is applied up to eight days (full distribution plus two).


input showS4 = yes;
def S4 = if((distributionDays or stallDays) and (totalAllDS==6 or totalAllDS==7 or totalAllDS==8) ,1,0);

AddChartBubble("time condition" = showS4 and S4, "price location" = high, text =  if ShowChartBubbleDescription then "S4 Full Distribution (" + PercentageGreenBars + "%)" else "S4", GlobalColor("S4"));


######################################################################
#                       Break Below Ema21         (S5)               #
######################################################################
#Sell if index closes 0.2% or greater below the ema21
#S5, S6, S7, S8 Reset by B3. Once you have S5, S6 or S7, you can't have another until is reset by an B3

# signal for on off the S5
def B3S5ResetSignal = S5Reset; #!oneFTD  and daysafterS5Reset[1] >= daysafterB3; # and !oneS5Reset

#S5 Break below ema21
# Sell if index closes 0.2% or greater below ema21

input showS5 = yes;

def S5belowEma21 = cls <= (ema21 * 0.998);
def S5 =  S5belowEma21 ;

def oneS5 = totalB3toS5 < totalB3toS5[1];

def lastoneS5  = If (oneS5, BarNumber(), lastoneS5[1]);
def daysAfterS5 = BarNumber() - lastoneS5;


AddChartBubble("time condition" = showS5 and oneS5, "price location" = high, text =  if ShowChartBubbleDescription then "S5 Break Below close<ema21. Reset B3, B4 & B5 (" + PercentageGreenBars + "%)" else "S5", GlobalColor("S5"));


######################################################################
#                  Overdue Break Below Ema21       (S6)              #
######################################################################
#Overdue break below ema21 (S6)
#Sell if index closes down 0.2% or greater below the ema21 after 30 days have been passed since last B3 without triggering an S5

input showS6 = yes;

def S6 =  s5 and daysAfterB3 >= 29 ;

def SumB3toS6 = if oneB3 then SumB3toS6[1] + 1 else if !oneB3 and !s6  then SumB3toS6[1] else 0;
def oneS6 = SumB3toS6 < SumB3toS6[1];

AddChartBubble("time condition" = showS6 and oneS6, "price location" = high, text =  if ShowChartBubbleDescription then "S6 Overdue Break low<ema21 (" + PercentageGreenBars + "%)" else "S6", GlobalColor("S6"));

######################################################################
#                      Trending Below ema21       (S7)               #
######################################################################
#Trending Below ema21 (S7)
#After and S5 signal has triggered, sell on the 5th consecutive day that then high is below the ema21. The S7 signal is only triggered when the idex closes down for the day. If the indexes closes up on the 5th day from the S5, then wait for the next down day to trigger the S7


input showS7 = yes;

# signal for on off the B4

def S7 = daysAfterS5 >= 4 and high <= ema21 and redDay ; # !oneS5 and !oneB3

def SumB3toS7 = if oneB3 then SumB3toS7[1] + 1 else if !S7 and !oneb3 then SumB3toS7[1] else 0;
def oneS7 = SumB3toS7 < SumB3toS7[1];

AddChartBubble("time condition" = showS7 and oneS7 , "price location" = high, text =  if ShowChartBubbleDescription then "S7 Trending Below ema21. high<ema21 " + (daysAfterS5+1) + "days from (S5) (" + PercentageGreenBars + "%)" else "S7", GlobalColor("S7"));

######################################################################
#                       Living Below Ema21        (S8)               #
######################################################################
#Trending Below ema21 (S7)
#After and S5 signal has triggered, sell on then 10th and every 5th consecutive day after that (15th, 20th, 25th etc.) that then high is below the ema21. The S8 signal is only triggered when the index closes down for the day. If the indexes closes up on the 10th, 15th, 20th, etc. from the S5, then wait for the next down day to trigger the S8


input showS8 = yes;

# signal for on off the B4
#def S5B4ResetSignalw = !oneFTD  and daysAfterB3 < daysAfterS5Reset; # and !oneS5Reset
def fiveDaysS5 = (daysAfterS5+1) % 5 == 0;

def S8Signal = if daysAfterS5 >=9 and s7 and !oneb3 then 1 else 0;


def S8_5 =  S8Signal and fiveDaysS5;
def S8_6 = if !S8_5[1] then S8Signal and fiveDaysS5[1] else 0;
def S8_7 = if !S8_6[1] and !S8_5[2] then S8Signal and fiveDaysS5[2] else 0;
def S8_8 = if !S8_7[1] and !S8_6[2] and !S8_5[3] then S8Signal and fiveDaysS5[3] else 0;
def S8_9 = if !S8_8[1]  and !S8_7[2] and !S8_6[3] and !S8_5[4] then S8Signal and fiveDaysS5[4] else 0;

def S8 = S8_5 or S8_6 or S8_7 or S8_8 or S8_9;

AddChartBubble("time condition" = showS8 and S8, "price location" = high, text =  if ShowChartBubbleDescription then "S8 Living Below Ema21, high<ema21 / " + (daysAfterS5+1) + "days from (S5) (" + PercentageGreenBars + "%)" else "S8", GlobalColor("S8"));



######################################################################
#                         Break Below MA50          (S9)             #
######################################################################
#Sell if index closes below 50ma.
#Only triggers if a corresponding buy signal related to that moving average B6 triggered previously
#Sell S9 is reset by B6
#Exception: -> Index closes in the upper half of range ("Shakeouk"), and Index closes within 1% of the 50ma
input ShowS9 = yes;
#Go to B6 for the code of the signal.

AddChartBubble("time condition" = ShowS9 and oneS9 , "price location" = high, text =  if ShowChartBubbleDescription then "S9 Break Below MA50, close<MA50, reset B6 (" + PercentageGreenBars + "%)" else "S9", GlobalColor("S9"));


######################################################################
#                               Bad Break           (S10)            #
######################################################################
#Sell if the close is down 2.25% of greater in the bottom 25% of range
#close below the ma50 or Intraday high below ema21
#Range as (close-low)/(high-low) for gap down (close-low)/(close[1]-low)

#def range = if high<low[1] then (close - low)/(close[1]-low) else (close-low)/(high-low);

input showS10 = yes;

def S10 = PercentageGreenBars<=-2.25 and (close < ma50 or high < ema21) and PriceRangeIBD <= 0.25 ;

AddChartBubble("time condition" = showS10 and S10, "price location" = high, text =  if ShowChartBubbleDescription then "S10 Bad Break (" + PercentageGreenBars + "%)" else "S10", GlobalColor("S10"));



######################################################################
#                        Downside Reversal Day      (S11)            #
######################################################################
#Sell after downside reversal day
#New Intra-day high within prior 13 days. On the seminar was noted as weeks but on the simulator were days.
#close in bottom quartile of range
#Close lower than the previous day (redday)
#Spread of 1.75% or greater. Spread is calculated as the (Intraday High-Intraday Low)/ (Intraday Low)

input showS11 = yes;
#for S11 go to B9

AddChartBubble("time condition" = showS11 and S11, "price location" = high, text =  if ShowChartBubbleDescription then "S11 Downside Reversal Day (" + PercentageGreenBars + "%)" else "S11", GlobalColor("S11"));



######################################################################
#                               Lower Low          (S12)             #
######################################################################
#Sell after closing below the last marked low  (9days lookback and 9day forward)

input showS12 = yes;


def LL = if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else if _markedLow1[1] then if low <LL[1] then low else LL[1] else LL[1];
def S12 = close < LL;

def SumOneS12 = if _markedLow1[LookbackPeriod] then SumOneS12[1] + 1 else if !_markedLow1[LookbackPeriod] and !S12 then SumOneS12[1] else 0;
def oneS12 = SumOneS12 < SumOneS12[1];

AddchartBubble(showS12 and oneS12,high, if ShowChartBubbleDescription then "S12 closing below Lower low" else "S12",GlobalColor("S12"));

######################################################################
#                       Distribution Cluster       (S13)             #
######################################################################
#Sell after distribution cluster count ("Cluster Count") increases to four within a rolling eight-day period.
#Sell after each successive incease in the cluster count from four up to eight days within a rolling eight-day period.
#For purposes of distribution clusters, all stall days and distribution days apply.
#The cluster count is reset once the cluster count falls to three within a rolling eight-day period.
#The distribution cluster changes the Power-Trend (if it is in effect) to a Power-Trend Under Pressure.
input showS13 = yes;

def S13 = if((distributionDays or stallDays) and (totalDSdays8==4 or totalDSdays8==5 or totalDSdays8==6 or totalDSdays8==7 or totalDSdays8==8 ) and totalDSdays8 != totalDSdays8[1]  ,1.0,0.0);;

AddchartBubble(showS13 and S13,high, if ShowChartBubbleDescription then "S13 Distribution Cluster" else "S13",GlobalColor("S13"));

######################################################################
#                    Break Below Higher High       (S14)             #
######################################################################
input showS14 = yes;
#Sell after closing below the last marked high that triggered a B8 (Higher High)

def barNumberB8 = If (oneB8, BarNumber(), barNumberB8[1]);;

def S14 =  close<HH and high[1]>hh and (barnumber() - barNumberB8)>0; #Incase of gapdown we use previous days high
def SumOneS14 = if _MarkedHigh1[LookbackPeriod] then SumOneS14[1] + 1 else if !_MarkedHigh1[LookbackPeriod] and !S14 then SumOneS14[1] else 0;;
def oneS14 = SumOneS14 < SumOneS14[1];


AddchartBubble(showS14 and oneS14,high, if ShowChartBubbleDescription then "S14 Breakaway Close Below Higher High" else "S14",GlobalColor("S14"));


######################################################################
########################## Circuit breaker ###########################
######################################################################
#Circuit breaker - occurs when you've got a 10% break of the market from the high point
Input showCircuitBreaker = yes;

def RecentHigh = if RallyDay1 then high else if !rallyDay1 then if high >RecentHigh[1] then high else RecentHigh[1] else RecentHigh[1]; # find the highest high in a periog Rallyday R1

def CB = if close < RecentHigh - RecentHigh*0.1 then 1 else 0;

def CountCB = if oneFTD then CountCB[1] + 1 else if !CB and !oneFTD then CountCB[1] else 0;
def oneCB = CountCB < CountCB[1];

AddchartBubble(showCircuitBreaker and oneCB,high, if ShowChartBubbleDescription then "Circuit Breaker 10% break of Highs. BS OFF" else "CB",GlobalColor("Circuit Breaker"));


######################################################################
#                          Market Exposure                           #
######################################################################

def BSOnB10 = if oneFTD then BSOnB10[1] + 1 else if !(oneS2 or (S4 ) or oneCB ) and !oneFTD then BSOnB10[1] else 0; #I could't add the market exposure ==0 with the (S4 ) on above code.

def S2ml = BSOnforS2ml and minorLowS2 and BSOnB10 and !oneFTD;

input ShowMElabel = yes;

def BS = B1 + B2 + oneB3 + oneB4 + B5 + oneB6 + B7 + oneB8 + oneB9 + (BSOnB10 and B10); #B10 is on when BuySwitch is on.
def SS = oneS1 + oneS2 + s2ml + S3 + S4 + oneS5 + oneS6 + oneS7 + S8 + oneS9 + S10 + S11 + oneS12 + S13 + oneS14 + oneCB;
def TS = BS-SS;

def countME = if rallyDay1 then 0 else if countME[1] > 5 then 5 else if countME[1]<0 then 0 else countME[1] + TS ;
def MarketExposure = if countME <0  then 0 else if countME>5 then 5 else countME;

#Market Exposure when the Restraint Rule  is applied
def countMERR = if rallyDay1 then 0 else if countMERR[1] > 2 then 2 else if countMERR[1]<0 then 0 else countMERR[1] + TS ;
def MarketExposureRR = if countMERR <0  then 0 else if countMERR>2 then 2 else countMERR;

#Market Exposure when the Power Trend is applied
def countMEPT = if rallyDay1 then 0 else if countMEPT[1] > 7 then 7 else if countMEPT[1]<2 then 2 else countMEPT[1] + TS ;
def MarketExposurePT = if countMEPT <2  then 2 else if countMEPT>7 then 7 else countMEPT;

# Market Exposure when the PowerTrendUnderPressure (+5 Floor +1)
def countMEPTup = if rallyDay1 then 0 else if countMEPTup[1] > 5 then 5 else if countMEPTup[1]<1 then 1 else countMEPTup[1] + TS ;
def MarketExposurePTup = if countMEPTup <1  then 1 else if countMEPTup >5 then 5 else countMEPTup;


# Market Exposure when the PowerTrendUnderPressureWithoutFloor
def countMEPTwf = if rallyDay1 then 0 else if countMEPTwf[1] > 5 then 5 else if countMEPTwf[1]<0 then 0 else countMEPTwf[1] + TS ;
def MarketExposurePTwf = if countMEPTwf <0  then 0 else if countMEPTwf >5 then 5 else countMEPTwf;


#color Label settings
def mid = 2;
def upper = 7;
def lower = 0;
def bl = 75;

def CLrValue = if MarketExposure < mid then 255 else if MarketExposure > upper then 0 else 255 - Round((MarketExposure - mid) / (upper - mid) * 255, 0);
def CLgValue = if MarketExposure > mid then 255 else if MarketExposure < lower then 0 else 255 - Round((mid - MarketExposure) / (mid - lower) * 255, 0);






######################################################################
#                            Restraint Rule                          #
######################################################################
#To avoid getting invested too fast, limit exposure to a maximum of +2

#Rules to remove the Restraint Rule: The index makes significant progress above the close of the initial follow-through day OR
#A buy signal (B4) is triggered (trending above ema21)

#Significant progress is defined as the percentage requirement nedeed for a FTD based on volatility (FTDPriceRequirement: currently 1%). Example if you close above 1% from the close of the FTD then we lift the Restraint Rule. OR
#If you trending above the FTD

#Restraint Rule applies if:
#The maket exposure was 0 prior FTD
#If the buy switch turns on due to closing above a prior high, then the restraint rule will apply until the index makes significant progress as defined by the (FTDPriceRequirement).

input ShowRestraintRuleLabel = yes;

def CloseofOneFTD = if oneFTD then close else CloseofOneFTD[1];
def RestraintLifted = if oneB4 or close > CloseofOneFTD*FTDPriceRequirement  then 1 else 0;

def MarkExpPriorFTD = If oneFTD and MarketExposure[1]==0 then 1 else 0;

def RR =  if MarkExpPriorFTD and !rallyDay1 and !RestraintLifted then 1 else 0;
Addlabel(ShowRestraintRuleLabel and RR,"Restraint on. Max MarketExposure Expose +2",Color.ORANGE);


######################################################################
#                              Power Trend                           #
######################################################################
#Power Trend Turns On:
#The ema21>ma50 for at least 5 consecutive days without an S9(break below ma50)
#The Index closes up or flat for the day.
#The ma50 is in uptrend for at least 1 day
#The Index low has been above the ema21 for at least 10 consecutive days
#Buy Switch override -> BSOn is forced to stay on
#Buy Switch count can go now up to +7 (100% exposure =+5) +2 buffer for sell signals.
#Don't act on anything higher than +5. This rule was created to provide a buffer to keep you incested during a significant uptrend.
#Start counting additional buy signals from the point that the PowerTrend turns on.

#Once the buy count is at +2 or higher, a floor of +2 is in effect.

Input showPowerTrendLabel = yes;


#Condition that End Power Trend:
#ema21 line crosses below the ma50 and index closes down for the day OR
#S2 Major Low OR
#Circuit Breaker.

#After Power Trend has ended
#No longer forces buy switch on (it may still be on, but it doesn't have to be on)
#Buy signal count is reset to a maximum of +5
#There is no longer a floor.
def PToff = ema21 crosses below ma50 and redDay or oneS2 or oneCB ;

#Condition that re-set a Power Trend from an Under Pressure condtion:
#The conditons that turn on a power-trend must be present, and
#10 consecutives days must pass without an S2 minor low an S9 and S13.
def p=10;
def PTOnreset =  fold q=0 to p with r do if(sum(!minorLowS2[q] and !ones9[q] and !s13[q],p)==p,1,0);

###### PT ON-OFF #####
def n=5;
def PTonSignal = fold i=0 to n with s do if(sum(ema21[i]>ma50[i] and !ones9[i],n)==n and ma50>ma50[1] and upday and sum(low>ema21,10)==10,1,0);


def SumPTon = if PTonSignal then 1 else if PToff then 0 else SumPTon[1];
def PTon = SumPTon;


#Condition that turn a Power Trend to a Power Trend under pressure is:
#S2 minor low signal (and minorLowS2 and BSon)
#S13 rule (Distribution cluster) is triggered

def PTonUnderPressure = PTon and (minorLowS2 or S13);
def SumPTup = if PTonUnderPressure then 1 else if PToff then 0 else SumPTup[1];
def PTup = SumPTup;

#When the power trend is in an under-pressure position with a floor, the max exposure count is reduced to +5 and the floor is reduced to +1 before any signals are applied on that day.

#Condition than turn a Power Trend to a Power Trend under pressure without a floor
#S9 signal (break below ma50)
#When an S9 occurs, the max exposure count remains at +5 but the floor of +1 is removed.

def PTonUnderPressureWithoutFloor = PTon and S9;
def SumPTupWF = if PTonUnderPressureWithoutFloor then 1 else if PToff then 0 else SumPTupWF[1];
def PTWF = SumPTupWF;

def PowerTrendUnderPressure = PTup and !Ptonreset;
def PowerTrendUnderPressureWithoutFloor = PTwf and !Ptonreset;
def PowerTrendOnNopressure = PTon and !PowerTrendUnderPressure and !PowerTrendUnderPressureWithoutFloor;
#def SumPTon = if PTonSignal then 1 else if PToff then 0 else SumPTon[1];


######################################################################
#                             Buy Switch                             #
######################################################################
#BS rules
#When the buy switch is on, you act on all buy and sell signals regardless of the distribution count.
#When the buy switch is off, you only act on sell signals.

#Events that turn Buy Switch On:
#Follow Through day (B1)
#Note on the day buy switch turns on, act on all buy signals that are in effect (except for buy signals B9 and B10) from the low (Rallyday1).

#Events that turn Buy Switch OFF
#Full distribution Count (S4) and market exposure is 0
#Failed rally attempt (S2) of a "major low"
#Circuit breaker - occurs when you've got a 10% break of the market from the high point

#What if the Buy Switch is Off, the MarketExposure count is zero, we haven't a follow-through day but the market is acting strong? How do you turn on the buy switch?
#A close above the prior high oneB8 since the last confirmed rally will turn the Buy Switch on.

############################## Buy Switch ##############################
input ShowBuySwitchLabel = yes;
input ShowBuySwitchBuddle = yes;


def BSOn = if (oneFTD or PTon or oneB8) then BSOn[1] + 1 else if !(oneS2 or (S4 and (if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else  MarketExposure)==0) or oneCB or oneS14 ) and !oneFTD then BSOn[1] else 0;


#def BSOff = BSOn < BSOn[1];
#def oneBSOn =if(BSOn > BSOn[1] and BSOn[1] == BSOn[2],1 ,0);

#AddchartBubble(ShowBuySwitchBuddle and oneBSOn, high,"Buy Switch On",GlobalColor("BuySwitch On"));
#AddchartBubble(ShowBuySwitchBuddle and BSOff, high,"Buy Switch Off",GlobalColor("BuySwitch Off"));


#BS Off MarketExposure Exposure

def TSforBSoff = -SS;

def countMEforBSoff = if rallyDay1 then 0 else if countMEforBSoff[1] > 5 then 5 else if countMEforBSoff[1]<0 then 0 else (if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else  MarketExposure) + TSforBSoff ;
def MarketExposureforBSoff = if countMEforBSoff <0  then 0 else if countMEforBSoff >5 then 5 else countMEforBSoff;

def CurrentME = if RR then MarketExposureRR else if PTon and !PowerTrendUnderPressure and !PowerTrendUnderPressureWithoutFloor  then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else if !bson then MarketExposureforBSoff else  MarketExposure;


Addlabel(ShowMElabel,"Market Expose: (+" + CurrentME + ") " + if CurrentME == 0  then "0%" else if CurrentME==1 then "33%" else if CurrentME==2 then "55%" else if CurrentME==3 then "75%" else if CurrentME == 4 then "90%" else "100%", CreateColor(CLrValue, CLgValue, bl));

#Addlabel(ShowMElabel,"Count: (" + (if RR then countMERR else if PTon then countMEPT  else if PowerTrendUnderPressure then countMEPTup else if PowerTrendUnderPressureWithoutFloor then countMEPTwf else if !bson then countMEforBSoff else countME) + ")", CreateColor(CLrValue, CLgValue, bl));


#AddchartBubble(B1 OR B2 OR oneB3 OR oneB4 OR B5 OR oneB6 OR B7 OR oneB8 OR oneB9 OR (BSOnB10 and B10) OR oneS1 OR oneS2 OR s2ml OR S3 OR S4 OR oneS5 OR oneS6 OR oneS7 OR S8 OR oneS9 OR S10 OR S11 OR oneS12 OR S13 OR oneS14 OR oneCB,high,if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else if !bson then MarketExposureforBSoff else  MarketExposure,color.orange);



######################################################
######### BS labels added on the Power Trend #########
######################################################



######################################################
####### Distribution Day Fall Off   Bubble B10 #######
######################################################


AddChartBubble("time condition" = showB10 and B10 and BSOn, "price location" = high, text =  if ShowChartBubbleDescription then "B10 Distribution Day Fall Off (" + PercentageGreenBars + "%)" else "B10", GlobalColor("B10"));

######################################################
##### Failed Rally Attempt. Bubble S2 Minor Low  #####
######################################################

AddChartBubble("time condition" = showS2ml and s2ml and BSon, "price location" = high, text =  if ShowChartBubbleDescription then "S2 Undercut MinorLow Low or the rally (" + PercentageGreenBars + "%)" else "S2ml", GlobalColor("S2ml"));

##### BS Labels ####

Addlabel(ShowBuySwitchLabel,"Buy Switch: ",color.WHITE);
Addlabel(ShowBuySwitchLabel,if BSOn then "ON" else "OFF", if BSon then color.green else color.Red);



Addlabel(showPowerTrendLabel,if PowerTrendUnderPressure then "Power Trend under pressure: Max (+5) Floor (+1)" else if PowerTrendUnderPressureWithoutFloor then "Power Trend under pressure without floor: Max (+5) Floor (0)" else If PowerTrendOnNopressure then "Power Trend: Max (+7) Floor (+2)" else "Power Trend:",color.white);
Addlabel(showPowerTrendLabel,If PTon then "ON" else "OFF",if Pton then Color.GREEN else color.red);


######################################################################
#                      Active Label Description                      #
######################################################################
input ShowSignalsLabelDescription = yes;
#### Buy Signals Labels ####
Addlabel(ShowSignalsLabelDescription and B1,"B1: Buy on initial follow-through day.",GlobalColor("B1"));
Addlabel(ShowSignalsLabelDescription and B2,"B2: Buy on all additional follow-through days within 25-days from a rally day that closes above the low of the initial follow-through day.",GlobalColor("B2"));
Addlabel(ShowSignalsLabelDescription and oneB3,"B3:  Low Above Ema21. Once you have B3, you can't have another until is reset by an S5.",GlobalColor("B3"));
Addlabel(ShowSignalsLabelDescription and oneB4,"B4: Trending Above Ema21. Once you have B3, you can't have another until is reset by an S5.",GlobalColor("B4"));
Addlabel(ShowSignalsLabelDescription and B5,"B5: Living Above Ema21. Buy after a B3 signal has triggered, buy on the 10th and every 5th consecutive day after that in an upday.",GlobalColor("B5"));
Addlabel(ShowSignalsLabelDescription and oneB6,"B6: Low Above MA50. Buy on an upday with an uptrending ma50 (ma50>ma50[1]). Once you have B6, you can't have another until is reset by an S9.",GlobalColor("B6"));
Addlabel(ShowSignalsLabelDescription and B7,"B7: Accumulation Day. Buy Signal is additional FTD but after the 25 days of rally attempt and close in the upper 25% range and greater ema21.",GlobalColor("B7"));
Addlabel(ShowSignalsLabelDescription and oneB8,"B8: Higher High. Buy after closing above the last marked high (Last high before R1).",GlobalColor("B8"));
Addlabel(ShowSignalsLabelDescription and oneB9,"B9: Downside Reversal Buyback. After selling on a downside reversal (S11), buy back if the index closes above the intraday high of the downside reversal within two trading days.",GlobalColor("B9"));
Addlabel(ShowSignalsLabelDescription and B10,"B10: Distribution Day Fall Off. Buy on the day that the distribution count falls back to four (Full distibution minus two) as long as the close is above the ema21 and Buy Switch is on.",GlobalColor("B10"));


#### Sell Signals Labels ####
Addlabel(ShowSignalsLabelDescription and oneS1,"S1: Follow Through Day Undercut. Sell if index closes below the Low of the initial follow through day.",GlobalColor("S1"));
Addlabel(ShowSignalsLabelDescription and oneS2,"S2: Failed Rally Attempt. Sell if index undercuts the major low of rally attempt. Market exposure is reduced to zero and Buy Switch is turned off.",GlobalColor("S2"));
Addlabel(ShowSignalsLabelDescription and S2ml and BSon,"S2: Minor Low undercut of rally attempt. Market exposure is reduced by two. This does not turn off the Buy Switch. MinorLow defined as low that occurs when the buy switch is on (oneFTD).",GlobalColor("S2"));
Addlabel(ShowSignalsLabelDescription and S3,"S3: Full Distribution minus one. Sell after distribution count increases to five (Full distribution: six days). This rule is applied each new time that it occurs.",GlobalColor("S3"));
Addlabel(ShowSignalsLabelDescription and S4,"S4: Full Distribution. Sell after full distribution count (full distribution: six days). This rule is applied up to eight days (full distribution plus two)",GlobalColor("S4"));
Addlabel(ShowSignalsLabelDescription and oneS5,"S5: Break Below Ema21. Sell if index closes 0.2% or greater below the ema21. Once you have S5, you can't have another until is reset by an B3",GlobalColor("S5"));
Addlabel(ShowSignalsLabelDescription and oneS6,"S6: Overdue Break Below Ema21. Sell if index closes down 0.2% or greater below the ema21 after 30 days have been passed since last B3 without trigering an S5. Once you have S6, you can't have another until is reset by an B3.",GlobalColor("S6"));
Addlabel(ShowSignalsLabelDescription and oneS7,"S7: Trending Below ema21. Sell after S5 on then 5th consecutive day that then high is below the ema21 and DownDay Day. You can't have another S7 until is reset by an B3.",GlobalColor("S7"));
Addlabel(ShowSignalsLabelDescription and S8,"S8: Living Below Ema21. Sell after S5 on then 10th and every 5th consecutive day after that that then high is below the ema21. Down Day if not then wait for the next down day.",GlobalColor("S8"));
Addlabel(ShowSignalsLabelDescription and oneS9,"S9: Break Below MA50. Index closes below 50ma. Only triggers if a B6 triggered preiviously. Sell S9 is reset by B6.",GlobalColor("S9"));
Addlabel(ShowSignalsLabelDescription and S10,"S10: Bad Break. Sell if the close is down 2.25% or greater in the bottom 25% of range. Close below the ma50 or Intraday high below ema21.",GlobalColor("S10"));
Addlabel(ShowSignalsLabelDescription and S11,"S11: Downside Reversal Day. New Intra-day high within prior 13 days and close in bottom quartile of range and RedDay.",GlobalColor("S11"));
Addlabel(ShowSignalsLabelDescription and oneS12,"S12: Lower Low. Sell after closing below the last marked low  (9days lookback and 9day forward).",GlobalColor("S12"));
Addlabel(ShowSignalsLabelDescription and S13,"S13: Distribution Cluster. Distribution and Stalling days increases to four up to eight days within a rolling eight day period. The distribution cluster changes the Power-Trend (if it is in effect) to a Power-Trend Under Pressure.",GlobalColor("S13"));
Addlabel(ShowSignalsLabelDescription and oneS14,"S14: Break Below Higher High. Sell after closing below the last marked high that triggered a B8 (Higher High).",GlobalColor("S14"));

Addlabel(ShowSignalsLabelDescription and oneCB, "Circuit Breaker Index falls 10% of Highs since the follow-through. Buy Switch is turned off.",GlobalColor("Circuit Breaker"));
 
Last edited by a moderator:
Dear all, I have modified the IBD Distribution Days Study for TOS shared from SMO_MktVolumesDaily
with buy/sell signals
I hope you will find my work helpful on your trading journey.

Ruby:
# SMO_MktVolumesDaily.ts
# For daily charts only.
# Nasdaq total volume: $TVOL/Q ;   NYSE total volume: $TVOL
# Distribution day count tracking by IBD:
# https://www.investors.com/how-to-invest/investors-corner/
# tracking-distribution-days-a-crucial-habit/
# Stalling daysFromDate tracking by IBD:
# https://www.investors.com/how-to-invest/investors-corner/
# can-slim-market-tops-stalling-distribution/
#BuySell Signal added by dag 2022-03-27

# For daily charts only.
######################################################################################
declare once_per_bar;
input ShowChartBubbleDescription = no;
Input ShowDistrubutionLabels=no;


DefineGlobalColor("B1", Color.GREEN);
DefineGlobalColor("B2", Color.LIGHT_GREEN);
DefineGlobalColor("B3", Color.LIGHT_GREEN);
DefineGlobalColor("B4", Color.LIGHT_GREEN);
DefineGlobalColor("B5", Color.LIGHT_GREEN);
DefineGlobalColor("B6", Color.LIME);
DefineGlobalColor("B7", Color.LIGHT_GREEN);
DefineGlobalColor("B8", Color.LIGHT_GREEN);
DefineGlobalColor("B9", Color.LIGHT_GREEN);
DefineGlobalColor("B10", Color.LIGHT_GREEN);

DefineGlobalColor("S1", Color.LIGHT_RED);
DefineGlobalColor("S2", Color.RED);
DefineGlobalColor("S2ml", Color.DOWNTICK);
DefineGlobalColor("S3", Color.LIGHT_RED);
DefineGlobalColor("S4", Color.LIGHT_RED);
DefineGlobalColor("S5", Color.LIGHT_RED);
DefineGlobalColor("S6", Color.LIGHT_RED);
DefineGlobalColor("S7", Color.LIGHT_RED);
DefineGlobalColor("S8", Color.LIGHT_RED);
DefineGlobalColor("S9", Color.PLUM);
DefineGlobalColor("S10", Color.LIGHT_RED);
DefineGlobalColor("S11", Color.LIGHT_RED);
DefineGlobalColor("S12", Color.LIGHT_RED);
DefineGlobalColor("S13", Color.LIGHT_RED);
DefineGlobalColor("S14", Color.YELLOW);

DefineGlobalColor("Circuit Breaker", Color.RED);
DefineGlobalColor("BuySwitch On", Color.GREEN);
DefineGlobalColor("BuySwitch Off", Color.WHITE);




#############################################################################################

#SWING HIGH LOW
def LookbackPeriod = 9; #
input ShowHLLevels= no;

#--------------------------------------------------------------
def _highInPeriod1 = Highest(high, LookbackPeriod);
def _lowInPeriod1 = Lowest(low, LookbackPeriod);
#--------------------------------------------------------------
def marketLow1 = if _lowInPeriod1 < _lowInPeriod1[-LookbackPeriod] then _lowInPeriod1 else _lowInPeriod1[-LookbackPeriod];
def _markedLow1 = low == marketLow1;

rec _lastMarkedLow1 = CompoundValue(1, if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else _lastMarkedLow1[1], low);
#--------------------------------------------------------------
def marketHigh1 = if _highInPeriod1 > _highInPeriod1[-LookbackPeriod] then _highInPeriod1 else _highInPeriod1[-LookbackPeriod];
def _markedHigh1 = high == marketHigh1;

rec _lastMarkedHigh1 = CompoundValue(1, if IsNaN(_markedHigh1) then _lastMarkedHigh1[1] else if _markedHigh1 then high else _lastMarkedHigh1[1], high);
#--------------------------------------------------------------
plot Resistance1 = _lastMarkedHigh1;
plot Support1 = _lastMarkedLow1 ;

#--------------------------------------------------------------
Resistance1.SetPaintingStrategy(PaintingStrategy.DASHES);
Resistance1.SetDefaultColor(Color.GREEN);
Resistance1.SetHiding(!ShowHLLevels);
#--------------------------------------------------------------
Support1.SetPaintingStrategy(PaintingStrategy.DASHES);
Support1.SetDefaultColor(Color.RED);
Support1.SetHiding(!ShowHLLevels);
#--------------------------------------------------------------

input ShowHighLowBuble = yes;
AddchartBubble(ShowHighLowBuble and _markedHigh1,high, round(high,2),color.white);
AddchartBubble(ShowHighLowBuble and _markedLow1,low, round(Low,2),color.white, up = No);

########################################################################################


def length = 50;        # volume moving average lenth in days

input volumeSymbol = {default NASDAQ, NYSE, SPX, CurrentSymbol};
# Reset distribution day counts on FTD.
input distributionRstDay = 20191010;

def volCl;
def volHi;
def findSymbol;

# To make volume differences more visible, use a base volume number
# The subtracted volume number is then magnified to present a bigger difference
def volMin;    # base number for volume
def dropThreshold;

switch (volumeSymbol) {
# It was found there may be erratic volume data on close values
# On 2/19/2020, NYSE volume close values were 0 on 2/18 & 2/12
case NYSE:
    volCl = if close("$TVOL") == 0 then high("$TVOL") else close("$TVOL");
    #volCl = close("$TVOL");
    volHi = high("$TVOL");
# use SPX volume change percentage to replace erratic NYSE volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOLSPC") - close("$TVOLSPC")[1]) / close("$TVOLSPC")[1]) else volCl;
    volMin = 40000;
    dropThreshold = .942;
case NASDAQ:
    volCl = if close("$TVOLC/Q") == 0 then high("$TVOLC/Q") else close("$TVOLC/Q");
    #volCl = close("$TVOL/Q");
    volHi = high("$TVOLC/Q");
# use SPX volume change percentage to replace erratic NASDAQ volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOLSPC") - close("$TVOLSPC")[1]) / close("$TVOLSPC")[1]) else volCl;
    volMin = 30000;
    dropThreshold = .931;
case SPX:
    volCl = if close("$TVOLSPC") == 0 then high("$TVOLSPC") else close("$TVOLSPC");
    #volCl = close("$TVOLSPC");
    volHi = high("$TVOLSPC");
# use NYSE volume change percentage to replace erratic SPX volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOL") - close("$TVOL")[1]) / close("$TVOL")[1]) else volCl;
    volMin = 0;
    dropThreshold = 1;

case CurrentSymbol:
    volCl = if close == 0 then high else close;
    volHi = high;
 
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close - close[1]) / close[1]) else volCl;
    volMin = 10000;
    dropThreshold = .942;

}

def cls = close;

def lastBar = HighestAll(If (IsNaN(cls), Double.NaN, BarNumber()));
def volumes = if IsNaN(findSymbol) and BarNumber() == lastBar then volumes[1] else findSymbol;

def Vol = 3 * (volumes - volMin);
def VolAvg = 3 * (Average(volumes, length) - volMin);


# Display useful texts starting at upper left corner
# End of Day volume change
def VolChangePercentDay = If (IsNaN(volumes[1]), 0,
                              100 * (volumes - volumes[1]) / volumes[1]);

# InvalidDay was added since volume on 2019/11/29 (after Thanksgiving) was N/A.
AddLabel( ShowDistrubutionLabels, if VolChangePercentDay == 0 then "InvalidDay" else "" +
               "VolmChg=" + Concat("", Round(VolChangePercentDay)) +
               "%", if VolChangePercentDay < 0 then
               Color.DARK_GRAY else if cls > cls[1] then Color.DARK_GREEN
               else Color.DARK_RED);

# Count distributionDay only if market price drops 0.2% or more
def downDay = cls <= (cls[1] * 0.998);

def volIncrease = volumes > volumes[1];

#
# After 25 sessions, a distribution day expires
# Use 25 bar numbers to represent 25 live sessions. GetDay or alike includes weekends.
#

def lastDays = if (BarNumber() > lastBar - 25) then 1 else 0;

# a distribution day can fall off the count if the index rises 6% or more,
# on an intraday basis, from its close on the day the higher-volume loss appears.
# Remove distribution days after prices increase 6% WHEN the market is in an uptrend.
# Need to fix:
# During the market bottomed on 2-28-2020, the stock price rose to 9.8% with the market still in
# correction. The high volume selloff on 2-28 would still be counted as a distribution.
# The highest date should be after the distribution day

# Get proper high for future 25 days
def prHi = high;
def prLo = low;

def futureHigh = if IsNaN(prHi[-25]) then futureHigh[1] else prHi[-25];
def prHighest = Highest(futureHigh, 25);
# Note: This condition disqualifies D-Days after large bear rally
# This is acceptable for now since D-Days in bear market are not really useful
def priceInRange = (cls * 1.06 >= prHighest);

def distributionDay = downDay and volIncrease and lastDays and priceInRange;

# Count valid distribution days in last 25 days
def distDayCount = Sum(distributionDay, 25);


# A broad market correction makes the distribution day count irrelevant
# reset distribution count to 0
# Distribution day count should reset after 2nd confirmation day
# input distributionRstDay = 20191010;   a prior 2nd confirmation day
# input distributionRstDay = 20200402;   a prior 2nd confirmation day
#input distributionRstDay = 20191010;

def newDistributionCycle = GetYYYYMMDD() > distributionRstDay;
# Need to use above variable to restart d-day count
def newDistDays = Sum(distributionDay and newDistributionCycle, 25);

# Display bubble red is count > 5, yellow >3, else while
AddChartBubble(distributionDay and !newDistributionCycle, Vol, Concat("", distDayCount),
                if distDayCount < 3 then Color.WHITE
                else if distDayCount < 5 then Color.LIGHT_ORANGE
                else Color.RED);


# Show D-Day reset line at the reset date input by user
# It appears at the left side of the volume bar
AddVerticalLine( if (GetYYYYMMDD() == distributionRstDay ) then yes
                 else no,
                "                   2ndCnfm",
                Color.GREEN, Curve.MEDIUM_DASH);



# to do: Comparison of preholiday data may be invalid.

#------------------------------------------------------------
# Stalling day counts
# 1. market has been rising and price is within 3% of 25 day high
# 2. Price making a high
#      current close >= prior 2 day close, or
#      current close >= prior day high
# 3. volume >= 95% of prior day volume
# 4. close in lower half of daily range
# 5. small gain within 0.4% for SPX & NASDAQ
# 6. The above IBD criteria disclosed in one article generates too many stalling days
#    Additional rules from IBD book are used to further reduce stalling counts
#    6.1 close up smaller than prior 2 days
#    6.2 low is lower than high of prior day (No unfilled gap-up)
#    6.3 there is at least one decent gain in prior 2 days
#    6.4 daily trading range should be similar to last 2 days
# 7. stalling counts are reduced due to time (25 days) and significantly upward
#    movement (6%) of the index
# Ex. 2019/11/12 was a stalling day on SPX, 2019/12/18 was stalling for Nasdaq

def priceIsHigh = cls >= cls[2] or cls >= prHi[1];
def priceLowHalf = cls < (prHi - prLo) / 2 + prLo;
def priceGainSmall = cls - cls[1] > 0 and
                     ((cls - cls[1] < (cls[1] - cls[2])) or
                     ((cls - cls[1] < cls[2] - cls[3])));

# Added a 0.2% gap from prior day high to allow 2020/05/26 to count
# as a stalling day for NASDAQ
def priceGapFill = prLo < prHi[1] * 1.002;
def priceGainOk = (cls[1] - cls[2] > 0.002 * cls[2]) or
                   (cls[2] - cls[3] > 0.002 * cls[3]);
# price trading range is the high - low plus the gap up if any
def priceRange = if prLo > prHi[1] then prHi - prHi[1] else prHi - prLo;
def priceRangeBig = priceGainOk and priceRange > 0.8 * Min(priceRange[1], priceRange[2]);

def stallDay = cls - cls[25] > 0 and
               cls >= 0.97 * Highest( prHi, 25) and
               volumes > 0.95 * volumes[1] and
               cls - cls[1] > 0 and
               cls - cls[1] < 1.004 * cls[1] and
               priceIsHigh and priceLowHalf and priceGainSmall and priceGapFill and
               priceRangeBig and lastDays;

input ShowStallingDays = yes;
input ShowDistributionDays = yes;
#AddChartBubble(ShowStallingDays and stallDay , high, "Stalling Days", Color.MAGENTA);

# Count stalling days
def stallDayCount = Sum(stallDay, 25);

# calculate new stalling days after the reset day (e.g. follow-up date)
def newStallDays = Sum(stallDay and newDistributionCycle, 25);

# Display final distribution count (incl. stall days)
# red if >= 5, >3: yellow, else green
def totalDdays = distDayCount + stallDayCount;
def totalNdDays = newDistDays + newStallDays;

AddChartBubble(ShowDistributionDays and distributionDay and newDistributionCycle, high,
                if volCl == 0 then Concat("?", newDistDays) else Concat("", newDistDays),
                if totalNdDays < 3 then Color.WHITE
                else if totalNdDays < 5 then Color.LIGHT_ORANGE
                else Color.RED);


AddChartBubble(ShowStallingDays and stallDay and lastDays, high,
                if volCl == 0 then "?Stalling:" + Concat("", stallDayCount)
                              else "Stalling:" + Concat("", stallDayCount),
                if totalNdDays < 3 then Color.WHITE
                else if totalNdDays < 5 then Color.LIGHT_ORANGE
                else Color.RED);
AddChartBubble(volCl == 0 and !stallDay and !distributionDay, high, "?", Color.LIGHT_GRAY);


AddLabel(ShowDistrubutionLabels and totalDdays != totalNdDays, "AllDdays =" + Concat("", totalDdays), Color.GRAY);

#-------------------------------------------------------------------------------------
# Follow-through signals (FTD) are more likely to fail if distribution days
# occur in the first few days of a new uptrend. This is one key red flag.
# Quantification in script is implemented with a concept of critical score (critScore):
#   critScore = 3 for the 1st 5 days after FTD
#   critScore = 2 on the 6th, critScore = 1 on 7th day, critScore = 0 after 7th day
#   Total Distribution days = critScore + regular D-day count
#   critScore is used only if there is at least one D-day in the 1st 5th day after FTD
#-------------------------------------------------------------------------------------

def ftdBar = if GetYYYYMMDD() == distributionRstDay then BarNumber() else 0;
def lastFtdBar = HighestAll(ftdBar);
def daysAfterFTD = lastBar - lastFtdBar;

def critScore = if daysAfterFTD <= 0 then 0 else
                if daysAfterFTD <= 5 then 3 else
                if daysAfterFTD <= 6 then 2 else
                if daysAfterFTD <= 7 then 1 else 0;

def totalNdDaysC = totalNdDays + if totalNdDays > 0 then critScore else 0;

# Actual distribution day count is shown but color depends on totalNdDaysC
AddLabel(ShowDistrubutionLabels, "NewDdays =" + Concat("", totalNdDays ),
         if totalNdDaysC <= 2 then Color.GREEN
         else if totalNdDaysC <= 4 then Color.ORANGE
         else Color.RED);


# Add an indication of 1st rally day to start FTD count
# in a market correction period
# pink rally day is a day satisfying the following conditions:
# 1). Close above ½ of daily TRUE range and below prior day close
# 2). Low is the lowest during the market correction,
#     including future lows if available

# resolution of each 1st rally day is set to about 2 weeks

def rDayInterval = Round(25 / 2, 0);

def futureLow = if IsNaN(prLo[-rDayInterval]) then futureLow[1]
                else prLo[-rDayInterval];
def futureCls = if IsNaN(cls[-rDayInterval]) then futureCls[1]
                else cls[-rDayInterval];

# market correction is currently defined as down about 8% from top
# need to be refined so that it will work in a bear market that is forming a bottom.
# In this case, the 8% drop may not be required.
def mktCr    = prLo[1] <= Highest(high, 25) * dropThreshold; #.931; #.92;
def prRng    = TrueRange(prHi, cls, prLo); #prHi - prLo;


def pinkRday = cls > (prLo + prRng / 2) and cls < cls[1] and
               prLo <= Lowest(prLo[1], rDayInterval) and
               prLo <= Lowest(futureLow, rDayInterval);
 

# The real rally day has its close higher than prior close
# A rally day is invalidated if the low is broken in subsequent days
def realRday = cls > cls[1] and
               (prLo <= Lowest(prLo[1], rDayInterval) or
                prLo[1] <= Lowest(prLo[1], rDayInterval)) and
               (prLo <= Lowest(futureLow, rDayInterval) or
                prLo[1] <= Lowest(futureLow, rDayInterval)) and
               Sum(realRday[1], rDayInterval) == 0 and
               Sum(pinkRday[1], rDayInterval) == 0;

def RallyDay1 = (mktCr or mktCr[1]) and (pinkRday or realRday);

AddChartBubble(RallyDay1, high, "R1", Color.LIGHT_GREEN);



######################################################################
#                            FTD Requirements                        #
######################################################################
#1.Heavier volume than the previous daysAfterFTD  -> volIncrease

#2. Volatility is defined as the percentage gain of the up days over the past 200 days

def IBDlengthVolatility = 200;

def PercentageGreenBars = Round((close - close[1]) / close * 100, 2);

def upday = if close > close[1] then 1 else 0;
def SumUpdays = Sum(upday, IBDlengthVolatility);

def UpdaysPercentage = if upday then PercentageGreenBars else 0;
def SumUpdaysPercentage = Sum(UpdaysPercentage, IBDlengthVolatility);

def volatilityIBD = Round(SumUpdaysPercentage / SumUpdays, 2);

#Market Volatility and FTD price change requirements as percentageGreenBars IBD Market School.

def FTDPriceRequirement = if volatilityIBD < 0.4 then 1.007
else if volatilityIBD >= 0.4 and  volatilityIBD < 0.55 then 1.0085
else if  volatilityIBD >= 0.55 and volatilityIBD < 1 then 1.01
else 1.01245;


######################################################################
#                           FTD Requirements                         #
#--------------------------END-----END----END------------------------#
######################################################################



# Currently (April, 2020) a daily price (cls) increase of 1.25% minimum is
# the price requirement by either SPX or NASDAQ for a FTD.
# Must be day 4 after 1st rally attempt /w an exception (1st 3 days are strong).
# Must have higher volume on the FTD day

def redDay = if close < close[1] then 1 else 0;

def lastR1Bar  = If (RallyDay1, BarNumber(), lastR1Bar[1]);
def daysAfterR1 = BarNumber() - lastR1Bar;


#The Rally days is counting as day number 1  meaning daysAfterR1 = 0 so 4 days and more is daysAfterR1>=3

def isFTD = if daysAfterR1 >= 3  and BarNumber() > lastR1Bar and
               BarNumber() < lastR1Bar + 25 and lastR1Bar != 0 and
               cls >= (cls[1] * FTDPriceRequirement) and
               volIncrease then 1 else 0 ;


def totalR1toFTD = if RallyDay1 then totalR1toFTD[1] + 1  else if !RallyDay1 and !isFTD then totalR1toFTD[1] else 0;

def oneFTD = totalR1toFTD < totalR1toFTD[1];
AddChartBubble(oneFTD, high, "FTD", Color.LIGHT_ORANGE);

def lastOneFTDBar  = If (oneFTD, BarNumber(), lastOneFTDBar[1]);
def daysAfterOneFTD = BarNumber() - lastOneFTDBar;


######################################################################
#                    Distribution Days And Stalling days           #
######################################################################
#Distribution Day
DefineGlobalColor("DistibutionDay", Color.DARK_ORANGE);
DefineGlobalColor("StallingDay", Color.DARK_RED);

input ShowAllDistributionDays = no;
input ShowAllStallingDays = no;



def distributionDays = downDay and volIncrease and priceInRange;

def stallDays = cls - cls[25] > 0 and
               cls >= 0.97 * Highest( prHi, 25) and
               volumes > 0.95 * volumes[1] and
               cls - cls[1] > 0 and
               cls - cls[1] < 1.004 * cls[1] and
               priceIsHigh and priceLowHalf and priceGainSmall and priceGapFill and
               priceRangeBig;

def totalDS =  if stallDayCount >= distDayCount then distDayCount*2-1 else totalDdays;

def distributionDaysCount = sum(distributionDays,25); #check the last 25 days for the S3 and S4 signal.
def distributionDaysCount26 = sum(distributionDays,26); #check the last 26 days for the B10 Signal.
def distributionDaysCount8 = sum(distributionDays,8); #check the last 8 days for the S13 signal.

def stallDaysCount = sum(stallDays,25);
def stallDaysCount26 = sum(stallDays,26);
def stallDaysCount8 = sum(stallDays,8);

def totalDSdays = distributionDaysCount + stallDaysCount;
def totalDSdays26 = distributionDaysCount26 + stallDaysCount26;
def totalDSdays8 = distributionDaysCount8 + stallDaysCount8;

def totalAllDS =  if stallDaysCount >= distributionDaysCount then distributionDaysCount*2-1 else totalDSdays;
def totalAllDS26 =  if stallDaysCount26 >= distributionDaysCount26 then distributionDaysCount26*2-1 else totalDSdays26;


AddChartBubble("time condition" = ShowAllDistributionDays and distributionDays, "price location" = high, text =  if ShowChartBubbleDescription then "Distribution Day (" + PercentageGreenBars + "%)" else "DD", GlobalColor("DistibutionDay"));

AddChartBubble("time condition" = ShowAllStallingDays and stallDays, "price location" = high, text =  if ShowChartBubbleDescription then "Stalling Day (" + PercentageGreenBars + "%)" else "SD", GlobalColor("StallingDay"));



######################################################################
#                       Moving Averages EMA21 MA50                   #
######################################################################

def ema21 = MovAvgExponential("length" = 21)."AvgExp";
def ma50 = MovingAverage(data = close, "length" = 50 );


######################################################################
#                            FTD Signals     (B1)                    #
######################################################################
#Buy on initial follow-through day.
input showB1FTD = yes;
def B1= oneFTD;

AddChartBubble(showB1FTD and B1, high, if ShowChartBubbleDescription then "(B1) " + (daysAfterR1 + 1) + "days from R1 (+ " + PercentageGreenBars + "%)" else "B1", Color.GREEN);

######################################################################
#                         Additional FT Days    (B2)                 #
######################################################################
#Buy on all additional follow-through days within 25-days from a rally day that closes above the low of the initial follow-through day.
input showB2 = yes;
def LowOneFTD = if oneFTD then low else LowOneFTD[1];
def B2 = isFTD and !oneFTD and close > LowOneFTD and daysAfterR1 < 25;
def CountB2 = if B2 then CountB2[1] + 1  else if !B2 and daysAfterR1 < 25 then CountB2[1] else 0;

AddChartBubble("time condition" = showB2 and B2, "price location" = high, text =  if showChartBubbleDescription then "B2-" + CountB2 + " FTD (+ " + PercentageGreenBars + "%)" else "B2", color = GlobalColor("B2"));



######################################################################
#                            Low Above Ema21    (B3)                 #
######################################################################

#S5Reset for resetting the buy signals B3, B4Signal & B5
#S5 Break below ema21
#Sell if index closes 0.2% or greater below ema21
#B3, B4 & B5 Reset by S5. Once you have B3 or B4, you can't have another until is reset by an S5

def S5ResetbelowEma21 = cls <= (ema21 * 0.998);


#Buy on and up or flat day when then intra-day low is at or above then ema21
input showB3 = yes;
def B3 =  low >= ema21 and upday; # and !oneFTD;

def totalB3toS5 = if B3 then totalB3toS5[1] + 1 else if !B3 and !S5ResetbelowEma21 then totalB3toS5[1] else 0;
def totalS5toB3 = if S5ResetbelowEma21 then totalS5toB3[1] + 1 else if !B3 and !S5ResetbelowEma21 then totalS5toB3[1] else 0;

def oneB3 = totalS5toB3 < totalS5toB3[1];
def S5Reset = totalB3toS5 < totalB3toS5[1];

def lastoneB3Bar  = If (oneB3, BarNumber(), lastoneB3Bar[1]);
def daysAfterB3 = BarNumber() - lastoneB3Bar;


AddChartBubble("time condition" = showB3 and oneB3, "price location" = high, text = if showChartBubbleDescription then  "B3 low>ema21. Reset S5, S6, S7 & S8 (" + PercentageGreenBars + "%)" else "B3", color = GlobalColor("B3"));



######################################################################
#                    Trending Above Ema21    (B4)                    #
######################################################################
#Buy after a B3 signal has triggered, buy on the 5th consecutive day that the low is at or above the ema21. The B4 signal is only triggered when the index closes up or flat for the day. If the index close down on the 5th day from the B3, then wait for the next up day to trigger B4.

input showB4 = yes;

# signal for reset the B4
def S5B4ResetSignal = !S5Reset;

def B4Signal = if !oneB3 and daysAfterB3 >= 4 and B3 and S5B4ResetSignal then 1 else 0;

def SumB3toB4 = if oneB3 then SumB3toB4[1] + 1 else if !B4Signal and !oneB3 then SumB3toB4[1] else 0;
def oneB4 = SumB3toB4 < SumB3toB4[1];

AddChartBubble("time condition" = showB4 and oneB4 , "price location" = high, text =  if ShowChartBubbleDescription then "B4 Trending Above ema21. " + (daysAfterB3 + 1) + "days from (B3) (" + PercentageGreenBars + "%)" else "B4", GlobalColor("B4"));


######################################################################
#                        Living Above Ema21  (B5)                    #
######################################################################
#Buy after a B3 signal has triggered, buy on the 10th and every 5th consecutive day after that (15th, 20th, 25th, etc.) day's low is at or above the ema21. The B5 signal is only triggered when the index closes up or flat for the day. If the index close down on the 5th day from the B3, then wait for the next up day to trigger B5.


input showB5 = yes;
def fiveDaysB3 = (daysAfterB3+1) % 5 == 0;
# signal for on off the B5
def S5B5ResetSignal = !S5Reset; # !oneFTD  and daysAfterB3 < daysAfterS5Reset; # and !oneS5Reset

#B5 signal
def B5Signal = daysAfterB3 >= 9 and B3 and S5B5ResetSignal;

def B5_5 =  B5Signal and fiveDaysB3;
def B5_6 = if !B5_5[1] then B5Signal and fiveDaysB3[1] else 0;
def B5_7 = if !B5_6[1] and !B5_5[2] then B5Signal and fiveDaysB3[2] else 0;
def B5_8 = if !B5_7[1] and !B5_6[2] and !B5_5[3] then B5Signal and fiveDaysB3[3] else 0;
def B5_9 = if !B5_8[1]  and !B5_7[2] and !B5_6[3] and !B5_5[4] then B5Signal and fiveDaysB3[4] else 0;

def B5 = B5_5 or B5_6 or B5_7 or B5_8 or B5_9;

AddChartBubble("time condition" = showB5 and B5, "price location" = high, text =  if ShowChartBubbleDescription then "B5 Living above ema21 (" + PercentageGreenBars + "%) " + (daysAfterB3+1) + "days from B3. Low >= ema21" else "B5", GlobalColor("B5"));



######################################################################
#                          Low Above MA50      (B6)                  #
######################################################################
#Close Above ma50
#Buy on an up or flat day when then intraday low is at or above an uptreding ma50.
#Uptreding ma50 -> the day that the ma50 is up from then prior day.
#B6 Reset by S9. Once you have B6, you can't have another until is reset by an S9


#Sell if index closes below 50ma.
#Only triggers if a corresponding buy signal related to that moving average B6 triggered previously
#Sell S9 is reset by B6
#Exception: -> Index closes in the upper half of range ("Shakeouk"), and Index closes within 1% of the 50ma

def S9 = cls < (ma50 * 0.99) and close < hl2;
def B6 =  low >= ma50 and ma50 > ma50[1] and upday;
def SumB6toS9 = if B6 then SumB6toS9[1] + 1 else if !B6 and !S9 then SumB6toS9[1] else 0;
def oneS9 = SumB6toS9 < SumB6toS9[1];


#Close Above ma50
#Buy on and up or flat day when then intra-day low is at or above then ema21

input showB6 = yes;

def SumS9toB6 = if S9 then SumS9toB6[1] + 1 else if !B6 and !S9 then SumS9toB6[1] else 0;
def oneB6 = SumS9toB6 < SumS9toB6[1];


def lastoneB6Bar  = If (oneB6, BarNumber(), lastoneB6Bar[1]);
def daysAfterB6 = BarNumber() - lastoneB6Bar;


AddChartBubble("time condition" = showB6 and oneB6, "price location" = high, text =  if ShowChartBubbleDescription then "B6 Low Above ma50 in uptrending ma50, reset S9 (+" + PercentageGreenBars + "%)" else "B6", color = GlobalColor("B6"));


######################################################################
#                       Accumulation Day     (B7)                    #
######################################################################
#Accumulation Buy Signal is B2 but after the 25 days of rally attempt.
#Index closes up the percentage required for a followthrogh day. Low volatility FTD would apply as well
# Close in upper 25% of the range
#close must be greater than ema21
#Cannot coincide with a FTD or subsequent FTD (daysAfterR1 >= 25)
#For GapUp the range calculation is as following (Close-Previous day's close)/(high-previous day's close)

def PriceRangeIBD = if low > high[1] then (close - close[1]) / (high - close[1]) else if high<low[1] then (close - low)/(close[1]-low) else (close - low) / (high - low);

input showB7 = yes;
def B7 =  lastR1Bar != 0 and cls >= (cls[1] * FTDPriceRequirement) and
               volIncrease and !oneFTD and close > LowOneFTD and daysAfterR1 >= 25 and PriceRangeIBD >= 0.75 and close > ema21;


AddChartBubble("time condition" = showB7 and B7, "price location" = high, text =  if ShowChartBubbleDescription then "B7 Accumulation Day (+ " + PercentageGreenBars + "%)" else "B7", color = GlobalColor("B7"));


######################################################################
#                            Higher High     (B8)                    #
######################################################################
#Buy after closing above the last marked high (Last high before Rally1)
input showB8 = yes;

def HH = if IsNaN(_markedHigh1) then _lastMarkedHigh1[1]  else if _MarkedHigh1 then high else if _MarkedHigh1[1] then if high >hh[1] then high else hh[1] else hh[1];
def B8 = high > hh;



def SumB8Back = if _MarkedHigh1[LookbackPeriod] then SumB8Back[1] + 1 else if !_MarkedHigh1[LookbackPeriod] and !B8 then SumB8Back[1] else 0;
def oneB8 = SumB8Back < SumB8Back[1];

AddchartBubble(showB8 and oneb8,high,  if ShowChartBubbleDescription then "B8 Higher High" else "B8",Color.LIME);


######################################################################
#                    Downside Reversal Buyback    (B9)               #
######################################################################
#After selling on a downside reversal, buy back if the index closes above the intraday high of the downside reversal within two trading days.

################ S11 code ################
#Sell after downside reversal day
#New Intra-day high within prior 13 days. On seminar was noted as weeks but on simulator was days.
#close in bottom quartile of range
#Close lower than the previous day (redday)
#Spread of 1.75% or greater. Spread is calculated as the (Intraday High-Intraday Low)/ (Intraday Low)

def spreadIntraday = ((high-low)/low)*100;

def S11 = high == highest(high,13) and PriceRangeIBD <=0.25 and redday and spreadIntraday >= 1.75;

def lastoneS11Bar  = If (S11, BarNumber(), lastoneS11Bar[1]);
def daysAfterS11 = BarNumber() - lastoneS11Bar;

################ B9 code ################

input showB9 = yes;

def S11High = if(S11,high,S11High[1]);

def B9 = close > S11High and daysAfterS11<=2;

def SumB9S11 = if S11 then SumB9S11[1] + 1 else if !S11 and !b9  then SumB9S11[1] else 0;
def oneB9 = SumB9S11 < SumB9S11[1];

AddChartBubble("time condition" = showB9 and oneB9, "price location" = high, text =  if ShowChartBubbleDescription then "B9 Downside Reversal Buyback (" + PercentageGreenBars + "%)" else "B9", color = GlobalColor("B9"));


######################################################################
#                      Distribution Day Fall Off    (B10)            #
######################################################################
#Distribution Day
#Indicative of institutional Selling
#Price down greater than or equal to 0.2% on volume greater than or equal to prior day's volume.

#Stall Day (Stalling)
#Stalling is a form of heavy volume without further upsideGapThreeMethods price progress.
#Stall days occur on updays when the market is at or near new highs and close in the lower 25% range on heavier volume of  previous day.
#After the first stallDay, you do not consider additional stall days in the distribution count unitl you have more distribution days than stall days.
 
# full Distribution counts (Distribution and stall days.)
#Before 1992    DC:4 (Distribution Count)
#1991-2004      DC: 5
#2005-Present   DC:6

#B10
#Buy on the day that the distribution count falls back to four (Full distribution count minus two) as long as the close is above the ema21 and Buy Switch is on.

#totalDdays = distDayCount + stallDayCount
#def totalNdDays = newDistDays + newStallDays;

input showB10 = yes;


def B10 = totalAllDS==4 and totalAllDS26==5 and close > ema21; #Need fix entry for BSON



######################################################################
#                   Follow Through Day Undercut    (S1)              #
######################################################################
#Follow Through Day Undercut (S1)
#Sell if index closes below the Low of the initial follow through day.
#-Does not apply to additional FTDs
input showS1 = yes;

def S1 =  close < LowOneFTD ;
def SumOneS1 = if oneFTD then SumOneS1[1] + 1 else if !S1 and !oneFTD then SumOneS1[1] else 0;
def oneS1 = SumOneS1 < SumOneS1[1];

AddChartBubble("time condition" = showS1 and oneS1, "price location" = high, text =  if ShowChartBubbleDescription then "S1 FTD undercut (" + PercentageGreenBars + "%)" else "S1", color = GlobalColor("S1"));


######################################################################
#                  Failed Rally Attempt         Major Rule (S2)      #
######################################################################
#Sell if index undercuts the "major low" of rally attempt.
#-Market exposure is reduced to zero
#-Buy Switch is turned off.
input showS2 = yes;
input showS2ml = yes;
def MajorLow =  if RallyDay1 and low < low[1] then low else if RallyDay1 and low[1] < low then low[1] else MajorLow[1];
def S2 = low < MajorLow; # and MajorLow == MajorLow[1];

def SumoneS2 = if RallyDay1 then SumoneS2[1] + 1 else if !rallyDay1 and !s2  then SumoneS2[1] else 0;
def oneS2 = SumoneS2 < SumoneS2[1];

#def OneS2 = Sum(S2) == 1 and S2;

def minorLow = if RallyDay1[1] then low else if !rallyDay1 then if low < minorLow[1] then low else minorLow[1] else minorLow[1];



#Sell if index undercuts the "minor low" of rally attempt
#-Market exposure is reduced by two
#-This does not turn off the Buy Switch
#The "minor low" of a rally is defined as a low that occurs when the buy switch is on (oneFTD)
def minorLowS2 = !RallyDay1 and minorLow<minorLow[1];




AddChartBubble("time condition" = showS2 and OneS2, "price location" = high, text =  if ShowChartBubbleDescription then "S2 Failed Rally attempt (" + PercentageGreenBars + "%)" else "S2", GlobalColor("S2"));

#######################################################
# Go to Buy Switch for the bubble signal of minor low #
#######################################################
def BSOnforS2ml = if oneFTD then BSOnforS2ml[1] + 1 else if !oneS2 and !oneFTD then BSOnforS2ml[1] else 0;




######################################################################
#                   Full Distribution minus one   (S3)               #
######################################################################
# Sell after distribution count increases to five (Full distribution is currently six days).
#This rule is applied each new time that it occurs.

input showS3 = yes;


Def S3 = if((distributionDays or stallDays) and totalAllDS==5,1,0);


AddChartBubble("time condition" = showS3 and S3, "price location" = high, text =  if ShowChartBubbleDescription then "S3 Full Distribution minus one (" + PercentageGreenBars + "%)" else "S3", GlobalColor("S3"));


######################################################################
#                         Full Distribution       (S4)               #
######################################################################
#Sell after full distribution count (full distribution is currently six days).
#This rule is applied up to eight days (full distribution plus two).


input showS4 = yes;
def S4 = if((distributionDays or stallDays) and (totalAllDS==6 or totalAllDS==7 or totalAllDS==8) ,1,0);

AddChartBubble("time condition" = showS4 and S4, "price location" = high, text =  if ShowChartBubbleDescription then "S4 Full Distribution (" + PercentageGreenBars + "%)" else "S4", GlobalColor("S4"));


######################################################################
#                       Break Below Ema21         (S5)               #
######################################################################
#Sell if index closes 0.2% or greater below the ema21
#S5, S6, S7, S8 Reset by B3. Once you have S5, S6 or S7, you can't have another until is reset by an B3

# signal for on off the S5
def B3S5ResetSignal = S5Reset; #!oneFTD  and daysafterS5Reset[1] >= daysafterB3; # and !oneS5Reset

#S5 Break below ema21
# Sell if index closes 0.2% or greater below ema21

input showS5 = yes;

def S5belowEma21 = cls <= (ema21 * 0.998);
def S5 =  S5belowEma21 ;

def oneS5 = totalB3toS5 < totalB3toS5[1];

def lastoneS5  = If (oneS5, BarNumber(), lastoneS5[1]);
def daysAfterS5 = BarNumber() - lastoneS5;


AddChartBubble("time condition" = showS5 and oneS5, "price location" = high, text =  if ShowChartBubbleDescription then "S5 Break Below close<ema21. Reset B3, B4 & B5 (" + PercentageGreenBars + "%)" else "S5", GlobalColor("S5"));


######################################################################
#                  Overdue Break Below Ema21       (S6)              #
######################################################################
#Overdue break below ema21 (S6)
#Sell if index closes down 0.2% or greater below the ema21 after 30 days have been passed since last B3 without triggering an S5

input showS6 = yes;

def S6 =  s5 and daysAfterB3 >= 29 ;

def SumB3toS6 = if oneB3 then SumB3toS6[1] + 1 else if !oneB3 and !s6  then SumB3toS6[1] else 0;
def oneS6 = SumB3toS6 < SumB3toS6[1];

AddChartBubble("time condition" = showS6 and oneS6, "price location" = high, text =  if ShowChartBubbleDescription then "S6 Overdue Break low<ema21 (" + PercentageGreenBars + "%)" else "S6", GlobalColor("S6"));

######################################################################
#                      Trending Below ema21       (S7)               #
######################################################################
#Trending Below ema21 (S7)
#After and S5 signal has triggered, sell on the 5th consecutive day that then high is below the ema21. The S7 signal is only triggered when the idex closes down for the day. If the indexes closes up on the 5th day from the S5, then wait for the next down day to trigger the S7


input showS7 = yes;

# signal for on off the B4

def S7 = daysAfterS5 >= 4 and high <= ema21 and redDay ; # !oneS5 and !oneB3

def SumB3toS7 = if oneB3 then SumB3toS7[1] + 1 else if !S7 and !oneb3 then SumB3toS7[1] else 0;
def oneS7 = SumB3toS7 < SumB3toS7[1];

AddChartBubble("time condition" = showS7 and oneS7 , "price location" = high, text =  if ShowChartBubbleDescription then "S7 Trending Below ema21. high<ema21 " + (daysAfterS5+1) + "days from (S5) (" + PercentageGreenBars + "%)" else "S7", GlobalColor("S7"));

######################################################################
#                       Living Below Ema21        (S8)               #
######################################################################
#Trending Below ema21 (S7)
#After and S5 signal has triggered, sell on then 10th and every 5th consecutive day after that (15th, 20th, 25th etc.) that then high is below the ema21. The S8 signal is only triggered when the index closes down for the day. If the indexes closes up on the 10th, 15th, 20th, etc. from the S5, then wait for the next down day to trigger the S8


input showS8 = yes;

# signal for on off the B4
#def S5B4ResetSignalw = !oneFTD  and daysAfterB3 < daysAfterS5Reset; # and !oneS5Reset
def fiveDaysS5 = (daysAfterS5+1) % 5 == 0;

def S8Signal = if daysAfterS5 >=9 and s7 and !oneb3 then 1 else 0;


def S8_5 =  S8Signal and fiveDaysS5;
def S8_6 = if !S8_5[1] then S8Signal and fiveDaysS5[1] else 0;
def S8_7 = if !S8_6[1] and !S8_5[2] then S8Signal and fiveDaysS5[2] else 0;
def S8_8 = if !S8_7[1] and !S8_6[2] and !S8_5[3] then S8Signal and fiveDaysS5[3] else 0;
def S8_9 = if !S8_8[1]  and !S8_7[2] and !S8_6[3] and !S8_5[4] then S8Signal and fiveDaysS5[4] else 0;

def S8 = S8_5 or S8_6 or S8_7 or S8_8 or S8_9;

AddChartBubble("time condition" = showS8 and S8, "price location" = high, text =  if ShowChartBubbleDescription then "S8 Living Below Ema21, high<ema21 / " + (daysAfterS5+1) + "days from (S5) (" + PercentageGreenBars + "%)" else "S8", GlobalColor("S8"));



######################################################################
#                         Break Below MA50          (S9)             #
######################################################################
#Sell if index closes below 50ma.
#Only triggers if a corresponding buy signal related to that moving average B6 triggered previously
#Sell S9 is reset by B6
#Exception: -> Index closes in the upper half of range ("Shakeouk"), and Index closes within 1% of the 50ma
input ShowS9 = yes;
#Go to B6 for the code of the signal.

AddChartBubble("time condition" = ShowS9 and oneS9 , "price location" = high, text =  if ShowChartBubbleDescription then "S9 Break Below MA50, close<MA50, reset B6 (" + PercentageGreenBars + "%)" else "S9", GlobalColor("S9"));


######################################################################
#                               Bad Break           (S10)            #
######################################################################
#Sell if the close is down 2.25% of greater in the bottom 25% of range
#close below the ma50 or Intraday high below ema21
#Range as (close-low)/(high-low) for gap down (close-low)/(close[1]-low)

#def range = if high<low[1] then (close - low)/(close[1]-low) else (close-low)/(high-low);

input showS10 = yes;

def S10 = PercentageGreenBars<=-2.25 and (close < ma50 or high < ema21) and PriceRangeIBD <= 0.25 ;

AddChartBubble("time condition" = showS10 and S10, "price location" = high, text =  if ShowChartBubbleDescription then "S10 Bad Break (" + PercentageGreenBars + "%)" else "S10", GlobalColor("S10"));



######################################################################
#                        Downside Reversal Day      (S11)            #
######################################################################
#Sell after downside reversal day
#New Intra-day high within prior 13 days. On the seminar was noted as weeks but on the simulator were days.
#close in bottom quartile of range
#Close lower than the previous day (redday)
#Spread of 1.75% or greater. Spread is calculated as the (Intraday High-Intraday Low)/ (Intraday Low)

input showS11 = yes;
#for S11 go to B9

AddChartBubble("time condition" = showS11 and S11, "price location" = high, text =  if ShowChartBubbleDescription then "S11 Downside Reversal Day (" + PercentageGreenBars + "%)" else "S11", GlobalColor("S11"));



######################################################################
#                               Lower Low          (S12)             #
######################################################################
#Sell after closing below the last marked low  (9days lookback and 9day forward)

input showS12 = yes;


def LL = if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else if _markedLow1[1] then if low <LL[1] then low else LL[1] else LL[1];
def S12 = close < LL;

def SumOneS12 = if _markedLow1[LookbackPeriod] then SumOneS12[1] + 1 else if !_markedLow1[LookbackPeriod] and !S12 then SumOneS12[1] else 0;
def oneS12 = SumOneS12 < SumOneS12[1];

AddchartBubble(showS12 and oneS12,high, if ShowChartBubbleDescription then "S12 closing below Lower low" else "S12",GlobalColor("S12"));

######################################################################
#                       Distribution Cluster       (S13)             #
######################################################################
#Sell after distribution cluster count ("Cluster Count") increases to four within a rolling eight-day period.
#Sell after each successive incease in the cluster count from four up to eight days within a rolling eight-day period.
#For purposes of distribution clusters, all stall days and distribution days apply.
#The cluster count is reset once the cluster count falls to three within a rolling eight-day period.
#The distribution cluster changes the Power-Trend (if it is in effect) to a Power-Trend Under Pressure.
input showS13 = yes;

def S13 = if((distributionDays or stallDays) and (totalDSdays8==4 or totalDSdays8==5 or totalDSdays8==6 or totalDSdays8==7 or totalDSdays8==8 ) and totalDSdays8 != totalDSdays8[1]  ,1.0,0.0);;

AddchartBubble(showS13 and S13,high, if ShowChartBubbleDescription then "S13 Distribution Cluster" else "S13",GlobalColor("S13"));

######################################################################
#                    Break Below Higher High       (S14)             #
######################################################################
input showS14 = yes;
#Sell after closing below the last marked high that triggered a B8 (Higher High)

def barNumberB8 = If (oneB8, BarNumber(), barNumberB8[1]);;

def S14 =  close<HH and high[1]>hh and (barnumber() - barNumberB8)>0; #Incase of gapdown we use previous days high
def SumOneS14 = if _MarkedHigh1[LookbackPeriod] then SumOneS14[1] + 1 else if !_MarkedHigh1[LookbackPeriod] and !S14 then SumOneS14[1] else 0;;
def oneS14 = SumOneS14 < SumOneS14[1];


AddchartBubble(showS14 and oneS14,high, if ShowChartBubbleDescription then "S14 Breakaway Close Below Higher High" else "S14",GlobalColor("S14"));


######################################################################
########################## Circuit breaker ###########################
######################################################################
#Circuit breaker - occurs when you've got a 10% break of the market from the high point
Input showCircuitBreaker = yes;

def RecentHigh = if RallyDay1 then high else if !rallyDay1 then if high >RecentHigh[1] then high else RecentHigh[1] else RecentHigh[1]; # find the highest high in a periog Rallyday R1

def CB = if close < RecentHigh - RecentHigh*0.1 then 1 else 0;

def CountCB = if oneFTD then CountCB[1] + 1 else if !CB and !oneFTD then CountCB[1] else 0;
def oneCB = CountCB < CountCB[1];

AddchartBubble(showCircuitBreaker and oneCB,high, if ShowChartBubbleDescription then "Circuit Breaker 10% break of Highs. BS OFF" else "CB",GlobalColor("Circuit Breaker"));


######################################################################
#                          Market Exposure                           #
######################################################################

def BSOnB10 = if oneFTD then BSOnB10[1] + 1 else if !(oneS2 or (S4 ) or oneCB ) and !oneFTD then BSOnB10[1] else 0; #I could't add the market exposure ==0 with the (S4 ) on above code.

def S2ml = BSOnforS2ml and minorLowS2 and BSOnB10 and !oneFTD;

input ShowMElabel = yes;

def BS = B1 + B2 + oneB3 + oneB4 + B5 + oneB6 + B7 + oneB8 + oneB9 + (BSOnB10 and B10); #B10 is on when BuySwitch is on.
def SS = oneS1 + oneS2 + s2ml + S3 + S4 + oneS5 + oneS6 + oneS7 + S8 + oneS9 + S10 + S11 + oneS12 + S13 + oneS14 + oneCB;
def TS = BS-SS;

def countME = if rallyDay1 then 0 else if countME[1] > 5 then 5 else if countME[1]<0 then 0 else countME[1] + TS ;
def MarketExposure = if countME <0  then 0 else if countME>5 then 5 else countME;

#Market Exposure when the Restraint Rule  is applied
def countMERR = if rallyDay1 then 0 else if countMERR[1] > 2 then 2 else if countMERR[1]<0 then 0 else countMERR[1] + TS ;
def MarketExposureRR = if countMERR <0  then 0 else if countMERR>2 then 2 else countMERR;

#Market Exposure when the Power Trend is applied
def countMEPT = if rallyDay1 then 0 else if countMEPT[1] > 7 then 7 else if countMEPT[1]<2 then 2 else countMEPT[1] + TS ;
def MarketExposurePT = if countMEPT <2  then 2 else if countMEPT>7 then 7 else countMEPT;

# Market Exposure when the PowerTrendUnderPressure (+5 Floor +1)
def countMEPTup = if rallyDay1 then 0 else if countMEPTup[1] > 5 then 5 else if countMEPTup[1]<1 then 1 else countMEPTup[1] + TS ;
def MarketExposurePTup = if countMEPTup <1  then 1 else if countMEPTup >5 then 5 else countMEPTup;


# Market Exposure when the PowerTrendUnderPressureWithoutFloor
def countMEPTwf = if rallyDay1 then 0 else if countMEPTwf[1] > 5 then 5 else if countMEPTwf[1]<0 then 0 else countMEPTwf[1] + TS ;
def MarketExposurePTwf = if countMEPTwf <0  then 0 else if countMEPTwf >5 then 5 else countMEPTwf;


#color Label settings
def mid = 2;
def upper = 7;
def lower = 0;
def bl = 75;

def CLrValue = if MarketExposure < mid then 255 else if MarketExposure > upper then 0 else 255 - Round((MarketExposure - mid) / (upper - mid) * 255, 0);
def CLgValue = if MarketExposure > mid then 255 else if MarketExposure < lower then 0 else 255 - Round((mid - MarketExposure) / (mid - lower) * 255, 0);






######################################################################
#                            Restraint Rule                          #
######################################################################
#To avoid getting invested too fast, limit exposure to a maximum of +2

#Rules to remove the Restraint Rule: The index makes significant progress above the close of the initial follow-through day OR
#A buy signal (B4) is triggered (trending above ema21)

#Significant progress is defined as the percentage requirement nedeed for a FTD based on volatility (FTDPriceRequirement: currently 1%). Example if you close above 1% from the close of the FTD then we lift the Restraint Rule. OR
#If you trending above the FTD

#Restraint Rule applies if:
#The maket exposure was 0 prior FTD
#If the buy switch turns on due to closing above a prior high, then the restraint rule will apply until the index makes significant progress as defined by the (FTDPriceRequirement).

input ShowRestraintRuleLabel = yes;

def CloseofOneFTD = if oneFTD then close else CloseofOneFTD[1];
def RestraintLifted = if oneB4 or close > CloseofOneFTD*FTDPriceRequirement  then 1 else 0;

def MarkExpPriorFTD = If oneFTD and MarketExposure[1]==0 then 1 else 0;

def RR =  if MarkExpPriorFTD and !rallyDay1 and !RestraintLifted then 1 else 0;
Addlabel(ShowRestraintRuleLabel and RR,"Restraint on. Max MarketExposure Expose +2",Color.ORANGE);


######################################################################
#                              Power Trend                           #
######################################################################
#Power Trend Turns On:
#The ema21>ma50 for at least 5 consecutive days without an S9(break below ma50)
#The Index closes up or flat for the day.
#The ma50 is in uptrend for at least 1 day
#The Index low has been above the ema21 for at least 10 consecutive days
#Buy Switch override -> BSOn is forced to stay on
#Buy Switch count can go now up to +7 (100% exposure =+5) +2 buffer for sell signals.
#Don't act on anything higher than +5. This rule was created to provide a buffer to keep you incested during a significant uptrend.
#Start counting additional buy signals from the point that the PowerTrend turns on.

#Once the buy count is at +2 or higher, a floor of +2 is in effect.

Input showPowerTrendLabel = yes;


#Condition that End Power Trend:
#ema21 line crosses below the ma50 and index closes down for the day OR
#S2 Major Low OR
#Circuit Breaker.

#After Power Trend has ended
#No longer forces buy switch on (it may still be on, but it doesn't have to be on)
#Buy signal count is reset to a maximum of +5
#There is no longer a floor.
def PToff = ema21 crosses below ma50 and redDay or oneS2 or oneCB ;

#Condition that re-set a Power Trend from an Under Pressure condtion:
#The conditons that turn on a power-trend must be present, and
#10 consecutives days must pass without an S2 minor low an S9 and S13.
def p=10;
def PTOnreset =  fold q=0 to p with r do if(sum(!minorLowS2[q] and !ones9[q] and !s13[q],p)==p,1,0);

###### PT ON-OFF #####
def n=5;
def PTonSignal = fold i=0 to n with s do if(sum(ema21[i]>ma50[i] and !ones9[i],n)==n and ma50>ma50[1] and upday and sum(low>ema21,10)==10,1,0);


def SumPTon = if PTonSignal then 1 else if PToff then 0 else SumPTon[1];
def PTon = SumPTon;


#Condition that turn a Power Trend to a Power Trend under pressure is:
#S2 minor low signal (and minorLowS2 and BSon)
#S13 rule (Distribution cluster) is triggered

def PTonUnderPressure = PTon and (minorLowS2 or S13);
def SumPTup = if PTonUnderPressure then 1 else if PToff then 0 else SumPTup[1];
def PTup = SumPTup;

#When the power trend is in an under-pressure position with a floor, the max exposure count is reduced to +5 and the floor is reduced to +1 before any signals are applied on that day.

#Condition than turn a Power Trend to a Power Trend under pressure without a floor
#S9 signal (break below ma50)
#When an S9 occurs, the max exposure count remains at +5 but the floor of +1 is removed.

def PTonUnderPressureWithoutFloor = PTon and S9;
def SumPTupWF = if PTonUnderPressureWithoutFloor then 1 else if PToff then 0 else SumPTupWF[1];
def PTWF = SumPTupWF;

def PowerTrendUnderPressure = PTup and !Ptonreset;
def PowerTrendUnderPressureWithoutFloor = PTwf and !Ptonreset;
def PowerTrendOnNopressure = PTon and !PowerTrendUnderPressure and !PowerTrendUnderPressureWithoutFloor;
#def SumPTon = if PTonSignal then 1 else if PToff then 0 else SumPTon[1];


######################################################################
#                             Buy Switch                             #
######################################################################
#BS rules
#When the buy switch is on, you act on all buy and sell signals regardless of the distribution count.
#When the buy switch is off, you only act on sell signals.

#Events that turn Buy Switch On:
#Follow Through day (B1)
#Note on the day buy switch turns on, act on all buy signals that are in effect (except for buy signals B9 and B10) from the low (Rallyday1).

#Events that turn Buy Switch OFF
#Full distribution Count (S4) and market exposure is 0
#Failed rally attempt (S2) of a "major low"
#Circuit breaker - occurs when you've got a 10% break of the market from the high point

#What if the Buy Switch is Off, the MarketExposure count is zero, we haven't a follow-through day but the market is acting strong? How do you turn on the buy switch?
#A close above the prior high oneB8 since the last confirmed rally will turn the Buy Switch on.

############################## Buy Switch ##############################
input ShowBuySwitchLabel = yes;
input ShowBuySwitchBuddle = yes;


def BSOn = if (oneFTD or PTon or oneB8) then BSOn[1] + 1 else if !(oneS2 or (S4 and (if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else  MarketExposure)==0) or oneCB or oneS14 ) and !oneFTD then BSOn[1] else 0;


#def BSOff = BSOn < BSOn[1];
#def oneBSOn =if(BSOn > BSOn[1] and BSOn[1] == BSOn[2],1 ,0);

#AddchartBubble(ShowBuySwitchBuddle and oneBSOn, high,"Buy Switch On",GlobalColor("BuySwitch On"));
#AddchartBubble(ShowBuySwitchBuddle and BSOff, high,"Buy Switch Off",GlobalColor("BuySwitch Off"));


#BS Off MarketExposure Exposure

def TSforBSoff = -SS;

def countMEforBSoff = if rallyDay1 then 0 else if countMEforBSoff[1] > 5 then 5 else if countMEforBSoff[1]<0 then 0 else (if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else  MarketExposure) + TSforBSoff ;
def MarketExposureforBSoff = if countMEforBSoff <0  then 0 else if countMEforBSoff >5 then 5 else countMEforBSoff;

def CurrentME = if RR then MarketExposureRR else if PTon and !PowerTrendUnderPressure and !PowerTrendUnderPressureWithoutFloor  then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else if !bson then MarketExposureforBSoff else  MarketExposure;


Addlabel(ShowMElabel,"Market Expose: (+" + CurrentME + ") " + if CurrentME == 0  then "0%" else if CurrentME==1 then "33%" else if CurrentME==2 then "55%" else if CurrentME==3 then "75%" else if CurrentME == 4 then "90%" else "100%", CreateColor(CLrValue, CLgValue, bl));

#Addlabel(ShowMElabel,"Count: (" + (if RR then countMERR else if PTon then countMEPT  else if PowerTrendUnderPressure then countMEPTup else if PowerTrendUnderPressureWithoutFloor then countMEPTwf else if !bson then countMEforBSoff else countME) + ")", CreateColor(CLrValue, CLgValue, bl));


#AddchartBubble(B1 OR B2 OR oneB3 OR oneB4 OR B5 OR oneB6 OR B7 OR oneB8 OR oneB9 OR (BSOnB10 and B10) OR oneS1 OR oneS2 OR s2ml OR S3 OR S4 OR oneS5 OR oneS6 OR oneS7 OR S8 OR oneS9 OR S10 OR S11 OR oneS12 OR S13 OR oneS14 OR oneCB,high,if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else if !bson then MarketExposureforBSoff else  MarketExposure,color.orange);



######################################################
######### BS labels added on the Power Trend #########
######################################################



######################################################
####### Distribution Day Fall Off   Bubble B10 #######
######################################################


AddChartBubble("time condition" = showB10 and B10 and BSOn, "price location" = high, text =  if ShowChartBubbleDescription then "B10 Distribution Day Fall Off (" + PercentageGreenBars + "%)" else "B10", GlobalColor("B10"));

######################################################
##### Failed Rally Attempt. Bubble S2 Minor Low  #####
######################################################

AddChartBubble("time condition" = showS2ml and s2ml and BSon, "price location" = high, text =  if ShowChartBubbleDescription then "S2 Undercut MinorLow Low or the rally (" + PercentageGreenBars + "%)" else "S2ml", GlobalColor("S2ml"));

##### BS Labels ####

Addlabel(ShowBuySwitchLabel,"Buy Switch: ",color.WHITE);
Addlabel(ShowBuySwitchLabel,if BSOn then "ON" else "OFF", if BSon then color.green else color.Red);



Addlabel(showPowerTrendLabel,if PowerTrendUnderPressure then "Power Trend under pressure: Max (+5) Floor (+1)" else if PowerTrendUnderPressureWithoutFloor then "Power Trend under pressure without floor: Max (+5) Floor (0)" else If PowerTrendOnNopressure then "Power Trend: Max (+7) Floor (+2)" else "Power Trend:",color.white);
Addlabel(showPowerTrendLabel,If PTon then "ON" else "OFF",if Pton then Color.GREEN else color.red);


######################################################################
#                      Active Label Description                      #
######################################################################
input ShowSignalsLabelDescription = yes;
#### Buy Signals Labels ####
Addlabel(ShowSignalsLabelDescription and B1,"B1: Buy on initial follow-through day.",GlobalColor("B1"));
Addlabel(ShowSignalsLabelDescription and B2,"B2: Buy on all additional follow-through days within 25-days from a rally day that closes above the low of the initial follow-through day.",GlobalColor("B2"));
Addlabel(ShowSignalsLabelDescription and oneB3,"B3:  Low Above Ema21. Once you have B3, you can't have another until is reset by an S5.",GlobalColor("B3"));
Addlabel(ShowSignalsLabelDescription and oneB4,"B4: Trending Above Ema21. Once you have B3, you can't have another until is reset by an S5.",GlobalColor("B4"));
Addlabel(ShowSignalsLabelDescription and B5,"B5: Living Above Ema21. Buy after a B3 signal has triggered, buy on the 10th and every 5th consecutive day after that in an upday.",GlobalColor("B5"));
Addlabel(ShowSignalsLabelDescription and oneB6,"B6: Low Above MA50. Buy on an upday with an uptrending ma50 (ma50>ma50[1]). Once you have B6, you can't have another until is reset by an S9.",GlobalColor("B6"));
Addlabel(ShowSignalsLabelDescription and B7,"B7: Accumulation Day. Buy Signal is additional FTD but after the 25 days of rally attempt and close in the upper 25% range and greater ema21.",GlobalColor("B7"));
Addlabel(ShowSignalsLabelDescription and oneB8,"B8: Higher High. Buy after closing above the last marked high (Last high before R1).",GlobalColor("B8"));
Addlabel(ShowSignalsLabelDescription and oneB9,"B9: Downside Reversal Buyback. After selling on a downside reversal (S11), buy back if the index closes above the intraday high of the downside reversal within two trading days.",GlobalColor("B9"));
Addlabel(ShowSignalsLabelDescription and B10,"B10: Distribution Day Fall Off. Buy on the day that the distribution count falls back to four (Full distibution minus two) as long as the close is above the ema21 and Buy Switch is on.",GlobalColor("B10"));


#### Sell Signals Labels ####
Addlabel(ShowSignalsLabelDescription and oneS1,"S1: Follow Through Day Undercut. Sell if index closes below the Low of the initial follow through day.",GlobalColor("S1"));
Addlabel(ShowSignalsLabelDescription and oneS2,"S2: Failed Rally Attempt. Sell if index undercuts the major low of rally attempt. Market exposure is reduced to zero and Buy Switch is turned off.",GlobalColor("S2"));
Addlabel(ShowSignalsLabelDescription and S2ml and BSon,"S2: Minor Low undercut of rally attempt. Market exposure is reduced by two. This does not turn off the Buy Switch. MinorLow defined as low that occurs when the buy switch is on (oneFTD).",GlobalColor("S2"));
Addlabel(ShowSignalsLabelDescription and S3,"S3: Full Distribution minus one. Sell after distribution count increases to five (Full distribution: six days). This rule is applied each new time that it occurs.",GlobalColor("S3"));
Addlabel(ShowSignalsLabelDescription and S4,"S4: Full Distribution. Sell after full distribution count (full distribution: six days). This rule is applied up to eight days (full distribution plus two)",GlobalColor("S4"));
Addlabel(ShowSignalsLabelDescription and oneS5,"S5: Break Below Ema21. Sell if index closes 0.2% or greater below the ema21. Once you have S5, you can't have another until is reset by an B3",GlobalColor("S5"));
Addlabel(ShowSignalsLabelDescription and oneS6,"S6: Overdue Break Below Ema21. Sell if index closes down 0.2% or greater below the ema21 after 30 days have been passed since last B3 without trigering an S5. Once you have S6, you can't have another until is reset by an B3.",GlobalColor("S6"));
Addlabel(ShowSignalsLabelDescription and oneS7,"S7: Trending Below ema21. Sell after S5 on then 5th consecutive day that then high is below the ema21 and DownDay Day. You can't have another S7 until is reset by an B3.",GlobalColor("S7"));
Addlabel(ShowSignalsLabelDescription and S8,"S8: Living Below Ema21. Sell after S5 on then 10th and every 5th consecutive day after that that then high is below the ema21. Down Day if not then wait for the next down day.",GlobalColor("S8"));
Addlabel(ShowSignalsLabelDescription and oneS9,"S9: Break Below MA50. Index closes below 50ma. Only triggers if a B6 triggered preiviously. Sell S9 is reset by B6.",GlobalColor("S9"));
Addlabel(ShowSignalsLabelDescription and S10,"S10: Bad Break. Sell if the close is down 2.25% or greater in the bottom 25% of range. Close below the ma50 or Intraday high below ema21.",GlobalColor("S10"));
Addlabel(ShowSignalsLabelDescription and S11,"S11: Downside Reversal Day. New Intra-day high within prior 13 days and close in bottom quartile of range and RedDay.",GlobalColor("S11"));
Addlabel(ShowSignalsLabelDescription and oneS12,"S12: Lower Low. Sell after closing below the last marked low  (9days lookback and 9day forward).",GlobalColor("S12"));
Addlabel(ShowSignalsLabelDescription and S13,"S13: Distribution Cluster. Distribution and Stalling days increases to four up to eight days within a rolling eight day period. The distribution cluster changes the Power-Trend (if it is in effect) to a Power-Trend Under Pressure.",GlobalColor("S13"));
Addlabel(ShowSignalsLabelDescription and oneS14,"S14: Break Below Higher High. Sell after closing below the last marked high that triggered a B8 (Higher High).",GlobalColor("S14"));

Addlabel(ShowSignalsLabelDescription and oneCB, "Circuit Breaker Index falls 10% of Highs since the follow-through. Buy Switch is turned off.",GlobalColor("Circuit Breaker"));

@DeathFireArt I see the notes provided in the code, but want to know if any specific alert stands out in your opinion regarding tops/bottoms.
 
@DeathFireArt I see the notes provided in the code, but want to know if any specific alert stands out in your opinion regarding tops/bottoms.
Dear nktkobby
When there is a rally day, make your first buy (if possible) on the FTD (B1).

Distribution days are very important "DD"

and if you get a circuit breaker "CB" signal or S2 then you should exit all your position in the market.

General, I follow the Market expose Label for my position but please keep in mind that my code is not perfect and you should be aware if you get a lot to sell signals to act based on your comfort zone. :)
 
Last edited by a moderator:
Dear nktkobby
When there is a rally day, make your first buy (if possible) on the FTD (B1).

Distribution days are very important "DD"

and if you get a circuit breaker "CB" signal or S2 then you should exit all your position in the market.

General, I follow the Market expose Label for my position but please keep in mind that my code is not perfect and you should be aware if you get a lot to sell signals to act based on your comfort zone. :)
Thank you for the info...will test drive and provide feedback
 
Can you change the market exp label to a difference color. I can't in read it
Dear HDR
The Market Exposure Label color is dynamic changed, based on its value, however, you can use my chart setup:
http://tos.mx/UHZZ7gW

or you can replace the code as follows:

Addlabel(ShowMElabel,"Market Expose: (+" + CurrentME + ") " + if CurrentME == 0 then "0%" else if CurrentME==1 then "33%" else if CurrentME==2 then "55%" else if CurrentME==3 then "75%" else if CurrentME == 4 then "90%" else "100%", CreateColor(CLrValue, CLgValue, bl));

to

DefineGlobalColor("Market Expose", Color.WHITE);

Addlabel(ShowMElabel,"Market Expose: (+" + CurrentME + ") " + if CurrentME == 0 then "0%" else if CurrentME==1 then "33%" else if CurrentME==2 then "55%" else if CurrentME==3 then "75%" else if CurrentME == 4 then "90%" else "100%",GlobalColor("Market Expose"));


The color of the Label will be white and can be changed from the global color settings.
 
Dear all, I have modified the IBD Distribution Days Study for TOS shared from SMO_MktVolumesDaily
with buy/sell signals
I hope you will find my work helpful on your trading journey.

Ruby:
# SMO_MktVolumesDaily.ts
# For daily charts only.
# Nasdaq total volume: $TVOL/Q ;   NYSE total volume: $TVOL
# Distribution day count tracking by IBD:
# https://www.investors.com/how-to-invest/investors-corner/
# tracking-distribution-days-a-crucial-habit/
# Stalling daysFromDate tracking by IBD:
# https://www.investors.com/how-to-invest/investors-corner/
# can-slim-market-tops-stalling-distribution/
#BuySell Signal added by dag 2022-03-27

# For daily charts only.
######################################################################################
declare once_per_bar;
input ShowChartBubbleDescription = no;
Input ShowDistrubutionLabels=no;


DefineGlobalColor("B1", Color.GREEN);
DefineGlobalColor("B2", Color.LIGHT_GREEN);
DefineGlobalColor("B3", Color.LIGHT_GREEN);
DefineGlobalColor("B4", Color.LIGHT_GREEN);
DefineGlobalColor("B5", Color.LIGHT_GREEN);
DefineGlobalColor("B6", Color.LIME);
DefineGlobalColor("B7", Color.LIGHT_GREEN);
DefineGlobalColor("B8", Color.LIGHT_GREEN);
DefineGlobalColor("B9", Color.LIGHT_GREEN);
DefineGlobalColor("B10", Color.LIGHT_GREEN);

DefineGlobalColor("S1", Color.LIGHT_RED);
DefineGlobalColor("S2", Color.RED);
DefineGlobalColor("S2ml", Color.DOWNTICK);
DefineGlobalColor("S3", Color.LIGHT_RED);
DefineGlobalColor("S4", Color.LIGHT_RED);
DefineGlobalColor("S5", Color.LIGHT_RED);
DefineGlobalColor("S6", Color.LIGHT_RED);
DefineGlobalColor("S7", Color.LIGHT_RED);
DefineGlobalColor("S8", Color.LIGHT_RED);
DefineGlobalColor("S9", Color.PLUM);
DefineGlobalColor("S10", Color.LIGHT_RED);
DefineGlobalColor("S11", Color.LIGHT_RED);
DefineGlobalColor("S12", Color.LIGHT_RED);
DefineGlobalColor("S13", Color.LIGHT_RED);
DefineGlobalColor("S14", Color.YELLOW);

DefineGlobalColor("Circuit Breaker", Color.RED);
DefineGlobalColor("BuySwitch On", Color.GREEN);
DefineGlobalColor("BuySwitch Off", Color.WHITE);




#############################################################################################

#SWING HIGH LOW
def LookbackPeriod = 9; #
input ShowHLLevels= no;

#--------------------------------------------------------------
def _highInPeriod1 = Highest(high, LookbackPeriod);
def _lowInPeriod1 = Lowest(low, LookbackPeriod);
#--------------------------------------------------------------
def marketLow1 = if _lowInPeriod1 < _lowInPeriod1[-LookbackPeriod] then _lowInPeriod1 else _lowInPeriod1[-LookbackPeriod];
def _markedLow1 = low == marketLow1;

rec _lastMarkedLow1 = CompoundValue(1, if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else _lastMarkedLow1[1], low);
#--------------------------------------------------------------
def marketHigh1 = if _highInPeriod1 > _highInPeriod1[-LookbackPeriod] then _highInPeriod1 else _highInPeriod1[-LookbackPeriod];
def _markedHigh1 = high == marketHigh1;

rec _lastMarkedHigh1 = CompoundValue(1, if IsNaN(_markedHigh1) then _lastMarkedHigh1[1] else if _markedHigh1 then high else _lastMarkedHigh1[1], high);
#--------------------------------------------------------------
plot Resistance1 = _lastMarkedHigh1;
plot Support1 = _lastMarkedLow1 ;

#--------------------------------------------------------------
Resistance1.SetPaintingStrategy(PaintingStrategy.DASHES);
Resistance1.SetDefaultColor(Color.GREEN);
Resistance1.SetHiding(!ShowHLLevels);
#--------------------------------------------------------------
Support1.SetPaintingStrategy(PaintingStrategy.DASHES);
Support1.SetDefaultColor(Color.RED);
Support1.SetHiding(!ShowHLLevels);
#--------------------------------------------------------------

input ShowHighLowBuble = yes;
AddchartBubble(ShowHighLowBuble and _markedHigh1,high, round(high,2),color.white);
AddchartBubble(ShowHighLowBuble and _markedLow1,low, round(Low,2),color.white, up = No);

########################################################################################


def length = 50;        # volume moving average lenth in days

input volumeSymbol = {default NASDAQ, NYSE, SPX, CurrentSymbol};
# Reset distribution day counts on FTD.
input distributionRstDay = 20191010;

def volCl;
def volHi;
def findSymbol;

# To make volume differences more visible, use a base volume number
# The subtracted volume number is then magnified to present a bigger difference
def volMin;    # base number for volume
def dropThreshold;

switch (volumeSymbol) {
# It was found there may be erratic volume data on close values
# On 2/19/2020, NYSE volume close values were 0 on 2/18 & 2/12
case NYSE:
    volCl = if close("$TVOL") == 0 then high("$TVOL") else close("$TVOL");
    #volCl = close("$TVOL");
    volHi = high("$TVOL");
# use SPX volume change percentage to replace erratic NYSE volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOLSPC") - close("$TVOLSPC")[1]) / close("$TVOLSPC")[1]) else volCl;
    volMin = 40000;
    dropThreshold = .942;
case NASDAQ:
    volCl = if close("$TVOLC/Q") == 0 then high("$TVOLC/Q") else close("$TVOLC/Q");
    #volCl = close("$TVOL/Q");
    volHi = high("$TVOLC/Q");
# use SPX volume change percentage to replace erratic NASDAQ volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOLSPC") - close("$TVOLSPC")[1]) / close("$TVOLSPC")[1]) else volCl;
    volMin = 30000;
    dropThreshold = .931;
case SPX:
    volCl = if close("$TVOLSPC") == 0 then high("$TVOLSPC") else close("$TVOLSPC");
    #volCl = close("$TVOLSPC");
    volHi = high("$TVOLSPC");
# use NYSE volume change percentage to replace erratic SPX volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOL") - close("$TVOL")[1]) / close("$TVOL")[1]) else volCl;
    volMin = 0;
    dropThreshold = 1;

case CurrentSymbol:
    volCl = if close == 0 then high else close;
    volHi = high;
 
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close - close[1]) / close[1]) else volCl;
    volMin = 10000;
    dropThreshold = .942;

}

def cls = close;

def lastBar = HighestAll(If (IsNaN(cls), Double.NaN, BarNumber()));
def volumes = if IsNaN(findSymbol) and BarNumber() == lastBar then volumes[1] else findSymbol;

def Vol = 3 * (volumes - volMin);
def VolAvg = 3 * (Average(volumes, length) - volMin);


# Display useful texts starting at upper left corner
# End of Day volume change
def VolChangePercentDay = If (IsNaN(volumes[1]), 0,
                              100 * (volumes - volumes[1]) / volumes[1]);

# InvalidDay was added since volume on 2019/11/29 (after Thanksgiving) was N/A.
AddLabel( ShowDistrubutionLabels, if VolChangePercentDay == 0 then "InvalidDay" else "" +
               "VolmChg=" + Concat("", Round(VolChangePercentDay)) +
               "%", if VolChangePercentDay < 0 then
               Color.DARK_GRAY else if cls > cls[1] then Color.DARK_GREEN
               else Color.DARK_RED);

# Count distributionDay only if market price drops 0.2% or more
def downDay = cls <= (cls[1] * 0.998);

def volIncrease = volumes > volumes[1];

#
# After 25 sessions, a distribution day expires
# Use 25 bar numbers to represent 25 live sessions. GetDay or alike includes weekends.
#

def lastDays = if (BarNumber() > lastBar - 25) then 1 else 0;

# a distribution day can fall off the count if the index rises 6% or more,
# on an intraday basis, from its close on the day the higher-volume loss appears.
# Remove distribution days after prices increase 6% WHEN the market is in an uptrend.
# Need to fix:
# During the market bottomed on 2-28-2020, the stock price rose to 9.8% with the market still in
# correction. The high volume selloff on 2-28 would still be counted as a distribution.
# The highest date should be after the distribution day

# Get proper high for future 25 days
def prHi = high;
def prLo = low;

def futureHigh = if IsNaN(prHi[-25]) then futureHigh[1] else prHi[-25];
def prHighest = Highest(futureHigh, 25);
# Note: This condition disqualifies D-Days after large bear rally
# This is acceptable for now since D-Days in bear market are not really useful
def priceInRange = (cls * 1.06 >= prHighest);

def distributionDay = downDay and volIncrease and lastDays and priceInRange;

# Count valid distribution days in last 25 days
def distDayCount = Sum(distributionDay, 25);


# A broad market correction makes the distribution day count irrelevant
# reset distribution count to 0
# Distribution day count should reset after 2nd confirmation day
# input distributionRstDay = 20191010;   a prior 2nd confirmation day
# input distributionRstDay = 20200402;   a prior 2nd confirmation day
#input distributionRstDay = 20191010;

def newDistributionCycle = GetYYYYMMDD() > distributionRstDay;
# Need to use above variable to restart d-day count
def newDistDays = Sum(distributionDay and newDistributionCycle, 25);

# Display bubble red is count > 5, yellow >3, else while
AddChartBubble(distributionDay and !newDistributionCycle, Vol, Concat("", distDayCount),
                if distDayCount < 3 then Color.WHITE
                else if distDayCount < 5 then Color.LIGHT_ORANGE
                else Color.RED);


# Show D-Day reset line at the reset date input by user
# It appears at the left side of the volume bar
AddVerticalLine( if (GetYYYYMMDD() == distributionRstDay ) then yes
                 else no,
                "                   2ndCnfm",
                Color.GREEN, Curve.MEDIUM_DASH);



# to do: Comparison of preholiday data may be invalid.

#------------------------------------------------------------
# Stalling day counts
# 1. market has been rising and price is within 3% of 25 day high
# 2. Price making a high
#      current close >= prior 2 day close, or
#      current close >= prior day high
# 3. volume >= 95% of prior day volume
# 4. close in lower half of daily range
# 5. small gain within 0.4% for SPX & NASDAQ
# 6. The above IBD criteria disclosed in one article generates too many stalling days
#    Additional rules from IBD book are used to further reduce stalling counts
#    6.1 close up smaller than prior 2 days
#    6.2 low is lower than high of prior day (No unfilled gap-up)
#    6.3 there is at least one decent gain in prior 2 days
#    6.4 daily trading range should be similar to last 2 days
# 7. stalling counts are reduced due to time (25 days) and significantly upward
#    movement (6%) of the index
# Ex. 2019/11/12 was a stalling day on SPX, 2019/12/18 was stalling for Nasdaq

def priceIsHigh = cls >= cls[2] or cls >= prHi[1];
def priceLowHalf = cls < (prHi - prLo) / 2 + prLo;
def priceGainSmall = cls - cls[1] > 0 and
                     ((cls - cls[1] < (cls[1] - cls[2])) or
                     ((cls - cls[1] < cls[2] - cls[3])));

# Added a 0.2% gap from prior day high to allow 2020/05/26 to count
# as a stalling day for NASDAQ
def priceGapFill = prLo < prHi[1] * 1.002;
def priceGainOk = (cls[1] - cls[2] > 0.002 * cls[2]) or
                   (cls[2] - cls[3] > 0.002 * cls[3]);
# price trading range is the high - low plus the gap up if any
def priceRange = if prLo > prHi[1] then prHi - prHi[1] else prHi - prLo;
def priceRangeBig = priceGainOk and priceRange > 0.8 * Min(priceRange[1], priceRange[2]);

def stallDay = cls - cls[25] > 0 and
               cls >= 0.97 * Highest( prHi, 25) and
               volumes > 0.95 * volumes[1] and
               cls - cls[1] > 0 and
               cls - cls[1] < 1.004 * cls[1] and
               priceIsHigh and priceLowHalf and priceGainSmall and priceGapFill and
               priceRangeBig and lastDays;

input ShowStallingDays = yes;
input ShowDistributionDays = yes;
#AddChartBubble(ShowStallingDays and stallDay , high, "Stalling Days", Color.MAGENTA);

# Count stalling days
def stallDayCount = Sum(stallDay, 25);

# calculate new stalling days after the reset day (e.g. follow-up date)
def newStallDays = Sum(stallDay and newDistributionCycle, 25);

# Display final distribution count (incl. stall days)
# red if >= 5, >3: yellow, else green
def totalDdays = distDayCount + stallDayCount;
def totalNdDays = newDistDays + newStallDays;

AddChartBubble(ShowDistributionDays and distributionDay and newDistributionCycle, high,
                if volCl == 0 then Concat("?", newDistDays) else Concat("", newDistDays),
                if totalNdDays < 3 then Color.WHITE
                else if totalNdDays < 5 then Color.LIGHT_ORANGE
                else Color.RED);


AddChartBubble(ShowStallingDays and stallDay and lastDays, high,
                if volCl == 0 then "?Stalling:" + Concat("", stallDayCount)
                              else "Stalling:" + Concat("", stallDayCount),
                if totalNdDays < 3 then Color.WHITE
                else if totalNdDays < 5 then Color.LIGHT_ORANGE
                else Color.RED);
AddChartBubble(volCl == 0 and !stallDay and !distributionDay, high, "?", Color.LIGHT_GRAY);


AddLabel(ShowDistrubutionLabels and totalDdays != totalNdDays, "AllDdays =" + Concat("", totalDdays), Color.GRAY);

#-------------------------------------------------------------------------------------
# Follow-through signals (FTD) are more likely to fail if distribution days
# occur in the first few days of a new uptrend. This is one key red flag.
# Quantification in script is implemented with a concept of critical score (critScore):
#   critScore = 3 for the 1st 5 days after FTD
#   critScore = 2 on the 6th, critScore = 1 on 7th day, critScore = 0 after 7th day
#   Total Distribution days = critScore + regular D-day count
#   critScore is used only if there is at least one D-day in the 1st 5th day after FTD
#-------------------------------------------------------------------------------------

def ftdBar = if GetYYYYMMDD() == distributionRstDay then BarNumber() else 0;
def lastFtdBar = HighestAll(ftdBar);
def daysAfterFTD = lastBar - lastFtdBar;

def critScore = if daysAfterFTD <= 0 then 0 else
                if daysAfterFTD <= 5 then 3 else
                if daysAfterFTD <= 6 then 2 else
                if daysAfterFTD <= 7 then 1 else 0;

def totalNdDaysC = totalNdDays + if totalNdDays > 0 then critScore else 0;

# Actual distribution day count is shown but color depends on totalNdDaysC
AddLabel(ShowDistrubutionLabels, "NewDdays =" + Concat("", totalNdDays ),
         if totalNdDaysC <= 2 then Color.GREEN
         else if totalNdDaysC <= 4 then Color.ORANGE
         else Color.RED);


# Add an indication of 1st rally day to start FTD count
# in a market correction period
# pink rally day is a day satisfying the following conditions:
# 1). Close above ½ of daily TRUE range and below prior day close
# 2). Low is the lowest during the market correction,
#     including future lows if available

# resolution of each 1st rally day is set to about 2 weeks

def rDayInterval = Round(25 / 2, 0);

def futureLow = if IsNaN(prLo[-rDayInterval]) then futureLow[1]
                else prLo[-rDayInterval];
def futureCls = if IsNaN(cls[-rDayInterval]) then futureCls[1]
                else cls[-rDayInterval];

# market correction is currently defined as down about 8% from top
# need to be refined so that it will work in a bear market that is forming a bottom.
# In this case, the 8% drop may not be required.
def mktCr    = prLo[1] <= Highest(high, 25) * dropThreshold; #.931; #.92;
def prRng    = TrueRange(prHi, cls, prLo); #prHi - prLo;


def pinkRday = cls > (prLo + prRng / 2) and cls < cls[1] and
               prLo <= Lowest(prLo[1], rDayInterval) and
               prLo <= Lowest(futureLow, rDayInterval);
 

# The real rally day has its close higher than prior close
# A rally day is invalidated if the low is broken in subsequent days
def realRday = cls > cls[1] and
               (prLo <= Lowest(prLo[1], rDayInterval) or
                prLo[1] <= Lowest(prLo[1], rDayInterval)) and
               (prLo <= Lowest(futureLow, rDayInterval) or
                prLo[1] <= Lowest(futureLow, rDayInterval)) and
               Sum(realRday[1], rDayInterval) == 0 and
               Sum(pinkRday[1], rDayInterval) == 0;

def RallyDay1 = (mktCr or mktCr[1]) and (pinkRday or realRday);

AddChartBubble(RallyDay1, high, "R1", Color.LIGHT_GREEN);



######################################################################
#                            FTD Requirements                        #
######################################################################
#1.Heavier volume than the previous daysAfterFTD  -> volIncrease

#2. Volatility is defined as the percentage gain of the up days over the past 200 days

def IBDlengthVolatility = 200;

def PercentageGreenBars = Round((close - close[1]) / close * 100, 2);

def upday = if close > close[1] then 1 else 0;
def SumUpdays = Sum(upday, IBDlengthVolatility);

def UpdaysPercentage = if upday then PercentageGreenBars else 0;
def SumUpdaysPercentage = Sum(UpdaysPercentage, IBDlengthVolatility);

def volatilityIBD = Round(SumUpdaysPercentage / SumUpdays, 2);

#Market Volatility and FTD price change requirements as percentageGreenBars IBD Market School.

def FTDPriceRequirement = if volatilityIBD < 0.4 then 1.007
else if volatilityIBD >= 0.4 and  volatilityIBD < 0.55 then 1.0085
else if  volatilityIBD >= 0.55 and volatilityIBD < 1 then 1.01
else 1.01245;


######################################################################
#                           FTD Requirements                         #
#--------------------------END-----END----END------------------------#
######################################################################



# Currently (April, 2020) a daily price (cls) increase of 1.25% minimum is
# the price requirement by either SPX or NASDAQ for a FTD.
# Must be day 4 after 1st rally attempt /w an exception (1st 3 days are strong).
# Must have higher volume on the FTD day

def redDay = if close < close[1] then 1 else 0;

def lastR1Bar  = If (RallyDay1, BarNumber(), lastR1Bar[1]);
def daysAfterR1 = BarNumber() - lastR1Bar;


#The Rally days is counting as day number 1  meaning daysAfterR1 = 0 so 4 days and more is daysAfterR1>=3

def isFTD = if daysAfterR1 >= 3  and BarNumber() > lastR1Bar and
               BarNumber() < lastR1Bar + 25 and lastR1Bar != 0 and
               cls >= (cls[1] * FTDPriceRequirement) and
               volIncrease then 1 else 0 ;


def totalR1toFTD = if RallyDay1 then totalR1toFTD[1] + 1  else if !RallyDay1 and !isFTD then totalR1toFTD[1] else 0;

def oneFTD = totalR1toFTD < totalR1toFTD[1];
AddChartBubble(oneFTD, high, "FTD", Color.LIGHT_ORANGE);

def lastOneFTDBar  = If (oneFTD, BarNumber(), lastOneFTDBar[1]);
def daysAfterOneFTD = BarNumber() - lastOneFTDBar;


######################################################################
#                    Distribution Days And Stalling days           #
######################################################################
#Distribution Day
DefineGlobalColor("DistibutionDay", Color.DARK_ORANGE);
DefineGlobalColor("StallingDay", Color.DARK_RED);

input ShowAllDistributionDays = no;
input ShowAllStallingDays = no;



def distributionDays = downDay and volIncrease and priceInRange;

def stallDays = cls - cls[25] > 0 and
               cls >= 0.97 * Highest( prHi, 25) and
               volumes > 0.95 * volumes[1] and
               cls - cls[1] > 0 and
               cls - cls[1] < 1.004 * cls[1] and
               priceIsHigh and priceLowHalf and priceGainSmall and priceGapFill and
               priceRangeBig;

def totalDS =  if stallDayCount >= distDayCount then distDayCount*2-1 else totalDdays;

def distributionDaysCount = sum(distributionDays,25); #check the last 25 days for the S3 and S4 signal.
def distributionDaysCount26 = sum(distributionDays,26); #check the last 26 days for the B10 Signal.
def distributionDaysCount8 = sum(distributionDays,8); #check the last 8 days for the S13 signal.

def stallDaysCount = sum(stallDays,25);
def stallDaysCount26 = sum(stallDays,26);
def stallDaysCount8 = sum(stallDays,8);

def totalDSdays = distributionDaysCount + stallDaysCount;
def totalDSdays26 = distributionDaysCount26 + stallDaysCount26;
def totalDSdays8 = distributionDaysCount8 + stallDaysCount8;

def totalAllDS =  if stallDaysCount >= distributionDaysCount then distributionDaysCount*2-1 else totalDSdays;
def totalAllDS26 =  if stallDaysCount26 >= distributionDaysCount26 then distributionDaysCount26*2-1 else totalDSdays26;


AddChartBubble("time condition" = ShowAllDistributionDays and distributionDays, "price location" = high, text =  if ShowChartBubbleDescription then "Distribution Day (" + PercentageGreenBars + "%)" else "DD", GlobalColor("DistibutionDay"));

AddChartBubble("time condition" = ShowAllStallingDays and stallDays, "price location" = high, text =  if ShowChartBubbleDescription then "Stalling Day (" + PercentageGreenBars + "%)" else "SD", GlobalColor("StallingDay"));



######################################################################
#                       Moving Averages EMA21 MA50                   #
######################################################################

def ema21 = MovAvgExponential("length" = 21)."AvgExp";
def ma50 = MovingAverage(data = close, "length" = 50 );


######################################################################
#                            FTD Signals     (B1)                    #
######################################################################
#Buy on initial follow-through day.
input showB1FTD = yes;
def B1= oneFTD;

AddChartBubble(showB1FTD and B1, high, if ShowChartBubbleDescription then "(B1) " + (daysAfterR1 + 1) + "days from R1 (+ " + PercentageGreenBars + "%)" else "B1", Color.GREEN);

######################################################################
#                         Additional FT Days    (B2)                 #
######################################################################
#Buy on all additional follow-through days within 25-days from a rally day that closes above the low of the initial follow-through day.
input showB2 = yes;
def LowOneFTD = if oneFTD then low else LowOneFTD[1];
def B2 = isFTD and !oneFTD and close > LowOneFTD and daysAfterR1 < 25;
def CountB2 = if B2 then CountB2[1] + 1  else if !B2 and daysAfterR1 < 25 then CountB2[1] else 0;

AddChartBubble("time condition" = showB2 and B2, "price location" = high, text =  if showChartBubbleDescription then "B2-" + CountB2 + " FTD (+ " + PercentageGreenBars + "%)" else "B2", color = GlobalColor("B2"));



######################################################################
#                            Low Above Ema21    (B3)                 #
######################################################################

#S5Reset for resetting the buy signals B3, B4Signal & B5
#S5 Break below ema21
#Sell if index closes 0.2% or greater below ema21
#B3, B4 & B5 Reset by S5. Once you have B3 or B4, you can't have another until is reset by an S5

def S5ResetbelowEma21 = cls <= (ema21 * 0.998);


#Buy on and up or flat day when then intra-day low is at or above then ema21
input showB3 = yes;
def B3 =  low >= ema21 and upday; # and !oneFTD;

def totalB3toS5 = if B3 then totalB3toS5[1] + 1 else if !B3 and !S5ResetbelowEma21 then totalB3toS5[1] else 0;
def totalS5toB3 = if S5ResetbelowEma21 then totalS5toB3[1] + 1 else if !B3 and !S5ResetbelowEma21 then totalS5toB3[1] else 0;

def oneB3 = totalS5toB3 < totalS5toB3[1];
def S5Reset = totalB3toS5 < totalB3toS5[1];

def lastoneB3Bar  = If (oneB3, BarNumber(), lastoneB3Bar[1]);
def daysAfterB3 = BarNumber() - lastoneB3Bar;


AddChartBubble("time condition" = showB3 and oneB3, "price location" = high, text = if showChartBubbleDescription then  "B3 low>ema21. Reset S5, S6, S7 & S8 (" + PercentageGreenBars + "%)" else "B3", color = GlobalColor("B3"));



######################################################################
#                    Trending Above Ema21    (B4)                    #
######################################################################
#Buy after a B3 signal has triggered, buy on the 5th consecutive day that the low is at or above the ema21. The B4 signal is only triggered when the index closes up or flat for the day. If the index close down on the 5th day from the B3, then wait for the next up day to trigger B4.

input showB4 = yes;

# signal for reset the B4
def S5B4ResetSignal = !S5Reset;

def B4Signal = if !oneB3 and daysAfterB3 >= 4 and B3 and S5B4ResetSignal then 1 else 0;

def SumB3toB4 = if oneB3 then SumB3toB4[1] + 1 else if !B4Signal and !oneB3 then SumB3toB4[1] else 0;
def oneB4 = SumB3toB4 < SumB3toB4[1];

AddChartBubble("time condition" = showB4 and oneB4 , "price location" = high, text =  if ShowChartBubbleDescription then "B4 Trending Above ema21. " + (daysAfterB3 + 1) + "days from (B3) (" + PercentageGreenBars + "%)" else "B4", GlobalColor("B4"));


######################################################################
#                        Living Above Ema21  (B5)                    #
######################################################################
#Buy after a B3 signal has triggered, buy on the 10th and every 5th consecutive day after that (15th, 20th, 25th, etc.) day's low is at or above the ema21. The B5 signal is only triggered when the index closes up or flat for the day. If the index close down on the 5th day from the B3, then wait for the next up day to trigger B5.


input showB5 = yes;
def fiveDaysB3 = (daysAfterB3+1) % 5 == 0;
# signal for on off the B5
def S5B5ResetSignal = !S5Reset; # !oneFTD  and daysAfterB3 < daysAfterS5Reset; # and !oneS5Reset

#B5 signal
def B5Signal = daysAfterB3 >= 9 and B3 and S5B5ResetSignal;

def B5_5 =  B5Signal and fiveDaysB3;
def B5_6 = if !B5_5[1] then B5Signal and fiveDaysB3[1] else 0;
def B5_7 = if !B5_6[1] and !B5_5[2] then B5Signal and fiveDaysB3[2] else 0;
def B5_8 = if !B5_7[1] and !B5_6[2] and !B5_5[3] then B5Signal and fiveDaysB3[3] else 0;
def B5_9 = if !B5_8[1]  and !B5_7[2] and !B5_6[3] and !B5_5[4] then B5Signal and fiveDaysB3[4] else 0;

def B5 = B5_5 or B5_6 or B5_7 or B5_8 or B5_9;

AddChartBubble("time condition" = showB5 and B5, "price location" = high, text =  if ShowChartBubbleDescription then "B5 Living above ema21 (" + PercentageGreenBars + "%) " + (daysAfterB3+1) + "days from B3. Low >= ema21" else "B5", GlobalColor("B5"));



######################################################################
#                          Low Above MA50      (B6)                  #
######################################################################
#Close Above ma50
#Buy on an up or flat day when then intraday low is at or above an uptreding ma50.
#Uptreding ma50 -> the day that the ma50 is up from then prior day.
#B6 Reset by S9. Once you have B6, you can't have another until is reset by an S9


#Sell if index closes below 50ma.
#Only triggers if a corresponding buy signal related to that moving average B6 triggered previously
#Sell S9 is reset by B6
#Exception: -> Index closes in the upper half of range ("Shakeouk"), and Index closes within 1% of the 50ma

def S9 = cls < (ma50 * 0.99) and close < hl2;
def B6 =  low >= ma50 and ma50 > ma50[1] and upday;
def SumB6toS9 = if B6 then SumB6toS9[1] + 1 else if !B6 and !S9 then SumB6toS9[1] else 0;
def oneS9 = SumB6toS9 < SumB6toS9[1];


#Close Above ma50
#Buy on and up or flat day when then intra-day low is at or above then ema21

input showB6 = yes;

def SumS9toB6 = if S9 then SumS9toB6[1] + 1 else if !B6 and !S9 then SumS9toB6[1] else 0;
def oneB6 = SumS9toB6 < SumS9toB6[1];


def lastoneB6Bar  = If (oneB6, BarNumber(), lastoneB6Bar[1]);
def daysAfterB6 = BarNumber() - lastoneB6Bar;


AddChartBubble("time condition" = showB6 and oneB6, "price location" = high, text =  if ShowChartBubbleDescription then "B6 Low Above ma50 in uptrending ma50, reset S9 (+" + PercentageGreenBars + "%)" else "B6", color = GlobalColor("B6"));


######################################################################
#                       Accumulation Day     (B7)                    #
######################################################################
#Accumulation Buy Signal is B2 but after the 25 days of rally attempt.
#Index closes up the percentage required for a followthrogh day. Low volatility FTD would apply as well
# Close in upper 25% of the range
#close must be greater than ema21
#Cannot coincide with a FTD or subsequent FTD (daysAfterR1 >= 25)
#For GapUp the range calculation is as following (Close-Previous day's close)/(high-previous day's close)

def PriceRangeIBD = if low > high[1] then (close - close[1]) / (high - close[1]) else if high<low[1] then (close - low)/(close[1]-low) else (close - low) / (high - low);

input showB7 = yes;
def B7 =  lastR1Bar != 0 and cls >= (cls[1] * FTDPriceRequirement) and
               volIncrease and !oneFTD and close > LowOneFTD and daysAfterR1 >= 25 and PriceRangeIBD >= 0.75 and close > ema21;


AddChartBubble("time condition" = showB7 and B7, "price location" = high, text =  if ShowChartBubbleDescription then "B7 Accumulation Day (+ " + PercentageGreenBars + "%)" else "B7", color = GlobalColor("B7"));


######################################################################
#                            Higher High     (B8)                    #
######################################################################
#Buy after closing above the last marked high (Last high before Rally1)
input showB8 = yes;

def HH = if IsNaN(_markedHigh1) then _lastMarkedHigh1[1]  else if _MarkedHigh1 then high else if _MarkedHigh1[1] then if high >hh[1] then high else hh[1] else hh[1];
def B8 = high > hh;



def SumB8Back = if _MarkedHigh1[LookbackPeriod] then SumB8Back[1] + 1 else if !_MarkedHigh1[LookbackPeriod] and !B8 then SumB8Back[1] else 0;
def oneB8 = SumB8Back < SumB8Back[1];

AddchartBubble(showB8 and oneb8,high,  if ShowChartBubbleDescription then "B8 Higher High" else "B8",Color.LIME);


######################################################################
#                    Downside Reversal Buyback    (B9)               #
######################################################################
#After selling on a downside reversal, buy back if the index closes above the intraday high of the downside reversal within two trading days.

################ S11 code ################
#Sell after downside reversal day
#New Intra-day high within prior 13 days. On seminar was noted as weeks but on simulator was days.
#close in bottom quartile of range
#Close lower than the previous day (redday)
#Spread of 1.75% or greater. Spread is calculated as the (Intraday High-Intraday Low)/ (Intraday Low)

def spreadIntraday = ((high-low)/low)*100;

def S11 = high == highest(high,13) and PriceRangeIBD <=0.25 and redday and spreadIntraday >= 1.75;

def lastoneS11Bar  = If (S11, BarNumber(), lastoneS11Bar[1]);
def daysAfterS11 = BarNumber() - lastoneS11Bar;

################ B9 code ################

input showB9 = yes;

def S11High = if(S11,high,S11High[1]);

def B9 = close > S11High and daysAfterS11<=2;

def SumB9S11 = if S11 then SumB9S11[1] + 1 else if !S11 and !b9  then SumB9S11[1] else 0;
def oneB9 = SumB9S11 < SumB9S11[1];

AddChartBubble("time condition" = showB9 and oneB9, "price location" = high, text =  if ShowChartBubbleDescription then "B9 Downside Reversal Buyback (" + PercentageGreenBars + "%)" else "B9", color = GlobalColor("B9"));


######################################################################
#                      Distribution Day Fall Off    (B10)            #
######################################################################
#Distribution Day
#Indicative of institutional Selling
#Price down greater than or equal to 0.2% on volume greater than or equal to prior day's volume.

#Stall Day (Stalling)
#Stalling is a form of heavy volume without further upsideGapThreeMethods price progress.
#Stall days occur on updays when the market is at or near new highs and close in the lower 25% range on heavier volume of  previous day.
#After the first stallDay, you do not consider additional stall days in the distribution count unitl you have more distribution days than stall days.
 
# full Distribution counts (Distribution and stall days.)
#Before 1992    DC:4 (Distribution Count)
#1991-2004      DC: 5
#2005-Present   DC:6

#B10
#Buy on the day that the distribution count falls back to four (Full distribution count minus two) as long as the close is above the ema21 and Buy Switch is on.

#totalDdays = distDayCount + stallDayCount
#def totalNdDays = newDistDays + newStallDays;

input showB10 = yes;


def B10 = totalAllDS==4 and totalAllDS26==5 and close > ema21; #Need fix entry for BSON



######################################################################
#                   Follow Through Day Undercut    (S1)              #
######################################################################
#Follow Through Day Undercut (S1)
#Sell if index closes below the Low of the initial follow through day.
#-Does not apply to additional FTDs
input showS1 = yes;

def S1 =  close < LowOneFTD ;
def SumOneS1 = if oneFTD then SumOneS1[1] + 1 else if !S1 and !oneFTD then SumOneS1[1] else 0;
def oneS1 = SumOneS1 < SumOneS1[1];

AddChartBubble("time condition" = showS1 and oneS1, "price location" = high, text =  if ShowChartBubbleDescription then "S1 FTD undercut (" + PercentageGreenBars + "%)" else "S1", color = GlobalColor("S1"));


######################################################################
#                  Failed Rally Attempt         Major Rule (S2)      #
######################################################################
#Sell if index undercuts the "major low" of rally attempt.
#-Market exposure is reduced to zero
#-Buy Switch is turned off.
input showS2 = yes;
input showS2ml = yes;
def MajorLow =  if RallyDay1 and low < low[1] then low else if RallyDay1 and low[1] < low then low[1] else MajorLow[1];
def S2 = low < MajorLow; # and MajorLow == MajorLow[1];

def SumoneS2 = if RallyDay1 then SumoneS2[1] + 1 else if !rallyDay1 and !s2  then SumoneS2[1] else 0;
def oneS2 = SumoneS2 < SumoneS2[1];

#def OneS2 = Sum(S2) == 1 and S2;

def minorLow = if RallyDay1[1] then low else if !rallyDay1 then if low < minorLow[1] then low else minorLow[1] else minorLow[1];



#Sell if index undercuts the "minor low" of rally attempt
#-Market exposure is reduced by two
#-This does not turn off the Buy Switch
#The "minor low" of a rally is defined as a low that occurs when the buy switch is on (oneFTD)
def minorLowS2 = !RallyDay1 and minorLow<minorLow[1];




AddChartBubble("time condition" = showS2 and OneS2, "price location" = high, text =  if ShowChartBubbleDescription then "S2 Failed Rally attempt (" + PercentageGreenBars + "%)" else "S2", GlobalColor("S2"));

#######################################################
# Go to Buy Switch for the bubble signal of minor low #
#######################################################
def BSOnforS2ml = if oneFTD then BSOnforS2ml[1] + 1 else if !oneS2 and !oneFTD then BSOnforS2ml[1] else 0;




######################################################################
#                   Full Distribution minus one   (S3)               #
######################################################################
# Sell after distribution count increases to five (Full distribution is currently six days).
#This rule is applied each new time that it occurs.

input showS3 = yes;


Def S3 = if((distributionDays or stallDays) and totalAllDS==5,1,0);


AddChartBubble("time condition" = showS3 and S3, "price location" = high, text =  if ShowChartBubbleDescription then "S3 Full Distribution minus one (" + PercentageGreenBars + "%)" else "S3", GlobalColor("S3"));


######################################################################
#                         Full Distribution       (S4)               #
######################################################################
#Sell after full distribution count (full distribution is currently six days).
#This rule is applied up to eight days (full distribution plus two).


input showS4 = yes;
def S4 = if((distributionDays or stallDays) and (totalAllDS==6 or totalAllDS==7 or totalAllDS==8) ,1,0);

AddChartBubble("time condition" = showS4 and S4, "price location" = high, text =  if ShowChartBubbleDescription then "S4 Full Distribution (" + PercentageGreenBars + "%)" else "S4", GlobalColor("S4"));


######################################################################
#                       Break Below Ema21         (S5)               #
######################################################################
#Sell if index closes 0.2% or greater below the ema21
#S5, S6, S7, S8 Reset by B3. Once you have S5, S6 or S7, you can't have another until is reset by an B3

# signal for on off the S5
def B3S5ResetSignal = S5Reset; #!oneFTD  and daysafterS5Reset[1] >= daysafterB3; # and !oneS5Reset

#S5 Break below ema21
# Sell if index closes 0.2% or greater below ema21

input showS5 = yes;

def S5belowEma21 = cls <= (ema21 * 0.998);
def S5 =  S5belowEma21 ;

def oneS5 = totalB3toS5 < totalB3toS5[1];

def lastoneS5  = If (oneS5, BarNumber(), lastoneS5[1]);
def daysAfterS5 = BarNumber() - lastoneS5;


AddChartBubble("time condition" = showS5 and oneS5, "price location" = high, text =  if ShowChartBubbleDescription then "S5 Break Below close<ema21. Reset B3, B4 & B5 (" + PercentageGreenBars + "%)" else "S5", GlobalColor("S5"));


######################################################################
#                  Overdue Break Below Ema21       (S6)              #
######################################################################
#Overdue break below ema21 (S6)
#Sell if index closes down 0.2% or greater below the ema21 after 30 days have been passed since last B3 without triggering an S5

input showS6 = yes;

def S6 =  s5 and daysAfterB3 >= 29 ;

def SumB3toS6 = if oneB3 then SumB3toS6[1] + 1 else if !oneB3 and !s6  then SumB3toS6[1] else 0;
def oneS6 = SumB3toS6 < SumB3toS6[1];

AddChartBubble("time condition" = showS6 and oneS6, "price location" = high, text =  if ShowChartBubbleDescription then "S6 Overdue Break low<ema21 (" + PercentageGreenBars + "%)" else "S6", GlobalColor("S6"));

######################################################################
#                      Trending Below ema21       (S7)               #
######################################################################
#Trending Below ema21 (S7)
#After and S5 signal has triggered, sell on the 5th consecutive day that then high is below the ema21. The S7 signal is only triggered when the idex closes down for the day. If the indexes closes up on the 5th day from the S5, then wait for the next down day to trigger the S7


input showS7 = yes;

# signal for on off the B4

def S7 = daysAfterS5 >= 4 and high <= ema21 and redDay ; # !oneS5 and !oneB3

def SumB3toS7 = if oneB3 then SumB3toS7[1] + 1 else if !S7 and !oneb3 then SumB3toS7[1] else 0;
def oneS7 = SumB3toS7 < SumB3toS7[1];

AddChartBubble("time condition" = showS7 and oneS7 , "price location" = high, text =  if ShowChartBubbleDescription then "S7 Trending Below ema21. high<ema21 " + (daysAfterS5+1) + "days from (S5) (" + PercentageGreenBars + "%)" else "S7", GlobalColor("S7"));

######################################################################
#                       Living Below Ema21        (S8)               #
######################################################################
#Trending Below ema21 (S7)
#After and S5 signal has triggered, sell on then 10th and every 5th consecutive day after that (15th, 20th, 25th etc.) that then high is below the ema21. The S8 signal is only triggered when the index closes down for the day. If the indexes closes up on the 10th, 15th, 20th, etc. from the S5, then wait for the next down day to trigger the S8


input showS8 = yes;

# signal for on off the B4
#def S5B4ResetSignalw = !oneFTD  and daysAfterB3 < daysAfterS5Reset; # and !oneS5Reset
def fiveDaysS5 = (daysAfterS5+1) % 5 == 0;

def S8Signal = if daysAfterS5 >=9 and s7 and !oneb3 then 1 else 0;


def S8_5 =  S8Signal and fiveDaysS5;
def S8_6 = if !S8_5[1] then S8Signal and fiveDaysS5[1] else 0;
def S8_7 = if !S8_6[1] and !S8_5[2] then S8Signal and fiveDaysS5[2] else 0;
def S8_8 = if !S8_7[1] and !S8_6[2] and !S8_5[3] then S8Signal and fiveDaysS5[3] else 0;
def S8_9 = if !S8_8[1]  and !S8_7[2] and !S8_6[3] and !S8_5[4] then S8Signal and fiveDaysS5[4] else 0;

def S8 = S8_5 or S8_6 or S8_7 or S8_8 or S8_9;

AddChartBubble("time condition" = showS8 and S8, "price location" = high, text =  if ShowChartBubbleDescription then "S8 Living Below Ema21, high<ema21 / " + (daysAfterS5+1) + "days from (S5) (" + PercentageGreenBars + "%)" else "S8", GlobalColor("S8"));



######################################################################
#                         Break Below MA50          (S9)             #
######################################################################
#Sell if index closes below 50ma.
#Only triggers if a corresponding buy signal related to that moving average B6 triggered previously
#Sell S9 is reset by B6
#Exception: -> Index closes in the upper half of range ("Shakeouk"), and Index closes within 1% of the 50ma
input ShowS9 = yes;
#Go to B6 for the code of the signal.

AddChartBubble("time condition" = ShowS9 and oneS9 , "price location" = high, text =  if ShowChartBubbleDescription then "S9 Break Below MA50, close<MA50, reset B6 (" + PercentageGreenBars + "%)" else "S9", GlobalColor("S9"));


######################################################################
#                               Bad Break           (S10)            #
######################################################################
#Sell if the close is down 2.25% of greater in the bottom 25% of range
#close below the ma50 or Intraday high below ema21
#Range as (close-low)/(high-low) for gap down (close-low)/(close[1]-low)

#def range = if high<low[1] then (close - low)/(close[1]-low) else (close-low)/(high-low);

input showS10 = yes;

def S10 = PercentageGreenBars<=-2.25 and (close < ma50 or high < ema21) and PriceRangeIBD <= 0.25 ;

AddChartBubble("time condition" = showS10 and S10, "price location" = high, text =  if ShowChartBubbleDescription then "S10 Bad Break (" + PercentageGreenBars + "%)" else "S10", GlobalColor("S10"));



######################################################################
#                        Downside Reversal Day      (S11)            #
######################################################################
#Sell after downside reversal day
#New Intra-day high within prior 13 days. On the seminar was noted as weeks but on the simulator were days.
#close in bottom quartile of range
#Close lower than the previous day (redday)
#Spread of 1.75% or greater. Spread is calculated as the (Intraday High-Intraday Low)/ (Intraday Low)

input showS11 = yes;
#for S11 go to B9

AddChartBubble("time condition" = showS11 and S11, "price location" = high, text =  if ShowChartBubbleDescription then "S11 Downside Reversal Day (" + PercentageGreenBars + "%)" else "S11", GlobalColor("S11"));



######################################################################
#                               Lower Low          (S12)             #
######################################################################
#Sell after closing below the last marked low  (9days lookback and 9day forward)

input showS12 = yes;


def LL = if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else if _markedLow1[1] then if low <LL[1] then low else LL[1] else LL[1];
def S12 = close < LL;

def SumOneS12 = if _markedLow1[LookbackPeriod] then SumOneS12[1] + 1 else if !_markedLow1[LookbackPeriod] and !S12 then SumOneS12[1] else 0;
def oneS12 = SumOneS12 < SumOneS12[1];

AddchartBubble(showS12 and oneS12,high, if ShowChartBubbleDescription then "S12 closing below Lower low" else "S12",GlobalColor("S12"));

######################################################################
#                       Distribution Cluster       (S13)             #
######################################################################
#Sell after distribution cluster count ("Cluster Count") increases to four within a rolling eight-day period.
#Sell after each successive incease in the cluster count from four up to eight days within a rolling eight-day period.
#For purposes of distribution clusters, all stall days and distribution days apply.
#The cluster count is reset once the cluster count falls to three within a rolling eight-day period.
#The distribution cluster changes the Power-Trend (if it is in effect) to a Power-Trend Under Pressure.
input showS13 = yes;

def S13 = if((distributionDays or stallDays) and (totalDSdays8==4 or totalDSdays8==5 or totalDSdays8==6 or totalDSdays8==7 or totalDSdays8==8 ) and totalDSdays8 != totalDSdays8[1]  ,1.0,0.0);;

AddchartBubble(showS13 and S13,high, if ShowChartBubbleDescription then "S13 Distribution Cluster" else "S13",GlobalColor("S13"));

######################################################################
#                    Break Below Higher High       (S14)             #
######################################################################
input showS14 = yes;
#Sell after closing below the last marked high that triggered a B8 (Higher High)

def barNumberB8 = If (oneB8, BarNumber(), barNumberB8[1]);;

def S14 =  close<HH and high[1]>hh and (barnumber() - barNumberB8)>0; #Incase of gapdown we use previous days high
def SumOneS14 = if _MarkedHigh1[LookbackPeriod] then SumOneS14[1] + 1 else if !_MarkedHigh1[LookbackPeriod] and !S14 then SumOneS14[1] else 0;;
def oneS14 = SumOneS14 < SumOneS14[1];


AddchartBubble(showS14 and oneS14,high, if ShowChartBubbleDescription then "S14 Breakaway Close Below Higher High" else "S14",GlobalColor("S14"));


######################################################################
########################## Circuit breaker ###########################
######################################################################
#Circuit breaker - occurs when you've got a 10% break of the market from the high point
Input showCircuitBreaker = yes;

def RecentHigh = if RallyDay1 then high else if !rallyDay1 then if high >RecentHigh[1] then high else RecentHigh[1] else RecentHigh[1]; # find the highest high in a periog Rallyday R1

def CB = if close < RecentHigh - RecentHigh*0.1 then 1 else 0;

def CountCB = if oneFTD then CountCB[1] + 1 else if !CB and !oneFTD then CountCB[1] else 0;
def oneCB = CountCB < CountCB[1];

AddchartBubble(showCircuitBreaker and oneCB,high, if ShowChartBubbleDescription then "Circuit Breaker 10% break of Highs. BS OFF" else "CB",GlobalColor("Circuit Breaker"));


######################################################################
#                          Market Exposure                           #
######################################################################

def BSOnB10 = if oneFTD then BSOnB10[1] + 1 else if !(oneS2 or (S4 ) or oneCB ) and !oneFTD then BSOnB10[1] else 0; #I could't add the market exposure ==0 with the (S4 ) on above code.

def S2ml = BSOnforS2ml and minorLowS2 and BSOnB10 and !oneFTD;

input ShowMElabel = yes;

def BS = B1 + B2 + oneB3 + oneB4 + B5 + oneB6 + B7 + oneB8 + oneB9 + (BSOnB10 and B10); #B10 is on when BuySwitch is on.
def SS = oneS1 + oneS2 + s2ml + S3 + S4 + oneS5 + oneS6 + oneS7 + S8 + oneS9 + S10 + S11 + oneS12 + S13 + oneS14 + oneCB;
def TS = BS-SS;

def countME = if rallyDay1 then 0 else if countME[1] > 5 then 5 else if countME[1]<0 then 0 else countME[1] + TS ;
def MarketExposure = if countME <0  then 0 else if countME>5 then 5 else countME;

#Market Exposure when the Restraint Rule  is applied
def countMERR = if rallyDay1 then 0 else if countMERR[1] > 2 then 2 else if countMERR[1]<0 then 0 else countMERR[1] + TS ;
def MarketExposureRR = if countMERR <0  then 0 else if countMERR>2 then 2 else countMERR;

#Market Exposure when the Power Trend is applied
def countMEPT = if rallyDay1 then 0 else if countMEPT[1] > 7 then 7 else if countMEPT[1]<2 then 2 else countMEPT[1] + TS ;
def MarketExposurePT = if countMEPT <2  then 2 else if countMEPT>7 then 7 else countMEPT;

# Market Exposure when the PowerTrendUnderPressure (+5 Floor +1)
def countMEPTup = if rallyDay1 then 0 else if countMEPTup[1] > 5 then 5 else if countMEPTup[1]<1 then 1 else countMEPTup[1] + TS ;
def MarketExposurePTup = if countMEPTup <1  then 1 else if countMEPTup >5 then 5 else countMEPTup;


# Market Exposure when the PowerTrendUnderPressureWithoutFloor
def countMEPTwf = if rallyDay1 then 0 else if countMEPTwf[1] > 5 then 5 else if countMEPTwf[1]<0 then 0 else countMEPTwf[1] + TS ;
def MarketExposurePTwf = if countMEPTwf <0  then 0 else if countMEPTwf >5 then 5 else countMEPTwf;


#color Label settings
def mid = 2;
def upper = 7;
def lower = 0;
def bl = 75;

def CLrValue = if MarketExposure < mid then 255 else if MarketExposure > upper then 0 else 255 - Round((MarketExposure - mid) / (upper - mid) * 255, 0);
def CLgValue = if MarketExposure > mid then 255 else if MarketExposure < lower then 0 else 255 - Round((mid - MarketExposure) / (mid - lower) * 255, 0);






######################################################################
#                            Restraint Rule                          #
######################################################################
#To avoid getting invested too fast, limit exposure to a maximum of +2

#Rules to remove the Restraint Rule: The index makes significant progress above the close of the initial follow-through day OR
#A buy signal (B4) is triggered (trending above ema21)

#Significant progress is defined as the percentage requirement nedeed for a FTD based on volatility (FTDPriceRequirement: currently 1%). Example if you close above 1% from the close of the FTD then we lift the Restraint Rule. OR
#If you trending above the FTD

#Restraint Rule applies if:
#The maket exposure was 0 prior FTD
#If the buy switch turns on due to closing above a prior high, then the restraint rule will apply until the index makes significant progress as defined by the (FTDPriceRequirement).

input ShowRestraintRuleLabel = yes;

def CloseofOneFTD = if oneFTD then close else CloseofOneFTD[1];
def RestraintLifted = if oneB4 or close > CloseofOneFTD*FTDPriceRequirement  then 1 else 0;

def MarkExpPriorFTD = If oneFTD and MarketExposure[1]==0 then 1 else 0;

def RR =  if MarkExpPriorFTD and !rallyDay1 and !RestraintLifted then 1 else 0;
Addlabel(ShowRestraintRuleLabel and RR,"Restraint on. Max MarketExposure Expose +2",Color.ORANGE);


######################################################################
#                              Power Trend                           #
######################################################################
#Power Trend Turns On:
#The ema21>ma50 for at least 5 consecutive days without an S9(break below ma50)
#The Index closes up or flat for the day.
#The ma50 is in uptrend for at least 1 day
#The Index low has been above the ema21 for at least 10 consecutive days
#Buy Switch override -> BSOn is forced to stay on
#Buy Switch count can go now up to +7 (100% exposure =+5) +2 buffer for sell signals.
#Don't act on anything higher than +5. This rule was created to provide a buffer to keep you incested during a significant uptrend.
#Start counting additional buy signals from the point that the PowerTrend turns on.

#Once the buy count is at +2 or higher, a floor of +2 is in effect.

Input showPowerTrendLabel = yes;


#Condition that End Power Trend:
#ema21 line crosses below the ma50 and index closes down for the day OR
#S2 Major Low OR
#Circuit Breaker.

#After Power Trend has ended
#No longer forces buy switch on (it may still be on, but it doesn't have to be on)
#Buy signal count is reset to a maximum of +5
#There is no longer a floor.
def PToff = ema21 crosses below ma50 and redDay or oneS2 or oneCB ;

#Condition that re-set a Power Trend from an Under Pressure condtion:
#The conditons that turn on a power-trend must be present, and
#10 consecutives days must pass without an S2 minor low an S9 and S13.
def p=10;
def PTOnreset =  fold q=0 to p with r do if(sum(!minorLowS2[q] and !ones9[q] and !s13[q],p)==p,1,0);

###### PT ON-OFF #####
def n=5;
def PTonSignal = fold i=0 to n with s do if(sum(ema21[i]>ma50[i] and !ones9[i],n)==n and ma50>ma50[1] and upday and sum(low>ema21,10)==10,1,0);


def SumPTon = if PTonSignal then 1 else if PToff then 0 else SumPTon[1];
def PTon = SumPTon;


#Condition that turn a Power Trend to a Power Trend under pressure is:
#S2 minor low signal (and minorLowS2 and BSon)
#S13 rule (Distribution cluster) is triggered

def PTonUnderPressure = PTon and (minorLowS2 or S13);
def SumPTup = if PTonUnderPressure then 1 else if PToff then 0 else SumPTup[1];
def PTup = SumPTup;

#When the power trend is in an under-pressure position with a floor, the max exposure count is reduced to +5 and the floor is reduced to +1 before any signals are applied on that day.

#Condition than turn a Power Trend to a Power Trend under pressure without a floor
#S9 signal (break below ma50)
#When an S9 occurs, the max exposure count remains at +5 but the floor of +1 is removed.

def PTonUnderPressureWithoutFloor = PTon and S9;
def SumPTupWF = if PTonUnderPressureWithoutFloor then 1 else if PToff then 0 else SumPTupWF[1];
def PTWF = SumPTupWF;

def PowerTrendUnderPressure = PTup and !Ptonreset;
def PowerTrendUnderPressureWithoutFloor = PTwf and !Ptonreset;
def PowerTrendOnNopressure = PTon and !PowerTrendUnderPressure and !PowerTrendUnderPressureWithoutFloor;
#def SumPTon = if PTonSignal then 1 else if PToff then 0 else SumPTon[1];


######################################################################
#                             Buy Switch                             #
######################################################################
#BS rules
#When the buy switch is on, you act on all buy and sell signals regardless of the distribution count.
#When the buy switch is off, you only act on sell signals.

#Events that turn Buy Switch On:
#Follow Through day (B1)
#Note on the day buy switch turns on, act on all buy signals that are in effect (except for buy signals B9 and B10) from the low (Rallyday1).

#Events that turn Buy Switch OFF
#Full distribution Count (S4) and market exposure is 0
#Failed rally attempt (S2) of a "major low"
#Circuit breaker - occurs when you've got a 10% break of the market from the high point

#What if the Buy Switch is Off, the MarketExposure count is zero, we haven't a follow-through day but the market is acting strong? How do you turn on the buy switch?
#A close above the prior high oneB8 since the last confirmed rally will turn the Buy Switch on.

############################## Buy Switch ##############################
input ShowBuySwitchLabel = yes;
input ShowBuySwitchBuddle = yes;


def BSOn = if (oneFTD or PTon or oneB8) then BSOn[1] + 1 else if !(oneS2 or (S4 and (if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else  MarketExposure)==0) or oneCB or oneS14 ) and !oneFTD then BSOn[1] else 0;


#def BSOff = BSOn < BSOn[1];
#def oneBSOn =if(BSOn > BSOn[1] and BSOn[1] == BSOn[2],1 ,0);

#AddchartBubble(ShowBuySwitchBuddle and oneBSOn, high,"Buy Switch On",GlobalColor("BuySwitch On"));
#AddchartBubble(ShowBuySwitchBuddle and BSOff, high,"Buy Switch Off",GlobalColor("BuySwitch Off"));


#BS Off MarketExposure Exposure

def TSforBSoff = -SS;

def countMEforBSoff = if rallyDay1 then 0 else if countMEforBSoff[1] > 5 then 5 else if countMEforBSoff[1]<0 then 0 else (if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else  MarketExposure) + TSforBSoff ;
def MarketExposureforBSoff = if countMEforBSoff <0  then 0 else if countMEforBSoff >5 then 5 else countMEforBSoff;

def CurrentME = if RR then MarketExposureRR else if PTon and !PowerTrendUnderPressure and !PowerTrendUnderPressureWithoutFloor  then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else if !bson then MarketExposureforBSoff else  MarketExposure;


Addlabel(ShowMElabel,"Market Expose: (+" + CurrentME + ") " + if CurrentME == 0  then "0%" else if CurrentME==1 then "33%" else if CurrentME==2 then "55%" else if CurrentME==3 then "75%" else if CurrentME == 4 then "90%" else "100%", CreateColor(CLrValue, CLgValue, bl));

#Addlabel(ShowMElabel,"Count: (" + (if RR then countMERR else if PTon then countMEPT  else if PowerTrendUnderPressure then countMEPTup else if PowerTrendUnderPressureWithoutFloor then countMEPTwf else if !bson then countMEforBSoff else countME) + ")", CreateColor(CLrValue, CLgValue, bl));


#AddchartBubble(B1 OR B2 OR oneB3 OR oneB4 OR B5 OR oneB6 OR B7 OR oneB8 OR oneB9 OR (BSOnB10 and B10) OR oneS1 OR oneS2 OR s2ml OR S3 OR S4 OR oneS5 OR oneS6 OR oneS7 OR S8 OR oneS9 OR S10 OR S11 OR oneS12 OR S13 OR oneS14 OR oneCB,high,if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else if !bson then MarketExposureforBSoff else  MarketExposure,color.orange);



######################################################
######### BS labels added on the Power Trend #########
######################################################



######################################################
####### Distribution Day Fall Off   Bubble B10 #######
######################################################


AddChartBubble("time condition" = showB10 and B10 and BSOn, "price location" = high, text =  if ShowChartBubbleDescription then "B10 Distribution Day Fall Off (" + PercentageGreenBars + "%)" else "B10", GlobalColor("B10"));

######################################################
##### Failed Rally Attempt. Bubble S2 Minor Low  #####
######################################################

AddChartBubble("time condition" = showS2ml and s2ml and BSon, "price location" = high, text =  if ShowChartBubbleDescription then "S2 Undercut MinorLow Low or the rally (" + PercentageGreenBars + "%)" else "S2ml", GlobalColor("S2ml"));

##### BS Labels ####

Addlabel(ShowBuySwitchLabel,"Buy Switch: ",color.WHITE);
Addlabel(ShowBuySwitchLabel,if BSOn then "ON" else "OFF", if BSon then color.green else color.Red);



Addlabel(showPowerTrendLabel,if PowerTrendUnderPressure then "Power Trend under pressure: Max (+5) Floor (+1)" else if PowerTrendUnderPressureWithoutFloor then "Power Trend under pressure without floor: Max (+5) Floor (0)" else If PowerTrendOnNopressure then "Power Trend: Max (+7) Floor (+2)" else "Power Trend:",color.white);
Addlabel(showPowerTrendLabel,If PTon then "ON" else "OFF",if Pton then Color.GREEN else color.red);


######################################################################
#                      Active Label Description                      #
######################################################################
input ShowSignalsLabelDescription = yes;
#### Buy Signals Labels ####
Addlabel(ShowSignalsLabelDescription and B1,"B1: Buy on initial follow-through day.",GlobalColor("B1"));
Addlabel(ShowSignalsLabelDescription and B2,"B2: Buy on all additional follow-through days within 25-days from a rally day that closes above the low of the initial follow-through day.",GlobalColor("B2"));
Addlabel(ShowSignalsLabelDescription and oneB3,"B3:  Low Above Ema21. Once you have B3, you can't have another until is reset by an S5.",GlobalColor("B3"));
Addlabel(ShowSignalsLabelDescription and oneB4,"B4: Trending Above Ema21. Once you have B3, you can't have another until is reset by an S5.",GlobalColor("B4"));
Addlabel(ShowSignalsLabelDescription and B5,"B5: Living Above Ema21. Buy after a B3 signal has triggered, buy on the 10th and every 5th consecutive day after that in an upday.",GlobalColor("B5"));
Addlabel(ShowSignalsLabelDescription and oneB6,"B6: Low Above MA50. Buy on an upday with an uptrending ma50 (ma50>ma50[1]). Once you have B6, you can't have another until is reset by an S9.",GlobalColor("B6"));
Addlabel(ShowSignalsLabelDescription and B7,"B7: Accumulation Day. Buy Signal is additional FTD but after the 25 days of rally attempt and close in the upper 25% range and greater ema21.",GlobalColor("B7"));
Addlabel(ShowSignalsLabelDescription and oneB8,"B8: Higher High. Buy after closing above the last marked high (Last high before R1).",GlobalColor("B8"));
Addlabel(ShowSignalsLabelDescription and oneB9,"B9: Downside Reversal Buyback. After selling on a downside reversal (S11), buy back if the index closes above the intraday high of the downside reversal within two trading days.",GlobalColor("B9"));
Addlabel(ShowSignalsLabelDescription and B10,"B10: Distribution Day Fall Off. Buy on the day that the distribution count falls back to four (Full distibution minus two) as long as the close is above the ema21 and Buy Switch is on.",GlobalColor("B10"));


#### Sell Signals Labels ####
Addlabel(ShowSignalsLabelDescription and oneS1,"S1: Follow Through Day Undercut. Sell if index closes below the Low of the initial follow through day.",GlobalColor("S1"));
Addlabel(ShowSignalsLabelDescription and oneS2,"S2: Failed Rally Attempt. Sell if index undercuts the major low of rally attempt. Market exposure is reduced to zero and Buy Switch is turned off.",GlobalColor("S2"));
Addlabel(ShowSignalsLabelDescription and S2ml and BSon,"S2: Minor Low undercut of rally attempt. Market exposure is reduced by two. This does not turn off the Buy Switch. MinorLow defined as low that occurs when the buy switch is on (oneFTD).",GlobalColor("S2"));
Addlabel(ShowSignalsLabelDescription and S3,"S3: Full Distribution minus one. Sell after distribution count increases to five (Full distribution: six days). This rule is applied each new time that it occurs.",GlobalColor("S3"));
Addlabel(ShowSignalsLabelDescription and S4,"S4: Full Distribution. Sell after full distribution count (full distribution: six days). This rule is applied up to eight days (full distribution plus two)",GlobalColor("S4"));
Addlabel(ShowSignalsLabelDescription and oneS5,"S5: Break Below Ema21. Sell if index closes 0.2% or greater below the ema21. Once you have S5, you can't have another until is reset by an B3",GlobalColor("S5"));
Addlabel(ShowSignalsLabelDescription and oneS6,"S6: Overdue Break Below Ema21. Sell if index closes down 0.2% or greater below the ema21 after 30 days have been passed since last B3 without trigering an S5. Once you have S6, you can't have another until is reset by an B3.",GlobalColor("S6"));
Addlabel(ShowSignalsLabelDescription and oneS7,"S7: Trending Below ema21. Sell after S5 on then 5th consecutive day that then high is below the ema21 and DownDay Day. You can't have another S7 until is reset by an B3.",GlobalColor("S7"));
Addlabel(ShowSignalsLabelDescription and S8,"S8: Living Below Ema21. Sell after S5 on then 10th and every 5th consecutive day after that that then high is below the ema21. Down Day if not then wait for the next down day.",GlobalColor("S8"));
Addlabel(ShowSignalsLabelDescription and oneS9,"S9: Break Below MA50. Index closes below 50ma. Only triggers if a B6 triggered preiviously. Sell S9 is reset by B6.",GlobalColor("S9"));
Addlabel(ShowSignalsLabelDescription and S10,"S10: Bad Break. Sell if the close is down 2.25% or greater in the bottom 25% of range. Close below the ma50 or Intraday high below ema21.",GlobalColor("S10"));
Addlabel(ShowSignalsLabelDescription and S11,"S11: Downside Reversal Day. New Intra-day high within prior 13 days and close in bottom quartile of range and RedDay.",GlobalColor("S11"));
Addlabel(ShowSignalsLabelDescription and oneS12,"S12: Lower Low. Sell after closing below the last marked low  (9days lookback and 9day forward).",GlobalColor("S12"));
Addlabel(ShowSignalsLabelDescription and S13,"S13: Distribution Cluster. Distribution and Stalling days increases to four up to eight days within a rolling eight day period. The distribution cluster changes the Power-Trend (if it is in effect) to a Power-Trend Under Pressure.",GlobalColor("S13"));
Addlabel(ShowSignalsLabelDescription and oneS14,"S14: Break Below Higher High. Sell after closing below the last marked high that triggered a B8 (Higher High).",GlobalColor("S14"));

Addlabel(ShowSignalsLabelDescription and oneCB, "Circuit Breaker Index falls 10% of Highs since the follow-through. Buy Switch is turned off.",GlobalColor("Circuit Breaker"));
Wow, there are so many new features and improvements over the original script. It takes so much time and efforts to do it. Thanks a lot for sharing.
 
This is pretty awesome. I was wondering if those on this thread are aware of a IBD Podcast from April 21st, 2022 with Eric Krull. It was called "What Makes A Follow-through Day Successful?" If you watch the actual video online rather than the audio feed only there are some powerpoint slides to go with the discussion. I found the information extremely helpful to warn you of FTD's that are prone to failure and ones that are real winners.

I am new to Thinkscript and struggle to do simple things, teaching myself VBA to work with my spreadsheets is my programming effort. After, applying DeathFireArt's latest code I think there might be some useful nuggets from Eric Krull's study that you may want to program into the code.

The forum would not let me paste the URL for the webinar but if you go to Investors website just search Eric Krull and it should pop up.
 
This is pretty awesome. I was wondering if those on this thread are aware of a IBD Podcast from April 21st, 2022 with Eric Krull. It was called "What Makes A Follow-through Day Successful?" If you watch the actual video online rather than the audio feed only there are some powerpoint slides to go with the discussion. I found the information extremely helpful to warn you of FTD's that are prone to failure and ones that are real winners.

I am new to Thinkscript and struggle to do simple things, teaching myself VBA to work with my spreadsheets is my programming effort. After, applying DeathFireArt's latest code I think there might be some useful nuggets from Eric Krull's study that you may want to program into the code.

The forum would not let me paste the URL for the webinar but if you go to Investors website just search Eric Krull and it should pop up.
Dear SittingDuck, Good day.
Thanks for the info and kindly note that I have found the video that you are referencing and I will revert with any addons in my script for this case. However please note the following comments which were included in my script:


# Follow-through signals (FTD) are more likely to fail if distribution days
# occur in the first few days of a new uptrend. This is one key red flag.

Have a nice day ahead.
 
Dear SittingDuck, Good day.
Thanks for the info and kindly note that I have found the video that you are referencing and I will revert with any addons in my script for this case. However please note the following comments which were included in my script:


# Follow-through signals (FTD) are more likely to fail if distribution days
# occur in the first few days of a new uptrend. This is one key red flag.

Have a nice day ahead.

@DeathFireArt Hey man, reviewing the notes in the script and found 2ML which intrigues me a bit. What's the idea behind it?
 
@DeathFireArt Hey man, reviewing the notes in the script and found 2ML which intrigues me a bit. What's the idea behind it?
Dear nktkobby
"S2ml: Minor Low undercut of rally attempt. Market exposure is reduced by two. This does not turn off the Buy Switch. "

MinorLow is defined as a low that occurs when the buy switch is on (usually at Follow Throw day) and the price makes a lower low from the low of the FTD or any other correction that has a lower low after the Rally attempt, but not lower from the Rally day (R1). Technical that means that the rally is about to fail (high probability) but were cases when instead of failure a new bull market started. So actually the buy switch is not turned off but remains on in case another buy signal comes on the following days, so you can remain on the game instead of waiting for your next FTD.

Hope the above will make it more clear now. :)
 
Dear nktkobby
"S2ml: Minor Low undercut of rally attempt. Market exposure is reduced by two. This does not turn off the Buy Switch. "

MinorLow is defined as a low that occurs when the buy switch is on (usually at Follow Throw day) and the price makes a lower low from the low of the FTD or any other correction that has a lower low after the Rally attempt, but not lower from the Rally day (R1). Technical that means that the rally is about to fail (high probability) but were cases when instead of failure a new bull market started. So actually the buy switch is not turned off but remains on in case another buy signal comes on the following days, so you can remain on the game instead of waiting for your next FTD.

Hope the above will make it more clear now. :)
@DeathFireArt Thank you for clarifying. Been going through multiple charts to see which of the signals standout in either downtrend or uptrend, and 2ML was one of them. Thank you again
 
Dear all, I have modified the IBD Distribution Days Study for TOS shared from SMO_MktVolumesDaily
with buy/sell signals
I hope you will find my work helpful on your trading journey.

Ruby:
# SMO_MktVolumesDaily.ts
# For daily charts only.
# Nasdaq total volume: $TVOL/Q ;   NYSE total volume: $TVOL
# Distribution day count tracking by IBD:
# https://www.investors.com/how-to-invest/investors-corner/
# tracking-distribution-days-a-crucial-habit/
# Stalling daysFromDate tracking by IBD:
# https://www.investors.com/how-to-invest/investors-corner/
# can-slim-market-tops-stalling-distribution/
#BuySell Signal added by dag 2022-03-27

# For daily charts only.
######################################################################################
declare once_per_bar;
input ShowChartBubbleDescription = no;
Input ShowDistrubutionLabels=no;


DefineGlobalColor("B1", Color.GREEN);
DefineGlobalColor("B2", Color.LIGHT_GREEN);
DefineGlobalColor("B3", Color.LIGHT_GREEN);
DefineGlobalColor("B4", Color.LIGHT_GREEN);
DefineGlobalColor("B5", Color.LIGHT_GREEN);
DefineGlobalColor("B6", Color.LIME);
DefineGlobalColor("B7", Color.LIGHT_GREEN);
DefineGlobalColor("B8", Color.LIGHT_GREEN);
DefineGlobalColor("B9", Color.LIGHT_GREEN);
DefineGlobalColor("B10", Color.LIGHT_GREEN);

DefineGlobalColor("S1", Color.LIGHT_RED);
DefineGlobalColor("S2", Color.RED);
DefineGlobalColor("S2ml", Color.DOWNTICK);
DefineGlobalColor("S3", Color.LIGHT_RED);
DefineGlobalColor("S4", Color.LIGHT_RED);
DefineGlobalColor("S5", Color.LIGHT_RED);
DefineGlobalColor("S6", Color.LIGHT_RED);
DefineGlobalColor("S7", Color.LIGHT_RED);
DefineGlobalColor("S8", Color.LIGHT_RED);
DefineGlobalColor("S9", Color.PLUM);
DefineGlobalColor("S10", Color.LIGHT_RED);
DefineGlobalColor("S11", Color.LIGHT_RED);
DefineGlobalColor("S12", Color.LIGHT_RED);
DefineGlobalColor("S13", Color.LIGHT_RED);
DefineGlobalColor("S14", Color.YELLOW);

DefineGlobalColor("Circuit Breaker", Color.RED);
DefineGlobalColor("BuySwitch On", Color.GREEN);
DefineGlobalColor("BuySwitch Off", Color.WHITE);




#############################################################################################

#SWING HIGH LOW
def LookbackPeriod = 9; #
input ShowHLLevels= no;

#--------------------------------------------------------------
def _highInPeriod1 = Highest(high, LookbackPeriod);
def _lowInPeriod1 = Lowest(low, LookbackPeriod);
#--------------------------------------------------------------
def marketLow1 = if _lowInPeriod1 < _lowInPeriod1[-LookbackPeriod] then _lowInPeriod1 else _lowInPeriod1[-LookbackPeriod];
def _markedLow1 = low == marketLow1;

rec _lastMarkedLow1 = CompoundValue(1, if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else _lastMarkedLow1[1], low);
#--------------------------------------------------------------
def marketHigh1 = if _highInPeriod1 > _highInPeriod1[-LookbackPeriod] then _highInPeriod1 else _highInPeriod1[-LookbackPeriod];
def _markedHigh1 = high == marketHigh1;

rec _lastMarkedHigh1 = CompoundValue(1, if IsNaN(_markedHigh1) then _lastMarkedHigh1[1] else if _markedHigh1 then high else _lastMarkedHigh1[1], high);
#--------------------------------------------------------------
plot Resistance1 = _lastMarkedHigh1;
plot Support1 = _lastMarkedLow1 ;

#--------------------------------------------------------------
Resistance1.SetPaintingStrategy(PaintingStrategy.DASHES);
Resistance1.SetDefaultColor(Color.GREEN);
Resistance1.SetHiding(!ShowHLLevels);
#--------------------------------------------------------------
Support1.SetPaintingStrategy(PaintingStrategy.DASHES);
Support1.SetDefaultColor(Color.RED);
Support1.SetHiding(!ShowHLLevels);
#--------------------------------------------------------------

input ShowHighLowBuble = yes;
AddchartBubble(ShowHighLowBuble and _markedHigh1,high, round(high,2),color.white);
AddchartBubble(ShowHighLowBuble and _markedLow1,low, round(Low,2),color.white, up = No);

########################################################################################


def length = 50;        # volume moving average lenth in days

input volumeSymbol = {default NASDAQ, NYSE, SPX, CurrentSymbol};
# Reset distribution day counts on FTD.
input distributionRstDay = 20191010;

def volCl;
def volHi;
def findSymbol;

# To make volume differences more visible, use a base volume number
# The subtracted volume number is then magnified to present a bigger difference
def volMin;    # base number for volume
def dropThreshold;

switch (volumeSymbol) {
# It was found there may be erratic volume data on close values
# On 2/19/2020, NYSE volume close values were 0 on 2/18 & 2/12
case NYSE:
    volCl = if close("$TVOL") == 0 then high("$TVOL") else close("$TVOL");
    #volCl = close("$TVOL");
    volHi = high("$TVOL");
# use SPX volume change percentage to replace erratic NYSE volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOLSPC") - close("$TVOLSPC")[1]) / close("$TVOLSPC")[1]) else volCl;
    volMin = 40000;
    dropThreshold = .942;
case NASDAQ:
    volCl = if close("$TVOLC/Q") == 0 then high("$TVOLC/Q") else close("$TVOLC/Q");
    #volCl = close("$TVOL/Q");
    volHi = high("$TVOLC/Q");
# use SPX volume change percentage to replace erratic NASDAQ volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOLSPC") - close("$TVOLSPC")[1]) / close("$TVOLSPC")[1]) else volCl;
    volMin = 30000;
    dropThreshold = .931;
case SPX:
    volCl = if close("$TVOLSPC") == 0 then high("$TVOLSPC") else close("$TVOLSPC");
    #volCl = close("$TVOLSPC");
    volHi = high("$TVOLSPC");
# use NYSE volume change percentage to replace erratic SPX volume
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close("$TVOL") - close("$TVOL")[1]) / close("$TVOL")[1]) else volCl;
    volMin = 0;
    dropThreshold = 1;

case CurrentSymbol:
    volCl = if close == 0 then high else close;
    volHi = high;
 
    findSymbol = if volCl == 0 then volCl[1] * (1 + (close - close[1]) / close[1]) else volCl;
    volMin = 10000;
    dropThreshold = .942;

}

def cls = close;

def lastBar = HighestAll(If (IsNaN(cls), Double.NaN, BarNumber()));
def volumes = if IsNaN(findSymbol) and BarNumber() == lastBar then volumes[1] else findSymbol;

def Vol = 3 * (volumes - volMin);
def VolAvg = 3 * (Average(volumes, length) - volMin);


# Display useful texts starting at upper left corner
# End of Day volume change
def VolChangePercentDay = If (IsNaN(volumes[1]), 0,
                              100 * (volumes - volumes[1]) / volumes[1]);

# InvalidDay was added since volume on 2019/11/29 (after Thanksgiving) was N/A.
AddLabel( ShowDistrubutionLabels, if VolChangePercentDay == 0 then "InvalidDay" else "" +
               "VolmChg=" + Concat("", Round(VolChangePercentDay)) +
               "%", if VolChangePercentDay < 0 then
               Color.DARK_GRAY else if cls > cls[1] then Color.DARK_GREEN
               else Color.DARK_RED);

# Count distributionDay only if market price drops 0.2% or more
def downDay = cls <= (cls[1] * 0.998);

def volIncrease = volumes > volumes[1];

#
# After 25 sessions, a distribution day expires
# Use 25 bar numbers to represent 25 live sessions. GetDay or alike includes weekends.
#

def lastDays = if (BarNumber() > lastBar - 25) then 1 else 0;

# a distribution day can fall off the count if the index rises 6% or more,
# on an intraday basis, from its close on the day the higher-volume loss appears.
# Remove distribution days after prices increase 6% WHEN the market is in an uptrend.
# Need to fix:
# During the market bottomed on 2-28-2020, the stock price rose to 9.8% with the market still in
# correction. The high volume selloff on 2-28 would still be counted as a distribution.
# The highest date should be after the distribution day

# Get proper high for future 25 days
def prHi = high;
def prLo = low;

def futureHigh = if IsNaN(prHi[-25]) then futureHigh[1] else prHi[-25];
def prHighest = Highest(futureHigh, 25);
# Note: This condition disqualifies D-Days after large bear rally
# This is acceptable for now since D-Days in bear market are not really useful
def priceInRange = (cls * 1.06 >= prHighest);

def distributionDay = downDay and volIncrease and lastDays and priceInRange;

# Count valid distribution days in last 25 days
def distDayCount = Sum(distributionDay, 25);


# A broad market correction makes the distribution day count irrelevant
# reset distribution count to 0
# Distribution day count should reset after 2nd confirmation day
# input distributionRstDay = 20191010;   a prior 2nd confirmation day
# input distributionRstDay = 20200402;   a prior 2nd confirmation day
#input distributionRstDay = 20191010;

def newDistributionCycle = GetYYYYMMDD() > distributionRstDay;
# Need to use above variable to restart d-day count
def newDistDays = Sum(distributionDay and newDistributionCycle, 25);

# Display bubble red is count > 5, yellow >3, else while
AddChartBubble(distributionDay and !newDistributionCycle, Vol, Concat("", distDayCount),
                if distDayCount < 3 then Color.WHITE
                else if distDayCount < 5 then Color.LIGHT_ORANGE
                else Color.RED);


# Show D-Day reset line at the reset date input by user
# It appears at the left side of the volume bar
AddVerticalLine( if (GetYYYYMMDD() == distributionRstDay ) then yes
                 else no,
                "                   2ndCnfm",
                Color.GREEN, Curve.MEDIUM_DASH);



# to do: Comparison of preholiday data may be invalid.

#------------------------------------------------------------
# Stalling day counts
# 1. market has been rising and price is within 3% of 25 day high
# 2. Price making a high
#      current close >= prior 2 day close, or
#      current close >= prior day high
# 3. volume >= 95% of prior day volume
# 4. close in lower half of daily range
# 5. small gain within 0.4% for SPX & NASDAQ
# 6. The above IBD criteria disclosed in one article generates too many stalling days
#    Additional rules from IBD book are used to further reduce stalling counts
#    6.1 close up smaller than prior 2 days
#    6.2 low is lower than high of prior day (No unfilled gap-up)
#    6.3 there is at least one decent gain in prior 2 days
#    6.4 daily trading range should be similar to last 2 days
# 7. stalling counts are reduced due to time (25 days) and significantly upward
#    movement (6%) of the index
# Ex. 2019/11/12 was a stalling day on SPX, 2019/12/18 was stalling for Nasdaq

def priceIsHigh = cls >= cls[2] or cls >= prHi[1];
def priceLowHalf = cls < (prHi - prLo) / 2 + prLo;
def priceGainSmall = cls - cls[1] > 0 and
                     ((cls - cls[1] < (cls[1] - cls[2])) or
                     ((cls - cls[1] < cls[2] - cls[3])));

# Added a 0.2% gap from prior day high to allow 2020/05/26 to count
# as a stalling day for NASDAQ
def priceGapFill = prLo < prHi[1] * 1.002;
def priceGainOk = (cls[1] - cls[2] > 0.002 * cls[2]) or
                   (cls[2] - cls[3] > 0.002 * cls[3]);
# price trading range is the high - low plus the gap up if any
def priceRange = if prLo > prHi[1] then prHi - prHi[1] else prHi - prLo;
def priceRangeBig = priceGainOk and priceRange > 0.8 * Min(priceRange[1], priceRange[2]);

def stallDay = cls - cls[25] > 0 and
               cls >= 0.97 * Highest( prHi, 25) and
               volumes > 0.95 * volumes[1] and
               cls - cls[1] > 0 and
               cls - cls[1] < 1.004 * cls[1] and
               priceIsHigh and priceLowHalf and priceGainSmall and priceGapFill and
               priceRangeBig and lastDays;

input ShowStallingDays = yes;
input ShowDistributionDays = yes;
#AddChartBubble(ShowStallingDays and stallDay , high, "Stalling Days", Color.MAGENTA);

# Count stalling days
def stallDayCount = Sum(stallDay, 25);

# calculate new stalling days after the reset day (e.g. follow-up date)
def newStallDays = Sum(stallDay and newDistributionCycle, 25);

# Display final distribution count (incl. stall days)
# red if >= 5, >3: yellow, else green
def totalDdays = distDayCount + stallDayCount;
def totalNdDays = newDistDays + newStallDays;

AddChartBubble(ShowDistributionDays and distributionDay and newDistributionCycle, high,
                if volCl == 0 then Concat("?", newDistDays) else Concat("", newDistDays),
                if totalNdDays < 3 then Color.WHITE
                else if totalNdDays < 5 then Color.LIGHT_ORANGE
                else Color.RED);


AddChartBubble(ShowStallingDays and stallDay and lastDays, high,
                if volCl == 0 then "?Stalling:" + Concat("", stallDayCount)
                              else "Stalling:" + Concat("", stallDayCount),
                if totalNdDays < 3 then Color.WHITE
                else if totalNdDays < 5 then Color.LIGHT_ORANGE
                else Color.RED);
AddChartBubble(volCl == 0 and !stallDay and !distributionDay, high, "?", Color.LIGHT_GRAY);


AddLabel(ShowDistrubutionLabels and totalDdays != totalNdDays, "AllDdays =" + Concat("", totalDdays), Color.GRAY);

#-------------------------------------------------------------------------------------
# Follow-through signals (FTD) are more likely to fail if distribution days
# occur in the first few days of a new uptrend. This is one key red flag.
# Quantification in script is implemented with a concept of critical score (critScore):
#   critScore = 3 for the 1st 5 days after FTD
#   critScore = 2 on the 6th, critScore = 1 on 7th day, critScore = 0 after 7th day
#   Total Distribution days = critScore + regular D-day count
#   critScore is used only if there is at least one D-day in the 1st 5th day after FTD
#-------------------------------------------------------------------------------------

def ftdBar = if GetYYYYMMDD() == distributionRstDay then BarNumber() else 0;
def lastFtdBar = HighestAll(ftdBar);
def daysAfterFTD = lastBar - lastFtdBar;

def critScore = if daysAfterFTD <= 0 then 0 else
                if daysAfterFTD <= 5 then 3 else
                if daysAfterFTD <= 6 then 2 else
                if daysAfterFTD <= 7 then 1 else 0;

def totalNdDaysC = totalNdDays + if totalNdDays > 0 then critScore else 0;

# Actual distribution day count is shown but color depends on totalNdDaysC
AddLabel(ShowDistrubutionLabels, "NewDdays =" + Concat("", totalNdDays ),
         if totalNdDaysC <= 2 then Color.GREEN
         else if totalNdDaysC <= 4 then Color.ORANGE
         else Color.RED);


# Add an indication of 1st rally day to start FTD count
# in a market correction period
# pink rally day is a day satisfying the following conditions:
# 1). Close above ½ of daily TRUE range and below prior day close
# 2). Low is the lowest during the market correction,
#     including future lows if available

# resolution of each 1st rally day is set to about 2 weeks

def rDayInterval = Round(25 / 2, 0);

def futureLow = if IsNaN(prLo[-rDayInterval]) then futureLow[1]
                else prLo[-rDayInterval];
def futureCls = if IsNaN(cls[-rDayInterval]) then futureCls[1]
                else cls[-rDayInterval];

# market correction is currently defined as down about 8% from top
# need to be refined so that it will work in a bear market that is forming a bottom.
# In this case, the 8% drop may not be required.
def mktCr    = prLo[1] <= Highest(high, 25) * dropThreshold; #.931; #.92;
def prRng    = TrueRange(prHi, cls, prLo); #prHi - prLo;


def pinkRday = cls > (prLo + prRng / 2) and cls < cls[1] and
               prLo <= Lowest(prLo[1], rDayInterval) and
               prLo <= Lowest(futureLow, rDayInterval);
 

# The real rally day has its close higher than prior close
# A rally day is invalidated if the low is broken in subsequent days
def realRday = cls > cls[1] and
               (prLo <= Lowest(prLo[1], rDayInterval) or
                prLo[1] <= Lowest(prLo[1], rDayInterval)) and
               (prLo <= Lowest(futureLow, rDayInterval) or
                prLo[1] <= Lowest(futureLow, rDayInterval)) and
               Sum(realRday[1], rDayInterval) == 0 and
               Sum(pinkRday[1], rDayInterval) == 0;

def RallyDay1 = (mktCr or mktCr[1]) and (pinkRday or realRday);

AddChartBubble(RallyDay1, high, "R1", Color.LIGHT_GREEN);



######################################################################
#                            FTD Requirements                        #
######################################################################
#1.Heavier volume than the previous daysAfterFTD  -> volIncrease

#2. Volatility is defined as the percentage gain of the up days over the past 200 days

def IBDlengthVolatility = 200;

def PercentageGreenBars = Round((close - close[1]) / close * 100, 2);

def upday = if close > close[1] then 1 else 0;
def SumUpdays = Sum(upday, IBDlengthVolatility);

def UpdaysPercentage = if upday then PercentageGreenBars else 0;
def SumUpdaysPercentage = Sum(UpdaysPercentage, IBDlengthVolatility);

def volatilityIBD = Round(SumUpdaysPercentage / SumUpdays, 2);

#Market Volatility and FTD price change requirements as percentageGreenBars IBD Market School.

def FTDPriceRequirement = if volatilityIBD < 0.4 then 1.007
else if volatilityIBD >= 0.4 and  volatilityIBD < 0.55 then 1.0085
else if  volatilityIBD >= 0.55 and volatilityIBD < 1 then 1.01
else 1.01245;


######################################################################
#                           FTD Requirements                         #
#--------------------------END-----END----END------------------------#
######################################################################



# Currently (April, 2020) a daily price (cls) increase of 1.25% minimum is
# the price requirement by either SPX or NASDAQ for a FTD.
# Must be day 4 after 1st rally attempt /w an exception (1st 3 days are strong).
# Must have higher volume on the FTD day

def redDay = if close < close[1] then 1 else 0;

def lastR1Bar  = If (RallyDay1, BarNumber(), lastR1Bar[1]);
def daysAfterR1 = BarNumber() - lastR1Bar;


#The Rally days is counting as day number 1  meaning daysAfterR1 = 0 so 4 days and more is daysAfterR1>=3

def isFTD = if daysAfterR1 >= 3  and BarNumber() > lastR1Bar and
               BarNumber() < lastR1Bar + 25 and lastR1Bar != 0 and
               cls >= (cls[1] * FTDPriceRequirement) and
               volIncrease then 1 else 0 ;


def totalR1toFTD = if RallyDay1 then totalR1toFTD[1] + 1  else if !RallyDay1 and !isFTD then totalR1toFTD[1] else 0;

def oneFTD = totalR1toFTD < totalR1toFTD[1];
AddChartBubble(oneFTD, high, "FTD", Color.LIGHT_ORANGE);

def lastOneFTDBar  = If (oneFTD, BarNumber(), lastOneFTDBar[1]);
def daysAfterOneFTD = BarNumber() - lastOneFTDBar;


######################################################################
#                    Distribution Days And Stalling days           #
######################################################################
#Distribution Day
DefineGlobalColor("DistibutionDay", Color.DARK_ORANGE);
DefineGlobalColor("StallingDay", Color.DARK_RED);

input ShowAllDistributionDays = no;
input ShowAllStallingDays = no;



def distributionDays = downDay and volIncrease and priceInRange;

def stallDays = cls - cls[25] > 0 and
               cls >= 0.97 * Highest( prHi, 25) and
               volumes > 0.95 * volumes[1] and
               cls - cls[1] > 0 and
               cls - cls[1] < 1.004 * cls[1] and
               priceIsHigh and priceLowHalf and priceGainSmall and priceGapFill and
               priceRangeBig;

def totalDS =  if stallDayCount >= distDayCount then distDayCount*2-1 else totalDdays;

def distributionDaysCount = sum(distributionDays,25); #check the last 25 days for the S3 and S4 signal.
def distributionDaysCount26 = sum(distributionDays,26); #check the last 26 days for the B10 Signal.
def distributionDaysCount8 = sum(distributionDays,8); #check the last 8 days for the S13 signal.

def stallDaysCount = sum(stallDays,25);
def stallDaysCount26 = sum(stallDays,26);
def stallDaysCount8 = sum(stallDays,8);

def totalDSdays = distributionDaysCount + stallDaysCount;
def totalDSdays26 = distributionDaysCount26 + stallDaysCount26;
def totalDSdays8 = distributionDaysCount8 + stallDaysCount8;

def totalAllDS =  if stallDaysCount >= distributionDaysCount then distributionDaysCount*2-1 else totalDSdays;
def totalAllDS26 =  if stallDaysCount26 >= distributionDaysCount26 then distributionDaysCount26*2-1 else totalDSdays26;


AddChartBubble("time condition" = ShowAllDistributionDays and distributionDays, "price location" = high, text =  if ShowChartBubbleDescription then "Distribution Day (" + PercentageGreenBars + "%)" else "DD", GlobalColor("DistibutionDay"));

AddChartBubble("time condition" = ShowAllStallingDays and stallDays, "price location" = high, text =  if ShowChartBubbleDescription then "Stalling Day (" + PercentageGreenBars + "%)" else "SD", GlobalColor("StallingDay"));



######################################################################
#                       Moving Averages EMA21 MA50                   #
######################################################################

def ema21 = MovAvgExponential("length" = 21)."AvgExp";
def ma50 = MovingAverage(data = close, "length" = 50 );


######################################################################
#                            FTD Signals     (B1)                    #
######################################################################
#Buy on initial follow-through day.
input showB1FTD = yes;
def B1= oneFTD;

AddChartBubble(showB1FTD and B1, high, if ShowChartBubbleDescription then "(B1) " + (daysAfterR1 + 1) + "days from R1 (+ " + PercentageGreenBars + "%)" else "B1", Color.GREEN);

######################################################################
#                         Additional FT Days    (B2)                 #
######################################################################
#Buy on all additional follow-through days within 25-days from a rally day that closes above the low of the initial follow-through day.
input showB2 = yes;
def LowOneFTD = if oneFTD then low else LowOneFTD[1];
def B2 = isFTD and !oneFTD and close > LowOneFTD and daysAfterR1 < 25;
def CountB2 = if B2 then CountB2[1] + 1  else if !B2 and daysAfterR1 < 25 then CountB2[1] else 0;

AddChartBubble("time condition" = showB2 and B2, "price location" = high, text =  if showChartBubbleDescription then "B2-" + CountB2 + " FTD (+ " + PercentageGreenBars + "%)" else "B2", color = GlobalColor("B2"));



######################################################################
#                            Low Above Ema21    (B3)                 #
######################################################################

#S5Reset for resetting the buy signals B3, B4Signal & B5
#S5 Break below ema21
#Sell if index closes 0.2% or greater below ema21
#B3, B4 & B5 Reset by S5. Once you have B3 or B4, you can't have another until is reset by an S5

def S5ResetbelowEma21 = cls <= (ema21 * 0.998);


#Buy on and up or flat day when then intra-day low is at or above then ema21
input showB3 = yes;
def B3 =  low >= ema21 and upday; # and !oneFTD;

def totalB3toS5 = if B3 then totalB3toS5[1] + 1 else if !B3 and !S5ResetbelowEma21 then totalB3toS5[1] else 0;
def totalS5toB3 = if S5ResetbelowEma21 then totalS5toB3[1] + 1 else if !B3 and !S5ResetbelowEma21 then totalS5toB3[1] else 0;

def oneB3 = totalS5toB3 < totalS5toB3[1];
def S5Reset = totalB3toS5 < totalB3toS5[1];

def lastoneB3Bar  = If (oneB3, BarNumber(), lastoneB3Bar[1]);
def daysAfterB3 = BarNumber() - lastoneB3Bar;


AddChartBubble("time condition" = showB3 and oneB3, "price location" = high, text = if showChartBubbleDescription then  "B3 low>ema21. Reset S5, S6, S7 & S8 (" + PercentageGreenBars + "%)" else "B3", color = GlobalColor("B3"));



######################################################################
#                    Trending Above Ema21    (B4)                    #
######################################################################
#Buy after a B3 signal has triggered, buy on the 5th consecutive day that the low is at or above the ema21. The B4 signal is only triggered when the index closes up or flat for the day. If the index close down on the 5th day from the B3, then wait for the next up day to trigger B4.

input showB4 = yes;

# signal for reset the B4
def S5B4ResetSignal = !S5Reset;

def B4Signal = if !oneB3 and daysAfterB3 >= 4 and B3 and S5B4ResetSignal then 1 else 0;

def SumB3toB4 = if oneB3 then SumB3toB4[1] + 1 else if !B4Signal and !oneB3 then SumB3toB4[1] else 0;
def oneB4 = SumB3toB4 < SumB3toB4[1];

AddChartBubble("time condition" = showB4 and oneB4 , "price location" = high, text =  if ShowChartBubbleDescription then "B4 Trending Above ema21. " + (daysAfterB3 + 1) + "days from (B3) (" + PercentageGreenBars + "%)" else "B4", GlobalColor("B4"));


######################################################################
#                        Living Above Ema21  (B5)                    #
######################################################################
#Buy after a B3 signal has triggered, buy on the 10th and every 5th consecutive day after that (15th, 20th, 25th, etc.) day's low is at or above the ema21. The B5 signal is only triggered when the index closes up or flat for the day. If the index close down on the 5th day from the B3, then wait for the next up day to trigger B5.


input showB5 = yes;
def fiveDaysB3 = (daysAfterB3+1) % 5 == 0;
# signal for on off the B5
def S5B5ResetSignal = !S5Reset; # !oneFTD  and daysAfterB3 < daysAfterS5Reset; # and !oneS5Reset

#B5 signal
def B5Signal = daysAfterB3 >= 9 and B3 and S5B5ResetSignal;

def B5_5 =  B5Signal and fiveDaysB3;
def B5_6 = if !B5_5[1] then B5Signal and fiveDaysB3[1] else 0;
def B5_7 = if !B5_6[1] and !B5_5[2] then B5Signal and fiveDaysB3[2] else 0;
def B5_8 = if !B5_7[1] and !B5_6[2] and !B5_5[3] then B5Signal and fiveDaysB3[3] else 0;
def B5_9 = if !B5_8[1]  and !B5_7[2] and !B5_6[3] and !B5_5[4] then B5Signal and fiveDaysB3[4] else 0;

def B5 = B5_5 or B5_6 or B5_7 or B5_8 or B5_9;

AddChartBubble("time condition" = showB5 and B5, "price location" = high, text =  if ShowChartBubbleDescription then "B5 Living above ema21 (" + PercentageGreenBars + "%) " + (daysAfterB3+1) + "days from B3. Low >= ema21" else "B5", GlobalColor("B5"));



######################################################################
#                          Low Above MA50      (B6)                  #
######################################################################
#Close Above ma50
#Buy on an up or flat day when then intraday low is at or above an uptreding ma50.
#Uptreding ma50 -> the day that the ma50 is up from then prior day.
#B6 Reset by S9. Once you have B6, you can't have another until is reset by an S9


#Sell if index closes below 50ma.
#Only triggers if a corresponding buy signal related to that moving average B6 triggered previously
#Sell S9 is reset by B6
#Exception: -> Index closes in the upper half of range ("Shakeouk"), and Index closes within 1% of the 50ma

def S9 = cls < (ma50 * 0.99) and close < hl2;
def B6 =  low >= ma50 and ma50 > ma50[1] and upday;
def SumB6toS9 = if B6 then SumB6toS9[1] + 1 else if !B6 and !S9 then SumB6toS9[1] else 0;
def oneS9 = SumB6toS9 < SumB6toS9[1];


#Close Above ma50
#Buy on and up or flat day when then intra-day low is at or above then ema21

input showB6 = yes;

def SumS9toB6 = if S9 then SumS9toB6[1] + 1 else if !B6 and !S9 then SumS9toB6[1] else 0;
def oneB6 = SumS9toB6 < SumS9toB6[1];


def lastoneB6Bar  = If (oneB6, BarNumber(), lastoneB6Bar[1]);
def daysAfterB6 = BarNumber() - lastoneB6Bar;


AddChartBubble("time condition" = showB6 and oneB6, "price location" = high, text =  if ShowChartBubbleDescription then "B6 Low Above ma50 in uptrending ma50, reset S9 (+" + PercentageGreenBars + "%)" else "B6", color = GlobalColor("B6"));


######################################################################
#                       Accumulation Day     (B7)                    #
######################################################################
#Accumulation Buy Signal is B2 but after the 25 days of rally attempt.
#Index closes up the percentage required for a followthrogh day. Low volatility FTD would apply as well
# Close in upper 25% of the range
#close must be greater than ema21
#Cannot coincide with a FTD or subsequent FTD (daysAfterR1 >= 25)
#For GapUp the range calculation is as following (Close-Previous day's close)/(high-previous day's close)

def PriceRangeIBD = if low > high[1] then (close - close[1]) / (high - close[1]) else if high<low[1] then (close - low)/(close[1]-low) else (close - low) / (high - low);

input showB7 = yes;
def B7 =  lastR1Bar != 0 and cls >= (cls[1] * FTDPriceRequirement) and
               volIncrease and !oneFTD and close > LowOneFTD and daysAfterR1 >= 25 and PriceRangeIBD >= 0.75 and close > ema21;


AddChartBubble("time condition" = showB7 and B7, "price location" = high, text =  if ShowChartBubbleDescription then "B7 Accumulation Day (+ " + PercentageGreenBars + "%)" else "B7", color = GlobalColor("B7"));


######################################################################
#                            Higher High     (B8)                    #
######################################################################
#Buy after closing above the last marked high (Last high before Rally1)
input showB8 = yes;

def HH = if IsNaN(_markedHigh1) then _lastMarkedHigh1[1]  else if _MarkedHigh1 then high else if _MarkedHigh1[1] then if high >hh[1] then high else hh[1] else hh[1];
def B8 = high > hh;



def SumB8Back = if _MarkedHigh1[LookbackPeriod] then SumB8Back[1] + 1 else if !_MarkedHigh1[LookbackPeriod] and !B8 then SumB8Back[1] else 0;
def oneB8 = SumB8Back < SumB8Back[1];

AddchartBubble(showB8 and oneb8,high,  if ShowChartBubbleDescription then "B8 Higher High" else "B8",Color.LIME);


######################################################################
#                    Downside Reversal Buyback    (B9)               #
######################################################################
#After selling on a downside reversal, buy back if the index closes above the intraday high of the downside reversal within two trading days.

################ S11 code ################
#Sell after downside reversal day
#New Intra-day high within prior 13 days. On seminar was noted as weeks but on simulator was days.
#close in bottom quartile of range
#Close lower than the previous day (redday)
#Spread of 1.75% or greater. Spread is calculated as the (Intraday High-Intraday Low)/ (Intraday Low)

def spreadIntraday = ((high-low)/low)*100;

def S11 = high == highest(high,13) and PriceRangeIBD <=0.25 and redday and spreadIntraday >= 1.75;

def lastoneS11Bar  = If (S11, BarNumber(), lastoneS11Bar[1]);
def daysAfterS11 = BarNumber() - lastoneS11Bar;

################ B9 code ################

input showB9 = yes;

def S11High = if(S11,high,S11High[1]);

def B9 = close > S11High and daysAfterS11<=2;

def SumB9S11 = if S11 then SumB9S11[1] + 1 else if !S11 and !b9  then SumB9S11[1] else 0;
def oneB9 = SumB9S11 < SumB9S11[1];

AddChartBubble("time condition" = showB9 and oneB9, "price location" = high, text =  if ShowChartBubbleDescription then "B9 Downside Reversal Buyback (" + PercentageGreenBars + "%)" else "B9", color = GlobalColor("B9"));


######################################################################
#                      Distribution Day Fall Off    (B10)            #
######################################################################
#Distribution Day
#Indicative of institutional Selling
#Price down greater than or equal to 0.2% on volume greater than or equal to prior day's volume.

#Stall Day (Stalling)
#Stalling is a form of heavy volume without further upsideGapThreeMethods price progress.
#Stall days occur on updays when the market is at or near new highs and close in the lower 25% range on heavier volume of  previous day.
#After the first stallDay, you do not consider additional stall days in the distribution count unitl you have more distribution days than stall days.
 
# full Distribution counts (Distribution and stall days.)
#Before 1992    DC:4 (Distribution Count)
#1991-2004      DC: 5
#2005-Present   DC:6

#B10
#Buy on the day that the distribution count falls back to four (Full distribution count minus two) as long as the close is above the ema21 and Buy Switch is on.

#totalDdays = distDayCount + stallDayCount
#def totalNdDays = newDistDays + newStallDays;

input showB10 = yes;


def B10 = totalAllDS==4 and totalAllDS26==5 and close > ema21; #Need fix entry for BSON



######################################################################
#                   Follow Through Day Undercut    (S1)              #
######################################################################
#Follow Through Day Undercut (S1)
#Sell if index closes below the Low of the initial follow through day.
#-Does not apply to additional FTDs
input showS1 = yes;

def S1 =  close < LowOneFTD ;
def SumOneS1 = if oneFTD then SumOneS1[1] + 1 else if !S1 and !oneFTD then SumOneS1[1] else 0;
def oneS1 = SumOneS1 < SumOneS1[1];

AddChartBubble("time condition" = showS1 and oneS1, "price location" = high, text =  if ShowChartBubbleDescription then "S1 FTD undercut (" + PercentageGreenBars + "%)" else "S1", color = GlobalColor("S1"));


######################################################################
#                  Failed Rally Attempt         Major Rule (S2)      #
######################################################################
#Sell if index undercuts the "major low" of rally attempt.
#-Market exposure is reduced to zero
#-Buy Switch is turned off.
input showS2 = yes;
input showS2ml = yes;
def MajorLow =  if RallyDay1 and low < low[1] then low else if RallyDay1 and low[1] < low then low[1] else MajorLow[1];
def S2 = low < MajorLow; # and MajorLow == MajorLow[1];

def SumoneS2 = if RallyDay1 then SumoneS2[1] + 1 else if !rallyDay1 and !s2  then SumoneS2[1] else 0;
def oneS2 = SumoneS2 < SumoneS2[1];

#def OneS2 = Sum(S2) == 1 and S2;

def minorLow = if RallyDay1[1] then low else if !rallyDay1 then if low < minorLow[1] then low else minorLow[1] else minorLow[1];



#Sell if index undercuts the "minor low" of rally attempt
#-Market exposure is reduced by two
#-This does not turn off the Buy Switch
#The "minor low" of a rally is defined as a low that occurs when the buy switch is on (oneFTD)
def minorLowS2 = !RallyDay1 and minorLow<minorLow[1];




AddChartBubble("time condition" = showS2 and OneS2, "price location" = high, text =  if ShowChartBubbleDescription then "S2 Failed Rally attempt (" + PercentageGreenBars + "%)" else "S2", GlobalColor("S2"));

#######################################################
# Go to Buy Switch for the bubble signal of minor low #
#######################################################
def BSOnforS2ml = if oneFTD then BSOnforS2ml[1] + 1 else if !oneS2 and !oneFTD then BSOnforS2ml[1] else 0;




######################################################################
#                   Full Distribution minus one   (S3)               #
######################################################################
# Sell after distribution count increases to five (Full distribution is currently six days).
#This rule is applied each new time that it occurs.

input showS3 = yes;


Def S3 = if((distributionDays or stallDays) and totalAllDS==5,1,0);


AddChartBubble("time condition" = showS3 and S3, "price location" = high, text =  if ShowChartBubbleDescription then "S3 Full Distribution minus one (" + PercentageGreenBars + "%)" else "S3", GlobalColor("S3"));


######################################################################
#                         Full Distribution       (S4)               #
######################################################################
#Sell after full distribution count (full distribution is currently six days).
#This rule is applied up to eight days (full distribution plus two).


input showS4 = yes;
def S4 = if((distributionDays or stallDays) and (totalAllDS==6 or totalAllDS==7 or totalAllDS==8) ,1,0);

AddChartBubble("time condition" = showS4 and S4, "price location" = high, text =  if ShowChartBubbleDescription then "S4 Full Distribution (" + PercentageGreenBars + "%)" else "S4", GlobalColor("S4"));


######################################################################
#                       Break Below Ema21         (S5)               #
######################################################################
#Sell if index closes 0.2% or greater below the ema21
#S5, S6, S7, S8 Reset by B3. Once you have S5, S6 or S7, you can't have another until is reset by an B3

# signal for on off the S5
def B3S5ResetSignal = S5Reset; #!oneFTD  and daysafterS5Reset[1] >= daysafterB3; # and !oneS5Reset

#S5 Break below ema21
# Sell if index closes 0.2% or greater below ema21

input showS5 = yes;

def S5belowEma21 = cls <= (ema21 * 0.998);
def S5 =  S5belowEma21 ;

def oneS5 = totalB3toS5 < totalB3toS5[1];

def lastoneS5  = If (oneS5, BarNumber(), lastoneS5[1]);
def daysAfterS5 = BarNumber() - lastoneS5;


AddChartBubble("time condition" = showS5 and oneS5, "price location" = high, text =  if ShowChartBubbleDescription then "S5 Break Below close<ema21. Reset B3, B4 & B5 (" + PercentageGreenBars + "%)" else "S5", GlobalColor("S5"));


######################################################################
#                  Overdue Break Below Ema21       (S6)              #
######################################################################
#Overdue break below ema21 (S6)
#Sell if index closes down 0.2% or greater below the ema21 after 30 days have been passed since last B3 without triggering an S5

input showS6 = yes;

def S6 =  s5 and daysAfterB3 >= 29 ;

def SumB3toS6 = if oneB3 then SumB3toS6[1] + 1 else if !oneB3 and !s6  then SumB3toS6[1] else 0;
def oneS6 = SumB3toS6 < SumB3toS6[1];

AddChartBubble("time condition" = showS6 and oneS6, "price location" = high, text =  if ShowChartBubbleDescription then "S6 Overdue Break low<ema21 (" + PercentageGreenBars + "%)" else "S6", GlobalColor("S6"));

######################################################################
#                      Trending Below ema21       (S7)               #
######################################################################
#Trending Below ema21 (S7)
#After and S5 signal has triggered, sell on the 5th consecutive day that then high is below the ema21. The S7 signal is only triggered when the idex closes down for the day. If the indexes closes up on the 5th day from the S5, then wait for the next down day to trigger the S7


input showS7 = yes;

# signal for on off the B4

def S7 = daysAfterS5 >= 4 and high <= ema21 and redDay ; # !oneS5 and !oneB3

def SumB3toS7 = if oneB3 then SumB3toS7[1] + 1 else if !S7 and !oneb3 then SumB3toS7[1] else 0;
def oneS7 = SumB3toS7 < SumB3toS7[1];

AddChartBubble("time condition" = showS7 and oneS7 , "price location" = high, text =  if ShowChartBubbleDescription then "S7 Trending Below ema21. high<ema21 " + (daysAfterS5+1) + "days from (S5) (" + PercentageGreenBars + "%)" else "S7", GlobalColor("S7"));

######################################################################
#                       Living Below Ema21        (S8)               #
######################################################################
#Trending Below ema21 (S7)
#After and S5 signal has triggered, sell on then 10th and every 5th consecutive day after that (15th, 20th, 25th etc.) that then high is below the ema21. The S8 signal is only triggered when the index closes down for the day. If the indexes closes up on the 10th, 15th, 20th, etc. from the S5, then wait for the next down day to trigger the S8


input showS8 = yes;

# signal for on off the B4
#def S5B4ResetSignalw = !oneFTD  and daysAfterB3 < daysAfterS5Reset; # and !oneS5Reset
def fiveDaysS5 = (daysAfterS5+1) % 5 == 0;

def S8Signal = if daysAfterS5 >=9 and s7 and !oneb3 then 1 else 0;


def S8_5 =  S8Signal and fiveDaysS5;
def S8_6 = if !S8_5[1] then S8Signal and fiveDaysS5[1] else 0;
def S8_7 = if !S8_6[1] and !S8_5[2] then S8Signal and fiveDaysS5[2] else 0;
def S8_8 = if !S8_7[1] and !S8_6[2] and !S8_5[3] then S8Signal and fiveDaysS5[3] else 0;
def S8_9 = if !S8_8[1]  and !S8_7[2] and !S8_6[3] and !S8_5[4] then S8Signal and fiveDaysS5[4] else 0;

def S8 = S8_5 or S8_6 or S8_7 or S8_8 or S8_9;

AddChartBubble("time condition" = showS8 and S8, "price location" = high, text =  if ShowChartBubbleDescription then "S8 Living Below Ema21, high<ema21 / " + (daysAfterS5+1) + "days from (S5) (" + PercentageGreenBars + "%)" else "S8", GlobalColor("S8"));



######################################################################
#                         Break Below MA50          (S9)             #
######################################################################
#Sell if index closes below 50ma.
#Only triggers if a corresponding buy signal related to that moving average B6 triggered previously
#Sell S9 is reset by B6
#Exception: -> Index closes in the upper half of range ("Shakeouk"), and Index closes within 1% of the 50ma
input ShowS9 = yes;
#Go to B6 for the code of the signal.

AddChartBubble("time condition" = ShowS9 and oneS9 , "price location" = high, text =  if ShowChartBubbleDescription then "S9 Break Below MA50, close<MA50, reset B6 (" + PercentageGreenBars + "%)" else "S9", GlobalColor("S9"));


######################################################################
#                               Bad Break           (S10)            #
######################################################################
#Sell if the close is down 2.25% of greater in the bottom 25% of range
#close below the ma50 or Intraday high below ema21
#Range as (close-low)/(high-low) for gap down (close-low)/(close[1]-low)

#def range = if high<low[1] then (close - low)/(close[1]-low) else (close-low)/(high-low);

input showS10 = yes;

def S10 = PercentageGreenBars<=-2.25 and (close < ma50 or high < ema21) and PriceRangeIBD <= 0.25 ;

AddChartBubble("time condition" = showS10 and S10, "price location" = high, text =  if ShowChartBubbleDescription then "S10 Bad Break (" + PercentageGreenBars + "%)" else "S10", GlobalColor("S10"));



######################################################################
#                        Downside Reversal Day      (S11)            #
######################################################################
#Sell after downside reversal day
#New Intra-day high within prior 13 days. On the seminar was noted as weeks but on the simulator were days.
#close in bottom quartile of range
#Close lower than the previous day (redday)
#Spread of 1.75% or greater. Spread is calculated as the (Intraday High-Intraday Low)/ (Intraday Low)

input showS11 = yes;
#for S11 go to B9

AddChartBubble("time condition" = showS11 and S11, "price location" = high, text =  if ShowChartBubbleDescription then "S11 Downside Reversal Day (" + PercentageGreenBars + "%)" else "S11", GlobalColor("S11"));



######################################################################
#                               Lower Low          (S12)             #
######################################################################
#Sell after closing below the last marked low  (9days lookback and 9day forward)

input showS12 = yes;


def LL = if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else if _markedLow1[1] then if low <LL[1] then low else LL[1] else LL[1];
def S12 = close < LL;

def SumOneS12 = if _markedLow1[LookbackPeriod] then SumOneS12[1] + 1 else if !_markedLow1[LookbackPeriod] and !S12 then SumOneS12[1] else 0;
def oneS12 = SumOneS12 < SumOneS12[1];

AddchartBubble(showS12 and oneS12,high, if ShowChartBubbleDescription then "S12 closing below Lower low" else "S12",GlobalColor("S12"));

######################################################################
#                       Distribution Cluster       (S13)             #
######################################################################
#Sell after distribution cluster count ("Cluster Count") increases to four within a rolling eight-day period.
#Sell after each successive incease in the cluster count from four up to eight days within a rolling eight-day period.
#For purposes of distribution clusters, all stall days and distribution days apply.
#The cluster count is reset once the cluster count falls to three within a rolling eight-day period.
#The distribution cluster changes the Power-Trend (if it is in effect) to a Power-Trend Under Pressure.
input showS13 = yes;

def S13 = if((distributionDays or stallDays) and (totalDSdays8==4 or totalDSdays8==5 or totalDSdays8==6 or totalDSdays8==7 or totalDSdays8==8 ) and totalDSdays8 != totalDSdays8[1]  ,1.0,0.0);;

AddchartBubble(showS13 and S13,high, if ShowChartBubbleDescription then "S13 Distribution Cluster" else "S13",GlobalColor("S13"));

######################################################################
#                    Break Below Higher High       (S14)             #
######################################################################
input showS14 = yes;
#Sell after closing below the last marked high that triggered a B8 (Higher High)

def barNumberB8 = If (oneB8, BarNumber(), barNumberB8[1]);;

def S14 =  close<HH and high[1]>hh and (barnumber() - barNumberB8)>0; #Incase of gapdown we use previous days high
def SumOneS14 = if _MarkedHigh1[LookbackPeriod] then SumOneS14[1] + 1 else if !_MarkedHigh1[LookbackPeriod] and !S14 then SumOneS14[1] else 0;;
def oneS14 = SumOneS14 < SumOneS14[1];


AddchartBubble(showS14 and oneS14,high, if ShowChartBubbleDescription then "S14 Breakaway Close Below Higher High" else "S14",GlobalColor("S14"));


######################################################################
########################## Circuit breaker ###########################
######################################################################
#Circuit breaker - occurs when you've got a 10% break of the market from the high point
Input showCircuitBreaker = yes;

def RecentHigh = if RallyDay1 then high else if !rallyDay1 then if high >RecentHigh[1] then high else RecentHigh[1] else RecentHigh[1]; # find the highest high in a periog Rallyday R1

def CB = if close < RecentHigh - RecentHigh*0.1 then 1 else 0;

def CountCB = if oneFTD then CountCB[1] + 1 else if !CB and !oneFTD then CountCB[1] else 0;
def oneCB = CountCB < CountCB[1];

AddchartBubble(showCircuitBreaker and oneCB,high, if ShowChartBubbleDescription then "Circuit Breaker 10% break of Highs. BS OFF" else "CB",GlobalColor("Circuit Breaker"));


######################################################################
#                          Market Exposure                           #
######################################################################

def BSOnB10 = if oneFTD then BSOnB10[1] + 1 else if !(oneS2 or (S4 ) or oneCB ) and !oneFTD then BSOnB10[1] else 0; #I could't add the market exposure ==0 with the (S4 ) on above code.

def S2ml = BSOnforS2ml and minorLowS2 and BSOnB10 and !oneFTD;

input ShowMElabel = yes;

def BS = B1 + B2 + oneB3 + oneB4 + B5 + oneB6 + B7 + oneB8 + oneB9 + (BSOnB10 and B10); #B10 is on when BuySwitch is on.
def SS = oneS1 + oneS2 + s2ml + S3 + S4 + oneS5 + oneS6 + oneS7 + S8 + oneS9 + S10 + S11 + oneS12 + S13 + oneS14 + oneCB;
def TS = BS-SS;

def countME = if rallyDay1 then 0 else if countME[1] > 5 then 5 else if countME[1]<0 then 0 else countME[1] + TS ;
def MarketExposure = if countME <0  then 0 else if countME>5 then 5 else countME;

#Market Exposure when the Restraint Rule  is applied
def countMERR = if rallyDay1 then 0 else if countMERR[1] > 2 then 2 else if countMERR[1]<0 then 0 else countMERR[1] + TS ;
def MarketExposureRR = if countMERR <0  then 0 else if countMERR>2 then 2 else countMERR;

#Market Exposure when the Power Trend is applied
def countMEPT = if rallyDay1 then 0 else if countMEPT[1] > 7 then 7 else if countMEPT[1]<2 then 2 else countMEPT[1] + TS ;
def MarketExposurePT = if countMEPT <2  then 2 else if countMEPT>7 then 7 else countMEPT;

# Market Exposure when the PowerTrendUnderPressure (+5 Floor +1)
def countMEPTup = if rallyDay1 then 0 else if countMEPTup[1] > 5 then 5 else if countMEPTup[1]<1 then 1 else countMEPTup[1] + TS ;
def MarketExposurePTup = if countMEPTup <1  then 1 else if countMEPTup >5 then 5 else countMEPTup;


# Market Exposure when the PowerTrendUnderPressureWithoutFloor
def countMEPTwf = if rallyDay1 then 0 else if countMEPTwf[1] > 5 then 5 else if countMEPTwf[1]<0 then 0 else countMEPTwf[1] + TS ;
def MarketExposurePTwf = if countMEPTwf <0  then 0 else if countMEPTwf >5 then 5 else countMEPTwf;


#color Label settings
def mid = 2;
def upper = 7;
def lower = 0;
def bl = 75;

def CLrValue = if MarketExposure < mid then 255 else if MarketExposure > upper then 0 else 255 - Round((MarketExposure - mid) / (upper - mid) * 255, 0);
def CLgValue = if MarketExposure > mid then 255 else if MarketExposure < lower then 0 else 255 - Round((mid - MarketExposure) / (mid - lower) * 255, 0);






######################################################################
#                            Restraint Rule                          #
######################################################################
#To avoid getting invested too fast, limit exposure to a maximum of +2

#Rules to remove the Restraint Rule: The index makes significant progress above the close of the initial follow-through day OR
#A buy signal (B4) is triggered (trending above ema21)

#Significant progress is defined as the percentage requirement nedeed for a FTD based on volatility (FTDPriceRequirement: currently 1%). Example if you close above 1% from the close of the FTD then we lift the Restraint Rule. OR
#If you trending above the FTD

#Restraint Rule applies if:
#The maket exposure was 0 prior FTD
#If the buy switch turns on due to closing above a prior high, then the restraint rule will apply until the index makes significant progress as defined by the (FTDPriceRequirement).

input ShowRestraintRuleLabel = yes;

def CloseofOneFTD = if oneFTD then close else CloseofOneFTD[1];
def RestraintLifted = if oneB4 or close > CloseofOneFTD*FTDPriceRequirement  then 1 else 0;

def MarkExpPriorFTD = If oneFTD and MarketExposure[1]==0 then 1 else 0;

def RR =  if MarkExpPriorFTD and !rallyDay1 and !RestraintLifted then 1 else 0;
Addlabel(ShowRestraintRuleLabel and RR,"Restraint on. Max MarketExposure Expose +2",Color.ORANGE);


######################################################################
#                              Power Trend                           #
######################################################################
#Power Trend Turns On:
#The ema21>ma50 for at least 5 consecutive days without an S9(break below ma50)
#The Index closes up or flat for the day.
#The ma50 is in uptrend for at least 1 day
#The Index low has been above the ema21 for at least 10 consecutive days
#Buy Switch override -> BSOn is forced to stay on
#Buy Switch count can go now up to +7 (100% exposure =+5) +2 buffer for sell signals.
#Don't act on anything higher than +5. This rule was created to provide a buffer to keep you incested during a significant uptrend.
#Start counting additional buy signals from the point that the PowerTrend turns on.

#Once the buy count is at +2 or higher, a floor of +2 is in effect.

Input showPowerTrendLabel = yes;


#Condition that End Power Trend:
#ema21 line crosses below the ma50 and index closes down for the day OR
#S2 Major Low OR
#Circuit Breaker.

#After Power Trend has ended
#No longer forces buy switch on (it may still be on, but it doesn't have to be on)
#Buy signal count is reset to a maximum of +5
#There is no longer a floor.
def PToff = ema21 crosses below ma50 and redDay or oneS2 or oneCB ;

#Condition that re-set a Power Trend from an Under Pressure condtion:
#The conditons that turn on a power-trend must be present, and
#10 consecutives days must pass without an S2 minor low an S9 and S13.
def p=10;
def PTOnreset =  fold q=0 to p with r do if(sum(!minorLowS2[q] and !ones9[q] and !s13[q],p)==p,1,0);

###### PT ON-OFF #####
def n=5;
def PTonSignal = fold i=0 to n with s do if(sum(ema21[i]>ma50[i] and !ones9[i],n)==n and ma50>ma50[1] and upday and sum(low>ema21,10)==10,1,0);


def SumPTon = if PTonSignal then 1 else if PToff then 0 else SumPTon[1];
def PTon = SumPTon;


#Condition that turn a Power Trend to a Power Trend under pressure is:
#S2 minor low signal (and minorLowS2 and BSon)
#S13 rule (Distribution cluster) is triggered

def PTonUnderPressure = PTon and (minorLowS2 or S13);
def SumPTup = if PTonUnderPressure then 1 else if PToff then 0 else SumPTup[1];
def PTup = SumPTup;

#When the power trend is in an under-pressure position with a floor, the max exposure count is reduced to +5 and the floor is reduced to +1 before any signals are applied on that day.

#Condition than turn a Power Trend to a Power Trend under pressure without a floor
#S9 signal (break below ma50)
#When an S9 occurs, the max exposure count remains at +5 but the floor of +1 is removed.

def PTonUnderPressureWithoutFloor = PTon and S9;
def SumPTupWF = if PTonUnderPressureWithoutFloor then 1 else if PToff then 0 else SumPTupWF[1];
def PTWF = SumPTupWF;

def PowerTrendUnderPressure = PTup and !Ptonreset;
def PowerTrendUnderPressureWithoutFloor = PTwf and !Ptonreset;
def PowerTrendOnNopressure = PTon and !PowerTrendUnderPressure and !PowerTrendUnderPressureWithoutFloor;
#def SumPTon = if PTonSignal then 1 else if PToff then 0 else SumPTon[1];


######################################################################
#                             Buy Switch                             #
######################################################################
#BS rules
#When the buy switch is on, you act on all buy and sell signals regardless of the distribution count.
#When the buy switch is off, you only act on sell signals.

#Events that turn Buy Switch On:
#Follow Through day (B1)
#Note on the day buy switch turns on, act on all buy signals that are in effect (except for buy signals B9 and B10) from the low (Rallyday1).

#Events that turn Buy Switch OFF
#Full distribution Count (S4) and market exposure is 0
#Failed rally attempt (S2) of a "major low"
#Circuit breaker - occurs when you've got a 10% break of the market from the high point

#What if the Buy Switch is Off, the MarketExposure count is zero, we haven't a follow-through day but the market is acting strong? How do you turn on the buy switch?
#A close above the prior high oneB8 since the last confirmed rally will turn the Buy Switch on.

############################## Buy Switch ##############################
input ShowBuySwitchLabel = yes;
input ShowBuySwitchBuddle = yes;


def BSOn = if (oneFTD or PTon or oneB8) then BSOn[1] + 1 else if !(oneS2 or (S4 and (if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else  MarketExposure)==0) or oneCB or oneS14 ) and !oneFTD then BSOn[1] else 0;


#def BSOff = BSOn < BSOn[1];
#def oneBSOn =if(BSOn > BSOn[1] and BSOn[1] == BSOn[2],1 ,0);

#AddchartBubble(ShowBuySwitchBuddle and oneBSOn, high,"Buy Switch On",GlobalColor("BuySwitch On"));
#AddchartBubble(ShowBuySwitchBuddle and BSOff, high,"Buy Switch Off",GlobalColor("BuySwitch Off"));


#BS Off MarketExposure Exposure

def TSforBSoff = -SS;

def countMEforBSoff = if rallyDay1 then 0 else if countMEforBSoff[1] > 5 then 5 else if countMEforBSoff[1]<0 then 0 else (if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else  MarketExposure) + TSforBSoff ;
def MarketExposureforBSoff = if countMEforBSoff <0  then 0 else if countMEforBSoff >5 then 5 else countMEforBSoff;

def CurrentME = if RR then MarketExposureRR else if PTon and !PowerTrendUnderPressure and !PowerTrendUnderPressureWithoutFloor  then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else if !bson then MarketExposureforBSoff else  MarketExposure;


Addlabel(ShowMElabel,"Market Expose: (+" + CurrentME + ") " + if CurrentME == 0  then "0%" else if CurrentME==1 then "33%" else if CurrentME==2 then "55%" else if CurrentME==3 then "75%" else if CurrentME == 4 then "90%" else "100%", CreateColor(CLrValue, CLgValue, bl));

#Addlabel(ShowMElabel,"Count: (" + (if RR then countMERR else if PTon then countMEPT  else if PowerTrendUnderPressure then countMEPTup else if PowerTrendUnderPressureWithoutFloor then countMEPTwf else if !bson then countMEforBSoff else countME) + ")", CreateColor(CLrValue, CLgValue, bl));


#AddchartBubble(B1 OR B2 OR oneB3 OR oneB4 OR B5 OR oneB6 OR B7 OR oneB8 OR oneB9 OR (BSOnB10 and B10) OR oneS1 OR oneS2 OR s2ml OR S3 OR S4 OR oneS5 OR oneS6 OR oneS7 OR S8 OR oneS9 OR S10 OR S11 OR oneS12 OR S13 OR oneS14 OR oneCB,high,if RR then MarketExposureRR else if PTon then MarketExposurePT  else if PowerTrendUnderPressure then MarketExposurePTup else if PowerTrendUnderPressureWithoutFloor then MarketExposurePTwf else if !bson then MarketExposureforBSoff else  MarketExposure,color.orange);



######################################################
######### BS labels added on the Power Trend #########
######################################################



######################################################
####### Distribution Day Fall Off   Bubble B10 #######
######################################################


AddChartBubble("time condition" = showB10 and B10 and BSOn, "price location" = high, text =  if ShowChartBubbleDescription then "B10 Distribution Day Fall Off (" + PercentageGreenBars + "%)" else "B10", GlobalColor("B10"));

######################################################
##### Failed Rally Attempt. Bubble S2 Minor Low  #####
######################################################

AddChartBubble("time condition" = showS2ml and s2ml and BSon, "price location" = high, text =  if ShowChartBubbleDescription then "S2 Undercut MinorLow Low or the rally (" + PercentageGreenBars + "%)" else "S2ml", GlobalColor("S2ml"));

##### BS Labels ####

Addlabel(ShowBuySwitchLabel,"Buy Switch: ",color.WHITE);
Addlabel(ShowBuySwitchLabel,if BSOn then "ON" else "OFF", if BSon then color.green else color.Red);



Addlabel(showPowerTrendLabel,if PowerTrendUnderPressure then "Power Trend under pressure: Max (+5) Floor (+1)" else if PowerTrendUnderPressureWithoutFloor then "Power Trend under pressure without floor: Max (+5) Floor (0)" else If PowerTrendOnNopressure then "Power Trend: Max (+7) Floor (+2)" else "Power Trend:",color.white);
Addlabel(showPowerTrendLabel,If PTon then "ON" else "OFF",if Pton then Color.GREEN else color.red);


######################################################################
#                      Active Label Description                      #
######################################################################
input ShowSignalsLabelDescription = yes;
#### Buy Signals Labels ####
Addlabel(ShowSignalsLabelDescription and B1,"B1: Buy on initial follow-through day.",GlobalColor("B1"));
Addlabel(ShowSignalsLabelDescription and B2,"B2: Buy on all additional follow-through days within 25-days from a rally day that closes above the low of the initial follow-through day.",GlobalColor("B2"));
Addlabel(ShowSignalsLabelDescription and oneB3,"B3:  Low Above Ema21. Once you have B3, you can't have another until is reset by an S5.",GlobalColor("B3"));
Addlabel(ShowSignalsLabelDescription and oneB4,"B4: Trending Above Ema21. Once you have B3, you can't have another until is reset by an S5.",GlobalColor("B4"));
Addlabel(ShowSignalsLabelDescription and B5,"B5: Living Above Ema21. Buy after a B3 signal has triggered, buy on the 10th and every 5th consecutive day after that in an upday.",GlobalColor("B5"));
Addlabel(ShowSignalsLabelDescription and oneB6,"B6: Low Above MA50. Buy on an upday with an uptrending ma50 (ma50>ma50[1]). Once you have B6, you can't have another until is reset by an S9.",GlobalColor("B6"));
Addlabel(ShowSignalsLabelDescription and B7,"B7: Accumulation Day. Buy Signal is additional FTD but after the 25 days of rally attempt and close in the upper 25% range and greater ema21.",GlobalColor("B7"));
Addlabel(ShowSignalsLabelDescription and oneB8,"B8: Higher High. Buy after closing above the last marked high (Last high before R1).",GlobalColor("B8"));
Addlabel(ShowSignalsLabelDescription and oneB9,"B9: Downside Reversal Buyback. After selling on a downside reversal (S11), buy back if the index closes above the intraday high of the downside reversal within two trading days.",GlobalColor("B9"));
Addlabel(ShowSignalsLabelDescription and B10,"B10: Distribution Day Fall Off. Buy on the day that the distribution count falls back to four (Full distibution minus two) as long as the close is above the ema21 and Buy Switch is on.",GlobalColor("B10"));


#### Sell Signals Labels ####
Addlabel(ShowSignalsLabelDescription and oneS1,"S1: Follow Through Day Undercut. Sell if index closes below the Low of the initial follow through day.",GlobalColor("S1"));
Addlabel(ShowSignalsLabelDescription and oneS2,"S2: Failed Rally Attempt. Sell if index undercuts the major low of rally attempt. Market exposure is reduced to zero and Buy Switch is turned off.",GlobalColor("S2"));
Addlabel(ShowSignalsLabelDescription and S2ml and BSon,"S2: Minor Low undercut of rally attempt. Market exposure is reduced by two. This does not turn off the Buy Switch. MinorLow defined as low that occurs when the buy switch is on (oneFTD).",GlobalColor("S2"));
Addlabel(ShowSignalsLabelDescription and S3,"S3: Full Distribution minus one. Sell after distribution count increases to five (Full distribution: six days). This rule is applied each new time that it occurs.",GlobalColor("S3"));
Addlabel(ShowSignalsLabelDescription and S4,"S4: Full Distribution. Sell after full distribution count (full distribution: six days). This rule is applied up to eight days (full distribution plus two)",GlobalColor("S4"));
Addlabel(ShowSignalsLabelDescription and oneS5,"S5: Break Below Ema21. Sell if index closes 0.2% or greater below the ema21. Once you have S5, you can't have another until is reset by an B3",GlobalColor("S5"));
Addlabel(ShowSignalsLabelDescription and oneS6,"S6: Overdue Break Below Ema21. Sell if index closes down 0.2% or greater below the ema21 after 30 days have been passed since last B3 without trigering an S5. Once you have S6, you can't have another until is reset by an B3.",GlobalColor("S6"));
Addlabel(ShowSignalsLabelDescription and oneS7,"S7: Trending Below ema21. Sell after S5 on then 5th consecutive day that then high is below the ema21 and DownDay Day. You can't have another S7 until is reset by an B3.",GlobalColor("S7"));
Addlabel(ShowSignalsLabelDescription and S8,"S8: Living Below Ema21. Sell after S5 on then 10th and every 5th consecutive day after that that then high is below the ema21. Down Day if not then wait for the next down day.",GlobalColor("S8"));
Addlabel(ShowSignalsLabelDescription and oneS9,"S9: Break Below MA50. Index closes below 50ma. Only triggers if a B6 triggered preiviously. Sell S9 is reset by B6.",GlobalColor("S9"));
Addlabel(ShowSignalsLabelDescription and S10,"S10: Bad Break. Sell if the close is down 2.25% or greater in the bottom 25% of range. Close below the ma50 or Intraday high below ema21.",GlobalColor("S10"));
Addlabel(ShowSignalsLabelDescription and S11,"S11: Downside Reversal Day. New Intra-day high within prior 13 days and close in bottom quartile of range and RedDay.",GlobalColor("S11"));
Addlabel(ShowSignalsLabelDescription and oneS12,"S12: Lower Low. Sell after closing below the last marked low  (9days lookback and 9day forward).",GlobalColor("S12"));
Addlabel(ShowSignalsLabelDescription and S13,"S13: Distribution Cluster. Distribution and Stalling days increases to four up to eight days within a rolling eight day period. The distribution cluster changes the Power-Trend (if it is in effect) to a Power-Trend Under Pressure.",GlobalColor("S13"));
Addlabel(ShowSignalsLabelDescription and oneS14,"S14: Break Below Higher High. Sell after closing below the last marked high that triggered a B8 (Higher High).",GlobalColor("S14"));

Addlabel(ShowSignalsLabelDescription and oneCB, "Circuit Breaker Index falls 10% of Highs since the follow-through. Buy Switch is turned off.",GlobalColor("Circuit Breaker"));
Hello. I would like to congratulate you for the excellent study and also ask if there is a possibility to configure it for intra day trading. Thank you very much and congratulations!!!
 

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
590 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