• LIMITED TIME OFFER: use coupon code PRIMEDAY at checkout to save $50 off VIP membership (down to $149 / year). Valid through Tuesday 6/22.

Broadening Formation Pattern for ThinkorSwim


New member
Hello All, I've spent several nights trying to re-create this without any success: http://www.patternsmart.com/cart/index.php?route=product/product&product_id=430&search=broadening

Perhaps I'm missing something obvious. I've tried applying techniques from mobius' scalper alerts and tried re-tooling the built-in zig-zag code. Any offers to help would be appreciated. I would've just paid for it if the site looked legit and at least had an SSL cert. If anyone knows if this is available for purchase from a reputable site, I would happy to know that as well. Thanks!

BTW, I've also went over this forum post multiple times (https://usethinkscript.com/threads/second-highest-lowest-high-low-in-same-range.2064/) as it is trying to do something fundamentally similar: find the two highest highs and two lowest lows in a range of bars.
Last edited by a moderator:


New member
20:17 Vimes: Was browsing some old public thinkscript forums and came across a very old snippet from Robert Payne that looked interesting, I modified it to plot peak to peak and valley to valley moves together. Likely not practical yet for trading but perhaps an interesting snippet (at least for me:))

#Snippet only
#idea taken from a public robert payne snippet on valley lines
#extended to plot valley to valley and peak to peak lines together
#and made lookback adjustable
#perhaps useful for broadening formation or other potential extensions of price action
input len = 3;
input showBubble = no;

#valley section
def Valley = low < Lowest(low[1], len) and low < Lowest(low[-len], len);
plot ArrowUP = Valley;
plot line_v = if ArrowUP then low else Double.NaN;
def run_v = if Valley then 1 else run_v[1] + 1;
def rise_v = if Valley then low - GetValue(low, run_v[1]) else Double.NaN;
def slope_v = rise_v / run_v[1];
AddChartBubble(showBubble and Valley, low, slope_v, if slope_v > 0 then Color.LIGHT_GREEN else Color.PINK);

def slopeV = if Valley then slope_v else slopeV[1];
def lineV = if Valley then low else lineV[1] + slopeV;
plot LineExtension_V = lineV;

#peak section
def Peak = high > highest(high[1], len) and high > highest(high[-len], len);
plot ArrowDN = Peak;

plot line_p = if ArrowDN then high else Double.NaN;
def run_p = if Peak then 1 else run_p[1] + 1;
def rise_p = if Peak then high - GetValue(high, run_p[1]) else Double.NaN;
def slope_p = rise_p / run_p[1];
AddChartBubble(showBubble and Peak, high, slope_p, if slope_p > 0 then Color.LIGHT_GREEN else Color.PINK);
def slopeX_p = if Peak then slope_p else slopeX_p[1];
def lineX_p = if Peak then high else lineX_p[1] + slopeX_p;
plot LineExtension_p = lineX_p;

If I share a chart, will you guys look at it? I use triangles/broadening formations to find support and resistance and inside/outside bars to look for signals. Todays 3 min chart on the /ES will show you how using just that would have had you nailing all the big moves today.


New member
@MrDrB I am new to thinkscript ( few weeks of experience but programmer by trade). I am also new to this forum. I made an initial attempt at this. Are you still interested in working on this? I can send the script over. I would appreciate someone else looking over and verifying my logic. I am not done with the alerts part. I am not familiar with the best practices on think scripts.

Last edited:


New member
@RajB sure, i also have something similar completed but without logic that deals with some scenarios like when a prior lower-hi/higher-low can't be found, etc. If you've dealt with some of those cases, let me know, would love to work together!


New member
  • Nice, looking forward to working together
  • I think that scenario you are referring to is done. I took the approach of listing valleys/peaks and then using the last 2 valleys/peaks if present
  • Alerts are partially coded and I may get to it this weekend, they are partially coded and can't be relied on
  • toggle the debug flag input value for params etc. will give you my step by step decision-making process

Rich (BB code):
#Broadening Pattern
#Multiple Sources
# Fun with thinkscript: https://researchtrade.com/forum/read.php?7,2258,page=31
# UseThinkScript: https://usethinkscript.com/threads/create-column-based-on-peak-and-valley-indicator.901/
# https://funwiththinkscript.com/count-the-number-of-bars-between-successive-highs/
# Idea source: https://www.patternsmart.com/cart/index.php?route=product/product&product_id=430&search=broadening

# 20201019 Raj B, initial code upto calculating peaks/valleys, getting barNumbers , checking if broadening pattern conditions exist
# 20201020 Raj B. Added ability for separate left/right threshold to allow independent values
# 20201020 Raj B. Added initial column/watchlist alerts, needs testing
# TODO complete alert part

# Inputs

input LeftBarsThreshold = 5;
input RightBarsThreshold = 5;
input isAlertSetup = no;
input debug = yes;

# Setup
def bn = BarNumber();

# Calculate values for peaks
def peak = high > Highest(high[1], LeftBarsThreshold) and high >= Highest(high[-RightBarsThreshold], RightBarsThreshold);
plot peakBoolean = peak;

def peakValue = if peak then high else peakValue[1];
def peakBar = if peak then BarNumber() else Double.NaN;

# Calculate barNumbers for previous 2 peaks

def lastPeakBarNumber = HighestAll(if peak then bn else 0);
def prevPeakBarNumber = HighestAll(if peak and bn < lastPeakBarNumber then bn else 0);

# Get values for previous 2 peaks
def lastPeakValue = GetValue(high, bn - HighestAll(peakBar));
def prevPeakValue = GetValue(peakValue[1], bn - HighestAll(peakBar));

#  Calculate values for valleys/low points
def valley = low < Lowest(low[1], LeftBarsThreshold) and low <= Lowest(low[-RightBarsThreshold], RightBarsThreshold);
plot valleyBoolean = valley;

def valleyValue = if valley then low else valleyValue[1];
def valleyBar = if valley then BarNumber() else Double.NaN;

# Get barNumbers for previous 2 valleys
def lastValleyBarNumber = HighestAll(if valley then bn else 0);
def prevValleyBarNumber = HighestAll(if valley and bn < lastValleyBarNumber then bn else 0);

# Get values for previous 2 valleys
def lastValleyValue = GetValue(low, bn - HighestAll(valleyBar));
def prevValleyValue = GetValue(valleyValue[1], bn - HighestAll(valleyBar));

# Do we have valid values for peaks/valleys and
# are they  increasing peaks and decreasing valleys
def areLast2PeaksIncreasing =  !IsNaN(lastPeakValue) and !IsNaN(prevPeakValue) and lastPeakValue > prevPeakValue;
def areLast2ValleysDecreasing = !IsNaN(lastValleyValue) and !IsNaN(prevValleyValue) and lastValleyValue < prevValleyValue;

# Do we have interlaced peaks/valleys
def peaksValleysInterlaced = (prevValleyBarNumber > prevPeakBarNumber and prevValleyBarNumber < lastPeakBarNumber)
                              (prevPeakBarNumber > prevValleyBarNumber and prevPeakBarNumber < lastValleyBarNumber);

def  inBroadeningPattern = areLast2PeaksIncreasing and areLast2ValleysDecreasing and peaksValleysInterlaced;

# if we have a broadening pattern, get last 2 values and draw a line with extension

# get scaling factor for high side, low side
# initial line is drawn using last 2 peaks/valleys
# However, after those 2 points, we need to use scaling factors to extend the line y=mx+c
# Thank you to my middle school math teachers in India :)
# Expect valleyScalePerBar to be negative number

def peakScalePerBar = (lastPeakValue - prevPeakValue) / (lastPeakBarNumber - prevPeakBarNumber );
def valleyScalePerBar = (lastValleyValue - prevValleyValue) / (lastValleyBarNumber - prevValleyBarNumber );

def peakExtendedValue = lastPeakValue + (bn - lastPeakBarNumber) * peakScalePerBar;
def valleyExtendedValue = lastValleyValue + (bn - lastValleyBarNumber) * valleyScalePerBar;

#Draw UpperLine, initial with 2 points, then extend with scaling factor
plot upperLine = if !isAlertSetup and inBroadeningPattern and bn > lastPeakBarNumber
                 then peakExtendedValue
                 else if inBroadeningPattern and peak and bn >= prevPeakBarNumber
                 then high
                 else Double.NaN;

plot lowerLine = if !isAlertSetup and inBroadeningPattern and bn > lastValleyBarNumber
                 then valleyExtendedValue
                 else if inBroadeningPattern and valley and bn >= prevValleyBarNumber
                 then low
                 else Double.NaN;


# alert when crosses over/above line?

#get highestPrice after last peak

#def highValueAfterLastPeak = Highest(high[-(bn-lastPeakBarNumber)], RightBarsThreshold);

# scantype=1; for price cross above upper line
#def scantype_1 = inBroadeningPattern and high >

# scantype=2; for price cross below lower line
# scantype=3; for price is below lower line
def scantype_3 = inBroadeningPattern and high > peakExtendedValue;
# scantype=4; for price is above upper line
def scantype_4 = inBroadeningPattern and low < valleyExtendedValue;
# scantype=5; for price is inside two lines.
def scantype_5 = inBroadeningPattern and high < peakExtendedValue
                                     and low  > valleyExtendedValue;

# getDisplayValue for alerts/scans

AddLabel(scantype_3, "Above BF", Color.CYAN);
AddLabel(scantype_4, "Below BF", Color.CYAN);
AddLabel(scantype_5, "Between BF", Color.CYAN);

AddLabel(debug, "BarNumber:" + bn, Color.WHITE);
AddLabel(debug, "lastpeakbar:" + lastPeakBarNumber, Color.WHITE);
AddLabel(debug, "prevPeakGetValue:" + prevPeakBarNumber, Color.WHITE);
AddLabel(debug, "lastpeakvalue:" + lastPeakValue, Color.WHITE);
AddLabel(debug, "previouspeakvalue:" + prevPeakValue, Color.WHITE);

AddLabel(debug, "lastvalleybar:" + lastValleyBarNumber, Color.YELLOW);
AddLabel(debug, "prevValleyGetValue:" + prevValleyBarNumber, Color.YELLOW);
AddLabel(debug, "lastvalleyvalue:" + lastValleyValue, Color.YELLOW);
AddLabel(debug, "previousvalleyvalue:" + prevValleyValue, Color.YELLOW);

AddLabel(debug, "IncPeaks?:" + areLast2PeaksIncreasing, Color.WHITE);
AddLabel(debug, "DecPeaks?:" + areLast2ValleysDecreasing, Color.WHITE);
AddLabel(debug, "ValuesInterlaced?:" + peaksValleysInterlaced, Color.WHITE);
AddLabel(debug, "BroadPattern?:" + inBroadeningPattern, if inBroadeningPattern then Color.LIGHT_GREEN else Color.LIGHT_RED);

AddLabel(debug, "peakSideScaleFactor:" + peakScalePerBar, Color.YELLOW);
AddLabel(debug, "valleySideScaleFactor:" + valleyScalePerBar, Color.YELLOW);

AddLabel(debug, "peakExtendedValue:" + peakExtendedValue, Color.YELLOW);
AddLabel(debug, "valleyExtendedValue:" + valleyExtendedValue, Color.YELLOW);


New member
@RajB looks good so far. i'll take a fuller look tonight. it looks like we've gotten about as far as each other on this except i never intended on coding alerts. we can continue iterating from your code...any specific issues/features that i can help look into?


New member
@MrDrB Sounds good.
  • If you can sanity check my logic that would be great.
  • also, any do's/don't on TS coding would be appreciated, I am still relatively new to this space
  • I had performed limited testing and things seem to work. Did you have any edge/test cases that you have used before?
  • wanted to share this with the rest of the community as part of the effort
Last edited:

Similar threads