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

Popular posts from this blog

OVERSOLD BY LINEAR REGRESSION

OVERSOLD BY LINEAR REGRESSION

Top No-Code Algo Trading Platforms in India (2025) – Full Comparison & Ranking