// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ismailcarlik
//@version=5
indicator("Trend Lines, Supports and Resistances", shorttitle = "TSR", overlay = true, max_bars_back = 5000)
// ╔═════════════════════════════════════════════════════════════════════════════════════════════╗
// ║ * * * I N P U T S * * * ║
// ╚═════════════════════════════════════════════════════════════════════════════════════════════╝
grpPivotPoints = "Pivot Points ・ Common"
pvtLength = input.int(20, title = "Pivot Length", minval = 1, group = grpPivotPoints)
pvtMarkPivots = input.bool(false, title = "Mark Pivots", group = grpPivotPoints)
pvtAlertsEnabled = input.bool(true, title = "Alerts Enabled", inline = "tlAlerts", group = grpPivotPoints)
pvtAlertrequency = input.string("Once Per Bar", title = " ・ Frequency", options = ["Once Per Bar", "Once Per Bar Close", "All"], inline = "tlAlerts", group = grpPivotPoints, display = display.none)
grpTrendLines = "Trend Lines"
tlEnabled = input.bool(true, title = "Enabled", group = grpTrendLines)
tlPointsToCheck = input.int(3, title = "Points to Check", minval = 2, group = grpTrendLines)
tlMaxViolation = input.int(0, title = "Maximum Violation", minval = 0, group = grpTrendLines)
tlExceptBars = input.int(3, title = "Excepted Last Bars", minval = 0, group = grpTrendLines)
tlShowViolated = input.bool(false, title = "Show Violated Trend Lines", group = grpTrendLines)
tlExtension = input.string("Right", title = "Line Extension", options = ["None", "Left", "Right", "Both"], group = grpTrendLines)
tlShowLabels = input.bool(true, title = "Show Labels", group = grpTrendLines)
tlAlertsEnabled = input.bool(true, title = "Alerts Enabled", inline = "tlAlerts", group = grpTrendLines)
tlAlertrequency = input.string("Once Per Bar", title = " ・ Frequency", options = ["Once Per Bar", "Once Per Bar Close", "All"], inline = "tlAlerts", group = grpTrendLines, display = display.none)
grpSupportResistance = "Supports & Resistances"
srEnabled = input.bool(true, title = "Enabled", group = grpSupportResistance)
srPointsToCheck = input.int(3, title = "Points to Check", minval = 2, group = grpSupportResistance)
srMaxViolation = input.int(0, title = "Maximum Violation Allowed", minval = 0, group = grpSupportResistance)
srExceptBars = input.int(3, title = "Excepted Last Bars", minval = 0, group = grpSupportResistance)
srShowLabels = input.bool(true, title = "Show Labels", group = grpSupportResistance)
srAlertsEnabled = input.bool(true, title = "Alerts Enabled", inline = "srAlerts", group = grpSupportResistance)
srAlertrequency = input.string("Once Per Bar", title = " ・ Frequency", options = ["Once Per Bar", "Once Per Bar Close", "All"], inline = "srAlerts", group = grpSupportResistance, display = display.none)
grpVisual = "Style"
stlHighColor = input.color(color.blue, title = "High Color", inline = "colors", group = grpVisual)
stlLowColor = input.color(color.red, title = "Low Color", inline = "colors", group = grpVisual)
lineWidth = input.int(1, title = "Line Width", minval = 1, group = grpVisual)
// ╔═════════════════════════════════════════════════════════════════════════════════════════════╗
// ║ * * * T Y P E S * * * ║
// ╚═════════════════════════════════════════════════════════════════════════════════════════════╝
// @type Used to represent a point pair.
// @field firstPoint First point of pair.
// @field secondPoint Second point of pair.
type pointPair
chart.point firstPoint
chart.point secondPoint
// @type Used to represent a trend line.
// @field mainLine Main visible line of the trend.
// @field extensionLine Extension line of the trend.
// @field priceLabel Price label of the trend.
// @field isViolated Violation status of the trend.
type trendLine
line mainLine
line extensionLine = na
label priceLabel = na
bool isViolated = false
// @type Used to represent a support or resistance level.
// @field levelBox Level box for support or resistance.
// @field price Price level of the support or resistance.
// @field priceLabel Price label of the support or resistance.
type srLevel
box levelBox
float price
label priceLabel = na
// ╔═════════════════════════════════════════════════════════════════════════════════════════════╗
// ║ * * * V A R I A B L E S * * * ║
// ╚═════════════════════════════════════════════════════════════════════════════════════════════╝
tlExtendMode = str.lower(array.get(str.split(tlExtension, ""), 0))
tlAlertrequencyMode = switch tlAlertrequency
"Once Per Bar" => alert.freq_once_per_bar
"Once Per Bar Close" => alert.freq_once_per_bar_close
"All" => alert.freq_all
=> alert.freq_once_per_bar
srAlertrequencyMode = switch srAlertrequency
"Once Per Bar" => alert.freq_once_per_bar
"Once Per Bar Close" => alert.freq_once_per_bar_close
"All" => alert.freq_all
=> alert.freq_once_per_bar
pvtAlertrequencyMode = switch pvtAlertrequency
"Once Per Bar" => alert.freq_once_per_bar
"Once Per Bar Close" => alert.freq_once_per_bar_close
"All" => alert.freq_all
=> alert.freq_once_per_bar
var array
highPivots = array.new()
var array lowPivots = array.new()
var array uptrends = array.new()
var array downtrends = array.new()
var array supports = array.new()
var array resistances = array.new()
// ╔═════════════════════════════════════════════════════════════════════════════════════════════╗
// ║ * * * M E T H O D S * * * ║
// ╚═════════════════════════════════════════════════════════════════════════════════════════════╝
// @function Returns reversed version of array.
// @param id (chart.point[]) Array object.
// @returns (chart.point[]) Reversed version of given array.
method reversed(array id) =>
array reversedArray = array.new()
for [i, v] in id
reversedArray.unshift(v)
reversedArray
// @function Checks for the bars if highs above trend line price.
// @param id (trendLine) Trend line object.
// @param exceptBars (int) Count of last bars for exception.
// @returns (int) Count of the bars above trend line price.
method getHighsAbovePrice(trendLine id, int exceptBars) =>
historyReference = bar_index - id.mainLine.get_x1()
count = 0
if exceptBars < historyReference
for i = historyReference to exceptBars
if high[i] > line.get_price(id.mainLine, bar_index - i)
count += 1
count
// @function Checks for the bars if lows below trend line price.
// @param id (trendLine) Trend line object.
// @param exceptBars (int) Count of last bars for exception.
// @returns (int) Count of the bars below trend line price.
method getLowsBelowPrice(trendLine id, int exceptBars) =>
historyReference = bar_index - id.mainLine.get_x1()
count = 0
if exceptBars < historyReference
for i = historyReference to exceptBars
if low[i] < line.get_price(id.mainLine, bar_index - i)
count += 1
count
// @function Sets the trend lines status to violated.
// @param id (trendLine) Trend line object.
// @param trendColor (color) Color of the trend line.
// @returns (void)
method setViolated(trendLine id, color trendColor) =>
id.isViolated := true
line.delete(id.extensionLine)
label.delete(id.priceLabel)
line.set_style(id.mainLine, line.style_dotted)
line.set_extend(id.mainLine, extend = tlExtendMode)
line.set_color(id.mainLine, tlShowViolated ? color.new(trendColor, 50) : na)
// ╔═════════════════════════════════════════════════════════════════════════════════════════════╗
// ║ * * * F U N C T I O N S * * * ║
// ╚═════════════════════════════════════════════════════════════════════════════════════════════╝
// @function Compares two points and returns true if first one is higher.
// @param firstPoint (chart.point) First point to compare.
// @param secondPoint (chart.point) Second point to compare.
// @returns (bool) Whether the first point is higher than the second.
f_isHigher (chart.point firstPoint, chart.point secondPoint) =>
firstPoint.price > secondPoint.price
// @function Compares two points and returns true if first one is lower.
// @param firstPoint (chart.point) First point to compare.
// @param secondPoint (chart.point) Second point to compare.
// @returns (bool) Whether the first point is lower than the second.
f_isLower (chart.point firstPoint, chart.point secondPoint) =>
firstPoint.price < secondPoint.price
// @function Checks for violation of support level.
// @param point (chart.point) Point of support level.
// @param exceptBars (int) Count of last bars for exception.
// @returns (int) Count of violations.
f_checkSupportViolation (chart.point point, int exceptBars) =>
historyReference = bar_index - point.index
violationCount = 0
if exceptBars < historyReference
for i = historyReference to exceptBars
if low[i] < point.price
violationCount += 1
violationCount
// @function Checks for violation of reistance level.
// @param point (chart.point) Point of resistance level.
// @param exceptBars (int) Count of last bars for exception.
// @returns (int) Count of violations.
f_checkResistanceViolation(chart.point point, int exceptBars) =>
historyReference = bar_index - point.index
violationCount = 0
if exceptBars < historyReference
for i = historyReference to exceptBars
if high[i] > point.price
violationCount += 1
violationCount
// @function Draws support level to chart.
// @param index (int) Bar index of support level.
// @returns (void)
f_drawSupport(int index) =>
historyReference = bar_index - index
lowValue = low[historyReference]
boxColor = color.new(stlHighColor, 70)
textColor = color.new(stlHighColor, 50)
supportBox = box.new(left = index, top = math.min(open[historyReference], close[historyReference]), right = bar_index, bottom = lowValue, bgcolor = boxColor, border_color = boxColor)
supportLabel = srShowLabels ? label.new(x = bar_index - int(historyReference / 2), y = lowValue, text = "Support : " + str.tostring(lowValue, format.mintick), style = label.style_label_up, color = color.new(boxColor, 100), textcolor = textColor) : na
supports.push(srLevel.new(levelBox = supportBox, price = lowValue, priceLabel = supportLabel))
// @function Draws resistance level to chart.
// @param index (int) Bar index of reistance level.
// @returns (void)
f_drawResistance(int index) =>
historyReference = bar_index - index
highValue = high[historyReference]
boxColor = color.new(stlLowColor, 70)
textColor = color.new(stlLowColor, 50)
resistanceBox = box.new(left = index, top = highValue, right = bar_index, bottom = math.max(open[historyReference], close[historyReference]), bgcolor = boxColor, border_color = boxColor)
resistanceLabel = srShowLabels ? label.new(x = bar_index - int(historyReference / 2), y = highValue, text = "Resistance : " + str.tostring(highValue, format.mintick), style = label.style_label_down, color = color.new(boxColor, 100), textcolor = textColor) : na
resistances.push(srLevel.new(levelBox = resistanceBox, price = highValue, priceLabel = resistanceLabel))
// @function Gets all pair combinations of given point array.
// @param srcArray (chart.point[]) Source array.
// @returns (pointPair[]) Array of point pairs.
f_getAllPairCombinations(array srcArray) =>
int inputLength = array.size(srcArray)
array pairs = array.new()
for i = 0 to inputLength - 2 by 1
for j = i + 1 to inputLength - 1 by 1
pairs.push(pointPair.new(firstPoint = srcArray.get(i), secondPoint = srcArray.get(j)))
pairs
// @function Draws an uptrend to chart.
// @param start (chart.point) Starting point of trend line.
// @param end (chart.point) Ending point of trend line.
// @returns (void)
f_drawUptrend(chart.point start, chart.point end) =>
uExtension = tlExtendMode == "n" ? na : line.new(start, end, color = color.new(stlHighColor, 50), extend = tlExtendMode, style = line.style_dashed, width = lineWidth)
uMain = line.new(start, end, color = stlHighColor, style = line.style_arrow_both, width = lineWidth)
uPrice = line.get_price(uMain, bar_index)
uLabel = tlShowLabels ? label.new(x = bar_index, y = uPrice, text = "Uptrend : " + str.tostring(uPrice, format.mintick), style = label.style_label_left, color = color.new(stlHighColor, 80), textcolor = stlHighColor) : na
uptrends.push(trendLine.new(mainLine = uMain, extensionLine = uExtension, priceLabel = uLabel))
// @function Draws a downtrend to chart.
// @param start (chart.point) Starting point of trend line.
// @param end (chart.point) Ending point of trend line.
// @returns (void)
f_drawDowntrend(chart.point start, chart.point end) =>
uExtension = tlExtendMode == "n" ? na : line.new(start, end, color = color.new(stlLowColor, 50), extend = tlExtendMode, style = line.style_dashed, width = lineWidth)
uMain = line.new(start, end, color = stlLowColor, style = line.style_arrow_both, width = lineWidth)
uPrice = line.get_price(uMain, bar_index)
uLabel = tlShowLabels ? label.new(x = bar_index, y = uPrice, text = "Downtrend : " + str.tostring(uPrice, format.mintick), style = label.style_label_left, color = color.new(stlLowColor, 80), textcolor = stlLowColor) : na
downtrends.push(trendLine.new(mainLine = uMain, extensionLine = uExtension, priceLabel = uLabel))
// @function Clears all lines, boxes, labels off the chart and empties all trend line, support and resistance arrays.
// @returns (void)
f_clearAll() =>
for [i, v] in line.all
line.delete(v)
for [i, v] in box.all
box.delete(v)
for [i, v] in label.all
label.delete(v)
supports.clear()
resistances.clear()
uptrends.clear()
downtrends.clear()
// ╔═════════════════════════════════════════════════════════════════════════════════════════════╗
// ║ * * * C A L C U L A T I O N S * * * ║
// ╚═════════════════════════════════════════════════════════════════════════════════════════════╝
ph = ta.pivothigh(pvtLength, pvtLength)
pl = ta.pivotlow(pvtLength, pvtLength)
if not na(ph)
highPivots.unshift(chart.point.from_index(bar_index[pvtLength], ph))
if pvtAlertsEnabled
alert(str.format("New possible resistance (high pivot) detected at {0} now.", str.tostring(ph, format.mintick)), pvtAlertrequencyMode)
if not na(pl)
lowPivots.unshift(chart.point.from_index(bar_index[pvtLength], pl))
if pvtAlertsEnabled
alert(str.format("New possible support (low pivot) detected at {0} now.", str.tostring(pl, format.mintick)), pvtAlertrequencyMode)
// ╔═════════════════════════════════════════════════════════════════════════════════════════════╗
// ║ * * * P L O T S * * * ║
// ╚═════════════════════════════════════════════════════════════════════════════════════════════╝
if barstate.islast
f_clearAll()
if tlEnabled
for [i, v] in f_getAllPairCombinations(lowPivots.slice(0, tlPointsToCheck).reversed())
if f_isLower(v.firstPoint, v.secondPoint)
f_drawUptrend(v.firstPoint, v.secondPoint)
for [i, v] in uptrends
if v.getLowsBelowPrice(exceptBars = tlExceptBars) > tlMaxViolation
v.setViolated(trendColor = stlHighColor)
for [i, v] in uptrends
trendPrice = line.get_price(v.mainLine, bar_index)
if not v.isViolated and low <= trendPrice and tlAlertsEnabled
alert(str.format("Uptrend at {0} broken with a new low price at {1} now.", str.tostring(trendPrice, format.mintick), str.tostring(low, format.mintick)), tlAlertrequencyMode)
for [i, v] in f_getAllPairCombinations(highPivots.slice(0, tlPointsToCheck).reversed())
if f_isHigher(v.firstPoint, v.secondPoint)
f_drawDowntrend(v.firstPoint, v.secondPoint)
for [i, v] in downtrends
if v.getHighsAbovePrice(exceptBars = tlExceptBars) > tlMaxViolation
v.setViolated(trendColor = stlLowColor)
for [i, v] in downtrends
trendPrice = line.get_price(v.mainLine, bar_index)
if not v.isViolated and high >= trendPrice and tlAlertsEnabled
alert(str.format("Downtrend at {0} broken with a new high price at {1} now.", str.tostring(trendPrice, format.mintick), str.tostring(high, format.mintick)), tlAlertrequencyMode)
if srEnabled
sCount = 0, lIndex = 0
rCount = 0, hIndex = 0
while sCount < srPointsToCheck
if f_isLower(lowPivots.get(lIndex), lowPivots.get(lIndex + 1))
if f_checkSupportViolation(lowPivots.get(lIndex), exceptBars = srExceptBars) <= srMaxViolation
f_drawSupport(lowPivots.get(lIndex).index)
sCount += 1
lIndex += 1
while rCount < srPointsToCheck
if f_isHigher(highPivots.get(hIndex), highPivots.get(hIndex + 1))
if f_checkResistanceViolation(highPivots.get(hIndex), exceptBars = srExceptBars) <= srMaxViolation
f_drawResistance(highPivots.get(hIndex).index)
rCount += 1
hIndex += 1
for [i, v] in supports
if low <= v.price and srAlertsEnabled
alert(str.format("Support at {0} broken by new low price at {1} now.", str.tostring(v.price, format.mintick), str.tostring(low, format.mintick)), srAlertrequencyMode)
for [i, v] in resistances
if high >= v.price and srAlertsEnabled
alert(str.format("Resistance at {0} broken by new high price at {1} now.", str.tostring(v.price, format.mintick), str.tostring(high, format.mintick)), srAlertrequencyMode)
plotshape(not na(ph) and pvtMarkPivots ? ph : na, title = "High Pivots", style = shape.triangleup, color = stlHighColor, location = location.abovebar, size = size.tiny, offset = -pvtLength)
plotshape(not na(pl) and pvtMarkPivots ? pl : na, title = "Low Pivots", style = shape.triangledown, color = stlLowColor, location = location.belowbar, size = size.tiny, offset = -pvtLength)