Strategy · Pine Script v6 · On the chart

DeMark Pivot Points Strategy

by SynergOps

This strategy plots DeMark pivot, support, and resistance levels on the price chart.

View on GitHub

Repaints

GitHub stars7on the repository
Pine versionv6current
Lines of code266
Settings17inputs
LicenceMPL-2.0
Last update1 year ago4 Aug 2025

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

266 lines of Pine Script v6 from DeMark/pivots-demark.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

pivots-demark.pine
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © SynergOps

//@version=6
strategy("DeMark Pivot Points Strategy", "DeMark Strategy", overlay=true, max_lines_count=500, max_labels_count=500, default_qty_type=strategy.percent_of_equity, default_qty_value=10, commission_type=strategy.commission.percent, commission_value=0.1)

pivotTypeInput           = "DM"  // Fixed to DeMark pivots only
pivotAnchorInput         = "Daily"  // Fixed to Daily timeframe only
maxHistoricalPivotsInput = input.int(title="Number of Pivots Back", defval=5, minval=1, maxval=200, display = display.data_window)
isDailyBasedInput        = input.bool(title="Use Daily-based Values", defval=true, display = display.data_window, tooltip="When this option is unchecked, Pivot Points will use intraday data while calculating on intraday charts. If Extended Hours are displayed on the chart, they will be taken into account during the pivot level calculation. If intraday OHLC values are different from daily-based values (normal for stocks), the pivot levels will also differ.")
showLabelsInput          = input.bool(title="Show Labels", defval=true, group="labels", display = display.data_window)
showPricesInput          = input.bool(title="Show Prices", defval=true, group="labels", display = display.data_window)
positionLabelsInput      = input.string("Right", "Labels Position", options=["Left", "Right"], group="labels", display = display.data_window, active = showLabelsInput or showPricesInput)
linewidthInput           = input.int(title="Line Width", defval=2, minval=1, maxval=100, group="levels", display = display.data_window)

// Signal settings
showSignalsInput         = input.bool(title="Show PP Cross Signals", defval=true, group="signals", display = display.data_window)
signalSizeInput          = input.string(title="Signal Size", defval="small", options=["tiny", "small", "normal", "large", "huge"], group="signals", display = display.data_window)

// Strategy settings
enableStrategyInput      = input.bool(title="Enable Strategy Trading", defval=true, group="strategy", display = display.data_window)
riskPercentInput         = input.float(title="Risk per Trade (%)", defval=2.0, minval=0.1, maxval=10.0, step=0.1, group="strategy", display = display.data_window)

// Debug settings
showDebugInput           = input.bool(title="Show Debug Indicators", defval=false, group="debug", display = display.data_window, tooltip="Shows small triangles for all PP crosses, bias indicators, and debug plots for troubleshooting")

DEFAULT_COLOR = #FB8C00

pColorInput = input.color(color.gray, "P‏  ‏  ‏", inline="P", group="levels", display = display.data_window)
pShowInput = input.bool(true, "", inline="P", group="levels", display = display.data_window)
s1ColorInput = input.color(color.green, "S1", inline="S1/R1" , group="levels", display = display.data_window)
s1ShowInput = input.bool(true, "", inline="S1/R1", group="levels", display = display.data_window)
r1ColorInput = input.color(color.red, "‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏R1", inline="S1/R1", group="levels", display = display.data_window)
r1ShowInput = input.bool(true, "", inline="S1/R1", group="levels", display = display.data_window)

type graphicSettings
    string levelName
    color levelColor
    bool showLevel

var graphicSettingsArray = array.from(
      graphicSettings.new(" P", pColorInput, pShowInput),
      graphicSettings.new("R1", r1ColorInput, r1ShowInput), 
      graphicSettings.new("S1", s1ColorInput, s1ShowInput),
      graphicSettings.new("", #00000000, false))  // Placeholder for potential 4th level

// Fixed to Daily timeframe
pivotTimeframe = "1D"

// Fixed to 1 year multiplier for daily pivots
pivotYearMultiplier = 1

// DeMark pivots have 3 levels: P, S1, R1
numOfPivotLevels = 3

// Variables to track pivot point values for signal detection
var float currentPP = na
var float previousPP = na
var float currentS1 = na
var float currentR1 = na
var isBullishBias = false
var int pivotPeriodCounter = 0
var bool tradeExecutedToday = false  // Track if trade was executed for current pivot day

// Get signal size
signalSize = switch signalSizeInput
    "tiny" => size.tiny
    "small" => size.small
    "normal" => size.normal
    "large" => size.large
    "huge" => size.huge
    => size.small

type pivotGraphic
    line pivotLine
    label pivotLabel

method delete(pivotGraphic graphic) =>
    graphic.pivotLine.delete()
    graphic.pivotLabel.delete()

var drawnGraphics = matrix.new<pivotGraphic>()

localPivotTimeframeChange = timeframe.change(pivotTimeframe)
securityPivotTimeframeChange = timeframe.change(timeframe.period)

pivotTimeframeChangeCounter(condition) => 
    var count = 0
    if condition and bar_index > 0
        count += 1
    count

localPivots = ta.pivot_point_levels("DM", localPivotTimeframeChange)
securityPivotPointsArray = ta.pivot_point_levels("DM", securityPivotTimeframeChange)

securityTimeframe = timeframe.isintraday ? "1D" : timeframe.period
[securityPivots, securityPivotCounter] = request.security(syminfo.tickerid, pivotTimeframe, [securityPivotPointsArray, pivotTimeframeChangeCounter(securityPivotTimeframeChange)], lookahead = barmerge.lookahead_on)
pivotPointsArray = isDailyBasedInput ? securityPivots : localPivots

//@function Sets the ending points of the currently active pivots to `endTime`.
affixOldPivots(endTime) =>
    if drawnGraphics.rows() > 0
        lastGraphics = drawnGraphics.row(drawnGraphics.rows() - 1)

        for graphic in lastGraphics
            graphic.pivotLine.set_x2(endTime)
            if positionLabelsInput == "Right"
                graphic.pivotLabel.set_x(endTime)

//@function Draws pivot lines and labels from `startTime` to the approximate end of the period.
drawNewPivots(startTime) =>
    
    newGraphics = array.new<pivotGraphic>()

    for [index, coord] in pivotPointsArray
        if index < array.size(graphicSettingsArray)  // Safety check for array bounds
            levelSettings = graphicSettingsArray.get(index)
            if not na(coord) and levelSettings.showLevel
                lineEndTime = startTime + timeframe.in_seconds(pivotTimeframe) * 1000
                pivotLine = line.new(startTime, coord, lineEndTime, coord, xloc = xloc.bar_time, color=levelSettings.levelColor, width=linewidthInput)
                pivotLabel = label.new(x = positionLabelsInput == "Left" ? startTime : lineEndTime,
                                   y = coord,
                                   text = (showLabelsInput ? levelSettings.levelName + " " : "") + (showPricesInput ? "(" + str.tostring(coord, format.mintick) + ")" : ""),
                                   style = positionLabelsInput == "Left" ? label.style_label_right : label.style_label_left,
                                   textcolor = levelSettings.levelColor,
                                   color = #00000000,
                                   xloc=xloc.bar_time)
                
                newGraphics.push(pivotGraphic.new(pivotLine, pivotLabel))
    
    drawnGraphics.add_row(array_id = newGraphics)

    if drawnGraphics.rows() > maxHistoricalPivotsInput
        oldGraphics = drawnGraphics.remove_row(0)
        
        for graphic in oldGraphics
            graphic.delete()


localPivotDrawConditionStatic = not isDailyBasedInput and localPivotTimeframeChange
securityPivotDrawConditionStatic = isDailyBasedInput and securityPivotCounter != securityPivotCounter[1]

// Track pivot point values for signal detection
if not na(pivotPointsArray) and array.size(pivotPointsArray) > 0
    newPP = array.get(pivotPointsArray, 0)[1]  // Get PP value (index 0, coordinate)
    newS1 = array.size(pivotPointsArray) > 2 ? array.get(pivotPointsArray, 2)[1] : na  // Get S1 value (index 2)
    newR1 = array.size(pivotPointsArray) > 1 ? array.get(pivotPointsArray, 1)[1] : na  // Get R1 value (index 1)
    
    if not na(newPP)
        // For daily pivots on intraday timeframes, use daily timeframe change
        dailyChange = timeframe.change("1D")
        
        // Always update currentPP to reflect the most recent pivot point
        if na(currentPP) or newPP != currentPP
            // Update previousPP when we get a new daily period or new PP value
            if dailyChange or (not na(currentPP) and newPP != currentPP)
                previousPP := currentPP
                pivotPeriodCounter := pivotPeriodCounter + 1
                tradeExecutedToday := false  // Reset trade flag for new pivot day
            currentPP := newPP
            currentS1 := newS1
            currentR1 := newR1
            
        // Calculate bias on every bar once we have both current and previous PP
        if not na(previousPP) and not na(currentPP)
            isBullishBias := currentPP > previousPP

// Removed multi-yearly logic since we're only using daily timeframe
localPivotDrawConditionDeveloping = not isDailyBasedInput and time_close == time_close(pivotTimeframe)
securityPivotDrawConditionDeveloping = false

if (securityPivotDrawConditionStatic or localPivotDrawConditionStatic)
    affixOldPivots(time)
    drawNewPivots(time)

// Debug: Plot current PP value to verify it's being calculated correctly (optional)
plot(showDebugInput ? currentPP : na, title="Current PP", color=color.new(color.yellow, 50), linewidth=1)

// Signal detection: Check if price closes above/below the current pivot point
// Calculate close position relative to PP on each bar for consistency
closedAbovePP = not na(currentPP) and close > currentPP
closedBelowPP = not na(currentPP) and close < currentPP
prevClosedAbovePP = not na(currentPP) and close[1] > currentPP
prevClosedBelowPP = not na(currentPP) and close[1] < currentPP

// Detect when candle closes above PP after previously being below (or vice versa)
crossedAbove = closedAbovePP and prevClosedBelowPP
crossedBelow = closedBelowPP and prevClosedAbovePP

// Debug plots (optional) - shows small indicators for troubleshooting
plotchar(showDebugInput and crossedAbove, "CrossedAbove", "▲", location.belowbar, color.green, size=size.tiny)
plotchar(showDebugInput and crossedBelow, "CrossedBelow", "▼", location.abovebar, color.red, size=size.tiny)
plotchar(showDebugInput and isBullishBias, "BullishBias", "B", location.top, color.blue, size=size.tiny)
plotchar(showDebugInput and not na(previousPP), "HasPrevPP", "P", location.bottom, color.orange, size=size.tiny)

// Debug values in data window (optional)
plot(showDebugInput ? previousPP : na, title="Previous PP", color=color.new(color.orange, 80), linewidth=1, display=display.data_window)
plot(showDebugInput ? (isBullishBias ? 1 : 0) : na, title="Bias (1=Bull, 0=Bear)", color=color.new(color.purple, 50), display=display.data_window)

// Signal logic with bias filtering - now that bias detection is working
if showSignalsInput and not na(currentPP) and not na(previousPP)
    if crossedAbove and isBullishBias
        // Bullish bias + crossing above PP = bullish signal
        label.new(bar_index, low - (high - low) * 0.1, "▲", 
                 color=color.new(color.green, 0), 
                 textcolor=color.green, 
                 style=label.style_none, 
                 size=signalSize,
                 tooltip="Bullish PP Cross")
    else if crossedBelow and not isBullishBias
        // Bearish bias + crossing below PP = bearish signal
        label.new(bar_index, high + (high - low) * 0.1, "▼", 
                 color=color.new(color.red, 0), 
                 textcolor=color.red, 
                 style=label.style_none, 
                 size=signalSize,
                 tooltip="Bearish PP Cross")

// Strategy Logic
if enableStrategyInput and not na(currentPP) and not na(previousPP) and not na(currentS1) and not na(currentR1)
    
    // Check if price touches PP line (high >= PP >= low)
    priceTouchesP = high >= currentPP and low <= currentPP
    
    // Entry conditions based on bias, price touching P line, and no trade executed today
    longCondition = isBullishBias and priceTouchesP and strategy.position_size == 0 and not tradeExecutedToday
    shortCondition = not isBullishBias and priceTouchesP and strategy.position_size == 0 and not tradeExecutedToday
    
    // Calculate position size based on risk percentage
    riskAmount = strategy.equity * (riskPercentInput / 100)
    
    if longCondition
        // Long entry: Entry at P, Stop at S1, Target at R1
        stopDistance = currentPP - currentS1
        if stopDistance > 0
            positionSize = riskAmount / stopDistance
            strategy.entry("Long", strategy.long, qty=positionSize, limit=currentPP)
            strategy.exit("Long Exit", "Long", stop=currentS1, limit=currentR1)
            tradeExecutedToday := true  // Mark trade as executed for this pivot day
            
            // Show entry signal
            label.new(bar_index, currentPP, "L", style=label.style_label_down, color=color.green, textcolor=color.white, size=size.small)
    
    if shortCondition
        // Short entry: Entry at P, Stop at R1, Target at S1
        stopDistance = currentR1 - currentPP
        if stopDistance > 0
            positionSize = riskAmount / stopDistance
            strategy.entry("Short", strategy.short, qty=positionSize, limit=currentPP)
            strategy.exit("Short Exit", "Short", stop=currentR1, limit=currentS1)
            tradeExecutedToday := true  // Mark trade as executed for this pivot day
            
            // Show entry signal
            label.new(bar_index, currentPP, "S", style=label.style_label_up, color=color.red, textcolor=color.white, size=size.small)

// If possible, draw pivots from the beginning of the chart if none were found
var FIRST_BAR_TIME = time
if (barstate.islastconfirmedhistory and drawnGraphics.columns() == 0)

    if not na(securityPivots) and securityPivotCounter > 0
        if isDailyBasedInput
            drawNewPivots(FIRST_BAR_TIME)
        else 
            runtime.error("Not enough intraday data to calculate Pivot Points. Lower the Pivots Timeframe or turn on the 'Use Daily-based Values' option in the indicator settings.")
    else
        runtime.error("Not enough data to calculate Pivot Points. Lower the Pivots Timeframe in the indicator settings.")

Overview

DeMark Pivot Points Strategy can keep several historical daily pivots, draw labels and prices, and mark crosses of the pivot. When strategy trading is enabled, it sizes trades from a risk-per-trade percentage of equity. The main settings change lookback, whether daily or intraday values feed the calculation, line and signal appearance, and which levels are shown. The script uses lookahead, so higher-timeframe values can affect historical bars. Exit and stop rules are not visible in the supplied code.

Details

Published by

SynergOps on GitHub

Type

Strategy, Pine Script v6

Draws

On the price chart

Category

Support & resistance, Breakout

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • Reads higher-timeframe data with lookahead on, so the history can show signals that were not there in real time

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Number of Pivots Backint5maxHistoricalPivotsInput
Use Daily-based ValuesbooltrueisDailyBasedInput
Show LabelsbooltrueshowLabelsInput
Show PricesbooltrueshowPricesInput
Labels PositionstringRightpositionLabelsInput
Line Widthint2linewidthInput
Show PP Cross SignalsbooltrueshowSignalsInput
Signal SizestringsmallsignalSizeInput
Enable Strategy TradingbooltrueenableStrategyInput
Risk per Trade (%)float2.0riskPercentInput
Show Debug IndicatorsboolfalseshowDebugInput
P‏ ‏ ‏colorcolor.graypColorInput
Show all 17 settings
SettingTypeDefaultVariable
pShowInputbooltruepShowInput
S1colorcolor.greens1ColorInput
s1ShowInputbooltrues1ShowInput
‏ ‏ ‏ ‏ ‏ ‏ ‏ ‏R1colorcolor.redr1ColorInput
r1ShowInputbooltruer1ShowInput
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

This trend-following strategy executes a classic Donchian channel breakout system designed to cross-validate backtest results against external framework benchmarks. It triggers a long entry when the closing price breaks above the highest high of the prior entr

Strategy v6 BreakoutTrend
Updated today · AGPL-3.0 GitHub repository of Forven S01509 - Simple Donchian 20/10 Breakout
257 stars

This strategy plots running pivot highs as red R circles and pivot lows as blue S circles directly on the price chart. It computes an EMA 3 and a Cto trend line from close prices, then generates long signals on upward EMA-Cto crossovers combined with price pos

Strategy v6 TrendMoving averages Repaints
Updated 6 weeks ago · None stated GitHub repository of Best Chart 1m Strategy Candle Bar

STRG-HOLP

by harryguiacorn
219 stars

STRG-HOLP is a trading strategy that identifies High of Low Period (HOLP) and Low of High Period (LOHP) breakout setups. It tracks the lowest low or highest high over a user-defined lookback window. A long trade opens when the close breaks above the high of th

Strategy v5 BreakoutSupport & resistance
Updated 1 year ago · None stated GitHub repository of STRG-HOLP
106 stars

This script monitors opening gap conditions and breakout behavior relative to the first candle's range across up to twenty configurable stock tickers. It detects whether following bars remain inside the opening range before breaking out, displaying the status

Strategy v4 Screeners & dashboardsBreakout Repaints
Updated 2 years ago · GPL-3.0 GitHub repository of 1St Bar GAP+BkOut Screener v2 by RM

This trend breakout strategy plots Donchian Channels consisting of upper, lower, and middle bands directly on the main price chart. Long positions enter when price touches the upper channel and pulls back to or below the middle band, placing a stop loss at the

Strategy v6 TrendBreakout
Updated 2 months ago · MPL-2.0 GitHub repository of Donchian Strategy - Dynamic TP, Fixed SL, 1% Risk, Daily Target

More strategys

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers