3 Bar Play - Pattern Scan?

C

ccrkk

New member
VIP
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.


 
Last edited by a moderator:
C

ccrkk

New member
VIP
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!
 
M

mashume

Active member
VIP
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
 
N

nrok2118

New member
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



Thanks in advance for any help!
 
Last edited by a moderator:
S

StudyTimeIsOver

New member
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().


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.



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:
N

nrok2118

New member
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:
L

Len20

Member
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)



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:
L

Len20

Member
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
 
A

asragov

New member
2019 Donor
VIP
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
 
L

Len20

Member
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
 
L

Len20

Member
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:
P

perig

New member
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?

 
P

perig

New member
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?

 
L

Len20

Member
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".
You're right. It's currently coded to detect the pattern only once the trigger bar has started. Now that you mention it, I will try to update it so potential pattern candidates are also detected during the "pause" bars. That makes sense. Should be pretty easy. I'll also look into that PYPL setup you mentioned. Hopefully there is a bug I can fix that will get us more results.
 
L

Len20

Member
Ok figured it out. The reason PYPL didn't indicate at opening is because the trigger bar low dipped below the pause bar low. Strictly speaking, that broke the rules of the official 3 bar play. It's supposed to stay above it at all times.

But since this is customizable, I just updated my code above to version 1.4, which has the new option for the trigger bar to have the same min low as the pause bars min low (as opposed to the pause bars actual low). It also now shows the "potential" indicator on pause bars that meet criteria for potential setup, not just on the trigger bar.
 
S

Stocksdfb

New member
Len, I copied your script and for some reason its not detecting the 3 bar play. Could you tell me what I am doing wrong?

This was from WFC today perfect set up.
http://imgbox.com/k0EyaUKm
 
Last edited:
L

Len20

Member
@Stocksdfb
The 3 bars in that image don't look quite like a 3 bar play. The first bar is too short relative to the middle one. Here's a screenshot from the Live Traders video, mentioned above, with a summary:
 
T

Thomas

Active member
Play with price, and time frame...concerns in quality of symbol, whether it has momentum and volume. https://tos.mx/CTjpAOk
These scans are open to interpretation,..but the pattern was produced on smaller time frames which I suspect "jared," trades.
 
Last edited:
P

perig

New member
@Len20 thanks for updating the script. I played with it quite a bit yesterday. I added a few things like a yellow highlight for igniting bars but my code is not clean so I'll share later if you are interested. Still some igniting bars being detected where they shouldn't but that's just refining the parameters possibly.

I also added price bubbles that show the entry price and stop price once there is a potential play so that I can see right away my entry and stop prices (because maths...). I also want to add a line or bubble for profit exit price that can be added in the inputs like 1.5R or 2R, etc.


If still interested in improving this script further, a few suggestions:
  • To add a layer of proper detection for the igniting bar (still seems to detect incorrect bars), maybe looking at the number of green bars withing the last x bars or the ratio of green to red bars, or if the wick is really big), something like that (would need to think about this more).
  • Add a function that would look back for the pivots and resistance on a larger timescale (if I understand right now it's only '14', so it would be 14 minutes on the minute chart, not quite right I think) before marking the igniting bar, for example lookback 5-10 days or something like that.
  • Alternatively, use some sort of pivot study to detect the last 3 support and resistance on daily time scale for example (not sure what would be the best).
  • Add price bubbles for entry, stop and target (as we could know what they should be from the price data and pre-defined strategies)
  • Add a label or bubble or some sort of text that would show the calculated lot size for a defined amount set as an input for max loss (i.e. $10, $100). That would be calculated with the entry-stop amount.
  • Add an option to flash the background some color when an igniting bar or a potential 3-bar play is detected live, that way it's super visible when watching live in the morning on a large watchlist.
  • Would be great to have an option to hide all the past 3-bar plays or igniting bars that did not form a 3-bar play as this is really for real time detection not to look back in the past (not really important but could help keep the plots clean).


anyway, some thoughts....
 

Similar threads

Top