Indicator · Pine Script v5 · On the chart

MTF Supply/Demand v2

by g-moe· code credited to goodgains

This indicator draws multi-timeframe supply and demand zones as boxes on the price chart.

View on GitHub

Repaints

GitHub stars3on the repository
Pine versionv5runs as is
Lines of code290
Settings12inputs
LicenceNone statedfile: MPL-2.0
Last update5 months ago27 May 2026

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 indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

290 lines of Pine Script v5 from Tradingview/mtf-supply-demand-v2.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

mtf-supply-demand-v2.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © goodgains


//@version=5
indicator("MTF Supply/Demand v2",  overlay = true, max_labels_count = 500, max_boxes_count = 500, max_lines_count = 500, max_bars_back = 1000)

// @keywords: multi-timeframe-zones, supply-demand-boxes, break-of-structure, pivot-swing-length, atr-zone-thickness, mtf-security-call, zone-labels, demand-supply-levels, overlap-control, instructions-label, higher-tf-levels, zone-extension, bos-detection, poi-zones, swing-pivots, chart-overlay-zones, historical-boxes, zone-management, liquidity-zones, structure-break

//--INSTRUCTIONS--//
var label instruction_label = na
instructions_tooltip = "• Supply/Demand = zones where buyers or sellers will look to enter the market.
  "

instructions = input.bool(title='Show Instructions', defval=false, inline='1', tooltip = instructions_tooltip)
if barstate.islast and instructions
    if not na(instruction_label[1])
        label.delete(instruction_label[1])
    instruction_label := label.new(bar_index + 5, close, instructions_tooltip, xloc = xloc.bar_index , yloc = yloc.price, color = color.black , style = label.style_label_left, textcolor = color.white, size = size.large, textalign = text.align_left) 
//--INSTRUCTIONS--//


//--SUPPLY DEMAND INPUTS--//
sd_length = input.int(10, title = 'Supply/Demand Length', group = 'Supply/Demand Settings', minval = 3, maxval = 50)
sd_width = input.float(7.00, title = 'Supply/Demand Width', group = 'Supply/Demand Settings', minval = 1, maxval = 15, step = 0.5)

supply_color = input.color(color.new(#c30616,70), title = 'Supply', group = 'Supply/Demand Settings', inline = '3')
demand_color = input.color(color.new(#05cd0e,70), title = 'Demand', group = 'Supply/Demand Settings', inline = '3')
sd_labelcolor = input.color(color.new(color.white,20), title = 'Label', group = 'Supply/Demand Settings', inline = '3')

show_supplydemand1 = input.bool(true, title = 'Chart Supply Demand Zones', group = 'Supply/Demand Settings', inline = '4')
_sd_1 = input.bool(false, title = 'Create Zones for 1 Minutes', group = 'Supply/Demand Settings', inline = '5')
_sd_5 = input.bool(false, title = 'Create Zones for 5 Minutes', group = 'Supply/Demand Settings', inline = '6')

_allow_overlap = input.bool(true, title = 'Allow Zones to Overlap', group = 'Supply/Demand Settings', inline = '14')
//--SUPPLY DEMAND INPUTS--//


//--PRICE ACTION INPUTS--//
show_priceaction = input.bool(false, title = 'Price Action Labels', group = 'Price Action Label Settings', inline = '0', tooltip = 'Price Action Labels mark at each swing high or swing low. \n\nHH = Higher High \nLH = Lower High \nLL=
 Lower Low \nLH = Lower High')
pa_color = input.color(color.new(color.white, 60), title = 'Label', group = 'Price Action Label Settings', inline = '1')
//--PRICE ACTION INPUTS--//


//-----FUNCTIONS-----//
//--FUNCTION TO ADD NEW AND REMOVE LAST IN ARRAY--//
f_array_add_pop(array, new_value_to_add) =>
    array.unshift(array, new_value_to_add)
    array.pop(array)


//--FUNCTION SWING H & L LABELS--//
f_sh_sl_labels(current, previous, swing_type) =>

    var string label_text = na
    if swing_type == 1
        if current >= previous
            label_text := 'HH'
        else
            label_text := 'LH'
        label.new(bar_index - sd_length, current, text = label_text, style=label.style_label_down, textcolor = pa_color, color = color.new(pa_color, 100), size = size.tiny, tooltip = label_text)
    
    else if swing_type == -1
        if current >= previous
            label_text := 'HL'
        else
            label_text := 'LL'
        label.new(bar_index - sd_length, current, text = label_text, style=label.style_label_up, textcolor = pa_color, color = color.new(pa_color, 100), size = size.tiny, tooltip = label_text)



//--FUNCTION MAKE SURE SUPPLY ISNT OVERLAPPING--//
f_check_overlapping(new_poi, box_array, atr) =>

    atr_threshold = atr * 2
    okay_to_draw = true

    for i = 0 to array.size(box_array) - 1
        top = box.get_top(array.get(box_array, i))
        bottom = box.get_bottom(array.get(box_array, i))
        poi = (top + bottom) / 2

        upper_boundary = poi + atr_threshold
        lower_boundary = poi - atr_threshold

        if new_poi >= lower_boundary and new_poi <= upper_boundary
            okay_to_draw := false
            break
        else 
            okay_to_draw := true
    okay_to_draw



// function to get swing h/l, previous swing h/l, and tell trend
f_get_swing_values(use_tf, sd_length) =>

    var float sh = na
    var float sl = na
    var float csh = na
    var float csl = na
    var int csh_time = na
    var int csl_time = na
    var float atr = na

    if use_tf 
        sh := ta.pivothigh(high, sd_length, sd_length)
        sl := ta.pivotlow(low, sd_length, sd_length)

        csh := ta.valuewhen(ta.pivothigh(high, sd_length, sd_length), high[sd_length], 0)
        csl := ta.valuewhen(ta.pivotlow(low, sd_length, sd_length), low[sd_length], 0)

        csh_time := ta.valuewhen(ta.pivothigh(high, sd_length, sd_length), time[sd_length], 0)
        csl_time := ta.valuewhen(ta.pivotlow(low, sd_length, sd_length), time[sd_length], 0)

        atr := ta.atr(50)

    [sh, sl, csh, csl, csh_time, csl_time, atr]



//--FUNCTION TO DRAW SUPPLY OR DEMAND ZONE--//
f_supply_demand(box_array, box_type, atr, box_left, level, str_text) =>

    atr_buffer = atr * (sd_width / 10)
    box_right = time

    var float box_top = 0.00
    var float box_bottom = 0.00
    var float poi = 0.00
    var bool new_supply_or_demand = false

    if box_type == 1
        box_top := level
        box_bottom := box_top - atr_buffer
        poi := (box_top + box_bottom) / 2
    else if box_type == -1
        box_bottom := level
        box_top := box_bottom + atr_buffer
        poi := (box_top + box_bottom) / 2

    // okay_to_draw = f_check_overlapping(poi, box_array, atr)
    okay_to_draw = true
    if okay_to_draw
        new_supply_or_demand := true

        //delete oldest box, and then create a new box and add it to the array
        if box_type == 1 
            box.delete( array.get(box_array, array.size(box_array) - 1) )
            f_array_add_pop(box_array, box.new( left = box_left, top = box_top, right = box_right, bottom = box_bottom,
             border_color = supply_color, border_width = 0, bgcolor = supply_color, extend = extend.right,
             text =str_text, text_halign = text.align_right, text_valign = text.align_center, 
             text_color = sd_labelcolor, text_size = size.small, xloc = xloc.bar_time, border_style = line.style_solid))
        
        else if box_type == -1
            box.delete( array.get(box_array, array.size(box_array) - 1) )
            f_array_add_pop(box_array, box.new( left = box_left, top = box_top, right = box_right, bottom = box_bottom,
             border_color = demand_color , border_width = 0, bgcolor = demand_color , extend = extend.right,
             text =str_text, text_halign = text.align_right, text_valign = text.align_center, 
             text_color = sd_labelcolor, text_size = size.small, xloc = xloc.bar_time, border_style = line.style_solid))
    



//--FUNCTION TO CHANGE SUPPLY/DEMAND TO A BOS IF BROKEN--// (THIS VERSION WE JUST DELETE)
f_sd_to_bos(box_array, bos_array, zone_type) =>
    for i = 0 to array.size(box_array) - 1
        level_to_break = zone_type == 1 ? box.get_top(array.get(box_array,i)) : box.get_bottom(array.get(box_array,i))
        if (zone_type == 1  and close >= level_to_break) or (zone_type == -1  and close <= level_to_break) 
            box.delete(array.get(box_array, i))



//--FUNCTION MANAGE CURRENT BOXES BY CHANGING ENDPOINT--//
f_extend_box_endpoint(box_array) =>
    for i = 0 to array.size(box_array) - 1
        box.set_right(array.get(box_array, i), time + (timeframe.in_seconds() * 1000 * 100))

//-----FUNCTIONS END-----//












//-----DEFINE VARIABLES-----//

//--ARRAYS FOR SWING H/L & BN--//
var current_supply_box = array.new_box(25, na)
var current_demand_box = array.new_box(25, na)

var supply_box1 = array.new_box(20, na)
var demand_box1 = array.new_box(20, na)
var supply_box5 = array.new_box(20, na)
var demand_box5 = array.new_box(20, na)

//--ARRAYS FOR SWING H/L & BN--//


//--ARRAYS FOR BOS--//
var supply_bos = array.new_box(5, na)
var demand_bos = array.new_box(5, na)
//--ARRAYS FOR BOS--//
//-----DEFINE VARIABLES-----//



//-----START CALCULATING-----//
[sh, sl, csh, csl, csh_time, csl_time, atr] = request.security(syminfo.ticker, "", f_get_swing_values(true, sd_length))


[sh1, sl1, csh1, csl1, csh_time1, csl_time1, atr1] = 
 request.security(syminfo.ticker, "1", f_get_swing_values(_sd_1, sd_length), lookahead = barmerge.lookahead_on )
[sh5, sl5, csh5, csl5, csh_time5, csl_time5, atr5] = 
 request.security(syminfo.ticker, "5", f_get_swing_values(_sd_5, sd_length), lookahead = barmerge.lookahead_on )


// draw price action labels for current chart
var float psh = na
var float psl = na
psh := not na(sh) ? csh[1] : psh[1]
psl := not na(sl) ? csl[1] : psl[1]

if show_priceaction
    if not na(sh) 
        f_sh_sl_labels(csh, psh, 1)
    else if not na(sl)
        f_sh_sl_labels(csl, psl, -1)


// draw supply/demands
if show_supplydemand1
    if not na(sh) 
        f_supply_demand(current_supply_box, 1, atr, csh_time, csh, "")
    if not na(sl) 
        f_supply_demand(current_demand_box, -1, atr, csl_time, csl, "")

if _sd_1 and timeframe.in_seconds() <= timeframe.in_seconds("1")
    if na(sh1) and not na(sh1[1]) 
        f_supply_demand(supply_box1, 1, atr1, csh_time1, csh1, "1min")
    if na(sl1) and not na(sl1[1]) 
        f_supply_demand(demand_box1, -1, atr1, csl_time1, csl1, "1min")

if _sd_5 and timeframe.in_seconds() <= timeframe.in_seconds("5")
    if na(sh5) and not na(sh5[1]) 
        f_supply_demand(supply_box5, 1, atr5, csh_time5, csh5, "5min")
    if na(sl5) and not na(sl5[1]) 
        f_supply_demand(demand_box5, -1, atr5, csl_time5, csl5, "5min")



// bar_mult = timeframe.in_seconds("30") / timeframe.in_seconds() 
// bgcolor(not na(sh30) ? color.new(color.red,50) : not na(sl30) ? color.new(color.green,50) : na, offset = -sd_length * bar_mult)




// move s/d zones to bos
f_sd_to_bos(current_supply_box, supply_bos, 1)
f_sd_to_bos(current_demand_box, demand_bos, -1)

f_sd_to_bos(supply_box1, supply_bos, 1)
f_sd_to_bos(demand_box1, demand_bos, -1)

f_sd_to_bos(supply_box5, supply_bos, 1)
f_sd_to_bos(demand_box5, demand_bos, -1)






// extend lines
if barstate.islast

    f_extend_box_endpoint(current_supply_box)
    f_extend_box_endpoint(current_demand_box)

    f_extend_box_endpoint(supply_box1)
    f_extend_box_endpoint(demand_box1)

    f_extend_box_endpoint(supply_box5)
    f_extend_box_endpoint(demand_box5)

Overview

MTF Supply/Demand v2 uses a swing length to locate pivots and a width setting to set zone thickness, and it can optionally add one-minute and five-minute zones. Users can allow overlapping zones, recolor supply, demand, and labels, and turn on price-action labels that mark higher highs, lower highs, and lower lows at swings. An optional instruction label explains the zones. The code reads higher-timeframe data with lookahead enabled.

Details

Published by

g-moe on GitHub

Code credit

goodgains, as written in the script's header

Type

Indicator, Pine Script v5

Draws

On the price chart

Category

Support & resistance, SMC / ICT / price action

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
Show Instructionsboolfalseinstructions
Supply/Demand Lengthint10sd_length
Supply/Demand Widthfloat7.00sd_width
Supplycolorcolor.new(#c30616,70)supply_color
Demandcolorcolor.new(#05cd0e,70)demand_color
Labelcolorcolor.new(color.white,20)sd_labelcolor
Chart Supply Demand Zonesbooltrueshow_supplydemand1
Create Zones for 1 Minutesboolfalse_sd_1
Create Zones for 5 Minutesboolfalse_sd_5
Allow Zones to Overlapbooltrue_allow_overlap
Price Action Labelsboolfalseshow_priceaction
Labelcolorcolor.new(color.white, 60)pa_color
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

ICT Equal Highs and Lows

by ArunKBhaskar
575 stars

This overlay indicator finds pivot highs and lows on the price chart and compares the last two highs and the last two lows. When the percentage difference between a pair is below the Equal HL threshold, it treats them as equal levels and draws a box. Left and

Indicator v5 SMC / ICT / price actionSupport & resistance
Updated 1 year ago · MPL-2.0 GitHub repository of ICT Equal Highs and Lows

DE-tracker

by Allen1212
303 stars

This indicator plots labels on the TradingView price chart to mark ARK Invest's buy and sell trades for the DE stock. Buy transactions appear in green with X1 or X2 multipliers showing the number of ARK ETFs involved on the same day; sells appear in red. The m

Indicator v4 Support & resistanceSMC / ICT / price action
Updated 2 years ago · None stated GitHub repository of DE-tracker

This Pine Script indicator plots a ZigZag on the price chart. It identifies reversal points when price moves more than a set percentage from the last pivot. The script calculates forecasts using zigzag data and labels detected geometric patterns including harm

Indicator v4 TrendSupport & resistance

Golden Bullet Strategy

by TradersPost
218 stars

The Golden Bullet Strategy is an overlay signal indicator based on Smart Money Concepts and time-based liquidity models. It identifies liquidity sweeps of a designated reference session range during a specified hunt window. It then confirms Market Structure Sh

Indicator v6 SMC / ICT / price actionSessions & time
Updated 3 months ago · None stated GitHub repository of Golden Bullet Strategy

Open Source Fractal

by cantolab
183 stars

This indicator implements the Fractal Model concept to plot market structure shifts, liquidity sweeps, and Change in State of Delivery (CISD) levels. It overlays custom higher-timeframe candle previews, open prices, and time markers directly on the price chart

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 2 months ago · MIT GitHub repository of Open Source Fractal

Swing Failure Signals

by Ganador1
154 stars

This indicator calculates pivot highs and pivot lows to detect liquidity sweeps and Swing Failure Patterns. It identifies when price breaks past recent key swing highs or lows within a configurable historical window, then looks for a quick reversal or change i

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 2 months ago · Apache-2.0 GitHub repository of Swing Failure Signals

More indicators

Trade Your Charts With a Regulated Broker

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

Compare brokers