Dear Coders.
Need your Help convert this TV Script indicator to MT4 please...
There is the Swing High and Swing Low based Volume Profile with build Zag Zag of BigBeluga concept's.
Swing Profile [BigBeluga] is a dynamic swing-based volume profiling tool that builds a complete volume profile for each completed market swing.
Instead of using fixed sessions or time ranges, the indicator anchors its profile strictly between confirmed swing highs and swing lows, allowing traders to analyze where volume accumulated inside each directional leg.
The profile updates in real time while a swing is still forming and finalizes once the swing direction flips, giving both historical and live insight into volume behavior.
Code: Select all
// This work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
// https://creativecommons.org/licenses/by-nc-sa/4.0/
// © BigBeluga
//@version=6
indicator("Swing Profile [BigBeluga]", overlay = true, max_labels_count = 500, max_boxes_count = 500, calc_bars_count = 2000, max_bars_back = 5000)
// INPUTS ---------------------------------------------------------------------
swingLen = input.int(50, "Swing Length", minval = 10)
swingColorUp = input.color(color.lime, "▲", inline = "colors")
swingColorDown = input.color(color.orange, "▼", inline = "colors")
showProfile = input.bool(true, "Profile", group = "Swing Volume Profile")
showHeatMap = input.bool(false, "HeatMap", group = "Swing Volume Profile")
showZigZag = input.bool(true, "ZigZag", inline = "Zz")
zigZagStyleInput = input.string("Dotted", "", ["Dotted", "Dashed", "Solid"], inline = "Zz")
showPOC = input.bool(true, "PoC", "", inline = "Poc", group = "Point of Control")
pocWidthInput = input.int(2, "", inline = "Poc", group = "Point of Control")
pocColor = input.color(color.red, "", inline = "Poc", group = "Point of Control")
transparentNA = color.new(color.black, 100)
string dataSizeInput = input.string("Small", title = "Data Size", options = ["Tiny", "Small", "Normal", "Large", "Huge"])
// Size switcher for labels
getLabelSize() =>
switch dataSizeInput
"Small" => size.small
"Normal" => size.normal
"Large" => size.large
"Huge" => size.huge
=> size.tiny
getZigZagLineStyle() =>
switch zigZagStyleInput
"Dotted" => line.style_dotted
"Dashed" => line.style_dashed
"Solid" => line.style_solid
// Persistent storage containers
var polyline profilePoly = na
var dataLabel = label(na)
var pocLineHandle = line(na)
var profileBoxes = array.new<box>()
// Structs for swing points
type SwingData
float price
int index
var highSwing = SwingData.new(na, na)
var lowSwing = SwingData.new(na, na)
// Direction flag: true = down move, false = up move
var isDownMove = false
// CALCULATIONS ---------------------------------------------------------------
// Detect swing highs and lows
highestSwingHigh = ta.highest(swingLen)
lowestSwingLow = ta.lowest(swingLen)
if high == highestSwingHigh
isDownMove := true
if low == lowestSwingLow
isDownMove := false
// Store completed swing high
if high[1] == highestSwingHigh[1] and high < highestSwingHigh
highSwing.index := bar_index[1]
highSwing.price := high[1]
// Store completed swing low
if low[1] == lowestSwingLow[1] and low > lowestSwingLow
lowSwing.index := bar_index[1]
lowSwing.price := low[1]
// Default bin size based on ATR
priceBinSize = ta.atr(200) * 0.5
// HISTORICAL SWING LEG -------------------------------------------------------
// When direction flips we finalize previous swing leg and build full profile
if isDownMove != isDownMove[1]
// Clear previous drawings
profilePoly.delete()
dataLabel.delete()
for b in profileBoxes
b.delete()
profileBoxes.clear()
swingMainColor = isDownMove ? swingColorDown : swingColorUp
// Draw zigzag connector
if showZigZag
line.new(highSwing.index, highSwing.price, lowSwing.index, lowSwing.price, color = swingMainColor, style = getZigZagLineStyle())
// Determine swing boundaries
swingBottom = math.min(highSwing.price, lowSwing.price)
swingTop = math.max(highSwing.price, lowSwing.price)
// Number of volume bins
binCount = int(math.abs(highSwing.price - lowSwing.price) / priceBinSize)
binStep = math.abs(highSwing.price - lowSwing.price) / binCount
lastSwingIndex = math.max(highSwing.index, lowSwing.index)
prevSwingIndex = math.min(highSwing.index, lowSwing.index)
// Arrays for volume accumulation
volumeBins = array.new<float>(binCount, 0.)
profilePoints = array.new<chart.point>()
buyVolumeBins = array.new<float>(binCount, 0.)
sellVolumeBins = array.new<float>(binCount, 0.)
barOffset = bar_index - lastSwingIndex
// Accumulate volume per price bin
for i = barOffset to (lastSwingIndex - prevSwingIndex) + barOffset
candleClose = close[i]
candleOpen = open[i]
candleVol = volume[i]
for k = 0 to binCount - 1
binMidPrice = swingBottom + (k * binStep) + (binStep / 2)
if math.abs(binMidPrice - candleClose) < binStep
volumeBins.set(k, volumeBins.get(k) + candleVol)
if candleClose > candleOpen
buyVolumeBins.set(k, buyVolumeBins.get(k) + candleVol)
else
sellVolumeBins.set(k, sellVolumeBins.get(k) + candleVol)
// Draw boxes and build profile outline
for k = 0 to binCount - 1
binLow = swingBottom + (k * binStep)
binHigh = binLow + binStep
volumeRatio = volumeBins.get(k) / volumeBins.max()
profileWidth = int(volumeRatio * ((lastSwingIndex - prevSwingIndex) / 2))
// Draw POC line
if volumeRatio == 1 and showPOC
line.new(showProfile ? prevSwingIndex + profileWidth : prevSwingIndex, math.avg(binLow, binHigh), lastSwingIndex, math.avg(binLow, binHigh), color = pocColor, width = pocWidthInput)
// Create volume box
box.new(
showProfile ? prevSwingIndex + profileWidth : prevSwingIndex,
binHigh,
showHeatMap ? lastSwingIndex : prevSwingIndex,
binLow,
transparentNA,
0,
bgcolor = showProfile or showHeatMap ? color.from_gradient(volumeRatio, 0, 1, color.new(swingMainColor, 90), color.new(volumeRatio == 1 and showPOC ? pocColor : swingMainColor, 50)) : transparentNA,
text = showHeatMap ? "" : volumeRatio == 1 ? str.tostring(volumeBins.max(), format.volume) : ""
)
// Build polygon outline
if k == 0
profilePoints.push(chart.point.from_index(prevSwingIndex, binLow))
profilePoints.push(chart.point.from_index(prevSwingIndex + profileWidth, binLow))
profilePoints.push(chart.point.from_index(prevSwingIndex + profileWidth, binHigh))
if k == binCount - 1
profilePoints.push(chart.point.from_index(prevSwingIndex, binHigh))
// Draw outer polyline
if showProfile
polyline.new(profilePoints, false, true, line_color = showHeatMap ? color.new(swingMainColor, 50) : swingMainColor)
// Summary label for historical swing
swingArrowText = isDownMove ? "▼" : "▲"
swingTooltip =
"Total Volume: " + str.tostring(volumeBins.sum(), format.volume)
+ "\nBuy Volume: " + str.tostring(buyVolumeBins.sum(), format.volume)
+ "\nSell Volume: " + str.tostring(sellVolumeBins.sum(), format.volume)
+ "\nDelta Volume: " + str.tostring((buyVolumeBins.sum() - sellVolumeBins.sum()) / volumeBins.sum() * 100, format.percent)
label.new(prevSwingIndex, isDownMove ? swingTop : swingBottom, swingArrowText,
style = isDownMove ? label.style_label_down : label.style_label_up,
color = transparentNA,
textcolor = swingMainColor,
size = getLabelSize(),
tooltip = swingTooltip)
// REAL-TIME SWING LEG --------------------------------------------------------
// Real-time profile rebuilding while swing is still forming
if not isDownMove
// Clear boxes on every update
for b in profileBoxes
b.delete()
profileBoxes.clear()
if barstate.islast
swingMainColor = chart.fg_color
// Remove previous zigzag preview line
line.delete(line.new(highSwing.index, highSwing.price, lowSwing.index, lowSwing.price, color = swingMainColor, style = line.style_dashed)[1])
swingBottom = math.min(highSwing.price, lowSwing.price)
swingTop = math.max(highSwing.price, lowSwing.price)
binCount = int(math.abs(highSwing.price - lowSwing.price) / priceBinSize)
binStep = math.abs(highSwing.price - lowSwing.price) / binCount
lastSwingIndex = math.max(highSwing.index, lowSwing.index)
prevSwingIndex = math.min(highSwing.index, lowSwing.index)
volumeBins = array.new<float>(binCount, 0.)
profilePoints = array.new<chart.point>()
buyVolumeBins = array.new<float>(binCount, 0.)
sellVolumeBins = array.new<float>(binCount, 0.)
barOffset = bar_index - lastSwingIndex
// Accumulate volume in real time
for i = barOffset to (lastSwingIndex - prevSwingIndex) + barOffset
candleClose = close[i]
candleOpen = open[i]
candleVol = volume[i]
for k = 0 to binCount - 1
binMidPrice = swingBottom + (k * binStep) + (binStep / 2)
if math.abs(binMidPrice - candleClose) < binStep
volumeBins.set(k, volumeBins.get(k) + candleVol)
if candleClose > candleOpen
buyVolumeBins.set(k, buyVolumeBins.get(k) + candleVol)
else
sellVolumeBins.set(k, sellVolumeBins.get(k) + candleVol)
// Draw real-time boxes
for k = 0 to binCount - 1
binLow = swingBottom + (k * binStep)
binHigh = binLow + binStep
volumeRatio = volumeBins.get(k) / volumeBins.max()
profileWidth = int(volumeRatio * ((lastSwingIndex - prevSwingIndex) / 2))
if volumeRatio == 1 and showPOC
pocLineHandle := line.new(showProfile ? prevSwingIndex + profileWidth : prevSwingIndex, math.avg(binLow, binHigh), lastSwingIndex, math.avg(binLow, binHigh), color = pocColor, width = pocWidthInput)
line.delete(pocLineHandle[1])
profileBoxes.push(box.new(
showProfile ? prevSwingIndex + profileWidth : prevSwingIndex,
binHigh,
showHeatMap ? lastSwingIndex : prevSwingIndex,
binLow,
transparentNA,
0,
bgcolor = showProfile or showHeatMap ? color.from_gradient(volumeRatio, 0, 1, color.new(swingMainColor, 90), color.new(volumeRatio == 1 and showPOC ? pocColor : swingMainColor, 50)) : transparentNA,
text = showHeatMap ? "" : volumeRatio == 1 ? str.tostring(volumeBins.max(), format.volume) : ""
))
if k == 0
profilePoints.push(chart.point.from_index(prevSwingIndex, binLow))
profilePoints.push(chart.point.from_index(prevSwingIndex + profileWidth, binLow))
profilePoints.push(chart.point.from_index(prevSwingIndex + profileWidth, binHigh))
if k == binCount - 1
profilePoints.push(chart.point.from_index(prevSwingIndex, binHigh))
// Update polyline in real time
if showProfile
polyline.delete(profilePoly[1])
profilePoly := polyline.new(profilePoints, false, true, line_color = showHeatMap ? color.new(swingMainColor, 50) : swingMainColor)
// Label with summary data
swingTooltip = "T - Total Volume\nB - Buy Volume\nS - Sell Volume\nD - Delta Volume"
swingDataText =
"T: " + str.tostring(volumeBins.sum(), format.volume)
+ "\nB: " + str.tostring(buyVolumeBins.sum(), format.volume)
+ "\nS: " + str.tostring(sellVolumeBins.sum(), format.volume)
+ "\nD: " + str.tostring((buyVolumeBins.sum() - sellVolumeBins.sum()) / volumeBins.sum() * 100, format.percent)
dataLabel := label.new(prevSwingIndex, not isDownMove ? swingTop : swingBottom, swingDataText,
style = not isDownMove ? label.style_label_down : label.style_label_up,
color = transparentNA,
textcolor = swingMainColor,
size = getLabelSize(),
tooltip = swingTooltip,
textalign = text.align_left)
label.delete(dataLabel[1])
// MIRROR OF ABOVE FOR isDownMove == true ------------------------------------------
// Mirror logic for bullish direction
else
for b in profileBoxes
b.delete()
profileBoxes.clear()
if barstate.islast
swingMainColor = chart.fg_color
line.delete(line.new(highSwing.index, highSwing.price, lowSwing.index, lowSwing.price, color = swingMainColor, style = line.style_dashed)[1])
swingBottom = math.min(highSwing.price, lowSwing.price)
swingTop = math.max(highSwing.price, lowSwing.price)
binCount = int(math.abs(highSwing.price - lowSwing.price) / priceBinSize)
binStep = math.abs(highSwing.price - lowSwing.price) / binCount
lastSwingIndex = math.max(highSwing.index, lowSwing.index)
prevSwingIndex = math.min(highSwing.index, lowSwing.index)
volumeBins = array.new<float>(binCount, 0.)
profilePoints = array.new<chart.point>()
buyVolumeBins = array.new<float>(binCount, 0.)
sellVolumeBins = array.new<float>(binCount, 0.)
barOffset = bar_index - lastSwingIndex
for i = barOffset to (lastSwingIndex - prevSwingIndex) + barOffset
candleClose = close[i]
candleOpen = open[i]
candleVol = volume[i]
for k = 0 to binCount - 1
binMidPrice = swingBottom + (k * binStep) + (binStep / 2)
if math.abs(binMidPrice - candleClose) < binStep
volumeBins.set(k, volumeBins.get(k) + candleVol)
if candleClose > candleOpen
buyVolumeBins.set(k, buyVolumeBins.get(k) + candleVol)
else
sellVolumeBins.set(k, sellVolumeBins.get(k) + candleVol)
for k = 0 to binCount - 1
binLow = swingBottom + (k * binStep)
binHigh = binLow + binStep
volumeRatio = volumeBins.get(k) / volumeBins.max()
profileWidth = int(volumeRatio * ((lastSwingIndex - prevSwingIndex) / 2))
if volumeRatio == 1 and showPOC
pocLineHandle := line.new(showProfile ? prevSwingIndex + profileWidth : prevSwingIndex, math.avg(binLow, binHigh), lastSwingIndex, math.avg(binLow, binHigh), color = pocColor, width = pocWidthInput)
line.delete(pocLineHandle[1])
profileBoxes.push(box.new(
showProfile ? prevSwingIndex + profileWidth : prevSwingIndex,
binHigh,
showHeatMap ? lastSwingIndex : prevSwingIndex,
binLow,
transparentNA,
0,
bgcolor = showProfile or showHeatMap ? color.from_gradient(volumeRatio, 0, 1, color.new(swingMainColor, 90), color.new(volumeRatio == 1 and showPOC ? pocColor : swingMainColor, 50)) : transparentNA,
text = showHeatMap ? "" : volumeRatio == 1 ? str.tostring(volumeBins.max(), format.volume) : ""
))
if k == 0
profilePoints.push(chart.point.from_index(prevSwingIndex, binLow))
profilePoints.push(chart.point.from_index(prevSwingIndex + profileWidth, binLow))
profilePoints.push(chart.point.from_index(prevSwingIndex + profileWidth, binHigh))
if k == binCount - 1
profilePoints.push(chart.point.from_index(prevSwingIndex, binHigh))
if showProfile
polyline.delete(profilePoly[1])
profilePoly := polyline.new(profilePoints, false, true, line_color = showHeatMap ? color.new(swingMainColor, 50) : swingMainColor)
swingTooltip = "T - Total Volume\nB - Buy Volume\nS - Sell Volume\nD - Delta Volume"
swingDataText =
"T: " + str.tostring(volumeBins.sum(), format.volume)
+ "\nB: " + str.tostring(buyVolumeBins.sum(), format.volume)
+ "\nS: " + str.tostring(sellVolumeBins.sum(), format.volume)
+ "\nD: " + str.tostring((buyVolumeBins.sum() - sellVolumeBins.sum()) / volumeBins.sum() * 100, format.percent)
dataLabel := label.new(prevSwingIndex, not isDownMove ? swingTop : swingBottom, swingDataText,
style = not isDownMove ? label.style_label_down : label.style_label_up,
color = transparentNA,
textcolor = swingMainColor,
size = getLabelSize(),
tooltip = swingTooltip,
textalign = text.align_left)
label.delete(dataLabel[1])
// Smoothing MA inputs
GRP = "Moving Average"
maTypeInput = input.string("None", "Type", options = ["None", "SMA", "EMA", "SMMA (RMA)", "WMA", "VWMA"], group = GRP, display = display.none)
maLengthInput = input.int(25, "Length", group = GRP, display = display.none, active = maTypeInput != "None")
src = input(close, title="Source", group = GRP, active = maTypeInput != "None")
// Smoothing MA Calculation
ma(source, length, MAtype) =>
switch MAtype
"SMA" => ta.sma(source, length)
"EMA" => ta.ema(source, length)
"SMMA (RMA)" => ta.rma(source, length)
"WMA" => ta.wma(source, length)
"VWMA" => ta.vwma(source, length)
plot(ma(src, maLengthInput, maTypeInput), color=color.yellow, title="MA")