Indicator · Pine Script v6 · On the chart

Order Flow Heatmap

by grzresearch

This indicator builds a volume profile heatmap over a lookback window and draws it on the price chart in the style of order flow tools.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code648
Settings29inputs
LicenceMPL-2.0
Last update2 days ago27 Oct 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

648 lines of Pine Script v6 from order_flow_heatmap.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

order_flow_heatmap.pine
// ══════════════════════════════════════════════════════════════════════════════
// ORDER FLOW HEATMAP - BOOKMAP STYLE
// ══════════════════════════════════════════════════════════════════════════════
// Debugged Version - Sichere Array-Handhabung
// ══════════════════════════════════════════════════════════════════════════════

//@version=6
indicator("Order Flow Heatmap", shorttitle="OF-Heatmap", overlay=true, 
     max_boxes_count=500, max_lines_count=500, max_labels_count=200)

// ══════════════════════════════════════════════════════════════════════════════
// KONSTANTEN
// ══════════════════════════════════════════════════════════════════════════════

const int MAX_ROWS = 50

// ══════════════════════════════════════════════════════════════════════════════
// EINGABE-PARAMETER
// ══════════════════════════════════════════════════════════════════════════════

grp_heat = "Heatmap-Einstellungen"
lookback = input.int(100, "Lookback Bars", minval=20, maxval=500, group=grp_heat)
row_count = input.int(40, "Preis-Levels (Rows)", minval=20, maxval=50, group=grp_heat)
show_heatmap = input.bool(true, "Heatmap anzeigen", group=grp_heat)
heatmap_opacity = input.int(80, "Heatmap Transparenz", minval=50, maxval=95, group=grp_heat)
heatmap_extend_right = input.int(20, "Heatmap nach rechts erweitern", minval=0, maxval=50, group=grp_heat)

grp_color = "Farbschema"
color_scheme = input.string("Bookmap Blue", "Farbschema", options=["Bookmap Blue", "Fire", "Green Matrix", "Purple", "Thermal"], group=grp_color)
intensity_mult = input.float(1.5, "Intensitäts-Multiplikator", minval=0.5, maxval=3.0, step=0.1, group=grp_color)
use_dynamic_range = input.bool(true, "Dynamische Preis-Range", group=grp_color)

grp_liq = "Liquiditäts-Levels"
show_liquidity_levels = input.bool(true, "Liquiditäts-Levels anzeigen", group=grp_liq)
liquidity_threshold = input.float(1.8, "Liquiditäts-Schwelle (x Avg)", minval=1.0, maxval=5.0, step=0.1, group=grp_liq)
show_hvn = input.bool(true, "High Volume Nodes (HVN)", group=grp_liq)
show_lvn = input.bool(true, "Low Volume Nodes (LVN)", group=grp_liq)
lvn_threshold = input.float(0.3, "LVN Schwelle (x Avg)", minval=0.1, maxval=0.5, step=0.05, group=grp_liq)

grp_bubble = "Volume Bubbles"
show_bubbles = input.bool(true, "Volume Bubbles anzeigen", group=grp_bubble)
bubble_threshold_small = input.float(1.3, "Kleine Bubble Schwelle", minval=1.0, maxval=2.0, step=0.1, group=grp_bubble)
bubble_threshold_medium = input.float(1.8, "Mittlere Bubble Schwelle", minval=1.5, maxval=3.0, step=0.1, group=grp_bubble)
bubble_threshold_large = input.float(2.5, "Große Bubble Schwelle", minval=2.0, maxval=5.0, step=0.1, group=grp_bubble)

grp_delta = "Delta & Imbalance"
show_delta_bg = input.bool(true, "Delta Hintergrund", group=grp_delta)
show_cvd = input.bool(true, "CVD (Cumulative Volume Delta)", group=grp_delta)
show_imbalance = input.bool(true, "Imbalance Marker", group=grp_delta)
imbalance_threshold = input.float(2.5, "Imbalance Ratio", minval=1.5, maxval=5.0, step=0.25, group=grp_delta)

grp_poc = "POC & Value Area"
show_poc = input.bool(true, "POC (Point of Control)", group=grp_poc)
show_value_area = input.bool(true, "Value Area", group=grp_poc)
va_percent = input.float(70.0, "Value Area %", minval=50.0, maxval=90.0, step=5.0, group=grp_poc)
poc_line_style = input.string("Solid", "POC Linien-Stil", options=["Solid", "Dashed", "Dotted"], group=grp_poc)

grp_vwap = "VWAP & Anchors"
show_vwap = input.bool(true, "Session VWAP", group=grp_vwap)
show_vwap_bands = input.bool(true, "VWAP Bänder (±1σ, ±2σ)", group=grp_vwap)

grp_stack = "Stacked Imbalances"
show_stacked = input.bool(true, "Stacked Imbalances anzeigen", group=grp_stack)
stack_count = input.int(3, "Min. aufeinanderfolgende Imbalances", minval=2, maxval=5, group=grp_stack)

// ══════════════════════════════════════════════════════════════════════════════
// SICHERE ARRAY HELPER FUNKTIONEN
// ══════════════════════════════════════════════════════════════════════════════

// Sichere array.get Funktion mit Bounds-Check
safe_array_get(array<float> arr, int idx) =>
    result = 0.0
    if not na(arr) and array.size(arr) > 0 and idx >= 0 and idx < array.size(arr)
        result := array.get(arr, idx)
    result

// Sichere array.set Funktion mit Bounds-Check
safe_array_set(array<float> arr, int idx, float val) =>
    if not na(arr) and array.size(arr) > 0 and idx >= 0 and idx < array.size(arr)
        array.set(arr, idx, val)

// ══════════════════════════════════════════════════════════════════════════════
// FARBPALETTEN
// ══════════════════════════════════════════════════════════════════════════════

get_heatmap_color(float intensity, string scheme, int opacity) =>
    clamped = math.min(math.max(intensity, 0.0), 1.0)
    color result = color.new(#0a1628, opacity)
    
    if scheme == "Bookmap Blue"
        if clamped >= 0.95
            result := color.new(#f44336, opacity - 50)
        else if clamped >= 0.85
            result := color.new(#ff9800, opacity - 45)
        else if clamped >= 0.70
            result := color.new(#ffeb3b, opacity - 40)
        else if clamped >= 0.55
            result := color.new(#00e5ff, opacity - 30)
        else if clamped >= 0.40
            result := color.new(#00acc1, opacity - 20)
        else if clamped >= 0.25
            result := color.new(#1565c0, opacity - 10)
        else if clamped >= 0.15
            result := color.new(#0d3a6e, opacity - 5)
    
    else if scheme == "Fire"
        if clamped >= 0.8
            result := color.new(#ffeb3b, opacity - 50)
        else if clamped >= 0.6
            result := color.new(#ff5722, opacity - 35)
        else if clamped >= 0.4
            result := color.new(#b71c1c, opacity - 20)
        else if clamped >= 0.2
            result := color.new(#6d0000, opacity - 10)
    
    else if scheme == "Green Matrix"
        if clamped >= 0.8
            result := color.new(#00ff41, opacity - 50)
        else if clamped >= 0.6
            result := color.new(#00cc00, opacity - 35)
        else if clamped >= 0.4
            result := color.new(#006600, opacity - 20)
        else if clamped >= 0.2
            result := color.new(#003300, opacity - 10)
    
    else if scheme == "Purple"
        if clamped >= 0.8
            result := color.new(#e040fb, opacity - 50)
        else if clamped >= 0.6
            result := color.new(#ba68c8, opacity - 35)
        else if clamped >= 0.4
            result := color.new(#6a0dad, opacity - 20)
        else if clamped >= 0.2
            result := color.new(#2e003e, opacity - 10)
    
    else if scheme == "Thermal"
        if clamped >= 0.8
            result := color.new(#ffff00, opacity - 50)
        else if clamped >= 0.65
            result := color.new(#ff6600, opacity - 40)
        else if clamped >= 0.5
            result := color.new(#ff0066, opacity - 30)
        else if clamped >= 0.35
            result := color.new(#aa00aa, opacity - 20)
        else if clamped >= 0.2
            result := color.new(#0000aa, opacity - 10)
    
    result

// ══════════════════════════════════════════════════════════════════════════════
// PREIS-RANGE BERECHNUNG
// ══════════════════════════════════════════════════════════════════════════════

atr_val = ta.atr(14)
safe_atr = nz(atr_val, close * 0.02)
volatility_range = safe_atr * 20

raw_highest = ta.highest(high, lookback)
raw_lowest = ta.lowest(low, lookback)

highest_price = use_dynamic_range ? math.max(raw_highest, close + volatility_range/2) : raw_highest
lowest_price = use_dynamic_range ? math.min(raw_lowest, close - volatility_range/2) : raw_lowest

price_range = math.max(highest_price - lowest_price, safe_atr * 5)
row_height = price_range / math.max(row_count, 1)

// ══════════════════════════════════════════════════════════════════════════════
// VOLUME PROFILE ARRAYS - SICHERE INITIALISIERUNG
// ══════════════════════════════════════════════════════════════════════════════

// Arrays einmalig mit var initialisieren - NICHT leeren!
var array<float> vol_profile = array.new_float(MAX_ROWS, 0.0)
var array<float> buy_vol = array.new_float(MAX_ROWS, 0.0)
var array<float> sell_vol = array.new_float(MAX_ROWS, 0.0)
var array<float> delta_profile = array.new_float(MAX_ROWS, 0.0)

// Persistent Variablen für POC/VA
var int poc_idx = 0
var float poc_price = 0.0
var float vah_price = 0.0
var float val_price = 0.0
var float max_vol_level = 0.0
var float avg_vol_level = 0.0

// ══════════════════════════════════════════════════════════════════════════════
// DELTA BERECHNUNG
// ══════════════════════════════════════════════════════════════════════════════

bar_range = high - low
close_position = bar_range > 0 ? (close - low) / bar_range : 0.5

est_buy_vol = volume * close_position
est_sell_vol = volume * (1 - close_position)

delta = est_buy_vol - est_sell_vol
delta_percent = volume > 0 ? (delta / volume) * 100 : 0

var float cvd = 0.0
cvd += nz(delta, 0)

// ══════════════════════════════════════════════════════════════════════════════
// VOLUME PROFILE BERECHNEN - NUR AUF LETZTEM BAR
// ══════════════════════════════════════════════════════════════════════════════

if barstate.islast
    // Prüfen ob Arrays valide sind
    if array.size(vol_profile) == MAX_ROWS
        // Arrays mit 0 füllen (nicht löschen!)
        for i = 0 to MAX_ROWS - 1
            array.set(vol_profile, i, 0.0)
            array.set(buy_vol, i, 0.0)
            array.set(sell_vol, i, 0.0)
            array.set(delta_profile, i, 0.0)
        
        // Volumen auf Preis-Levels verteilen
        for j = 0 to math.min(lookback - 1, 499)
            bar_h = high[j]
            bar_l = low[j]
            bar_c = close[j]
            bar_v = nz(volume[j], 0)
            bar_rng = bar_h - bar_l
            
            if bar_v > 0 and bar_rng > 0
                cp = (bar_c - bar_l) / bar_rng
                bv = bar_v * cp
                sv = bar_v * (1 - cp)
                
                for i = 0 to row_count - 1
                    level_low = lowest_price + (i * row_height)
                    level_high = level_low + row_height
                    
                    if bar_h >= level_low and bar_l <= level_high
                        overlap_low = math.max(bar_l, level_low)
                        overlap_high = math.min(bar_h, level_high)
                        overlap_ratio = (overlap_high - overlap_low) / bar_rng
                        
                        vol_contrib = bar_v * overlap_ratio
                        buy_contrib = bv * overlap_ratio
                        sell_contrib = sv * overlap_ratio
                        
                        array.set(vol_profile, i, array.get(vol_profile, i) + vol_contrib)
                        array.set(buy_vol, i, array.get(buy_vol, i) + buy_contrib)
                        array.set(sell_vol, i, array.get(sell_vol, i) + sell_contrib)
                        array.set(delta_profile, i, array.get(delta_profile, i) + (buy_contrib - sell_contrib))
        
        // POC finden
        max_vol_level := 0.0
        poc_idx := 0
        for i = 0 to row_count - 1
            v = array.get(vol_profile, i)
            if v > max_vol_level
                max_vol_level := v
                poc_idx := i
        
        poc_price := lowest_price + (poc_idx * row_height) + (row_height / 2)
        
        // Durchschnitt berechnen
        total_vol = 0.0
        for i = 0 to row_count - 1
            total_vol += array.get(vol_profile, i)
        avg_vol_level := row_count > 0 ? total_vol / row_count : 0
        
        // Value Area berechnen
        if total_vol > 0
            target_vol = total_vol * (va_percent / 100)
            va_vol = array.get(vol_profile, poc_idx)
            upper_idx = poc_idx
            lower_idx = poc_idx
            
            iterations = 0
            while va_vol < target_vol and iterations < row_count
                iterations += 1
                upper_v = upper_idx < row_count - 1 ? array.get(vol_profile, upper_idx + 1) : 0.0
                lower_v = lower_idx > 0 ? array.get(vol_profile, lower_idx - 1) : 0.0
                
                if upper_v >= lower_v and upper_idx < row_count - 1
                    upper_idx += 1
                    va_vol += upper_v
                else if lower_idx > 0
                    lower_idx -= 1
                    va_vol += lower_v
                else
                    break
            
            vah_price := lowest_price + ((upper_idx + 1) * row_height)
            val_price := lowest_price + (lower_idx * row_height)

// ══════════════════════════════════════════════════════════════════════════════
// HEATMAP ZEICHNEN
// ══════════════════════════════════════════════════════════════════════════════

var array<box> heatmap_boxes = array.new_box(0)

if barstate.islast and show_heatmap and max_vol_level > 0
    // Alte Boxes löschen
    if array.size(heatmap_boxes) > 0
        for i = 0 to array.size(heatmap_boxes) - 1
            box.delete(array.get(heatmap_boxes, i))
        array.clear(heatmap_boxes)
    
    for i = 0 to row_count - 1
        level_low = lowest_price + (i * row_height)
        level_high = level_low + row_height
        vol_at_level = safe_array_get(vol_profile, i)
        
        intensity = (vol_at_level / max_vol_level) * intensity_mult
        box_color = get_heatmap_color(intensity, color_scheme, heatmap_opacity)
        
        bar_width = math.round(3 + (intensity * 15))
        
        new_box = box.new(
             left=bar_index - lookback,
             top=level_high,
             right=bar_index + heatmap_extend_right,
             bottom=level_low,
             bgcolor=box_color,
             border_color=color.new(color.white, 100),
             border_width=0
         )
        array.push(heatmap_boxes, new_box)
        
        if intensity > 0.6
            intense_box = box.new(
                 left=bar_index + heatmap_extend_right,
                 top=level_high,
                 right=bar_index + heatmap_extend_right + bar_width,
                 bottom=level_low,
                 bgcolor=get_heatmap_color(intensity * 1.2, color_scheme, heatmap_opacity - 20),
                 border_color=color.new(color.white, 100),
                 border_width=0
             )
            array.push(heatmap_boxes, intense_box)

// ══════════════════════════════════════════════════════════════════════════════
// LIQUIDITÄTS-LEVELS (HVN / LVN)
// ══════════════════════════════════════════════════════════════════════════════

var array<line> liq_lines = array.new_line(0)
var array<label> liq_labels = array.new_label(0)

if barstate.islast and show_liquidity_levels and avg_vol_level > 0 and max_vol_level > 0
    // Alte löschen
    if array.size(liq_lines) > 0
        for i = 0 to array.size(liq_lines) - 1
            line.delete(array.get(liq_lines, i))
        array.clear(liq_lines)
    
    if array.size(liq_labels) > 0
        for i = 0 to array.size(liq_labels) - 1
            label.delete(array.get(liq_labels, i))
        array.clear(liq_labels)
    
    for i = 0 to row_count - 1
        vol_at_level = safe_array_get(vol_profile, i)
        level_price = lowest_price + (i * row_height) + (row_height / 2)
        delta_at_level = safe_array_get(delta_profile, i)
        
        // HVN
        if show_hvn and vol_at_level > avg_vol_level * liquidity_threshold
            intensity = vol_at_level / max_vol_level
            lw = intensity > 0.85 ? 3 : intensity > 0.6 ? 2 : 1
            line_col = delta_at_level > 0 ? color.new(#4caf50, 30) : color.new(#ff5722, 30)
            
            hvn_line = line.new(
                 x1=bar_index - lookback,
                 y1=level_price,
                 x2=bar_index + heatmap_extend_right + 30,
                 y2=level_price,
                 color=line_col,
                 width=lw,
                 style=line.style_solid
             )
            array.push(liq_lines, hvn_line)
            
            hvn_label = label.new(
                 x=bar_index + heatmap_extend_right + 32,
                 y=level_price,
                 text="HVN " + str.tostring(vol_at_level / 1000, "#.#") + "K",
                 style=label.style_label_left,
                 color=color.new(delta_at_level > 0 ? #4caf50 : #ff5722, 70),
                 textcolor=delta_at_level > 0 ? #4caf50 : #ff5722,
                 size=size.tiny
             )
            array.push(liq_labels, hvn_label)
        
        // LVN
        if show_lvn and vol_at_level < avg_vol_level * lvn_threshold and vol_at_level > 0
            lvn_line = line.new(
                 x1=bar_index - lookback,
                 y1=level_price,
                 x2=bar_index + heatmap_extend_right + 30,
                 y2=level_price,
                 color=color.new(#9c27b0, 50),
                 width=1,
                 style=line.style_dotted
             )
            array.push(liq_lines, lvn_line)

// ══════════════════════════════════════════════════════════════════════════════
// POC & VALUE AREA ZEICHNEN
// ══════════════════════════════════════════════════════════════════════════════

var line poc_line = na
var line vah_line = na
var line val_line = na
var box va_box = na
var label poc_label = na

poc_style = poc_line_style == "Solid" ? line.style_solid : poc_line_style == "Dashed" ? line.style_dashed : line.style_dotted

if barstate.islast and poc_price > 0
    line.delete(poc_line)
    line.delete(vah_line)
    line.delete(val_line)
    box.delete(va_box)
    label.delete(poc_label)
    
    if show_poc
        poc_line := line.new(
             x1=bar_index - lookback,
             y1=poc_price,
             x2=bar_index + heatmap_extend_right + 50,
             y2=poc_price,
             color=color.new(#f44336, 10),
             width=3,
             style=poc_style
         )
        
        poc_label := label.new(
             x=bar_index + heatmap_extend_right + 52,
             y=poc_price,
             text="POC " + str.tostring(poc_price, "#.##"),
             style=label.style_label_left,
             color=color.new(#f44336, 50),
             textcolor=#f44336,
             size=size.small
         )
    
    if show_value_area and vah_price > 0 and val_price > 0
        vah_line := line.new(
             x1=bar_index - lookback,
             y1=vah_price,
             x2=bar_index + heatmap_extend_right + 50,
             y2=vah_price,
             color=color.new(#2196f3, 30),
             width=2,
             style=line.style_dashed
         )
        
        val_line := line.new(
             x1=bar_index - lookback,
             y1=val_price,
             x2=bar_index + heatmap_extend_right + 50,
             y2=val_price,
             color=color.new(#2196f3, 30),
             width=2,
             style=line.style_dashed
         )
        
        va_box := box.new(
             left=bar_index - lookback,
             top=vah_price,
             right=bar_index + heatmap_extend_right + 50,
             bottom=val_price,
             bgcolor=color.new(#2196f3, 95),
             border_color=color.new(#2196f3, 80),
             border_width=1
         )

// ══════════════════════════════════════════════════════════════════════════════
// VWAP MIT BÄNDERN
// ══════════════════════════════════════════════════════════════════════════════

vwap_val = ta.vwap(hlc3)

var float sum_vol = 0.0
var float sum_vol_price = 0.0
var float sum_vol_price_sq = 0.0

if ta.change(time("D")) != 0
    sum_vol := 0.0
    sum_vol_price := 0.0
    sum_vol_price_sq := 0.0

sum_vol += nz(volume, 0)
sum_vol_price += nz(volume, 0) * hlc3
sum_vol_price_sq += nz(volume, 0) * math.pow(hlc3, 2)

vwap_calc = sum_vol > 0 ? sum_vol_price / sum_vol : close
vwap_var = sum_vol > 0 ? math.max((sum_vol_price_sq / sum_vol) - math.pow(vwap_calc, 2), 0) : 0
vwap_std = math.sqrt(vwap_var)

vwap_upper1 = vwap_calc + vwap_std
vwap_lower1 = vwap_calc - vwap_std
vwap_upper2 = vwap_calc + (vwap_std * 2)
vwap_lower2 = vwap_calc - (vwap_std * 2)

plot(show_vwap ? vwap_calc : na, "VWAP", color=color.new(#ff9800, 20), linewidth=2)
plot(show_vwap and show_vwap_bands ? vwap_upper1 : na, "VWAP +1σ", color=color.new(#ff9800, 60), linewidth=1)
plot(show_vwap and show_vwap_bands ? vwap_lower1 : na, "VWAP -1σ", color=color.new(#ff9800, 60), linewidth=1)
plot(show_vwap and show_vwap_bands ? vwap_upper2 : na, "VWAP +2σ", color=color.new(#ff9800, 80), linewidth=1, style=plot.style_circles)
plot(show_vwap and show_vwap_bands ? vwap_lower2 : na, "VWAP -2σ", color=color.new(#ff9800, 80), linewidth=1, style=plot.style_circles)

// ══════════════════════════════════════════════════════════════════════════════
// VOLUME BUBBLES
// ══════════════════════════════════════════════════════════════════════════════

avg_volume = ta.sma(volume, 20)
vol_ratio = nz(avg_volume, 1) > 0 ? nz(volume, 0) / nz(avg_volume, 1) : 0

is_small_vol = vol_ratio >= bubble_threshold_small and vol_ratio < bubble_threshold_medium
is_medium_vol = vol_ratio >= bubble_threshold_medium and vol_ratio < bubble_threshold_large
is_large_vol = vol_ratio >= bubble_threshold_large

is_buy_bar = close > open
is_sell_bar = close < open

plotshape(show_bubbles and is_small_vol and is_buy_bar, title="Buy Small", style=shape.circle, location=location.absolute, color=color.new(#4caf50, 40), size=size.tiny)
plotshape(show_bubbles and is_medium_vol and is_buy_bar, title="Buy Medium", style=shape.circle, location=location.absolute, color=color.new(#4caf50, 25), size=size.small)
plotshape(show_bubbles and is_large_vol and is_buy_bar, title="Buy Large", style=shape.circle, location=location.absolute, color=color.new(#00e676, 10), size=size.normal)

plotshape(show_bubbles and is_small_vol and is_sell_bar, title="Sell Small", style=shape.circle, location=location.absolute, color=color.new(#f44336, 40), size=size.tiny)
plotshape(show_bubbles and is_medium_vol and is_sell_bar, title="Sell Medium", style=shape.circle, location=location.absolute, color=color.new(#f44336, 25), size=size.small)
plotshape(show_bubbles and is_large_vol and is_sell_bar, title="Sell Large", style=shape.circle, location=location.absolute, color=color.new(#ff1744, 10), size=size.normal)

// ══════════════════════════════════════════════════════════════════════════════
// DELTA HINTERGRUND
// ══════════════════════════════════════════════════════════════════════════════

delta_intensity = math.min(math.abs(delta_percent) / 100, 0.2)
delta_bg_color = delta > 0 ? color.new(#4caf50, 92 - math.round(delta_intensity * 20)) : color.new(#f44336, 92 - math.round(delta_intensity * 20))
bgcolor(show_delta_bg ? delta_bg_color : na, title="Delta Background")

// ══════════════════════════════════════════════════════════════════════════════
// IMBALANCE DETECTION
// ══════════════════════════════════════════════════════════════════════════════

buy_sell_ratio = est_sell_vol > 0 ? est_buy_vol / est_sell_vol : 999
strong_buy_imbalance = buy_sell_ratio > imbalance_threshold and vol_ratio > bubble_threshold_small
strong_sell_imbalance = buy_sell_ratio < (1 / imbalance_threshold) and vol_ratio > bubble_threshold_small

plotshape(show_imbalance and strong_buy_imbalance, title="Buy Imbalance", style=shape.triangleup, location=location.belowbar, color=color.new(#00e676, 0), size=size.tiny)
plotshape(show_imbalance and strong_sell_imbalance, title="Sell Imbalance", style=shape.triangledown, location=location.abovebar, color=color.new(#ff5252, 0), size=size.tiny)

// ══════════════════════════════════════════════════════════════════════════════
// STACKED IMBALANCES
// ══════════════════════════════════════════════════════════════════════════════

var int consec_buy_imb = 0
var int consec_sell_imb = 0

if strong_buy_imbalance
    consec_buy_imb += 1
    consec_sell_imb := 0
else if strong_sell_imbalance
    consec_sell_imb += 1
    consec_buy_imb := 0
else
    consec_buy_imb := 0
    consec_sell_imb := 0

stacked_buy = consec_buy_imb >= stack_count
stacked_sell = consec_sell_imb >= stack_count

plotshape(show_stacked and stacked_buy and consec_buy_imb == stack_count, title="Stacked Buy", style=shape.diamond, location=location.belowbar, color=color.new(#00e676, 0), size=size.small)
plotshape(show_stacked and stacked_sell and consec_sell_imb == stack_count, title="Stacked Sell", style=shape.diamond, location=location.abovebar, color=color.new(#ff5252, 0), size=size.small)

// ══════════════════════════════════════════════════════════════════════════════
// CVD VISUALISIERUNG
// ══════════════════════════════════════════════════════════════════════════════

cvd_max = ta.highest(math.abs(cvd), lookback)
cvd_norm = cvd_max > 0 ? (cvd / cvd_max) * (price_range * 0.08) : 0
cvd_plot_price = lowest_price + cvd_norm + (price_range * 0.04)

cvd_color = cvd > 0 ? color.new(#4caf50, 60) : color.new(#f44336, 60)
plot(show_cvd ? cvd_plot_price : na, "CVD", color=cvd_color, linewidth=2, style=plot.style_area)

// ══════════════════════════════════════════════════════════════════════════════
// INFO DASHBOARD
// ══════════════════════════════════════════════════════════════════════════════

var table dash = table.new(position.top_right, 2, 12, bgcolor=color.new(#0a1628, 5), border_width=1, border_color=color.new(#00bcd4, 50))

if barstate.islast
    table.cell(dash, 0, 0, "📊 ORDER FLOW", text_color=#00e5ff, text_size=size.small, bgcolor=color.new(#0d47a1, 40))
    table.cell(dash, 1, 0, "v2.2", text_color=#00e5ff, text_size=size.small, bgcolor=color.new(#0d47a1, 40))
    
    table.cell(dash, 0, 1, "POC", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 1, str.tostring(poc_price, "#.##"), text_color=#f44336, text_size=size.tiny)
    
    table.cell(dash, 0, 2, "VAH", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 2, str.tostring(vah_price, "#.##"), text_color=#2196f3, text_size=size.tiny)
    
    table.cell(dash, 0, 3, "VAL", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 3, str.tostring(val_price, "#.##"), text_color=#2196f3, text_size=size.tiny)
    
    table.cell(dash, 0, 4, "VWAP", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 4, str.tostring(vwap_calc, "#.##"), text_color=#ff9800, text_size=size.tiny)
    
    delta_col = delta > 0 ? #4caf50 : #f44336
    delta_str = (delta > 0 ? "+" : "") + str.tostring(delta / 1000, "#.#") + "K"
    table.cell(dash, 0, 5, "Delta", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 5, delta_str, text_color=delta_col, text_size=size.tiny)
    
    cvd_col = cvd > 0 ? #4caf50 : #f44336
    cvd_str = (cvd > 0 ? "+" : "") + str.tostring(cvd / 1000000, "#.##") + "M"
    table.cell(dash, 0, 6, "CVD", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 6, cvd_str, text_color=cvd_col, text_size=size.tiny)
    
    vol_col = vol_ratio > bubble_threshold_large ? #ff9800 : vol_ratio > bubble_threshold_small ? #ffeb3b : color.gray
    table.cell(dash, 0, 7, "Vol Ratio", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 7, str.tostring(vol_ratio, "#.##") + "x", text_color=vol_col, text_size=size.tiny)
    
    imb_text = strong_buy_imbalance ? "🟢 BUY" : strong_sell_imbalance ? "🔴 SELL" : "⚪ Neutral"
    imb_col = strong_buy_imbalance ? #4caf50 : strong_sell_imbalance ? #f44336 : color.gray
    table.cell(dash, 0, 8, "Imbalance", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 8, imb_text, text_color=imb_col, text_size=size.tiny)
    
    stack_text = stacked_buy ? "⬆️ " + str.tostring(consec_buy_imb) + "x" : stacked_sell ? "⬇️ " + str.tostring(consec_sell_imb) + "x" : "—"
    stack_col = stacked_buy ? #00e676 : stacked_sell ? #ff5252 : color.gray
    table.cell(dash, 0, 9, "Stacked", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 9, stack_text, text_color=stack_col, text_size=size.tiny)
    
    pos_poc = close > poc_price ? "↑ Über" : close < poc_price ? "↓ Unter" : "="
    pos_col = close > poc_price ? #4caf50 : close < poc_price ? #f44336 : color.gray
    table.cell(dash, 0, 10, "vs POC", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 10, pos_poc, text_color=pos_col, text_size=size.tiny)
    
    in_va = close >= val_price and close <= vah_price
    va_text = in_va ? "✓ In VA" : "✗ Out"
    table.cell(dash, 0, 11, "Value Area", text_color=color.silver, text_size=size.tiny)
    table.cell(dash, 1, 11, va_text, text_color=in_va ? #4caf50 : #ff9800, text_size=size.tiny)

// ══════════════════════════════════════════════════════════════════════════════
// ALERTS
// ══════════════════════════════════════════════════════════════════════════════

alertcondition(ta.cross(close, poc_price), title="POC Touch", message="📍 Preis berührt POC!")
alertcondition(strong_buy_imbalance, title="Buy Imbalance", message="🟢 Kauf-Imbalance!")
alertcondition(strong_sell_imbalance, title="Sell Imbalance", message="🔴 Verkauf-Imbalance!")
alertcondition(stacked_buy and consec_buy_imb == stack_count, title="Stacked Buy", message="⬆️⬆️ STACKED BUY!")
alertcondition(stacked_sell and consec_sell_imb == stack_count, title="Stacked Sell", message="⬇️⬇️ STACKED SELL!")
alertcondition(ta.crossover(close, vah_price), title="VAH Breakout", message="↑ VAH Breakout!")
alertcondition(ta.crossunder(close, val_price), title="VAL Breakdown", message="↓ VAL Breakdown!")
alertcondition(is_large_vol, title="High Volume", message="🔥 High Volume!")

// ══════════════════════════════════════════════════════════════════════════════

Overview

Order Flow Heatmap marks the point of control, value area, high and low volume nodes, volume bubbles for large bars, session VWAP with deviation bands and imbalance markers, including stacked imbalances. Delta and cumulative delta are approximated from candle data, not true order flow. Colour scheme, rows, thresholds and opacity are adjustable.

Details

Published by

grzresearch on GitHub

Draws

On the price chart

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

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

SettingTypeDefaultVariable
Lookback Barsint100lookback
Preis-Levels (Rows)int40row_count
Heatmap anzeigenbooltrueshow_heatmap
Heatmap Transparenzint80heatmap_opacity
Heatmap nach rechts erweiternint20heatmap_extend_right
FarbschemastringBookmap Bluecolor_scheme
Intensitäts-Multiplikatorfloat1.5intensity_mult
Dynamische Preis-Rangebooltrueuse_dynamic_range
Liquiditäts-Levels anzeigenbooltrueshow_liquidity_levels
Liquiditäts-Schwelle (x Avg)float1.8liquidity_threshold
High Volume Nodes (HVN)booltrueshow_hvn
Low Volume Nodes (LVN)booltrueshow_lvn
Show all 29 settings
SettingTypeDefaultVariable
LVN Schwelle (x Avg)float0.3lvn_threshold
Volume Bubbles anzeigenbooltrueshow_bubbles
Kleine Bubble Schwellefloat1.3bubble_threshold_small
Mittlere Bubble Schwellefloat1.8bubble_threshold_medium
Große Bubble Schwellefloat2.5bubble_threshold_large
Delta Hintergrundbooltrueshow_delta_bg
CVD (Cumulative Volume Delta)booltrueshow_cvd
Imbalance Markerbooltrueshow_imbalance
Imbalance Ratiofloat2.5imbalance_threshold
POC (Point of Control)booltrueshow_poc
Value Areabooltrueshow_value_area
Value Area %float70.0va_percent
POC Linien-StilstringSolidpoc_line_style
Session VWAPbooltrueshow_vwap
VWAP Bänder (±1σ, ±2σ)booltrueshow_vwap_bands
Stacked Imbalances anzeigenbooltrueshow_stacked
Min. aufeinanderfolgende Imbalancesint3stack_count
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 overlay indicator draws a fixed-range volume profile on the price chart, splitting traded activity into up and down volume at each price level. It also plots a sentiment profile, a developing point of control, and value-area high and low around a configur

Indicator v5 VolumeSupport & resistance
Updated 2 months ago · MIT GitHub repository of Volume Profile and Indicator by DGT

MGIC-tracker

by Allen1212
303 stars

The MGIC-tracker is a TradingView indicator that overlays ARK Invest trade records onto stock price charts. It plots buy and sell labels at the exact timestamp of each transaction, showing share volume and ETF weight. Users select a specific ETF type via setti

Indicator v4 Support & resistanceVolume
Updated 2 years ago · None stated GitHub repository of MGIC-tracker
228 stars

Comprehensive Volume Profile is an overlay indicator that calculates and plots volume distribution across specified time ranges such as daily sessions, weekly, monthly, or custom bar lengths. It identifies key structural price levels including the Point of Con

Indicator v6 VolumeSupport & resistance
Updated 3 months ago · None stated GitHub repository of Comprehensive Volume Profile

Moon Dev GEX Arrows

by moondevonyt
92 stars

This overlay indicator plots option strike walls and an optional session VWAP on the price chart for SPY or ES. It spaces major strikes by the selected instrument, draws a set number of strikes on each side, and marks wall-trap and sweep events after a cooldow

Indicator v5 Support & resistanceSessions & time
Updated 2 months ago · Custom licence GitHub repository of Moon Dev GEX Arrows

More Volume scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers