Triple 3 Inside Bars Indicator and Scanner for ThinkorSwim

BenTen

Administrative
Staff member
Staff
VIP
Lifetime
Triple 3 Inside Bars Indicator and Scanner for ThinkorSwim
Inspired by the Double Inside Day and Narrow Range 4 (NR4) indicators. These patterns are quite popular among traders, especially those who mainly use price action to trade breakouts. Here is the triple inside bars study for ThinkorSwim.

When you have multiple inside bars, it means the stock the currently consolidating. It has 3 candles and each of them are smaller than the previous one.

You can use this indicator to plot triple inside bars on your chart and scan for stocks that are consolidating at the moment. Either trade between the consolidation zone or wait for a breakout/breakdown to happen.

mN1f7HS.png


zy1lFaM.png


thinkScript Code

Code:
# Triple Inside Day Bars
# Author: Kory Gill, [USER=212]@korygill[/USER]
# Common variables. Using variables reduces calls to TOS iData server.

declare upper;
declare once_per_bar;

def vHigh = high;
def vLow = low;
def nan = Double.NaN;

def range = vHigh - vLow;
def isInsideBar = if range < range[1] and vHigh < vHigh[1] and vLow > vLow[1]
                  then 1
                  else 0;

def triplebar = isInsideBar and isInsideBar[1] and isInsideBar[2];
plot signal = triplebar;
signal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
signal.SetLineWeight(2);
signal.AssignValueColor(Color.WHITE);
plot signal2 = triplebar;
signal2.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
signal2.SetLineWeight(2);
signal2.AssignValueColor(Color.WHITE);

Shareable Link

https://tos.mx/GPgKnV
Credit:
- @korygill
 

Attachments

  • mN1f7HS.png
    mN1f7HS.png
    285.3 KB · Views: 347
  • zy1lFaM.png
    zy1lFaM.png
    66.9 KB · Views: 317
Last edited by a moderator:
Hi Ben,
Not sure why my chart doesn't look like the above. All I see is a aqua line. Any suggestions?
Thanks!
 
Its been talked about alot and is supposed to be accurate and profitable. The only thing I dont understand is how do they find a 3 Bar Play ? Is there a scanner or a indicator ? All the stocks how do you find a 3 Bar Play LOL ?
 
There are standard TOS candlestick studies called ThreeInsideUp and ThreeInsideDown, perhaps you might want to explore those. I don't use those though but you can easily convert those to scans as needed

Additionally there are also standard TOS candlestick patterns called Three OutSideUp and ThreeOutsideDown
 
Last edited:
Being curious myself I just ran scans on all 4 patterns I mentioned above and obtained the following results on a scan of the S&P 500 (daily)

ThreeInsideUp - 6
ThreeInsideDown - 2
ThreeOutsideUp - 3
ThreeOutsideDown - 11
 
Hello everyone - new member here getting started in the world of trading. I very much enjoy backtesting and forward testing to assemble a foundation for strategy. I was wondering if someone would be able to help me out coding a scanner for something I wish to test. It's a momentum strategy that involves an inside bar pause and then carries in the direction of the trend. The idea is listed below:

First Bar:
  1. The "high" of the bar has to set a new 20 day high (or high of the previous 20 candles for intra day scans)
  2. Range of the bar has to be greater than the previous 14 day ATR range
Second Bar:
  1. The "high" of the bar has to be 'less than or equal to' to high of the previous bar.
  2. The "low" of the bar has to be in the upper 50% range of the first bar.

The idea can also be reversed where the first bar is trending downward and sets a new 20 day low and the second bar low is less than or equal to the first bar low and is within the lower 50% range of the first bar. Any help would be greatly appreciated and I look forward to interacting more on this forum! If you have any questions just let me know and thank you!

Below is an example of what I'm looking for. Ignore the third bar for scanning purposes as that's the bar I'll be attempting to get an entry on.


3bar.png
 
Last edited by a moderator:
I appreciate the response Ben! (great show by the way) That's definitely a great starting point for what I'm looking for. It seems to have everything I'm looking or except for the first bar setting a new 20 day high/low and having a range greater than the recent ATR. Do you have any suggestions as to how to add this? I spent time looking at the TOS tutorial but couldn't piece anything together. Again, appreciate the help!
 
I took a stab at your scanner, though it doesn't produce any results today. I can't say if that's the state of the day or something wrong with the scanner. Here's code to play with though:

Code:
def h = high;
def l = low;
def tr = ATR(14);
def hh = highest(high, 20);

def first_bar = if h[1] >= hh and h[1] - l[1] >= tr[-1] then 1 else double.nan;

def second_bar = if h <= h[1] and l >= l[1] + ((h[1] - l[1]) * 0.5) then 1 else double.nan;

plot signal = if first_bar == 1 and second_bar == 1 then 1 else double.nan;

Hope that sets you on the right track, or in the right direction at least.

Happy Trading,
Mashume
 
I don't know how to write a script, so I'm looking for some help.

Ideally, I'm simply looking to scan for (in an intraday scan)
1) An "igniting bar" 2X+ the size of the average sized bar over the last "X" amount of bars
2) An "inside bar" with the low greater than 50% of the igniting bar (for the bullish case)
3) These bars being newly formed
4) Bonus points for the volume on the igniting bar greater than 2X the average volume on "X" amount of bars

3bar.png


Thanks in advance for any help!
 
Last edited by a moderator:
Hey ya'll,

This is my first post. I want to start by saying I love the site. Love tinkering with ThinkScript and you guys are doing some amazing things.

I watched a video from Jared @ Live Traders on YouTube about 3 and 4 Bar Plays. The concept seems simple:
  • Igniter Bar (2 bars ago)- Long bar with a volume spike (I'm using a hard-coded 1.5x previous bar volume, but would like it to be an input value)
  • Pause Bar (1 bar ago) - (open or close) > Igniter bar open + 1/2 bodyweight(). I probably need to define some additional rules here but it's a start
  • Signal Bar - close > open and open + $0.10 > Igniter bar open + 1/2 bodyweight().
zNtAOgM.png


I really liked the strategy and created a study for it. It's a work in progress. I would like to refine it to make it a better indicator. I have some work to do including:
  • Include a rule for the Igniter bar body length
In my screenshot below, I have 2 indicators A and B. Both meet my rules but B is clearly more in-line with my expectations, although had I acted on A, I would be better off. Ultimately I would like to:
  • Get this to send me alerts when I get stronger 3 bar plays indicators like higher volume, maybe volume compared to average volume
  • Create a scanner for different timeframes
  • Make this semi-automatic where I receive an alert, review, and submit an order. I have a day job, so I can't spend every last minute glued to my screen but I have some flexibility.

r4FdhSU.png


Code:
def igniteBarLen = absvalue(close from 2 bars ago - open from 2 bars ago);
def pauseBarLen = absvalue(close from 1 bars ago - open from 1 bars ago);

def preIgniteBarQual = (open from 2 bars ago between
    open from 3 bars ago and close from 3 bars ago);
def pauseBarQual = open from 1 bars ago is greater than open from 2 bars ago + igniteBarLen;
def signalBarQual = close >= open and
    open + 0.10 is greater than open from 2 bars ago + (igniteBarLen * 0.5);

def preIgniteBarVolQual = volume from 3 bars ago >= 10000;
def igniteBarVolQual = volume from 2 bars ago is greater than volume from 3 bars ago * 1.5;
def signalBarVolQual = volume is greater than volume from 1 bars ago;

plot isThreeBarQualifier = igniteBarLen > (pauseBarLen * 2) and
    preIgniteBarQual and
    igniteBarVolQual and
    pauseBarQual and
    signalBarQual;

Scanner:

Code:
# IgniteBarLen > PauseBarLen
absvalue(close from 2 bars ago - open from 2 bars ago) > (absvalue(close from 1 bars ago - open from 1 bars ago) * 2)

and

# preIgniteBarQual
(open from 2 bars ago between
    open from 3 bars ago and close from 3 bars ago)

and

#igniteBarVolQual

volume from 3 bars ago >= 10000

and

#pauseBarQual

open from 1 bars ago is greater than open from 2 bars ago + absvalue(close from 2 bars ago - open from 2 bars ago)

and

#signalBarQual

close >= open and
    open + 0.10 is greater than open from 2 bars ago + (absValue(open[2] - close[2]) * 0.5)
 
Last edited:
Thanks Study!!! I used your code and modified it to my basic parameters and got something working!

This is for a basic bullish 3 bar play setup (only looking for an igniting bar and pause bar, ill manually enter on the third). Key points:
-Igniting bar is greater than 2X the average sized bar over last 10 candles
-Pause bar does not exceed the igniting bar high and it's low is within 50%
-Igniting bar's close is within 75% of the high (I got some hits with extreme top wicks, I don't want much selling pressure)

Now I need work work on volume, not sure how VolumeAvg works, but I'll be looking to compare the igniting bar volume to the average volume over the previous candles.

*EDIT

I learned a few things to tidy the code up, and also added volume! This now includes the volume of the igniting bar 2X greater than the previous bars (which i turned into a input)

Code:
input barsback = 10;

def igniteBarLen = absvalue(high[1] - low[1]);

def pauseBarLen = absvalue(high - low);

def pauseBarQual = high <= high[1]
    and
    igniteBarLen > (pauseBarLen*2) ;

def setupbarlen = (average(high[1],barsback) - average(low[1],barsback));

def ignitebarbull =  close[1] > open[1]
    and
    (close[1] - low[1]) > (high[1] - low[1]) *.75;

def IgniteBarVolQual = volume[1] > (average(volume[2], barsback))*2 ;


plot isThreeBarQualifier = 
    pauseBarQual
and
    ignitebarbull
and
    IgniteBarVolQual
and
    igniteBarLen > (setupbarlen*2);
 
Last edited:
Thanks for the idea. I wanted to try to add the 4 bar play too, and tried to follow the criteria that Live Traders mentions in that video. This is my result. It seems to match the pattern pretty well.

6/29/20 updated to v1.6 (includes bullish and bearish)

ocLsc5x.png


Code:
# 3&4 Bar Play v1.6 by Len20
# based on Live Traders video https://www.youtube.com/watch?v=xEjUd82NVVg

# v1.1 fixed small mistake
# v1.2 individual lookback customization, ignitor bar height now includes wicks, can now customize pause bars minimum low (was ignitor midpoint),
#      non-uptrend and no resistance requirements can be disabled, better uptrend detection, inputs more consistent, tweaks, fixes, more comments
# v1.3 trigger bar need not be green = more potential pattern matches
# v1.4 "Show Potential" now includes possible pattern setups during middle ("pause") bars, bug fix, trigger bar min low can now be same as pause bars min low ("Relaxed"), instead of pause bars actual low (official)
# v1.5 "Show potential" now includes ignitor bar. Can now color bars. Background color change on signals. Pattern will signal when forming live, disappears if it fails. Uptrend detection lookback now won't overlap pre-market, reg hours, or afterhours time zones. Recoded some logic. Other fixes, tweaks.
# v1.6 Now includes bullish & bearish indicators (selectable). Scanner has 3 modes. Bugfixes

# Magenta arrow = potential pattern forming
# White arrow   = confirmed (trigger bar high broke the high of ignitor and pause bars)

# Official Criteria:
# 1st bar ("ignitor") spike in price, range, maybe volume & 1st or 2nd bar of a move
# 1st bar not in middle of an uptrend
# 1st bar should be above resistance (closes above recent high)
# middle bar/bars ("pause") low is above midpoint of 1st bar
# middle bar/bars high is near 1st bar high
# middle bar/bars can be green or red
# trigger bar (potential) low is greater than low of middle bar/bars
# trigger bar (confirmed) high is above the high of the ingnitor and middle bars


# Inputs are asked for bullish mode, and will be reversed automatically for bearish
input Mode = {default "Both", "Bullish", "Bearish"};
input Show_Arrows_Potential = yes;      # ignitor and pause bars match pattern, trigger has potential
input Show_Arrows_Confirmed = yes;      # trigger bar successfully crossed previous bars high
input Color_Bars_Potential  = yes;      # ignitor and pause bars match pattern, trigger has potential
input Color_Bars_Confirmed  = yes;      # trigger bar successfully crossed previous bars high
input Flash_Background_Start = {default "Off", "Ignitor", "Pause Bars", "Trigger"};   # BG color change starting with selected bar match
input trigger_Bar_Low_Min = {default "Official", "Relaxed"};   # Trigger bar low min based on pause bars low (official), or pause bars minimum
input Show_During_Trend = yes;          # Strict pattern not supposed to be in middle of uptrend
input trend_Limit_Perc_Chg = 1.0;       # Only used if Show_During_Trend = no (Still signal if uptrend is below % entered)
input trend_LookBack = 10;              # Only used if Show_During_Trend = no
input prev_High_LookBack = 5;           # Ignitor bar must break previous period high
input avg_Vol_LookBack = 10;            # for qualifying ignitor bar
input avg_Range_LookBack = 10;          # for qualifying ignitor bar
input ignitor_Range_Spike_Perc = 200;   # Minimum ignitor range % relative to avg range (200% = 2x avg range)
input ignitor_Vol_Spike_Perc = 100;     # Minimum volume % relative to avg volume
input pause_Low_Min_Perc_Bull  = 45;    # Pause bars low min % of ignitor bar range (50% = middle of ingitor bar, lower is looser)
input pause_High_Min_Perc_Bull = 80;    # Pause bars high min % of ignitor bar range (90% recommended, lower is looser)
input pause_High_max_Perc_Bull = 110;   # Pause bars high max % of ignitor bar range (110% recommended, higher is looser)

Assert(prev_High_LookBack >= 1, "prev_High_Low_LookBack must be greater than zero");
Assert(trend_LookBack >= 1, "trend_LookBack must be greater than zero");
Assert(avg_Vol_LookBack >= 1, "avg_Vol_LookBack must be greater than zero");
Assert(avg_Range_LookBack >= 1, "avg_Range_LookBack must be greater than zero");
Assert(ignitor_Range_Spike_Perc >= 110, "ignitor_Range_Spike_Perc must be greater than 110%");
Assert(pause_Low_Min_Perc_Bull >= 0, "pause_Low_Min_Perc_Bull must be greater than zero");
Assert(pause_High_Min_Perc_Bull >= pause_Low_Min_Perc_Bull, "pause_High_Min_Perc_Bull must be greater than pause_Low_Min_Perc_Bull");
Assert(pause_High_max_Perc_Bull >= 100, "pause_High_max_Perc_Bull must be greater than or equal to 100%");

def indMode;
switch (Mode) {
case "Both":    indMode = 1;
case "Bullish": indMode = 2;
case "Bearish": indMode = 3; }

def showBull = indMode == 1 or indMode == 2;
def showBear = indMode == 1 or indMode == 3;

def triggerMode;
switch (trigger_Bar_Low_Min) {
case "Official": triggerMode = 1;
case "Relaxed":  triggerMode = 2; }

def flashBGMode;
switch (Flash_Background_Start) {
case "Off":        flashBGMode = 1;
case "Ignitor":    flashBGMode = 2;
case "Pause Bars": flashBGMode = 3;
case "Trigger":    flashBGMode = 4; }

def start = 0930;
def end = 1600;
def pre = secondsTillTime(start) > 0;
def regHrs = secondsfromtime(start) >= 0 and secondsTillTime(end) > 0;
def AH =  secondsfromtime(end) >= 0;
def tLookback = max(1, rounddown(trend_LookBack / 2, 0));
def tLookbackRecent = max(1, rounddown(trend_LookBack / 4, 0));
def trendLimMult = 1 + (trend_Limit_Perc_Chg * .01);
def h = high;
def l = low;
def o = open;
def c = close;
def v = volume;
def liveBar = isNaN(c[-1]);
def upBar = o < c;
def downBar = o > c;
def range = h - l;
def avgRange = ATR(avg_Range_LookBack);
def avgVol = Average(v, avg_Vol_LookBack);
def hh = Highest(h, prev_High_LookBack);
def ll = Lowest(l, prev_High_LookBack);

def movAvg = MovingAverage(AverageType.SIMPLE, c, tLookback);
def movAvgRecent = MovingAverage(AverageType.SIMPLE, c, tLookbackRecent);

def diffTimeZone1 = (pre and !pre[tLookback * 2]) or (regHrs and !regHrs[tLookback * 2]) or (AH and !AH[tLookback * 2]);
def diffTimeZone2 = (pre and !pre[tLookbackRecent * 2]) or (regHrs and !regHrs[tLookbackRecent * 2]) or (AH and !AH[tLookbackRecent * 2]);

def notUptrend1 = diffTimeZone1 or (movAvg <= movAvg[tLookback] * trendLimMult);
def notUptrend2 = diffTimeZone2 or (movAvgRecent <= movAvgRecent[tLookbackRecent] * trendLimMult);
def notUptrend = Show_During_Trend or (notUptrend1 and notUptrend2);

def notDntrend1 = diffTimeZone1 or (movAvg >= movAvg[tLookback] * trendLimMult);
def notDntrend2 = diffTimeZone2 or (movAvgRecent >= movAvgRecent[tLookbackRecent] * trendLimMult);
def notDntrend = Show_During_Trend or (notDntrend1 and notDntrend2);

def bullIgnitorMinHigh = max(hh[1] , h[1] + (range[1] * (ignitor_Range_Spike_Perc - 100) * .01 * .7));
def bull_ignitor_bar = upBar and notUptrend[1] and h >= bullIgnitorMinHigh and range > range[1] and range >= avgRange[1] * ignitor_Range_Spike_Perc * .01 and v >= avgVol[1] * ignitor_Vol_Spike_Perc *.01;

def bearIgnitorLowMax = min(ll[1] , l[1] - (range[1] * (ignitor_Range_Spike_Perc - 100) * .01 * .7));
def bear_ignitor_bar = downBar and notDntrend[1] and l <= bearIgnitorLowMax and range > range[1] and range >= avgRange[1] * ignitor_Range_Spike_Perc * .01 and v >= avgVol[1] * ignitor_Vol_Spike_Perc *.01;


def bullPauseLowMin = l + (range * pause_Low_Min_Perc_Bull * .01);
def bullPauseHighMin = l + (range * pause_High_Min_Perc_Bull * .01);
def bullPauseHighMax = l + (range * pause_High_max_Perc_Bull * .01);
def bull_pause_bar1 = bull_ignitor_bar[1] and l >= bullPauseLowMin[1] and h <= bullPauseHighMax[1] and h >= bullPauseHighMin[1];
def bull_pause_bar2 =  bull_pause_bar1[1] and l >= bullPauseLowMin[2] and h <= bullPauseHighMax[2] and h >= bullPauseHighMin[2];

def bearPauseHighMax = h - (range * pause_Low_Min_Perc_Bull * .01);
def bearPauseLowMax = h - (range * pause_High_Min_Perc_Bull * .01);
def bearPauseLowMin = h - (range * pause_High_max_Perc_Bull * .01);
def bear_pause_bar1 = bear_ignitor_bar[1] and h <= bearPauseHighMax[1] and l >= bearPauseLowMin[1] and l <= bearPauseLowMax[1];
def bear_pause_bar2 =  bear_pause_bar1[1] and h <= bearPauseHighMax[2] and l >= bearPauseLowMin[2] and l <= bearPauseLowMax[2];


def bull_potential_3BP = bull_pause_bar1[1] and range > 0 and if triggerMode == 1 then l >= l[1] else l >= bullPauseLowMin[2];
def bull_confirmed_3BP = bull_potential_3BP and h > Max(h[1], h[2]);
def bull_potential_4BP = bull_pause_bar2[1] and range > 0 and if triggerMode == 1 then l >= min(l[1], l[2]) else l >= bullPauseLowMin[3];
def bull_confirmed_4BP = bull_potential_4BP and h > Max(Max(h[1], h[2]), h[3]);

def bear_potential_3BP = bear_pause_bar1[1] and range > 0 and if triggerMode == 1 then h <= h[1] else h <= bearPauseHighMax[2];
def bear_confirmed_3BP = bear_potential_3BP and l < min(l[1], l[2]);
def bear_potential_4BP = bear_pause_bar2[1] and range > 0 and if triggerMode == 1 then h <= max(h[1], h[2]) else h <= bearPauseHighMax[3];
def bear_confirmed_4BP = bear_potential_4BP and l < min(min(l[1], l[2]), l[3]);


# The 3rd bar can be both pause_bar2 and 3BP trigger bar at same time
def bull_show_confirmed = bull_confirmed_3BP or bull_confirmed_4BP;
def bull_show_potential = (bull_potential_3BP or bull_potential_4BP) and liveBar;
def bull_show_pause2  = bull_pause_bar2 and (if liveBar then 1 else (bull_potential_4BP[-1] and liveBar[-1]) or bull_confirmed_4BP[-1]);
def bull_show_pause1  = bull_pause_bar1 and (if liveBar then 1 else bull_show_pause2[-1] or (bull_potential_3BP[-1] and liveBar[-1]) or bull_confirmed_3BP[-1]);
def bull_show_ignitor = bull_ignitor_bar and (if liveBar then 1 else bull_show_pause1[-1]);

def bear_show_confirmed = bear_confirmed_3BP or bear_confirmed_4BP;
def bear_show_potential = (bear_potential_3BP or bear_potential_4BP) and liveBar;
def bear_show_pause2  = bear_pause_bar2 and (if liveBar then 1 else (bear_potential_4BP[-1] and liveBar[-1]) or bear_confirmed_4BP[-1]);
def bear_show_pause1  = bear_pause_bar1 and (if liveBar then 1 else bear_show_pause2[-1] or (bear_potential_3BP[-1] and liveBar[-1]) or bear_confirmed_3BP[-1]);
def bear_show_ignitor = bear_ignitor_bar and (if liveBar then 1 else bear_show_pause1[-1]);


plot confirmed_arrows_bull = showBull and Show_Arrows_Confirmed and bull_show_confirmed;
plot potential_arrows_bull = showBull and Show_Arrows_Potential and (bull_show_ignitor or bull_show_pause1 or bull_show_pause2 or bull_show_potential);

plot confirmed_arrows_bear = showBear and Show_Arrows_Confirmed and bear_show_confirmed;
plot potential_arrows_bear = showBear and Show_Arrows_Potential and (bear_show_ignitor or bear_show_pause1 or bear_show_pause2 or bear_show_potential);

def paintPotentialBull = showBull and Color_Bars_Potential and (bull_show_ignitor or bull_show_pause1 or bull_show_pause2 or bull_show_potential);
def paintConfirmedBull = showBull and Color_Bars_Confirmed and bull_show_confirmed;

def paintPotentialBear = showBear and Color_Bars_Potential and (bear_show_ignitor or bear_show_pause1 or bear_show_pause2 or bear_show_potential);
def paintConfirmedBear = showBear and Color_Bars_Confirmed and bear_show_confirmed;

def flashBGbull = showBull and
                if flashBGMode == 2 then (bull_show_ignitor or bull_show_pause1 or bull_show_pause2 or bull_show_potential or bull_show_confirmed) else
                if flashBGMode == 3 then (bull_show_pause1 or bull_show_pause2 or bull_show_potential or bull_show_confirmed) else
                if flashBGMode == 4 then (bull_show_potential or bull_show_confirmed) else 0;

def flashBGbear = showBear and
                if flashBGMode == 2 then (bear_show_ignitor or bear_show_pause1 or bear_show_pause2 or bear_show_potential or bear_show_confirmed) else
                if flashBGMode == 3 then (bear_show_pause1 or bear_show_pause2 or bear_show_potential or bear_show_confirmed) else
                if flashBGMode == 4 then (bear_show_potential or bear_show_confirmed) else 0;


#####  COLORS AND STYLE  #####
confirmed_arrows_bull.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
confirmed_arrows_bull.SetDefaultColor(Color.WHITE);
confirmed_arrows_bull.SetLineWeight(3);

potential_arrows_bull.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
potential_arrows_bull.SetDefaultColor(Color.MAGENTA);
potential_arrows_bull.SetLineWeight(3);

confirmed_arrows_bear.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
confirmed_arrows_bear.SetDefaultColor(Color.WHITE);
confirmed_arrows_bear.SetLineWeight(3);

potential_arrows_bear.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
potential_arrows_bear.SetDefaultColor(Color.MAGENTA);
potential_arrows_bear.SetLineWeight(3);

assignPriceColor(if paintPotentialBull then if c < o  then Color.MAGENTA else createColor(200,255,20) else color.CURRENT);
assignPriceColor(if paintConfirmedBull then if c < o  then Color.MAGENTA else createColor(200,255,20) else color.CURRENT);
assignPriceColor(if paintPotentialBear then if c <= o then Color.MAGENTA else createColor(200,255,20) else color.CURRENT);
assignPriceColor(if paintConfirmedBear then if c <= o then Color.MAGENTA else createColor(200,255,20) else color.CURRENT);

assignBackgroundColor(if flashBGbull then color.LIGHT_GRAY else color.current);
assignBackgroundColor(if flashBGbear then color.LIGHT_GRAY else color.current);

and here is the BULLISH scan
Code:
# Bullish 3&4 Bar Play SCAN v1.6 by Len20
# based on Live Traders video https://www.youtube.com/watch?v=xEjUd82NVVg

# v1.1 fixed small mistake
# v1.2 individual lookback customization, ignitor bar height now includes wicks, can now customize pause bars minimum low (was ignitor midpoint),
#      non-uptrend and no resistance requirements can be disabled, better uptrend detection, inputs more consistent, tweaks, fixes, more comments
# v1.3 trigger bar need not be green = more potential pattern matches
# v1.4 "Show Potential" now includes possible pattern setups during middle ("pause") bars, bug fix, trigger bar min low can now be same as pause bars min low ("Relaxed"), instead of pause bars actual low (official)
# v1.5 "Show potential" now includes ignitor bar. Can now color bars. Background color change on signals. Pattern will signal when forming live, disappears if it fails. Uptrend detection lookback now won't overlap pre-market, reg hours, or afterhours time zones. Recoded some logic. Other fixes, tweaks.
# v1.6 Now includes bullish & bearish indicators (selectable). Scanner has 3 modes. Bugfixes


# CHOOSE SCAN MODE #
# ( 1 = Ignitor Bar , 2 = Pause Bars , 3 = Trigger Bar )
input scanMode = 3;


input Show_Arrows_Potential = yes;         # ignitor and pause bars match pattern, trigger has potential
input Show_Arrows_Confirmed = yes;         # trigger bar successfully crossed previous bars high
input Color_Bars_Potential  = yes;         # ignitor and pause bars match pattern, trigger has potential
input Color_Bars_Confirmed  = yes;         # trigger bar successfully crossed previous bars high
input Flash_Background_Start = {default "Off", "Ignitor", "Pause Bars", "Trigger"};   # BG color change starting with selected bar match
input trigger_Bar_Low_Min = {default "Official", "Relaxed"};   # Trigger bar low min based on pause bars low (official), or pause bars minimum
input Show_During_Uptrend = yes;           # Strict pattern not supposed to be in middle of uptrend
input uptrend_Limit_Perc_Inc = 1.0;        # Only used if Show_During_Uptrend = no (Still signal if uptrend is below % entered)
input uptrend_LookBack = 10;               # Only used if Show_During_Uptrend = no
input prev_High_LookBack = 5;              # Ignitor bar must break previous period high
input avg_Vol_LookBack = 10;               # for qualifying ignitor bar
input avg_Range_LookBack = 10;             # for qualifying ignitor bar
input ignitor_Range_Spike_Perc = 200;      # Minimum ignitor range % relative to avg range (200% = 2x avg range)
input ignitor_Vol_Spike_Perc = 100;        # Minimum volume % relative to avg volume
input pause_Low_Min_Perc  = 45;            # Pause bars low min % of ignitor bar range (50% = middle of ingitor bar, lower is looser)
input pause_High_Min_Perc = 80;            # Pause bars high min % of ignitor bar range (90% recommended, lower is looser)
input pause_High_max_Perc = 110;           # Pause bars high max % of ignitor bar range (110% recommended, higher is looser)

Assert(prev_High_LookBack >= 1, "prev_High_LookBack must be greater than zero");
Assert(uptrend_LookBack >= 1, "uptrend_LookBack must be greater than zero");
Assert(avg_Vol_LookBack >= 1, "avg_Vol_LookBack must be greater than zero");
Assert(avg_Range_LookBack >= 1, "avg_Range_LookBack must be greater than zero");
Assert(ignitor_Range_Spike_Perc >= 110, "ignitor_Range_Spike_Perc must be greater than 110%");
Assert(pause_Low_Min_Perc >= 0, "pause_Low_Min_Perc must be greater than zero");
Assert(pause_High_Min_Perc >= pause_Low_Min_Perc, "pause_High_Min_Perc must be greater than pause_Low_Min_Perc");
Assert(pause_High_max_Perc >= 100, "pause_High_max_Perc must be greater than or equal to 100%");

def triggerMode;
switch (trigger_Bar_Low_Min) {
case "Official": triggerMode = 1;
case "Relaxed": triggerMode = 2; }

def flashBGMode;
switch (Flash_Background_Start) {
case "Off": flashBGMode = 1;
case "Ignitor": flashBGMode = 2;
case "Pause Bars": flashBGMode = 3;
case "Trigger": flashBGMode = 4; }

def start = 0930;
def end = 1600;
def pre = secondsTillTime(start) > 0;
def regHrs = secondsfromtime(start) >= 0 and secondsTillTime(end) > 0;
def AH =  secondsfromtime(end) >= 0;
def utLookback = max(1, rounddown(uptrend_LookBack / 2, 0));
def utLookbackRecent = max(1, rounddown(uptrend_LookBack / 4, 0));
def uptrendLimMult = 1 + (uptrend_Limit_Perc_Inc * .01);
def h = high;
def l = low;
def o = open;
def c = close;
def v = volume;
#def liveBar = isNaN(c[-1]);
def liveBar = 1;
def upBar = o < c;
def range = h - l;
def avgRange = ATR(avg_Range_LookBack);
def avgVol = Average(v, avg_Vol_LookBack);
def hh = Highest(h, prev_High_LookBack);

def movAvg = MovingAverage(AverageType.SIMPLE, c, utLookback);
def movAvgRecent = MovingAverage(AverageType.SIMPLE, c, utLookbackRecent);

def diffTimeZone1 = (pre and !pre[utLookback * 2]) or (regHrs and !regHrs[utLookback * 2]) or (AH and !AH[utLookback * 2]);
def diffTimeZone2 = (pre and !pre[utLookbackRecent * 2]) or (regHrs and !regHrs[utLookbackRecent * 2]) or (AH and !AH[utLookbackRecent * 2]);

def notUptrend1 = diffTimeZone1 or (movAvg <= movAvg[utLookback] * uptrendLimMult);
def notUptrend2 = diffTimeZone2 or (movAvgRecent <= movAvgRecent[utLookbackRecent] * uptrendLimMult);
def notUptrend = Show_During_Uptrend or (notUptrend1 and notUptrend2);

def ignitorMinHigh = max(hh[1] , h[1] + (range[1] * (ignitor_Range_Spike_Perc - 100) * .01 * .7));
def ignitor_bar = upBar and notUptrend[1] and h >= ignitorMinHigh and range > range[1] and range >= avgRange[1] * ignitor_Range_Spike_Perc * .01 and v >= avgVol[1] * ignitor_Vol_Spike_Perc *.01;

def pauseLowMin = l + (range * pause_Low_Min_Perc * .01);
def pauseHighMin = l + (range * pause_High_Min_Perc * .01);
def pauseHighMax = l + (range * pause_High_max_Perc * .01);
def pause_bar1 = ignitor_bar[1] and l >= pauseLowMin[1] and h <= pauseHighMax[1] and h >= pauseHighMin[1];
def pause_bar2 =  pause_bar1[1] and l >= pauseLowMin[2] and h <= pauseHighMax[2] and h >= pauseHighMin[2];
 
def potential_3BP = pause_bar1[1] and range > 0 and if triggerMode == 1 then l >= l[1] else l >= pauseLowMin[2];
def confirmed_3BP = potential_3BP and h > Max(h[1], h[2]);
def potential_4BP = pause_bar2[1] and range > 0 and if triggerMode == 1 then l >= min(l[1], l[2]) else l >= pauseLowMin[3];
def confirmed_4BP = potential_4BP and h > Max(Max(h[1], h[2]), h[3]);

# The 3rd bar can be both pause_bar2 and 3BP trigger bar at same time
def show_confirmed = confirmed_3BP or confirmed_4BP;
def show_potential = (potential_3BP or potential_4BP) and liveBar;
def show_pause2  = pause_bar2 and (if liveBar then 1 else (potential_4BP[-1] and liveBar[-1]) or confirmed_4BP[-1]);
def show_pause1  = pause_bar1 and (if liveBar then 1 else show_pause2[-1] or (potential_3BP[-1] and liveBar[-1]) or confirmed_3BP[-1]);
def show_ignitor = ignitor_bar and (if liveBar then 1 else show_pause1[-1]);

def potentials = if scanMode == 1 then (show_ignitor or show_pause1 or show_pause2 or show_potential) else
                 if scanMode == 2 then (show_pause1 or show_pause2 or show_potential) else
                 if scanMode == 3 then show_potential else 0;

plot scan = potentials or (show_confirmed within 3 bars);

and lastly here is the BEARISH scan
Code:
# Bearish 3&4 Bar Play SCAN v1.6 by Len20
# based on Live Traders video https://www.youtube.com/watch?v=xEjUd82NVVg

# v1.1 fixed small mistake
# v1.2 individual lookback customization, ignitor bar height now includes wicks, can now customize pause bars minimum low (was ignitor midpoint),
#      non-uptrend and no resistance requirements can be disabled, better uptrend detection, inputs more consistent, tweaks, fixes, more comments
# v1.3 trigger bar need not be green = more potential pattern matches
# v1.4 "Show Potential" now includes possible pattern setups during middle ("pause") bars, bug fix, trigger bar min low can now be same as pause bars min low ("Relaxed"), instead of pause bars actual low (official)
# v1.5 "Show potential" now includes ignitor bar. Can now color bars. Background color change on signals. Pattern will signal when forming live, disappears if it fails. Uptrend detection lookback now won't overlap pre-market, reg hours, or afterhours time zones. Recoded some logic. Other fixes, tweaks.
# v1.6 Now includes bullish & bearish indicators (selectable). Scanner has 3 modes. Bugfixes


# CHOOSE SCAN MODE #
# ( 1 = Ignitor Bar , 2 = Pause Bars , 3 = Trigger Bar )
input scanMode = 3;


input Show_Arrows_Potential = yes;         # ignitor and pause bars match pattern, trigger has potential
input Show_Arrows_Confirmed = yes;         # trigger bar successfully crossed below pause bars low
input Color_Bars_Potential  = yes;         # ignitor and pause bars match pattern, trigger has potential
input Color_Bars_Confirmed  = yes;         # trigger bar successfully crossed below pause bars low
input Flash_Background_Start = {default "Off", "Ignitor", "Pause Bars", "Trigger"};   # BG color change starting with selected bar match
input trigger_Bar_High_Max = {default "Official", "Relaxed"};   # Trigger bar high max based on pause bars high (official), or pause bars max
input Show_During_Dntrend = yes;           # Strict pattern not supposed to be in middle of downtrend
input dntrend_Limit_Perc_Dec = 1.0;        # Only used if Show_During_Dntrend = no (Still signal if downtrend is below % entered)
input dntrend_LookBack = 10;               # Only used if Show_During_Dntrend = no
input prev_Low_LookBack = 5;               # Ignitor bar must break previous period low
input avg_Vol_LookBack = 10;               # for qualifying ignitor bar
input avg_Range_LookBack = 10;             # for qualifying ignitor bar
input ignitor_Range_Spike_Perc = 200;      # Minimum ignitor range % relative to avg range (200% = 2x avg range)
input ignitor_Vol_Spike_Perc = 100;        # Minimum volume % relative to avg volume
input pause_High_Min_Perc_Down  = 45;      # Pause bars high min % of ignitor bar range (50% = middle of ingitor bar, lower is looser)
input pause_Low_Min_Perc_Down = 80;        # Pause bars low min % of ignitor bar range (90% recommended, lower is looser)
input pause_Low_Max_Perc_Down = 110;       # Pause bars low max % of ignitor bar range (110% recommended, higher is looser)

Assert(prev_Low_LookBack >= 1, "prev_Low_LookBack must be greater than zero");
Assert(dntrend_LookBack >= 1, "dntrend_LookBack must be greater than zero");
Assert(avg_Vol_LookBack >= 1, "avg_Vol_LookBack must be greater than zero");
Assert(avg_Range_LookBack >= 1, "avg_Range_LookBack must be greater than zero");
Assert(ignitor_Range_Spike_Perc >= 110, "ignitor_Range_Spike_Perc must be greater than 110%");
Assert(pause_High_Min_Perc_Down >= 0, "pause_High_Min_Perc_Down must be greater than zero");
Assert(pause_Low_Min_Perc_Down >= pause_High_Min_Perc_Down, "pause_Low_Min_Perc_Down must be greater than pause_High_Min_Perc_Down");
Assert(pause_Low_Max_Perc_Down >= 100, "pause_Low_Max_Perc_Down must be greater than or equal to 100%");

def triggerMode;
switch (trigger_Bar_High_Max) {
case "Official": triggerMode = 1;
case "Relaxed": triggerMode = 2; }

def flashBGMode;
switch (Flash_Background_Start) {
case "Off": flashBGMode = 1;
case "Ignitor": flashBGMode = 2;
case "Pause Bars": flashBGMode = 3;
case "Trigger": flashBGMode = 4; }

def start = 0930;
def end = 1600;
def pre = secondsTillTime(start) > 0;
def regHrs = secondsfromtime(start) >= 0 and secondsTillTime(end) > 0;
def AH =  secondsfromtime(end) >= 0;
def dtLookback = max(1, rounddown(dntrend_LookBack / 2, 0));
def dtLookbackRecent = max(1, rounddown(dntrend_LookBack / 4, 0));
def dntrendLimMult = 1 - (dntrend_Limit_Perc_Dec * .01);
def h = high;
def l = low;
def o = open;
def c = close;
def v = volume;
#def liveBar = isNaN(c[-1]);
def liveBar = 1;
def downBar = o > c;
def range = h - l;
def avgRange = ATR(avg_Range_LookBack);
def avgVol = Average(v, avg_Vol_LookBack);
def ll = Lowest(l, prev_Low_LookBack);

def movAvg = MovingAverage(AverageType.SIMPLE, c, dtLookback);
def movAvgRecent = MovingAverage(AverageType.SIMPLE, c, dtLookbackRecent);

def diffTimeZone1 = (pre and !pre[dtLookback * 2]) or (regHrs and !regHrs[dtLookback * 2]) or (AH and !AH[dtLookback * 2]);
def diffTimeZone2 = (pre and !pre[dtLookbackRecent * 2]) or (regHrs and !regHrs[dtLookbackRecent * 2]) or (AH and !AH[dtLookbackRecent * 2]);

def notDntrend1 = diffTimeZone1 or (movAvg >= movAvg[dtLookback] * dntrendLimMult);
def notDntrend2 = diffTimeZone2 or (movAvgRecent >= movAvgRecent[dtLookbackRecent] * dntrendLimMult);
def notDntrend = Show_During_Dntrend or (notDntrend1 and notDntrend2);

def ignitorLowMax = min(ll[1] , l[1] - (range[1] * (ignitor_Range_Spike_Perc - 100) * .01 * .7));
def ignitor_bar = downBar and notDntrend[1] and l <= ignitorLowMax and range > range[1] and range >= avgRange[1] * ignitor_Range_Spike_Perc * .01 and v >= avgVol[1] * ignitor_Vol_Spike_Perc *.01;

def pauseHighMax = h - (range * pause_High_Min_Perc_Down * .01);
def pauseLowMax = h - (range * pause_Low_Min_Perc_Down * .01);
def pauseLowMin = h - (range * pause_Low_Max_Perc_Down * .01);
def pause_bar1 = ignitor_bar[1] and h <= pauseHighMax[1] and l >= pauseLowMin[1] and l <= pauseLowMax[1];
def pause_bar2 =  pause_bar1[1] and h <= pauseHighMax[2] and l >= pauseLowMin[2] and l <= pauseLowMax[2];

def potential_3BP = pause_bar1[1] and range > 0 and if triggerMode == 1 then h <= h[1] else h <= pauseHighMax[2];
def confirmed_3BP = potential_3BP and l < min(l[1], l[2]);
def potential_4BP = pause_bar2[1] and range > 0 and if triggerMode == 1 then h <= max(h[1], h[2]) else h <= pauseHighMax[3];
def confirmed_4BP = potential_4BP and l < min(min(l[1], l[2]), l[3]);

# The 3rd bar can be both pause_bar2 and 3BP trigger bar at same time

def show_confirmed = confirmed_3BP or confirmed_4BP;
def show_potential = (potential_3BP or potential_4BP) and liveBar;
def show_pause2  = pause_bar2 and (if liveBar then 1 else (potential_4BP[-1] and liveBar[-1]) or confirmed_4BP[-1]);
def show_pause1  = pause_bar1 and (if liveBar then 1 else show_pause2[-1] or (potential_3BP[-1] and liveBar[-1]) or confirmed_3BP[-1]);
def show_ignitor = ignitor_bar and (if liveBar then 1 else show_pause1[-1]);

def potentials = if scanMode == 1 then (show_ignitor or show_pause1 or show_pause2 or show_potential) else
                 if scanMode == 2 then (show_pause1 or show_pause2 or show_potential) else
                 if scanMode == 3 then show_potential else 0;

plot scan = potentials or (show_confirmed within 3 bars);
 
Last edited:
Just updated the 3 and 4 Bar Play above to version 1.2
Changes:
individual lookback customization, ignitor bar height now includes wicks, can now customize pause bars minimum low (was ignitor midpoint),
non-uptrend and no resistance requirements can be disabled, better uptrend detection, inputs more consistent, tweaks, fixes, more comments
 
Thanks - I played around with it a bit and was not able to get many results (reducing range spike, volume spike, etc.) - would be curious to know settings and time frame(s) that return more results, if it doesn't end up with too many false signals
 
I'm not getting many results either. The pattern is supposed to work all time frames, though I've only tested on 5 min and lower. I thought it would be a more common pattern, though I think the main limiting factor is the criteria that it occurs above a recent high, and that it shouldn't occur in the middle of an uptrend. In testing, I got more results by setting Show_Only_Above_Prev_High and Hide_During_Uptrend to No. But then that breaks the pattern rules.

Some input adjustments that may increase results slightly but loosen the pattern criteria is to lower these a bit:
ignitor_Range_Spike_Perc
ignitor_Vol_Spike_Perc
pause_Low_Min_Perc
pause_High_Min_Perc


and/or increase:
pause_High_max_Perc
 
Updated the 3 Bar Play and 4 Bar Play to V1.3
Changed: trigger bar need not be green or stay green - more potential pattern matches & avoids repainting. Reasoning is that the trigger bar need not necessarily make its move immediately at start of bar, so it could be red before moving up. Minimum low is preserved though. Also prevents repainting if it goes from green back to red.

Edit: It actually does still repaint if the trigger bar low drops below the low of the pause bars
 
Last edited:
EDIT: I was able to show the potential by reducing the "pause high min perc" input


Thanks a lot for making this. I set it up today and tried the study. I was not getting any signals so I modified the parameters to try to get any signal, even the one questionable by changing volume, etc. However it seems that it is not catching this new daily play today on BYND (4 bar play). It is showing one in the past though.

Is it because that trigger bar is not yet visible? It should still show though. I should be able to see that it is a possible 4-bar play. I have the "potential" set to "Yes".

Thoughts?

quTRfLZ.png
 
I'm still having issues with the parameters on this. It looks very promising but it seems miss some quite obvious ones. Here is one on PYPL yesterday 6/10/20.

I loosened the parameters to detect more but it seems to detect everything except the actual 3-bar play...

@Len20, ideas?

dyRuE87.png
 

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

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
296 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