market structure pivot trend
Market Structure Pivots Trend — Indicator by AustrianTradingMachine
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © AustrianTradingMachine
// ── HEADER ───────────────────────────────────────────────────────────────────
// @version=6
//
// @description This indicator identifies and visualizes key market structure by
// plotting confirmed, non-repainting pivot points. It goes beyond
// simple markers by classifying the pivots, extending dynamic
// support/resistance lines, and providing a visual representation of
// the current trend state.
//
// Key Features:
//
// 1. **Non-Repainting Pivot Detection:**
// - Uses a standard lookback method (`left` and `right` bars) to
// identify historical pivot points.
// - **Note on Confirmation:** Pivots are only confirmed *after*
// the `Pivot Right Bars` input has passed. This is essential
// for ensuring the signal is non-repainting, but it
// introduces an inherent lag.
// - Automatically classifies the sequence of pivots according to
// Dow Theory: Higher Highs (HH), Higher Lows (HL), Lower Highs (LH),
// and Lower Lows (LL).
//
// 2. **Pivot Classification Filter:**
// - **Price Tolerance Filter:** A user-defined percentage tolerance
// filters out insignificant market noise. A pivot is only classified
// as 'Higher' or 'Lower' if its price exceeds the previous one by
// more than the set tolerance, leading to more robust signals.
// - **Equal Pivot Detection:** The tolerance also enables the
// detection of Equal Highs (EH) and Equal Lows (EL), highlighting
// consolidation zones.
//
// 3. **Dynamic Support & Resistance Lines:**
// - When enabled, the indicator extends a horizontal line from the most
// recent confirmed pivot high and low.
// - These lines update in real-time, providing clear S/R levels.
// - Lines automatically terminate if the price decisively breaks through
// them or if a new pivot of the same type is formed.
//
// 4. **Trend State Visualization:**
// - A built-in state machine analyzes the sequence of pivots and breaks
// to determine the current market trend (Uptrend, Downtrend, or Neutral).
// - Plots a dynamic step-line based on recent highs/lows to visually
// represent the current trend state.
//
// 5. **Full Customization & Alerts:**
// - **Display Options:** Choose between simple triangles or detailed text
// labels that show the pivot type, price, and the absolute or
// relative change from the previous pivot of the same type.
// - **Visual Styling:** Full control over colors for all six pivot types
// (HH, LH, EH, LL, HL, EL) and line styles.
// - **Comprehensive Alerts:** Set up alerts for every new pivot
// formation (e.g., HH, LL), for S/R line breaks, and for
// changes in the overall trend state (Up, Down, Neutral).
//
// ---
//
// **DISCLAIMER**
//
// 1. **For Informational/Educational Use Only:** This indicator is
// provided for informational and educational purposes only. It does
// not constitute financial, investment, or trading advice, nor is
// it a recommendation to buy or sell any asset.
//
// 2. **Use at Your Own Risk:** All trading decisions you make based on
// the information or signals generated by this indicator are made
// solely at your own risk.
//
// 3. **No Guarantee of Performance:** Past performance is not an
// indicator of future results. The author makes no guarantee
// regarding the accuracy of the signals or future profitability.
//
// 4. **No Liability:** The author shall not be held liable for any
// financial losses or damages incurred directly or indirectly from
// the use of this indicator.
//
// 5. **Signals Are Not Recommendations:** The alerts and visual signals
// (e.g., crossovers) generated by this tool are not direct
// recommendations to buy or sell. They are technical observations
// for your own analysis and consideration.
indicator(
title = "Market Structure Pivots Trend",
shorttitle = "MSPT",
overlay = true,
max_lines_count = 500,
max_labels_count = 500)
// ── LOG ──────────────────────────────────────────────────────────────────────
// 2025-11-01 0.1.0
// Initial release of indicator
// ── CONTROL ──────────────────────────────────────────────────────────────────
const string NAME = "MSPT"
const string VERSION = "0.1.0"
const string STATUS = "rel" // "rel", "dev", "dep"
if barstate.isfirst
switch STATUS
"rel" => log.info("Indicator '" + NAME + " " + VERSION + "' released")
"dev" => log.warning("Indicator '" + NAME + " " + VERSION + "' in development")
"dep" => log.error("Indicator '" + NAME + " " + VERSION + "' deprecated!")
=> runtime.error("Undefined status of indicator " + NAME + " " + VERSION)
// ── IMPORTS ──────────────────────────────────────────────────────────────────
import AustrianTradingMachine/LibPvot/1 as LibPvot
// ── USER INPUTS ──────────────────────────────────────────────────────────────
const string gB = "basic"
int pivLe = input.int(
title = "Pivot Left Bars",
group = gB,
defval = 10, minval = 1, step = 1,
tooltip = "Defines the strength of a pivot. A pivot high must have no higher high for this
number of bars to its left (and vice-versa for lows). Higher values result in
fewer, but more significant pivot points.")
int pivRi = input.int(
title = "Pivot Right Bars",
group = gB,
defval = 5, minval = 1, step = 1,
tooltip = "Defines the number of confirmation bars. A pivot is only confirmed after this
number of bars to its right has closed without breaking the pivot level. Higher
values result in more strongly confirmed signals, but with greater lag.")
float pivTol = input.float(
title = "Pivot Price Tolerance (%)",
group = gB,
defval = 0.05, minval = 0, step = 0.01, maxval = 100,
tooltip = "Percentage tolerance for comparing pivot prices.\n\nPivots with a price difference WITHIN this
tolerance are considered 'equal'")
const string gD = "Display"
const string iH = "High"
const string iL = "Low"
enum PivDsp
triangle = "Triangle"
label = "Label"
PivDsp pivDsp = input.enum(
title = "Pivot Display",
group = gD,
defval = PivDsp.label)
enum PivVal
abs = "Absolute"
rel = "Relative"
PivVal pivVal = input.enum(
title = "Pivot Value Change",
group = gD,
defval = PivVal.rel,
tooltip = "Controls the value shown in parentheses on pivot labels:\n
'Absolute' shows the exact price difference from the previous pivot.\n
'Relative' shows the percentage change from the previous pivot.")
bool extPiv = input.bool(
title = "Extend Pivots",
group = gD,
defval = true,
tooltip = "If enabled, draws horizontal lines extending forward from the most recent confirmed
pivot high and pivot low, acting as dynamic support/resistance levels.")
int linWdth = input.int(
title = "Line Width",
group = gD,
defval = 1, minval = 1, step = 1)
enum LneTyp
solid = "Solid"
dotted = "Dotted"
dashed = "Dashed"
LneTyp lneTyp = input.enum(
title = "Line Type",
group = gD,
defval = LneTyp.solid)
const string gC = "color"
color colPivHH = input.color(
title = "Pivot Higher High",
group = gC,
inline = iH,
defval = color.new(color = #f70d1f, transp = 0))
color colPivLH = input.color(
title = "Pivot Lower High",
group = gC,
inline = iH,
defval = color.new(color = #f76571, transp = 0))
color colPivEH = input.color(
title = "Pivot Equal High",
group = gC,
inline = iH,
defval = color.new(color = #f0c6c9, transp = 0))
color colPivLL = input.color(
title = "Pivot Lower Low",
group = gC,
inline = iL,
defval = color.new(color = #05af0c, transp = 0))
color colPivHL = input.color(
title = "Pivot Higher Low",
group = gC,
inline = iL,
defval = color.new(color = #5a9b5d, transp = 0))
color colPivEL = input.color(
title = "Pivot Equal Low",
group = gC,
inline = iL,
defval = color.new(color = #9eaa9f, transp = 0))
color colPivHiPrc = input.color(
title = "Pivot High Price",
group = gC,
defval = color.new(color = #000000, transp = 0))
color colPivLoPrc = input.color(
title = "Pivot Low Price",
group = gC,
defval = color.new(color = #000000, transp = 0))
// ── CALCULATION ──────────────────────────────────────────────────────────────
[pivType, lastPivHi, lastPivLo, _, pivHiBroken, pivLoBroken, trendState] = LibPvot.marketStructure(
highSrc = high,
lowSrc = low,
leftLen = pivLe,
rightLen = pivRi,
srcTol = pivTol)
series bool isPivHi = switch pivType
LibPvot.PivType.hh => true
LibPvot.PivType.lh => true
LibPvot.PivType.eh => true
=> false
series bool isPivLo = switch pivType
LibPvot.PivType.ll => true
LibPvot.PivType.hl => true
LibPvot.PivType.el => true
=> false
var series float trendLineUp = na
var series float trendLineDown = na
if trendState == LibPvot.TrendState.up
trendLineUp := ta.lowest(low, pivLe)
trendLineDown := na
else if trendState == LibPvot.TrendState.down
trendLineUp := na
trendLineDown := ta.highest(high, pivLe)
else if trendState == LibPvot.TrendState.neutral
trendLineUp := na
trendLineDown := na
// ── PLOTTING ─────────────────────────────────────────────────────────────────
plot(
series = trendLineUp,
title = "Trend Line Up",
color = colPivLL,
linewidth = 2,
style = plot.style_steplinebr)
plot(
series = trendLineDown,
title = "Trend Line Down",
color = colPivHH,
linewidth = 2,
style = plot.style_steplinebr)
series string hiStyle = switch pivDsp
PivDsp.label => label.style_label_down
PivDsp.triangle => label.style_triangledown
series string loStyle = switch pivDsp
PivDsp.label => label.style_label_up
PivDsp.triangle => label.style_triangleup
series color pivHiColor = switch pivType
LibPvot.PivType.hh => colPivHH
LibPvot.PivType.lh => colPivLH
LibPvot.PivType.eh => colPivEH
=> na
series color pivLoColor = switch pivType
LibPvot.PivType.ll => colPivLL
LibPvot.PivType.hl => colPivHL
LibPvot.PivType.el => colPivEL
=> na
if isPivHi
series float absChangeHi = na
series float percChangeHi = na
if not na(lastPivHi[1])
absChangeHi := lastPivHi - lastPivHi[1]
percChangeHi := ((absChangeHi) / lastPivHi[1]) * 100
series string labelTextHi = str.tostring(pivType) + ": " + str.tostring(high[pivRi])
if not na(absChangeHi)
labelTextHi += switch pivVal
PivVal.abs => "\n(" + str.tostring(absChangeHi) + ")"
PivVal.rel => "\n(" + str.tostring(percChangeHi, format.percent) + ")"
label.new(
x = bar_index[pivRi],
y = high[pivRi],
xloc = xloc.bar_index,
yloc = yloc.abovebar,
color = pivHiColor,
text = pivDsp == PivDsp.label ? labelTextHi : na,
textcolor = colPivHiPrc,
style = hiStyle,
size = size.tiny)
if isPivLo
series float absChangeLo = na
series float percChangeLo = na
if not na(lastPivLo[1])
absChangeLo := lastPivLo - lastPivLo[1]
percChangeLo := ((absChangeLo) / lastPivLo[1]) * 100
series string labelTextLo = str.tostring(pivType) + ": " + str.tostring(low[pivRi])
if not na(absChangeLo)
labelTextLo += switch pivVal
PivVal.abs => "\n(" + str.tostring(absChangeLo) + ")"
PivVal.rel => "\n(" + str.tostring(percChangeLo, format.percent) + ")"
label.new(
x = bar_index[pivRi],
y = low[pivRi],
xloc = xloc.bar_index,
yloc = yloc.belowbar,
color = pivLoColor,
text = pivDsp == PivDsp.label ? labelTextLo : na,
textcolor = colPivLoPrc,
style = loStyle,
size = size.tiny)
series string lneStyl = switch lneTyp
LneTyp.solid => line.style_solid
LneTyp.dotted => line.style_dotted
LneTyp.dashed => line.style_dashed
var series line pivHiLine = na
var series line pivLoLine = na
if extPiv
if isPivHi
series int hiIndex = bar_index[pivRi]
if not na(pivHiLine)
line.set_x2(pivHiLine, hiIndex)
pivHiLine := line.new(
x1 = hiIndex,
y1 = lastPivHi,
x2 = bar_index,
y2 = lastPivHi,
color = pivHiColor,
style = lneStyl,
width = linWdth)
if not na(pivHiLine)
line.set_x2(pivHiLine, bar_index)
if pivHiBroken
pivHiLine := na
if isPivLo
series int loIndex = bar_index[pivRi]
if not na(pivLoLine)
line.set_x2(pivLoLine, loIndex)
pivLoLine := line.new(
x1 = loIndex,
y1 = lastPivLo,
x2 = bar_index,
y2 = lastPivLo,
color = pivLoColor,
style = lneStyl,
width = linWdth)
if not na(pivLoLine)
line.set_x2(pivLoLine, bar_index)
if pivLoBroken
pivLoLine := na
// ── ALERTS ───────────────────────────────────────────────────────────────────
alertcondition(
condition = pivType == LibPvot.PivType.hh,
title = "MSPT: Pivot Higher High detected",
message = "MSPT: Pivot Higher High detected @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = pivType == LibPvot.PivType.lh,
title = "MSPT: Pivot Lower High detected",
message = "MSPT: Pivot Lower High detected @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = pivType == LibPvot.PivType.eh,
title = "MSPT: Pivot Equal High detected",
message = "MSPT: Pivot Equal High detected @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = pivType == LibPvot.PivType.ll,
title = "MSPT: Pivot Lower Low detected",
message = "MSPT: Pivot Lower Low detected @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = pivType == LibPvot.PivType.hl,
title = "MSPT: Pivot Higher Low detected",
message = "MSPT: Pivot Higher Low detected @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = pivType == LibPvot.PivType.el,
title = "MSPT: Pivot Equal Low detected",
message = "MSPT: Pivot Equal Low detected @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = pivHiBroken,
title = "MSPT: Price crossed above Pivot High line",
message = "MSPT: Price crossed above Pivot High line @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = pivLoBroken,
title = "MSPT: Price crossed below Pivot Low line",
message = "MSPT: Price crossed below Pivot Low line @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = trendState == LibPvot.TrendState.up and trendState[1] != LibPvot.TrendState.up,
title = "MSPT: Trend is getting bullish",
message = "MSPT: Trend is getting bullish @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = trendState == LibPvot.TrendState.neutral and trendState[1] != LibPvot.TrendState.neutral,
title = "MSPT: Trend is getting neutral",
message = "MSPT: Trend is getting neutral @ {{exchange}}:{{ticker}}, TF {{interval}}.")
alertcondition(
condition = trendState == LibPvot.TrendState.down and trendState[1] != LibPvot.TrendState.down,
title = "MSPT: Trend is getting bearish",
message = "MSPT: Trend is getting bearish @ {{exchange}}:{{ticker}}, TF {{interval}}.")
Comments
Post a Comment