#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
# 20210803 Raj B. Cay7man identified a bug, removed logic that depends on isAlertSetup yes/no https://usethinkscript.com/threads/broadening-formation-pattern-for-thinkorswim.2732/post-71684 re
# Inputs
input LeftBarsThreshold = 5;
input RightBarsThreshold = 5;
#input isAlertSetup = no;
input debug = no;
# 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)
or
(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 inBroadeningPattern and bn > lastPeakBarNumber
then peakExtendedValue
else if inBroadeningPattern and peak and bn >= prevPeakBarNumber
then high
else Double.NaN;
upperLine.EnableApproximation();
upperLine.SetDefaultColor(Color.LIME);
upperLine.SetLineWeight(2);
plot lowerLine = if inBroadeningPattern and bn > lastValleyBarNumber
then valleyExtendedValue
else if inBroadeningPattern and valley and bn >= prevValleyBarNumber
then low
else Double.NaN;
lowerLine.EnableApproximation();
lowerLine.SetDefaultColor(Color.PLUM);
lowerLine.SetLineWeight(2);
# 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(inBroadeningPattern, "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);
###End Code