1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
1,164 lines of Pine Script v6 from src/futures/meridian-futures-profile.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.
meridian-futures-profile.pine
//@version=6
// SPDX-License-Identifier: MPL-2.0
// Meridian — Futures Profile · v0.2.0
indicator(
title = "Meridian - Futures Profile",
shorttitle = "Meridian Futures Profile",
overlay = true,
format = format.price,
precision = 2,
max_bars_back = 5000,
max_boxes_count = 500,
max_lines_count = 250,
max_labels_count = 300,
explicit_plot_zorder = true)
const string G_GENERAL = "01 — General"
const string G_SESSION = "02 — Session + TPO Blocks"
const string G_ROWS = "03 — Profile Rows"
const string G_DISPLAY = "04 — Profile Display"
const string G_LEVELS = "05 — Levels + Structure"
const string G_VOLUME = "06 — Estimated Volume Profile"
const string G_AMT = "07 — Auction Classification"
const string G_IBQUAL = "08 — Initial Balance Quality"
const string G_THEME = "09 — Meridian Theme"
const string G_DASH = "10 — Dashboard"
const string G_ALERTS = "11 — Alerts + Telemetry"
bool indicatorEnabled = input.bool(true, "Enable indicator", group = G_GENERAL)
string sessionTimezone = input.string("America/New_York", "Analysis time zone", group = G_GENERAL)
bool requireStandardCandles = input.bool(true, "Warn on non-standard charts", group = G_GENERAL,
tooltip = "Profiles are intended for standard time-based candles. Heikin Ashi, Renko, and other synthetic charts can distort ranges and volume.")
string rthSession = input.session("0930-1600:23456", "RTH session", group = G_SESSION)
int rthOpenHour = input.int(9, "RTH open hour", minval = 0, maxval = 23, inline = "open", group = G_SESSION)
int rthOpenMinute = input.int(30, "Minute", minval = 0, maxval = 59, inline = "open", group = G_SESSION)
string blockSizeInput = input.string("30 Minutes", "TPO block size",
options = ["5 Minutes", "10 Minutes", "15 Minutes", "30 Minutes", "60 Minutes"], group = G_SESSION)
int initialBalanceBlocks = input.int(2, "Initial Balance blocks", minval = 1, maxval = 12, group = G_SESSION,
tooltip = "Two 30-minute blocks produce the conventional 60-minute Initial Balance.")
int maximumChartMinutes = input.int(30, "Maximum chart timeframe", minval = 1, maxval = 60, group = G_SESSION,
tooltip = "The chart timeframe should not exceed the selected TPO block size. 1m, 5m, and 15m charts are recommended.")
string rowSizeMode = input.string("Auto", "Row-size mode", options = ["Auto", "Manual"], group = G_ROWS)
int manualTicksPerRow = input.int(8, "Manual ticks per row", minval = 1, maxval = 1000, group = G_ROWS)
int automaticTargetRows = input.int(70, "Automatic target rows", minval = 20, maxval = 150, group = G_ROWS)
int minimumAutomaticTicks = input.int(1, "Minimum automatic ticks", minval = 1, maxval = 100, group = G_ROWS)
int maximumRenderedRows = input.int(100, "Maximum rendered rows", minval = 30, maxval = 140, group = G_ROWS,
tooltip = "Profiles with more rows remain mathematically intact but are visually grouped to respect Pine drawing limits.")
int maximumRowsPerBlock = input.int(250, "Maximum rows per TPO block", minval = 50, maxval = 1000, group = G_ROWS,
tooltip = "Protects the script when a manual row size is too small for a volatile bar or gap.")
float valueAreaPercent = input.float(70.0, "Value area percentage", minval = 50.0, maxval = 95.0, step = 1.0, group = G_ROWS)
bool showCurrentProfile = input.bool(true, "Show current profile", group = G_DISPLAY)
bool showPreviousProfile = input.bool(true, "Show previous profile", group = G_DISPLAY)
int profileOffsetBars = input.int(5, "Profile offset from latest bar", minval = 1, maxval = 100, group = G_DISPLAY)
int maximumProfileWidthBars = input.int(20, "Maximum TPO width (bars)", minval = 6, maxval = 50, group = G_DISPLAY)
int profileGapBars = input.int(3, "Gap between profiles", minval = 1, maxval = 20, group = G_DISPLAY)
bool showRowCounts = input.bool(false, "Show TPO count on rows", group = G_DISPLAY)
bool showProfileNames = input.bool(true, "Show Current / Previous labels", group = G_DISPLAY)
bool showValueAreaShading = input.bool(true, "Highlight value-area rows", group = G_DISPLAY)
bool showProfileMidpoint = input.bool(true, "Show profile midpoint", group = G_DISPLAY)
bool showCurrentLevels = input.bool(true, "Show current POC / VAH / VAL", group = G_LEVELS)
bool extendPreviousLevels = input.bool(true, "Extend previous POC / VAH / VAL", group = G_LEVELS)
bool showInitialBalance = input.bool(true, "Show Initial Balance", group = G_LEVELS)
bool showIbExtensions = input.bool(true, "Show IB range extensions", group = G_LEVELS)
float firstIbExtension = input.float(0.5, "First IB extension", minval = 0.1, maxval = 5.0, step = 0.1, inline = "ibext", group = G_LEVELS)
float secondIbExtension = input.float(1.0, "Second", minval = 0.1, maxval = 10.0, step = 0.1, inline = "ibext", group = G_LEVELS)
bool showSessionOpenClose = input.bool(false, "Show session open / close", group = G_LEVELS)
int acceptanceCloses = input.int(2, "Closes for prior-value acceptance", minval = 1, maxval = 5, group = G_LEVELS)
bool showSinglePrintZones = input.bool(true, "Show previous single-print zones", group = G_LEVELS)
bool showPoorHighLow = input.bool(true, "Show previous poor high / low", group = G_LEVELS)
int levelExtensionBars = input.int(20, "Level extension beyond latest bar", minval = 0, maxval = 200, group = G_LEVELS)
int maximumSinglePrintZones = input.int(12, "Maximum single-print zones", minval = 1, maxval = 30, group = G_LEVELS)
int levelLineWidth = input.int(1, "Level line width", minval = 1, maxval = 4, group = G_LEVELS)
string levelLineStyle = input.string("Dashed", "Previous level style", options = ["Solid", "Dashed", "Dotted"], group = G_LEVELS)
bool showEstimatedVolume = input.bool(false, "Show estimated volume profile", group = G_VOLUME,
tooltip = "Distributes each confirmed chart bar's volume uniformly across every profile row touched by that bar. This is an estimate, not exchange-native volume at price.")
int estimatedVolumeWidthBars = input.int(12, "Maximum volume width (bars)", minval = 4, maxval = 30, group = G_VOLUME)
bool showEstimatedVolumeLevels = input.bool(true, "Show estimated VPOC / VAH / VAL", group = G_VOLUME)
float estimatedVolumeValueArea = input.float(70.0, "Estimated volume value area", minval = 50.0, maxval = 95.0, step = 1.0, group = G_VOLUME)
float doublePeakMinimumRatio = input.float(0.62, "Double-distribution secondary peak", minval = 0.30, maxval = 1.0, step = 0.01, group = G_AMT)
float doublePeakValleyRatio = input.float(0.55, "Double-distribution valley ratio", minval = 0.10, maxval = 0.90, step = 0.01, group = G_AMT)
int doublePeakMinimumSeparation = input.int(5, "Minimum peak separation (rows)", minval = 2, maxval = 30, group = G_AMT)
float shapeSkewThreshold = input.float(0.45, "P / b shape skew threshold", minval = 0.10, maxval = 2.0, step = 0.05, group = G_AMT)
float trendRangeExtension = input.float(1.75, "Trend-profile range / IB", minval = 1.1, maxval = 5.0, step = 0.05, group = G_AMT)
float strongPocMigrationRows = input.float(2.0, "Strong POC migration (rows)", minval = 0.5, maxval = 10.0, step = 0.5, group = G_AMT)
int ibQualityHistorySessions = input.int(40, "IB quality history", minval = 10, maxval = 120, group = G_IBQUAL)
float ibNarrowPercentile = input.float(25.0, "Narrow threshold percentile", minval = 5.0, maxval = 45.0, step = 1.0, inline = "ibclass", group = G_IBQUAL)
float ibWidePercentile = input.float(75.0, "Wide", minval = 55.0, maxval = 95.0, step = 1.0, inline = "ibclass", group = G_IBQUAL)
float ibExtremePercentile = input.float(90.0, "Extreme", minval = 70.0, maxval = 99.0, step = 1.0, inline = "ibclass2", group = G_IBQUAL)
color accentColor = input.color(#8B5CF6, "Brand / header", group = G_THEME)
color secondaryColor = input.color(#C4B5FD, "Secondary", group = G_THEME)
color valueAreaColor = input.color(#8B5CF6, "Current value area", group = G_THEME)
color outsideValueColor = input.color(#334155, "Current outside value", group = G_THEME)
color previousValueColor = input.color(#C4B5FD, "Previous value area", group = G_THEME)
color previousOutsideColor = input.color(#334155, "Previous outside value", group = G_THEME)
color pocColor = input.color(#38BDF8, "TPO POC / information", group = G_THEME)
color developingColor = input.color(#A1A1AA, "Developing", group = G_THEME)
color singlePrintColor = input.color(#38BDF8, "Single prints", group = G_THEME)
color poorColor = input.color(#FBBF24, "Warning / poor high-low", group = G_THEME)
color bullishColor = input.color(#2DD4BF, "Bullish migration", group = G_THEME)
color bearishColor = input.color(#F87171, "Bearish migration", group = G_THEME)
color neutralColor = input.color(#94A3B8, "Neutral", group = G_THEME)
color panelColor = input.color(#090E17, "Dashboard background", group = G_THEME)
color borderColor = input.color(#334155, "Dashboard border", group = G_THEME)
color textColor = input.color(#F8FAFC, "Text", group = G_THEME)
int profileTransparency = input.int(84, "Current profile transparency", minval = 70, maxval = 95, group = G_THEME)
int previousProfileTransparency = input.int(90, "Previous profile transparency", minval = 75, maxval = 98, group = G_THEME)
bool showDashboard = input.bool(true, "Show dashboard", group = G_DASH)
string dashboardTextSize = input.string("Small", "Dashboard text size", options = ["Tiny", "Small", "Normal"], group = G_DASH)
bool enableDynamicAlerts = input.bool(false, "Enable dynamic JSON alert() calls", group = G_ALERTS)
bool alertPreviousLevels = input.bool(true, "Alert on previous POC / VAH / VAL", group = G_ALERTS)
bool alertSinglePrints = input.bool(true, "Alert on previous single prints", group = G_ALERTS)
bool alertPoorHighLow = input.bool(true, "Alert on poor high / low repair", group = G_ALERTS)
bool alertAuctionChanges = input.bool(true, "Alert on auction-state changes", group = G_ALERTS)
f_line_style(string name) =>
switch name
"Solid" => line.style_solid
"Dotted" => line.style_dotted
=> line.style_dashed
f_text_size(string name) =>
switch name
"Tiny" => size.tiny
"Normal" => size.normal
=> size.small
f_percentile_rank(array<float> history, float value) =>
float result = na
int count = history.size()
if count >= 5 and not na(value)
int atOrBelow = 0
for i = 0 to count - 1
if history.get(i) <= value
atOrBelow += 1
result := 100.0 * atOrBelow / count
result
f_block_minutes(string name) =>
switch name
"5 Minutes" => 5
"10 Minutes" => 10
"15 Minutes" => 15
"60 Minutes" => 60
=> 30
f_default_row_ticks() =>
bool nasdaqFuture = syminfo.root == "NQ" or syminfo.root == "MNQ"
bool spFuture = syminfo.root == "ES" or syminfo.root == "MES"
nasdaqFuture ? 8 : spFuture ? 4 : 4
f_choose_ticks(float previousRange) =>
int fallbackTicks = math.max(f_default_row_ticks(), minimumAutomaticTicks)
int autoTicks = not na(previousRange) and previousRange > syminfo.mintick ?
int(math.ceil(previousRange / syminfo.mintick / automaticTargetRows)) : fallbackTicks
rowSizeMode == "Manual" ? manualTicksPerRow : math.max(autoTicks, minimumAutomaticTicks)
f_row_low(int row, float rowSize) =>
row * rowSize
f_row_high(int row, float rowSize) =>
(row + 1) * rowSize
f_row_center(int row, float rowSize) =>
(row + 0.5) * rowSize
f_int_get(map<int, int> sourceMap, int key) =>
sourceMap.contains(key) ? sourceMap.get(key) : 0
f_float_get(map<int, float> sourceMap, int key) =>
sourceMap.contains(key) ? sourceMap.get(key) : 0.0
f_tpo_count(map<int, int> sourceMap, int row, bool includeActive, int activeLowRow, int activeHighRow) =>
f_int_get(sourceMap, row) + (includeActive and row >= activeLowRow and row <= activeHighRow ? 1 : 0)
f_add_tpo_block(
map<int, int> countMap,
int lowRow,
int highRow,
int existingMinRow,
int existingMaxRow) =>
int newMinRow = existingMinRow
int newMaxRow = existingMaxRow
bool overflow = false
int rowsTouched = highRow - lowRow + 1
if rowsTouched > 0 and rowsTouched <= maximumRowsPerBlock
for row = lowRow to highRow
countMap.put(row, f_int_get(countMap, row) + 1)
newMinRow := na(existingMinRow) ? lowRow : math.min(existingMinRow, lowRow)
newMaxRow := na(existingMaxRow) ? highRow : math.max(existingMaxRow, highRow)
else if rowsTouched > maximumRowsPerBlock
overflow := true
[newMinRow, newMaxRow, overflow]
f_add_estimated_volume(
map<int, float> volumeMap,
int lowRow,
int highRow,
float barVolume) =>
bool overflow = false
int rowsTouched = highRow - lowRow + 1
if rowsTouched > 0 and rowsTouched <= maximumRowsPerBlock
float volumePerRow = nz(barVolume, 0.0) / rowsTouched
for row = lowRow to highRow
volumeMap.put(row, f_float_get(volumeMap, row) + volumePerRow)
else if rowsTouched > maximumRowsPerBlock
overflow := true
overflow
f_tpo_stats(
map<int, int> countMap,
int minRow,
int maxRow,
float rowSize,
bool includeActive,
int activeLowRow,
int activeHighRow,
float vaPercent) =>
bool valid = not na(minRow) and not na(maxRow) and maxRow >= minRow and rowSize > 0.0
int pocRow = int(na)
int vahRow = int(na)
int valRow = int(na)
int totalTpo = 0
int tpoAbove = 0
int tpoBelow = 0
int singleRows = 0
int maximumCount = 0
bool poorHigh = false
bool poorLow = false
float weightedMeanRow = na
float skewness = na
if valid
float midpointRow = (minRow + maxRow) * 0.5
float weightedSum = 0.0
for row = minRow to maxRow
int rowCount = f_tpo_count(countMap, row, includeActive, activeLowRow, activeHighRow)
totalTpo += rowCount
weightedSum += row * rowCount
bool betterPoc = rowCount > maximumCount
bool equalAndCloser = rowCount == maximumCount and rowCount > 0 and (na(pocRow) or math.abs(row - midpointRow) < math.abs(pocRow - midpointRow))
bool equalDistanceHigher = rowCount == maximumCount and rowCount > 0 and not na(pocRow) and math.abs(row - midpointRow) == math.abs(pocRow - midpointRow) and row > pocRow
if betterPoc or equalAndCloser or equalDistanceHigher
maximumCount := rowCount
pocRow := row
if totalTpo > 0 and not na(pocRow)
weightedMeanRow := weightedSum / totalTpo
float secondMoment = 0.0
float thirdMoment = 0.0
for row = minRow to maxRow
int rowCount = f_tpo_count(countMap, row, includeActive, activeLowRow, activeHighRow)
float centered = row - weightedMeanRow
secondMoment += centered * centered * rowCount
thirdMoment += centered * centered * centered * rowCount
if row > pocRow
tpoAbove += rowCount
else if row < pocRow
tpoBelow += rowCount
if row > minRow and row < maxRow and rowCount == 1
singleRows += 1
float varianceRows = secondMoment / totalTpo
float standardDeviationRows = math.sqrt(math.max(varianceRows, 0.0))
skewness := standardDeviationRows > 1e-9 ? (thirdMoment / totalTpo) / math.pow(standardDeviationRows, 3.0) : 0.0
int targetTpo = int(math.ceil(totalTpo * vaPercent / 100.0))
int includedTpo = f_tpo_count(countMap, pocRow, includeActive, activeLowRow, activeHighRow)
int valueLowRow = pocRow
int valueHighRow = pocRow
int safetyCounter = 0
while includedTpo < targetTpo and (valueLowRow > minRow or valueHighRow < maxRow) and safetyCounter <= maxRow - minRow + 2
int aboveCount = valueHighRow < maxRow ? f_tpo_count(countMap, valueHighRow + 1, includeActive, activeLowRow, activeHighRow) : -1
int belowCount = valueLowRow > minRow ? f_tpo_count(countMap, valueLowRow - 1, includeActive, activeLowRow, activeHighRow) : -1
if aboveCount >= belowCount
valueHighRow += 1
includedTpo += math.max(aboveCount, 0)
else
valueLowRow -= 1
includedTpo += math.max(belowCount, 0)
safetyCounter += 1
vahRow := valueHighRow
valRow := valueLowRow
poorHigh := f_tpo_count(countMap, maxRow, includeActive, activeLowRow, activeHighRow) > 1
poorLow := f_tpo_count(countMap, minRow, includeActive, activeLowRow, activeHighRow) > 1
float profileHigh = valid ? f_row_high(maxRow, rowSize) : na
float profileLow = valid ? f_row_low(minRow, rowSize) : na
float profileMid = valid ? (profileHigh + profileLow) * 0.5 : na
float pocPrice = valid and not na(pocRow) ? f_row_center(pocRow, rowSize) : na
float vahPrice = valid and not na(vahRow) ? f_row_high(vahRow, rowSize) : na
float valPrice = valid and not na(valRow) ? f_row_low(valRow, rowSize) : na
[valid, pocRow, vahRow, valRow, totalTpo, tpoAbove, tpoBelow, singleRows, maximumCount, poorHigh, poorLow, weightedMeanRow, skewness, profileHigh, profileLow, profileMid, pocPrice, vahPrice, valPrice]
f_volume_stats(
map<int, float> volumeMap,
int minRow,
int maxRow,
float rowSize,
float vaPercent) =>
bool valid = not na(minRow) and not na(maxRow) and maxRow >= minRow and rowSize > 0.0 and volumeMap.size() > 0
int pocRow = int(na)
int vahRow = int(na)
int valRow = int(na)
float totalVolume = 0.0
float maximumVolume = 0.0
if valid
float midpointRow = (minRow + maxRow) * 0.5
for row = minRow to maxRow
float rowVolume = f_float_get(volumeMap, row)
totalVolume += rowVolume
bool betterPoc = rowVolume > maximumVolume
bool equalAndCloser = rowVolume == maximumVolume and rowVolume > 0.0 and (na(pocRow) or math.abs(row - midpointRow) < math.abs(pocRow - midpointRow))
if betterPoc or equalAndCloser
maximumVolume := rowVolume
pocRow := row
if totalVolume > 0.0 and not na(pocRow)
float targetVolume = totalVolume * vaPercent / 100.0
float includedVolume = f_float_get(volumeMap, pocRow)
int valueLowRow = pocRow
int valueHighRow = pocRow
int safetyCounter = 0
while includedVolume < targetVolume and (valueLowRow > minRow or valueHighRow < maxRow) and safetyCounter <= maxRow - minRow + 2
float aboveVolume = valueHighRow < maxRow ? f_float_get(volumeMap, valueHighRow + 1) : -1.0
float belowVolume = valueLowRow > minRow ? f_float_get(volumeMap, valueLowRow - 1) : -1.0
if aboveVolume >= belowVolume
valueHighRow += 1
includedVolume += math.max(aboveVolume, 0.0)
else
valueLowRow -= 1
includedVolume += math.max(belowVolume, 0.0)
safetyCounter += 1
vahRow := valueHighRow
valRow := valueLowRow
float pocPrice = valid and not na(pocRow) ? f_row_center(pocRow, rowSize) : na
float vahPrice = valid and not na(vahRow) ? f_row_high(vahRow, rowSize) : na
float valPrice = valid and not na(valRow) ? f_row_low(valRow, rowSize) : na
[valid, pocRow, vahRow, valRow, totalVolume, maximumVolume, pocPrice, vahPrice, valPrice]
f_profile_shape(
map<int, int> countMap,
int minRow,
int maxRow,
bool includeActive,
int activeLowRow,
int activeHighRow,
int pocRow,
float skewness,
float profileRange,
float initialBalanceRange) =>
int shapeCode = 0
string shapeText = "Developing"
bool doubleDistribution = false
if not na(minRow) and not na(maxRow) and not na(pocRow) and maxRow > minRow
int primaryPeakRow = pocRow
int primaryPeakCount = f_tpo_count(countMap, primaryPeakRow, includeActive, activeLowRow, activeHighRow)
int secondaryPeakRow = int(na)
int secondaryPeakCount = 0
for row = minRow to maxRow
int rowCount = f_tpo_count(countMap, row, includeActive, activeLowRow, activeHighRow)
if math.abs(row - primaryPeakRow) >= doublePeakMinimumSeparation and rowCount > secondaryPeakCount
secondaryPeakCount := rowCount
secondaryPeakRow := row
if not na(secondaryPeakRow) and secondaryPeakCount >= primaryPeakCount * doublePeakMinimumRatio
int valleyStart = math.min(primaryPeakRow, secondaryPeakRow)
int valleyEnd = math.max(primaryPeakRow, secondaryPeakRow)
int valleyCount = 1000000
if valleyEnd - valleyStart > 1
for row = valleyStart + 1 to valleyEnd - 1
valleyCount := math.min(valleyCount, f_tpo_count(countMap, row, includeActive, activeLowRow, activeHighRow))
doubleDistribution := valleyCount <= math.min(primaryPeakCount, secondaryPeakCount) * doublePeakValleyRatio
float pocLocation = (pocRow - minRow) / math.max(maxRow - minRow, 1.0)
bool trendProfile = not na(initialBalanceRange) and initialBalanceRange > 0.0 and profileRange / initialBalanceRange >= trendRangeExtension
if doubleDistribution
shapeCode := 4
shapeText := "Double Distribution"
else if trendProfile and (pocLocation >= 0.70 or pocLocation <= 0.30 or math.abs(nz(skewness)) >= shapeSkewThreshold)
shapeCode := 5
shapeText := "Trend Profile"
else if nz(skewness) <= -shapeSkewThreshold and pocLocation > 0.52
shapeCode := 2
shapeText := "P-shaped"
else if nz(skewness) >= shapeSkewThreshold and pocLocation < 0.48
shapeCode := 3
shapeText := "b-shaped"
else if math.abs(nz(skewness)) < shapeSkewThreshold and pocLocation >= 0.35 and pocLocation <= 0.65
shapeCode := 1
shapeText := "D-shaped"
else
shapeCode := 0
shapeText := "Neutral / Mixed"
[shapeCode, shapeText, doubleDistribution]
f_trim_boxes(array<box> objects, int used) =>
while objects.size() > used
box.delete(objects.pop())
f_trim_labels(array<label> objects, int used) =>
while objects.size() > used
label.delete(objects.pop())
f_set_box(
array<box> objects,
int index,
int left,
float top,
int right,
float bottom,
color borderColor,
color fillColor) =>
box object = index < objects.size() ? objects.get(index) : na
if na(object)
object := box.new(left, top, right, bottom, xloc = xloc.bar_index, border_color = borderColor, border_width = 1, bgcolor = fillColor)
if index < objects.size()
objects.set(index, object)
else
objects.push(object)
else
box.set_left(object, left)
box.set_top(object, top)
box.set_right(object, right)
box.set_bottom(object, bottom)
box.set_border_color(object, borderColor)
box.set_bgcolor(object, fillColor)
f_set_label(
array<label> objects,
int index,
int x,
float y,
string labelText,
color background,
color foreground,
string styleName,
string sizeName) =>
label object = index < objects.size() ? objects.get(index) : na
string styleValue = styleName == "Left" ? label.style_label_left : styleName == "Right" ? label.style_label_right : label.style_none
if na(object)
object := label.new(x, y, labelText, xloc = xloc.bar_index, style = styleValue, color = background, textcolor = foreground, size = f_text_size(sizeName))
if index < objects.size()
objects.set(index, object)
else
objects.push(object)
else
label.set_xy(object, x, y)
label.set_text(object, labelText)
label.set_style(object, styleValue)
label.set_color(object, background)
label.set_textcolor(object, foreground)
label.set_size(object, f_text_size(sizeName))
int blockMinutes = f_block_minutes(blockSizeInput)
int blockMilliseconds = blockMinutes * 60 * 1000
float chartSeconds = timeframe.in_seconds()
int chartMinutes = timeframe.isintraday ? int(math.round(chartSeconds / 60.0)) : 0
bool supportedTimeframe = timeframe.isintraday and chartMinutes >= 1 and chartMinutes <= maximumChartMinutes and chartMinutes <= blockMinutes
bool standardChart = chart.is_standard
bool chartWarning = not supportedTimeframe or (requireStandardCandles and not standardChart)
bool inRTH = not na(time(timeframe.period, rthSession, sessionTimezone))
int localYear = year(time, sessionTimezone)
int localMonth = month(time, sessionTimezone)
int localDay = dayofmonth(time, sessionTimezone)
int dateKey = localYear * 10000 + localMonth * 100 + localDay
bool newRTH = inRTH and (bar_index == 0 or not inRTH[1] or dateKey != dateKey[1])
bool endRTH = not inRTH and inRTH[1]
int rthStartTimestamp = timestamp(sessionTimezone, localYear, localMonth, localDay, rthOpenHour, rthOpenMinute)
int currentBlockId = inRTH ? int(math.floor((time - rthStartTimestamp) / blockMilliseconds)) : -1
var map<int, int> currentTpo = map.new<int, int>()
var map<int, int> previousTpo = map.new<int, int>()
var map<int, float> currentEstimatedVolume = map.new<int, float>()
var map<int, float> previousEstimatedVolume = map.new<int, float>()
var float currentRowSize = na
var float previousRowSize = na
var int currentMinRow = int(na)
var int currentMaxRow = int(na)
var int previousMinRow = int(na)
var int previousMaxRow = int(na)
var bool currentHasData = false
var bool previousHasData = false
var bool currentComplete = false
var bool currentOverflow = false
var bool previousOverflow = false
var int currentSessionStartBar = int(na)
var int currentSessionEndBar = int(na)
var int previousSessionStartBar = int(na)
var int previousSessionEndBar = int(na)
var float currentOpen = na
var float currentClose = na
var float previousOpen = na
var float previousClose = na
var float currentHigh = na
var float currentLow = na
var float previousHigh = na
var float previousLow = na
var float currentIbHigh = na
var float currentIbLow = na
var float previousIbHigh = na
var float previousIbLow = na
var float currentRotationFactor = 0.0
var float previousRotationFactor = 0.0
var int currentCommittedBlocks = 0
var int previousCommittedBlocks = 0
var array<float> ibRangeHistory = array.new_float()
var array<float> ibVolumeHistory = array.new_float()
var float currentIbVolume = 0.0
var float currentIbRangePercentile = na
var float currentIbVolumePercentile = na
var float currentIbEfficiency = na
var bool currentIbRecorded = false
var int activeBlockId = -1
var float activeBlockHigh = na
var float activeBlockLow = na
var float lastCommittedBlockHigh = na
var float lastCommittedBlockLow = na
if newRTH
if currentHasData
if not currentComplete
if activeBlockId >= 0 and not na(activeBlockHigh) and not na(activeBlockLow) and not na(currentRowSize)
int finalLowRow = int(math.floor(activeBlockLow / currentRowSize))
int finalHighRow = int(math.floor(activeBlockHigh / currentRowSize))
[newMinRow, newMaxRow, blockOverflow] = f_add_tpo_block(currentTpo, finalLowRow, finalHighRow, currentMinRow, currentMaxRow)
currentMinRow := newMinRow
currentMaxRow := newMaxRow
currentOverflow := currentOverflow or blockOverflow
if not na(lastCommittedBlockHigh)
currentRotationFactor += activeBlockHigh > lastCommittedBlockHigh ? 1.0 : activeBlockHigh < lastCommittedBlockHigh ? -1.0 : 0.0
currentRotationFactor += activeBlockLow > lastCommittedBlockLow ? 1.0 : activeBlockLow < lastCommittedBlockLow ? -1.0 : 0.0
currentCommittedBlocks += 1
lastCommittedBlockHigh := activeBlockHigh
lastCommittedBlockLow := activeBlockLow
currentClose := bar_index > 0 ? close[1] : close
currentSessionEndBar := bar_index > 0 ? bar_index - 1 : bar_index
currentComplete := true
previousTpo := currentTpo.copy()
previousEstimatedVolume := currentEstimatedVolume.copy()
previousRowSize := currentRowSize
previousMinRow := currentMinRow
previousMaxRow := currentMaxRow
previousSessionStartBar := currentSessionStartBar
previousSessionEndBar := currentSessionEndBar
previousOpen := currentOpen
previousClose := currentClose
previousHigh := currentHigh
previousLow := currentLow
previousIbHigh := currentIbHigh
previousIbLow := currentIbLow
previousRotationFactor := currentRotationFactor
previousCommittedBlocks := currentCommittedBlocks
previousOverflow := currentOverflow
previousHasData := currentTpo.size() > 0
float previousRangeForSizing = previousHasData and not na(previousHigh) and not na(previousLow) ? previousHigh - previousLow : na
currentRowSize := f_choose_ticks(previousRangeForSizing) * syminfo.mintick
currentTpo := map.new<int, int>()
currentEstimatedVolume := map.new<int, float>()
currentMinRow := int(na)
currentMaxRow := int(na)
currentHasData := true
currentComplete := false
currentOverflow := false
currentSessionStartBar := bar_index
currentSessionEndBar := int(na)
currentOpen := open
currentClose := na
currentHigh := high
currentLow := low
currentIbHigh := na
currentIbLow := na
currentIbVolume := 0.0
currentIbRangePercentile := na
currentIbVolumePercentile := na
currentIbEfficiency := na
currentIbRecorded := false
currentRotationFactor := 0.0
currentCommittedBlocks := 0
activeBlockId := -1
activeBlockHigh := na
activeBlockLow := na
lastCommittedBlockHigh := na
lastCommittedBlockLow := na
if inRTH and currentHasData and not na(currentRowSize)
currentHigh := na(currentHigh) ? high : math.max(currentHigh, high)
currentLow := na(currentLow) ? low : math.min(currentLow, low)
if currentBlockId >= 0 and currentBlockId < initialBalanceBlocks
currentIbHigh := na(currentIbHigh) ? high : math.max(currentIbHigh, high)
currentIbLow := na(currentIbLow) ? low : math.min(currentIbLow, low)
if barstate.isconfirmed
currentIbVolume += nz(volume, 0.0)
if currentBlockId >= initialBalanceBlocks and not currentIbRecorded and not na(currentIbHigh) and not na(currentIbLow)
float completedIbRange = currentIbHigh - currentIbLow
currentIbRangePercentile := f_percentile_rank(ibRangeHistory, completedIbRange)
currentIbVolumePercentile := f_percentile_rank(ibVolumeHistory, currentIbVolume)
float ibEndClose = bar_index > 0 ? close[1] : close
currentIbEfficiency := completedIbRange > syminfo.mintick ? math.min(1.0, math.abs(ibEndClose - currentOpen) / completedIbRange) * 100.0 : 0.0
ibRangeHistory.push(completedIbRange)
ibVolumeHistory.push(currentIbVolume)
while ibRangeHistory.size() > ibQualityHistorySessions
ibRangeHistory.shift()
while ibVolumeHistory.size() > ibQualityHistorySessions
ibVolumeHistory.shift()
currentIbRecorded := true
if activeBlockId < 0
activeBlockId := currentBlockId
activeBlockHigh := high
activeBlockLow := low
else if currentBlockId != activeBlockId
int completedLowRow = int(math.floor(activeBlockLow / currentRowSize))
int completedHighRow = int(math.floor(activeBlockHigh / currentRowSize))
[newMinRow, newMaxRow, blockOverflow] = f_add_tpo_block(currentTpo, completedLowRow, completedHighRow, currentMinRow, currentMaxRow)
currentMinRow := newMinRow
currentMaxRow := newMaxRow
currentOverflow := currentOverflow or blockOverflow
if not na(lastCommittedBlockHigh)
currentRotationFactor += activeBlockHigh > lastCommittedBlockHigh ? 1.0 : activeBlockHigh < lastCommittedBlockHigh ? -1.0 : 0.0
currentRotationFactor += activeBlockLow > lastCommittedBlockLow ? 1.0 : activeBlockLow < lastCommittedBlockLow ? -1.0 : 0.0
currentCommittedBlocks += 1
lastCommittedBlockHigh := activeBlockHigh
lastCommittedBlockLow := activeBlockLow
activeBlockId := currentBlockId
activeBlockHigh := high
activeBlockLow := low
else
activeBlockHigh := math.max(activeBlockHigh, high)
activeBlockLow := math.min(activeBlockLow, low)
if barstate.isconfirmed and showEstimatedVolume
int barLowRow = int(math.floor(low / currentRowSize))
int barHighRow = int(math.floor(high / currentRowSize))
bool volumeOverflow = f_add_estimated_volume(currentEstimatedVolume, barLowRow, barHighRow, volume)
currentOverflow := currentOverflow or volumeOverflow
if endRTH and currentHasData and not currentComplete and activeBlockId >= 0 and not na(currentRowSize)
int finalLowRow = int(math.floor(activeBlockLow / currentRowSize))
int finalHighRow = int(math.floor(activeBlockHigh / currentRowSize))
[newMinRow, newMaxRow, blockOverflow] = f_add_tpo_block(currentTpo, finalLowRow, finalHighRow, currentMinRow, currentMaxRow)
currentMinRow := newMinRow
currentMaxRow := newMaxRow
currentOverflow := currentOverflow or blockOverflow
if not na(lastCommittedBlockHigh)
currentRotationFactor += activeBlockHigh > lastCommittedBlockHigh ? 1.0 : activeBlockHigh < lastCommittedBlockHigh ? -1.0 : 0.0
currentRotationFactor += activeBlockLow > lastCommittedBlockLow ? 1.0 : activeBlockLow < lastCommittedBlockLow ? -1.0 : 0.0
currentCommittedBlocks += 1
lastCommittedBlockHigh := activeBlockHigh
lastCommittedBlockLow := activeBlockLow
currentClose := close[1]
currentSessionEndBar := bar_index - 1
currentComplete := true
activeBlockId := -1
activeBlockHigh := na
activeBlockLow := na
int activeLowRow = inRTH and activeBlockId >= 0 and not na(activeBlockLow) and not na(currentRowSize) ? int(math.floor(activeBlockLow / currentRowSize)) : int(na)
int activeHighRow = inRTH and activeBlockId >= 0 and not na(activeBlockHigh) and not na(currentRowSize) ? int(math.floor(activeBlockHigh / currentRowSize)) : int(na)
bool includeActiveBlock = inRTH and activeBlockId >= 0 and not na(activeLowRow) and not na(activeHighRow)
int displayCurrentMinRow = includeActiveBlock ? (na(currentMinRow) ? activeLowRow : math.min(currentMinRow, activeLowRow)) : currentMinRow
int displayCurrentMaxRow = includeActiveBlock ? (na(currentMaxRow) ? activeHighRow : math.max(currentMaxRow, activeHighRow)) : currentMaxRow
[curValid, curPocRow, curVahRow, curValRow, curTotalTpo, curTpoAbove, curTpoBelow, curSingleRows, curMaxCount, curPoorHigh, curPoorLow, curMeanRow, curSkewness, curProfileHigh, curProfileLow, curProfileMid, curPoc, curVah, curVal] =
f_tpo_stats(currentTpo, displayCurrentMinRow, displayCurrentMaxRow, currentRowSize, includeActiveBlock, activeLowRow, activeHighRow, valueAreaPercent)
[prevValid, prevPocRow, prevVahRow, prevValRow, prevTotalTpo, prevTpoAbove, prevTpoBelow, prevSingleRows, prevMaxCount, prevPoorHigh, prevPoorLow, prevMeanRow, prevSkewness, prevProfileHigh, prevProfileLow, prevProfileMid, prevPoc, prevVah, prevVal] =
f_tpo_stats(previousTpo, previousMinRow, previousMaxRow, previousRowSize, false, int(na), int(na), valueAreaPercent)
[curVolumeValid, curVolumePocRow, curVolumeVahRow, curVolumeValRow, curTotalEstimatedVolume, curMaxEstimatedVolume, curVolumePoc, curVolumeVah, curVolumeVal] =
f_volume_stats(currentEstimatedVolume, displayCurrentMinRow, displayCurrentMaxRow, currentRowSize, estimatedVolumeValueArea)
float currentIbRange = not na(currentIbHigh) and not na(currentIbLow) ? currentIbHigh - currentIbLow : na
float currentProfileRange = curValid ? curProfileHigh - curProfileLow : na
[curShapeCode, curShapeText, curDoubleDistribution] = f_profile_shape(
currentTpo, displayCurrentMinRow, displayCurrentMaxRow, includeActiveBlock, activeLowRow, activeHighRow,
curPocRow, curSkewness, currentProfileRange, currentIbRange)
string valueRelationship = not curValid or not prevValid ? "No Comparison" :
curVal > prevVah ? "Higher Value" :
curVah < prevVal ? "Lower Value" :
curVah > prevVah and curVal > prevVal ? "Overlapping Higher" :
curVah < prevVah and curVal < prevVal ? "Overlapping Lower" :
curVah <= prevVah and curVal >= prevVal ? "Inside Prior Value" :
curVah >= prevVah and curVal <= prevVal ? "Outside Prior Value" : "Overlapping Value"
float pocMigrationRows = curValid and prevValid ? (curPoc - prevPoc) / math.max(currentRowSize, syminfo.mintick) : na
string pocMigration = na(pocMigrationRows) ? "No Comparison" :
pocMigrationRows >= strongPocMigrationRows ? "Strongly Higher" :
pocMigrationRows > 0.5 ? "Higher" :
pocMigrationRows <= -strongPocMigrationRows ? "Strongly Lower" :
pocMigrationRows < -0.5 ? "Lower" : "Stable"
string openingLocation = not prevValid or na(currentOpen) ? "No Prior Profile" :
currentOpen > prevProfileHigh ? "Above Prior Range" :
currentOpen > prevVah ? "Above Prior Value" :
currentOpen >= prevVal ? "Inside Prior Value" :
currentOpen >= prevProfileLow ? "Below Prior Value" : "Below Prior Range"
string currentLocation = not curValid ? "No Profile" :
close > curProfileHigh ? "Above Profile" :
close > curVah ? "Above Value" :
math.abs(close - curPoc) <= currentRowSize ? "At POC" :
close >= curVal ? "Inside Value" :
close >= curProfileLow ? "Below Value" : "Below Profile"
bool upwardValue = str.contains(valueRelationship, "Higher")
bool downwardValue = str.contains(valueRelationship, "Lower")
bool pocUp = str.contains(pocMigration, "Higher")
bool pocDown = str.contains(pocMigration, "Lower")
bool aboveIb = not na(currentIbHigh) and close > currentIbHigh
bool belowIb = not na(currentIbLow) and close < currentIbLow
bool returnedInsidePriorValue = prevValid and close <= prevVah and close >= prevVal
bool openedAbovePriorValue = prevValid and currentOpen > prevVah
bool openedBelowPriorValue = prevValid and currentOpen < prevVal
string participantBehavior = curShapeCode == 1 and currentLocation == "Inside Value" ? "Two-sided Trade" :
openedAbovePriorValue and returnedInsidePriorValue ? "Responsive Selling" :
openedBelowPriorValue and returnedInsidePriorValue ? "Responsive Buying" :
upwardValue and pocUp and (aboveIb or close > curVah) ? "Initiative Buying" :
downwardValue and pocDown and (belowIb or close < curVal) ? "Initiative Selling" :
"Indeterminate"
int auctionCode = 0
string auctionState = "Transition"
if curDoubleDistribution
auctionCode := 4
auctionState := "Double Distribution"
else if openedAbovePriorValue and returnedInsidePriorValue and not pocUp
auctionCode := -3
auctionState := "Failed Upward Auction"
else if openedBelowPriorValue and returnedInsidePriorValue and not pocDown
auctionCode := 3
auctionState := "Failed Downward Auction"
else if curShapeCode == 5 and upwardValue and pocUp
auctionCode := 2
auctionState := "Upward Trend Auction"
else if curShapeCode == 5 and downwardValue and pocDown
auctionCode := -2
auctionState := "Downward Trend Auction"
else if upwardValue and pocUp and close >= curPoc
auctionCode := 1
auctionState := "Upward Imbalance"
else if downwardValue and pocDown and close <= curPoc
auctionCode := -1
auctionState := "Downward Imbalance"
else if curShapeCode == 1 and currentLocation == "Inside Value"
auctionCode := 5
auctionState := "Balanced Auction"
else if curValid and close >= curVal and close <= curVah
auctionCode := 6
auctionState := "Developing Balance"
color auctionColor = auctionCode == 1 or auctionCode == 2 or auctionCode == 3 ? bullishColor :
auctionCode == -1 or auctionCode == -2 or auctionCode == -3 ? bearishColor :
auctionCode == 4 ? accentColor : auctionCode == 5 or auctionCode == 6 ? secondaryColor : neutralColor
color migrationColor = upwardValue or pocUp ? bullishColor : downwardValue or pocDown ? bearishColor : neutralColor
var int[] previousSingleZoneStarts = array.new_int()
var int[] previousSingleZoneEnds = array.new_int()
array.clear(previousSingleZoneStarts)
array.clear(previousSingleZoneEnds)
if prevValid
int zoneStart = int(na)
for row = previousMinRow to previousMaxRow
int rowCount = f_int_get(previousTpo, row)
bool isSingle = row > previousMinRow and row < previousMaxRow and rowCount == 1
if isSingle and na(zoneStart)
zoneStart := row
bool closesZone = not isSingle and not na(zoneStart)
if closesZone
previousSingleZoneStarts.push(zoneStart)
previousSingleZoneEnds.push(row - 1)
zoneStart := int(na)
if not na(zoneStart)
previousSingleZoneStarts.push(zoneStart)
previousSingleZoneEnds.push(previousMaxRow - 1)
bool previousPocTouched = inRTH and prevValid and high >= prevPoc and low <= prevPoc
bool previousVahTouched = inRTH and prevValid and high >= prevVah and low <= prevVah
bool previousValTouched = inRTH and prevValid and high >= prevVal and low <= prevVal
var int previousPocState = 0
var int previousVahState = 0
var int previousValState = 0
if newRTH
previousPocState := 0
previousVahState := 0
previousValState := 0
float closesAbovePocCount = math.sum(prevValid and close > prevPoc ? 1.0 : 0.0, acceptanceCloses)
float closesBelowPocCount = math.sum(prevValid and close < prevPoc ? 1.0 : 0.0, acceptanceCloses)
float closesAboveVahCount = math.sum(prevValid and close > prevVah ? 1.0 : 0.0, acceptanceCloses)
float closesBelowValCount = math.sum(prevValid and close < prevVal ? 1.0 : 0.0, acceptanceCloses)
bool closesAbovePoc = prevValid and closesAbovePocCount >= acceptanceCloses
bool closesBelowPoc = prevValid and closesBelowPocCount >= acceptanceCloses
bool closesAboveVah = prevValid and closesAboveVahCount >= acceptanceCloses
bool closesBelowVal = prevValid and closesBelowValCount >= acceptanceCloses
if barstate.isconfirmed and inRTH and prevValid
if closesAbovePoc
previousPocState := 1
else if closesBelowPoc
previousPocState := -1
else if previousPocTouched
previousPocState := close >= prevPoc ? 2 : -2
if closesAboveVah
previousVahState := 1
else if previousVahTouched and close < prevVah
previousVahState := -2
if closesBelowVal
previousValState := -1
else if previousValTouched and close > prevVal
previousValState := 2
bool poorHighRepaired = inRTH and prevValid and prevPoorHigh and high > prevProfileHigh
bool poorLowRepaired = inRTH and prevValid and prevPoorLow and low < prevProfileLow
bool previousSinglePrintOverlap = false
if inRTH and prevValid and previousSingleZoneStarts.size() > 0
for zoneIndex = 0 to previousSingleZoneStarts.size() - 1
int startRow = previousSingleZoneStarts.get(zoneIndex)
int endRow = previousSingleZoneEnds.get(zoneIndex)
float zoneLow = f_row_low(startRow, previousRowSize)
float zoneHigh = f_row_high(endRow, previousRowSize)
previousSinglePrintOverlap := previousSinglePrintOverlap or (high >= zoneLow and low <= zoneHigh)
var array<box> currentProfileBoxes = array.new<box>()
var array<box> previousProfileBoxes = array.new<box>()
var array<box> estimatedVolumeBoxes = array.new<box>()
var array<box> singlePrintBoxes = array.new<box>()
var array<label> rowCountLabels = array.new<label>()
var array<label> profileNameLabels = array.new<label>()
if barstate.islast
int currentLeft = bar_index + profileOffsetBars
int estimatedVolumeLeft = currentLeft + maximumProfileWidthBars + 1
int previousLeft = currentLeft + maximumProfileWidthBars + (showEstimatedVolume ? estimatedVolumeWidthBars + 2 : 0) + profileGapBars
int currentUsed = 0
int rowLabelsUsed = 0
if indicatorEnabled and showCurrentProfile and curValid
int currentRows = displayCurrentMaxRow - displayCurrentMinRow + 1
int currentStride = math.max(1, int(math.ceil(currentRows / maximumRenderedRows)))
int groupCount = int(math.ceil(currentRows / currentStride))
int groupedMaxCount = 1
for groupIndex = 0 to groupCount - 1
int groupLowRow = displayCurrentMinRow + groupIndex * currentStride
int groupHighRow = math.min(groupLowRow + currentStride - 1, displayCurrentMaxRow)
int groupMaximum = 0
for row = groupLowRow to groupHighRow
groupMaximum := math.max(groupMaximum, f_tpo_count(currentTpo, row, includeActiveBlock, activeLowRow, activeHighRow))
groupedMaxCount := math.max(groupedMaxCount, groupMaximum)
for groupIndex = 0 to groupCount - 1
int groupLowRow = displayCurrentMinRow + groupIndex * currentStride
int groupHighRow = math.min(groupLowRow + currentStride - 1, displayCurrentMaxRow)
int groupMaximum = 0
bool groupSinglePrint = false
for row = groupLowRow to groupHighRow
int rowCount = f_tpo_count(currentTpo, row, includeActiveBlock, activeLowRow, activeHighRow)
groupMaximum := math.max(groupMaximum, rowCount)
groupSinglePrint := groupSinglePrint or (row > displayCurrentMinRow and row < displayCurrentMaxRow and rowCount == 1)
int profileWidth = math.max(1, int(math.round(groupMaximum / math.max(groupedMaxCount, 1.0) * maximumProfileWidthBars)))
bool isValue = groupHighRow >= curValRow and groupLowRow <= curVahRow
bool isPoc = groupHighRow >= curPocRow and groupLowRow <= curPocRow
color baseColor = isPoc ? pocColor : groupSinglePrint ? singlePrintColor : isValue and showValueAreaShading ? valueAreaColor : outsideValueColor
int transparency = isPoc ? math.max(profileTransparency - 12, 0) : profileTransparency
f_set_box(currentProfileBoxes, currentUsed, currentLeft, f_row_high(groupHighRow, currentRowSize), currentLeft + profileWidth, f_row_low(groupLowRow, currentRowSize), color.new(baseColor, math.min(transparency + 10, 100)), color.new(baseColor, transparency))
currentUsed += 1
if showRowCounts
f_set_label(rowCountLabels, rowLabelsUsed, currentLeft + profileWidth + 1, (f_row_high(groupHighRow, currentRowSize) + f_row_low(groupLowRow, currentRowSize)) * 0.5, str.tostring(groupMaximum), color.new(panelColor, 100), color.new(textColor, 12), "None", "Tiny")
rowLabelsUsed += 1
f_trim_boxes(currentProfileBoxes, currentUsed)
int previousUsed = 0
if indicatorEnabled and showPreviousProfile and prevValid
int previousRows = previousMaxRow - previousMinRow + 1
int previousStride = math.max(1, int(math.ceil(previousRows / maximumRenderedRows)))
int groupCount = int(math.ceil(previousRows / previousStride))
int groupedMaxCount = 1
for groupIndex = 0 to groupCount - 1
int groupLowRow = previousMinRow + groupIndex * previousStride
int groupHighRow = math.min(groupLowRow + previousStride - 1, previousMaxRow)
int groupMaximum = 0
for row = groupLowRow to groupHighRow
groupMaximum := math.max(groupMaximum, f_int_get(previousTpo, row))
groupedMaxCount := math.max(groupedMaxCount, groupMaximum)
for groupIndex = 0 to groupCount - 1
int groupLowRow = previousMinRow + groupIndex * previousStride
int groupHighRow = math.min(groupLowRow + previousStride - 1, previousMaxRow)
int groupMaximum = 0
bool groupSinglePrint = false
for row = groupLowRow to groupHighRow
int rowCount = f_int_get(previousTpo, row)
groupMaximum := math.max(groupMaximum, rowCount)
groupSinglePrint := groupSinglePrint or (row > previousMinRow and row < previousMaxRow and rowCount == 1)
int profileWidth = math.max(1, int(math.round(groupMaximum / math.max(groupedMaxCount, 1.0) * maximumProfileWidthBars)))
bool isValue = groupHighRow >= prevValRow and groupLowRow <= prevVahRow
bool isPoc = groupHighRow >= prevPocRow and groupLowRow <= prevPocRow
color baseColor = isPoc ? color.new(pocColor, 15) : groupSinglePrint ? color.new(singlePrintColor, 12) : isValue and showValueAreaShading ? previousValueColor : previousOutsideColor
f_set_box(previousProfileBoxes, previousUsed, previousLeft, f_row_high(groupHighRow, previousRowSize), previousLeft + profileWidth, f_row_low(groupLowRow, previousRowSize), color.new(baseColor, math.min(previousProfileTransparency + 10, 100)), color.new(baseColor, previousProfileTransparency))
previousUsed += 1
f_trim_boxes(previousProfileBoxes, previousUsed)
int volumeUsed = 0
if indicatorEnabled and showEstimatedVolume and curVolumeValid
int currentRows = displayCurrentMaxRow - displayCurrentMinRow + 1
int currentStride = math.max(1, int(math.ceil(currentRows / maximumRenderedRows)))
int groupCount = int(math.ceil(currentRows / currentStride))
float groupedMaximumVolume = 1.0
for groupIndex = 0 to groupCount - 1
int groupLowRow = displayCurrentMinRow + groupIndex * currentStride
int groupHighRow = math.min(groupLowRow + currentStride - 1, displayCurrentMaxRow)
float groupVolume = 0.0
for row = groupLowRow to groupHighRow
groupVolume += f_float_get(currentEstimatedVolume, row)
groupedMaximumVolume := math.max(groupedMaximumVolume, groupVolume)
for groupIndex = 0 to groupCount - 1
int groupLowRow = displayCurrentMinRow + groupIndex * currentStride
int groupHighRow = math.min(groupLowRow + currentStride - 1, displayCurrentMaxRow)
float groupVolume = 0.0
for row = groupLowRow to groupHighRow
groupVolume += f_float_get(currentEstimatedVolume, row)
int volumeWidth = math.max(1, int(math.round(groupVolume / groupedMaximumVolume * estimatedVolumeWidthBars)))
bool isValue = groupHighRow >= curVolumeValRow and groupLowRow <= curVolumeVahRow
bool isPoc = groupHighRow >= curVolumePocRow and groupLowRow <= curVolumePocRow
color volumeColor = isPoc ? pocColor : isValue ? secondaryColor : neutralColor
f_set_box(estimatedVolumeBoxes, volumeUsed, estimatedVolumeLeft, f_row_high(groupHighRow, currentRowSize), estimatedVolumeLeft + volumeWidth, f_row_low(groupLowRow, currentRowSize), color.new(volumeColor, 65), color.new(volumeColor, isPoc ? 24 : 56))
volumeUsed += 1
f_trim_boxes(estimatedVolumeBoxes, volumeUsed)
f_trim_labels(rowCountLabels, rowLabelsUsed)
int nameLabelsUsed = 0
if indicatorEnabled and showProfileNames and curValid and showCurrentProfile
f_set_label(profileNameLabels, nameLabelsUsed, currentLeft, curProfileHigh + currentRowSize, "CURRENT TPO", color.new(accentColor, 68), textColor, "Left", "Tiny")
nameLabelsUsed += 1
if indicatorEnabled and showProfileNames and prevValid and showPreviousProfile
f_set_label(profileNameLabels, nameLabelsUsed, previousLeft, prevProfileHigh + previousRowSize, "PREVIOUS TPO", color.new(previousValueColor, 66), textColor, "Left", "Tiny")
nameLabelsUsed += 1
if indicatorEnabled and showProfileNames and showEstimatedVolume and curVolumeValid
f_set_label(profileNameLabels, nameLabelsUsed, estimatedVolumeLeft, curProfileHigh + currentRowSize, "EST. VOLUME", color.new(secondaryColor, 68), textColor, "Left", "Tiny")
nameLabelsUsed += 1
f_trim_labels(profileNameLabels, nameLabelsUsed)
int singleZonesUsed = 0
if indicatorEnabled and showSinglePrintZones and prevValid and inRTH and previousSingleZoneStarts.size() > 0
int zonesToDraw = math.min(previousSingleZoneStarts.size(), maximumSinglePrintZones)
for zoneIndex = 0 to zonesToDraw - 1
int startRow = previousSingleZoneStarts.get(zoneIndex)
int endRow = previousSingleZoneEnds.get(zoneIndex)
float zoneLow = f_row_low(startRow, previousRowSize)
float zoneHigh = f_row_high(endRow, previousRowSize)
bool touched = currentHigh >= zoneLow and currentLow <= zoneHigh
bool repaired = currentHigh >= zoneHigh and currentLow <= zoneLow
color zoneColor = repaired ? neutralColor : touched ? poorColor : singlePrintColor
int zoneLeft = na(previousSessionEndBar) ? bar_index - 1 : previousSessionEndBar
int zoneRight = bar_index + levelExtensionBars
f_set_box(singlePrintBoxes, singleZonesUsed, zoneLeft, zoneHigh, zoneRight, zoneLow, color.new(zoneColor, 42), color.new(zoneColor, repaired ? 92 : touched ? 86 : 78))
singleZonesUsed += 1
f_trim_boxes(singlePrintBoxes, singleZonesUsed)
var array<line> keyLines = array.new<line>()
var array<label> keyLabels = array.new<label>()
f_set_level(
array<line> lineObjects,
array<label> labelObjects,
int index,
bool enabled,
int startX,
int endX,
float price,
string name,
color levelColor,
string styleValue,
int widthValue) =>
line levelLine = index < lineObjects.size() ? lineObjects.get(index) : na
label levelLabel = index < labelObjects.size() ? labelObjects.get(index) : na
if enabled and not na(price)
if na(levelLine)
levelLine := line.new(startX, price, endX, price, xloc = xloc.bar_index, extend = extend.none, color = levelColor, style = styleValue, width = widthValue)
if index < lineObjects.size()
lineObjects.set(index, levelLine)
else
lineObjects.push(levelLine)
else
line.set_xy1(levelLine, startX, price)
line.set_xy2(levelLine, endX, price)
line.set_color(levelLine, levelColor)
line.set_style(levelLine, styleValue)
line.set_width(levelLine, widthValue)
if na(levelLabel)
levelLabel := label.new(endX, price, name, xloc = xloc.bar_index, style = label.style_label_left, color = color.new(levelColor, 82), textcolor = textColor, size = size.tiny)
if index < labelObjects.size()
labelObjects.set(index, levelLabel)
else
labelObjects.push(levelLabel)
else
label.set_xy(levelLabel, endX, price)
label.set_text(levelLabel, name)
label.set_color(levelLabel, color.new(levelColor, 82))
label.set_textcolor(levelLabel, textColor)
else
if not na(levelLine)
line.delete(levelLine)
lineObjects.set(index, line(na))
if not na(levelLabel)
label.delete(levelLabel)
labelObjects.set(index, label(na))
if barstate.islast
while keyLines.size() < 20
keyLines.push(line(na))
while keyLabels.size() < 20
keyLabels.push(label(na))
int currentLevelEnd = bar_index + profileOffsetBars
int previousLevelEnd = bar_index + levelExtensionBars
int currentStart = na(currentSessionStartBar) ? bar_index : currentSessionStartBar
int previousStart = na(previousSessionEndBar) ? bar_index : previousSessionEndBar
string previousStyle = f_line_style(levelLineStyle)
f_set_level(keyLines, keyLabels, 0, indicatorEnabled and showCurrentLevels and curValid, currentStart, currentLevelEnd, curPoc, "TPO POC", pocColor, line.style_solid, 2)
f_set_level(keyLines, keyLabels, 1, indicatorEnabled and showCurrentLevels and curValid, currentStart, currentLevelEnd, curVah, "TPO VAH", developingColor, line.style_dashed, levelLineWidth)
f_set_level(keyLines, keyLabels, 2, indicatorEnabled and showCurrentLevels and curValid, currentStart, currentLevelEnd, curVal, "TPO VAL", developingColor, line.style_dashed, levelLineWidth)
f_set_level(keyLines, keyLabels, 3, indicatorEnabled and showProfileMidpoint and curValid, currentStart, currentLevelEnd, curProfileMid, "TPO MID", color.new(accentColor, 18), line.style_dotted, 1)
f_set_level(keyLines, keyLabels, 4, indicatorEnabled and showInitialBalance and not na(currentIbHigh), currentStart, currentLevelEnd, currentIbHigh, "IBH", color.new(secondaryColor, currentIbRecorded ? 8 : 28), currentIbRecorded ? line.style_solid : line.style_dashed, levelLineWidth)
f_set_level(keyLines, keyLabels, 5, indicatorEnabled and showInitialBalance and not na(currentIbLow), currentStart, currentLevelEnd, currentIbLow, "IBL", color.new(secondaryColor, currentIbRecorded ? 8 : 28), currentIbRecorded ? line.style_solid : line.style_dashed, levelLineWidth)
color prevPocColor = previousPocState == 1 or previousPocState == 2 ? bullishColor : previousPocState == -1 or previousPocState == -2 ? bearishColor : previousPocTouched ? poorColor : previousValueColor
color prevVahColor = previousVahState == 1 ? bullishColor : previousVahState == -2 ? bearishColor : previousVahTouched ? poorColor : previousValueColor
color prevValColor = previousValState == 2 ? bullishColor : previousValState == -1 ? bearishColor : previousValTouched ? poorColor : previousValueColor
string prevPocText = previousPocState == 1 ? "PRIOR POC · ACCEPTED ABOVE" : previousPocState == -1 ? "PRIOR POC · ACCEPTED BELOW" : previousPocState == 2 ? "PRIOR POC · RECLAIMED" : previousPocState == -2 ? "PRIOR POC · LOST" : "PRIOR POC"
string prevVahText = previousVahState == 1 ? "PRIOR VAH · ACCEPTED ABOVE" : previousVahState == -2 ? "PRIOR VAH · REJECTED" : "PRIOR VAH"
string prevValText = previousValState == -1 ? "PRIOR VAL · ACCEPTED BELOW" : previousValState == 2 ? "PRIOR VAL · RECLAIMED" : "PRIOR VAL"
f_set_level(keyLines, keyLabels, 6, indicatorEnabled and extendPreviousLevels and prevValid, previousStart, previousLevelEnd, prevPoc, prevPocText, prevPocColor, previousStyle, levelLineWidth)
f_set_level(keyLines, keyLabels, 7, indicatorEnabled and extendPreviousLevels and prevValid, previousStart, previousLevelEnd, prevVah, prevVahText, prevVahColor, previousStyle, levelLineWidth)
f_set_level(keyLines, keyLabels, 8, indicatorEnabled and extendPreviousLevels and prevValid, previousStart, previousLevelEnd, prevVal, prevValText, prevValColor, previousStyle, levelLineWidth)
f_set_level(keyLines, keyLabels, 9, indicatorEnabled and showPoorHighLow and prevValid and prevPoorHigh, previousStart, previousLevelEnd, prevProfileHigh, poorHighRepaired ? "POOR HIGH · REPAIRED" : "POOR HIGH", poorHighRepaired ? neutralColor : poorColor, line.style_dotted, levelLineWidth)
f_set_level(keyLines, keyLabels, 10, indicatorEnabled and showPoorHighLow and prevValid and prevPoorLow, previousStart, previousLevelEnd, prevProfileLow, poorLowRepaired ? "POOR LOW · REPAIRED" : "POOR LOW", poorLowRepaired ? neutralColor : poorColor, line.style_dotted, levelLineWidth)
f_set_level(keyLines, keyLabels, 11, indicatorEnabled and showEstimatedVolume and showEstimatedVolumeLevels and curVolumeValid, currentStart, currentLevelEnd, curVolumePoc, "EST. VPOC", secondaryColor, line.style_solid, 1)
f_set_level(keyLines, keyLabels, 12, indicatorEnabled and showEstimatedVolume and showEstimatedVolumeLevels and curVolumeValid, currentStart, currentLevelEnd, curVolumeVah, "EST. VVAH", color.new(secondaryColor, 18), line.style_dashed, 1)
f_set_level(keyLines, keyLabels, 13, indicatorEnabled and showEstimatedVolume and showEstimatedVolumeLevels and curVolumeValid, currentStart, currentLevelEnd, curVolumeVal, "EST. VVAL", color.new(secondaryColor, 18), line.style_dashed, 1)
float liveSessionClose = inRTH ? close : currentClose
f_set_level(keyLines, keyLabels, 14, indicatorEnabled and showSessionOpenClose and not na(currentOpen), currentStart, currentLevelEnd, currentOpen, "RTH OPEN", color.new(textColor, 28), line.style_solid, 1)
f_set_level(keyLines, keyLabels, 15, indicatorEnabled and showSessionOpenClose and not na(liveSessionClose), currentStart, currentLevelEnd, liveSessionClose, inRTH ? "LIVE CLOSE" : "RTH CLOSE", color.new(neutralColor, 22), line.style_dotted, 1)
float ibRangeForExtensions = not na(currentIbHigh) and not na(currentIbLow) ? currentIbHigh - currentIbLow : na
f_set_level(keyLines, keyLabels, 16, indicatorEnabled and showIbExtensions and not na(ibRangeForExtensions), currentStart, currentLevelEnd, currentIbHigh + ibRangeForExtensions * firstIbExtension, "IB +" + str.tostring(firstIbExtension, "#.##"), color.new(secondaryColor, 24), line.style_dashed, 1)
f_set_level(keyLines, keyLabels, 17, indicatorEnabled and showIbExtensions and not na(ibRangeForExtensions), currentStart, currentLevelEnd, currentIbLow - ibRangeForExtensions * firstIbExtension, "IB -" + str.tostring(firstIbExtension, "#.##"), color.new(secondaryColor, 24), line.style_dashed, 1)
f_set_level(keyLines, keyLabels, 18, indicatorEnabled and showIbExtensions and not na(ibRangeForExtensions), currentStart, currentLevelEnd, currentIbHigh + ibRangeForExtensions * secondIbExtension, "IB +" + str.tostring(secondIbExtension, "#.##"), color.new(secondaryColor, 42), line.style_dotted, 1)
f_set_level(keyLines, keyLabels, 19, indicatorEnabled and showIbExtensions and not na(ibRangeForExtensions), currentStart, currentLevelEnd, currentIbLow - ibRangeForExtensions * secondIbExtension, "IB -" + str.tostring(secondIbExtension, "#.##"), color.new(secondaryColor, 42), line.style_dotted, 1)
string structureState = not curValid ? "No Profile" :
curPoorHigh and curPoorLow ? "Poor High + Low" :
curPoorHigh ? "Poor High" :
curPoorLow ? "Poor Low" :
curSingleRows > 0 ? str.tostring(curSingleRows) + " Single Rows" : "Clean Extremes"
string ibState = not curValid or na(currentIbHigh) or na(currentIbLow) ? "IB Developing" :
close > currentIbHigh ? "Above IB" : close < currentIbLow ? "Below IB" : "Inside IB"
string ibQuality = not currentIbRecorded or na(currentIbRangePercentile) ? "BUILDING" :
currentIbRangePercentile >= ibExtremePercentile ? "EXTREME" :
currentIbRangePercentile >= ibWidePercentile ? "WIDE" :
currentIbRangePercentile <= ibNarrowPercentile ? "NARROW" : "NORMAL"
string ibDetail = not currentIbRecorded ? "Developing" :
ibQuality + " · " + str.tostring(currentIbRangePercentile, "#") + "p" +
(na(currentIbVolumePercentile) ? "" : " · Vol " + str.tostring(currentIbVolumePercentile, "#") + "p")
bool dataStale = barstate.islast and timenow > time_close + math.max(60 * 60 * 1000, int(timeframe.in_seconds() * 1000.0 * 4.0))
string dataState = dataStale ? "STALE / CLOSED" : barstate.isrealtime and not barstate.isconfirmed ? "LIVE" : "CONFIRMED"
string profileState = curValid ? curShapeText : "BUILDING"
string valueState = valueRelationship + " · " + pocMigration + " POC"
string locationState = currentLocation + " · " + ibState
string warningState = chartWarning ? "CHECK CHART" : currentOverflow or previousOverflow ? "ROW DENSITY" : dataState
var table dashboard = table.new(
position.top_right, 2, 7,
bgcolor = color.new(panelColor, 8),
frame_color = color.new(borderColor, 25), frame_width = 1,
border_color = color.new(borderColor, 78), border_width = 1)
if barstate.islast
bool visible = indicatorEnabled and showDashboard
color invisible = color.new(panelColor, 100)
color bg = visible ? color.new(panelColor, 8) : invisible
color fg = visible ? textColor : color.new(textColor, 100)
color muted = visible ? neutralColor : color.new(neutralColor, 100)
table.set_bgcolor(dashboard, bg)
table.set_frame_color(dashboard, visible ? color.new(borderColor, 25) : color.new(borderColor, 100))
table.set_border_color(dashboard, visible ? color.new(borderColor, 78) : color.new(borderColor, 100))
table.cell(dashboard, 0, 0, "Meridian -- Futures Profile", text_color = fg, bgcolor = visible ? color.new(accentColor, 72) : invisible, text_size = f_text_size(dashboardTextSize), text_halign = text.align_left)
table.cell(dashboard, 1, 0, "", text_color = fg, bgcolor = visible ? color.new(accentColor, 72) : invisible, text_size = f_text_size(dashboardTextSize))
table.cell(dashboard, 0, 1, "Auction", text_color = muted, text_size = f_text_size(dashboardTextSize))
table.cell(dashboard, 1, 1, auctionState, text_color = visible ? auctionColor : color.new(auctionColor, 100), text_size = f_text_size(dashboardTextSize), text_halign = text.align_right)
table.cell(dashboard, 0, 2, "Value", text_color = muted, text_size = f_text_size(dashboardTextSize))
table.cell(dashboard, 1, 2, valueState, text_color = fg, text_size = f_text_size(dashboardTextSize), text_halign = text.align_right)
table.cell(dashboard, 0, 3, "Profile", text_color = muted, text_size = f_text_size(dashboardTextSize))
table.cell(dashboard, 1, 3, profileState, text_color = fg, text_size = f_text_size(dashboardTextSize), text_halign = text.align_right)
table.cell(dashboard, 0, 4, "IB", text_color = muted, text_size = f_text_size(dashboardTextSize))
table.cell(dashboard, 1, 4, ibDetail, text_color = visible ? (ibQuality == "EXTREME" ? poorColor : ibQuality == "NARROW" ? developingColor : fg) : color.new(textColor, 100), text_size = f_text_size(dashboardTextSize), text_halign = text.align_right)
table.cell(dashboard, 0, 5, "Location", text_color = muted, text_size = f_text_size(dashboardTextSize))
table.cell(dashboard, 1, 5, locationState, text_color = fg, text_size = f_text_size(dashboardTextSize), text_halign = text.align_right)
table.cell(dashboard, 0, 6, "Data", text_color = muted, text_size = f_text_size(dashboardTextSize))
table.cell(dashboard, 1, 6, warningState, text_color = visible ? (chartWarning or currentOverflow or previousOverflow ? poorColor : dataStale ? poorColor : textColor) : color.new(textColor, 100), text_size = f_text_size(dashboardTextSize), text_halign = text.align_right)
bool previousPocEvent = barstate.isconfirmed and alertPreviousLevels and previousPocTouched and not previousPocTouched[1]
bool previousVahEvent = barstate.isconfirmed and alertPreviousLevels and previousVahTouched and not previousVahTouched[1]
bool previousValEvent = barstate.isconfirmed and alertPreviousLevels and previousValTouched and not previousValTouched[1]
bool singlePrintEvent = barstate.isconfirmed and alertSinglePrints and previousSinglePrintOverlap and not previousSinglePrintOverlap[1]
bool poorHighEvent = barstate.isconfirmed and alertPoorHighLow and poorHighRepaired and not poorHighRepaired[1]
bool poorLowEvent = barstate.isconfirmed and alertPoorHighLow and poorLowRepaired and not poorLowRepaired[1]
bool auctionChangeEvent = barstate.isconfirmed and alertAuctionChanges and inRTH and auctionCode != auctionCode[1]
string dynamicEvent = previousPocEvent ? "previous_poc_test" :
previousVahEvent ? "previous_vah_test" :
previousValEvent ? "previous_val_test" :
singlePrintEvent ? "single_print_test" :
poorHighEvent ? "poor_high_repair" :
poorLowEvent ? "poor_low_repair" :
auctionChangeEvent ? "auction_state_change" : ""
if enableDynamicAlerts and str.length(dynamicEvent) > 0
string jsonMessage = "{" +
"\"source\":\"meridian_futures_profile\"," +
"\"version\":\"0.2.0\"," +
"\"event\":\"" + dynamicEvent + "\"," +
"\"symbol\":\"" + syminfo.tickerid + "\"," +
"\"timeframe\":\"" + timeframe.period + "\"," +
"\"auction_state\":\"" + auctionState + "\"," +
"\"profile_shape\":\"" + curShapeText + "\"," +
"\"value_relationship\":\"" + valueRelationship + "\"," +
"\"poc\":" + str.tostring(nz(curPoc), format.mintick) + "," +
"\"vah\":" + str.tostring(nz(curVah), format.mintick) + "," +
"\"val\":" + str.tostring(nz(curVal), format.mintick) + "," +
"\"previous_poc\":" + str.tostring(nz(prevPoc), format.mintick) + "," +
"\"rotation_factor\":" + str.tostring(currentRotationFactor, "#.##") + "," +
"\"bar_time\":" + str.tostring(time) + "}"
alert(jsonMessage, alert.freq_once_per_bar_close)
alertcondition(previousPocEvent, "Meridian — Previous TPO POC tested", "Meridian Futures Profile: price tested the previous completed RTH TPO POC.")
alertcondition(previousVahEvent, "Meridian — Previous TPO VAH tested", "Meridian Futures Profile: price tested the previous completed RTH TPO VAH.")
alertcondition(previousValEvent, "Meridian — Previous TPO VAL tested", "Meridian Futures Profile: price tested the previous completed RTH TPO VAL.")
alertcondition(singlePrintEvent, "Meridian — Previous single-print zone tested", "Meridian Futures Profile: price entered a previous completed RTH single-print zone.")
alertcondition(poorHighEvent, "Meridian — Poor high repaired", "Meridian Futures Profile: price traded above the previous poor high.")
alertcondition(poorLowEvent, "Meridian — Poor low repaired", "Meridian Futures Profile: price traded below the previous poor low.")
alertcondition(auctionChangeEvent, "Meridian — Auction state changed", "Meridian Futures Profile: the confirmed Auction Market Theory state changed.")
Overview
Meridian - Futures Profile determines key auction levels including the Point of Control (POC), Value Area High (VAH), Value Area Low (VAL), and profile midpoints for current and previous sessions. Settings configure regular trading hours (RTH), TPO block intervals, automatic or manual row-size tick counts, and value area percentages. Current session profiles update as intraday bars complete, and the tool is restricted to standard timeframes below the selected block size.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Enable indicator
bool
true
indicatorEnabled
Analysis time zone
string
America/New_York
sessionTimezone
Warn on non-standard charts
bool
true
requireStandardCandles
RTH session
session
0930-1600:23456
rthSession
RTH open hour
int
9
rthOpenHour
Minute
int
30
rthOpenMinute
TPO block size
string
30 Minutes
blockSizeInput
Initial Balance blocks
int
2
initialBalanceBlocks
Maximum chart timeframe
int
30
maximumChartMinutes
Row-size mode
string
Auto
rowSizeMode
Manual ticks per row
int
8
manualTicksPerRow
Automatic target rows
int
70
automaticTargetRows
Show all 40 settings
Setting
Type
Default
Variable
Minimum automatic ticks
int
1
minimumAutomaticTicks
Maximum rendered rows
int
100
maximumRenderedRows
Maximum rows per TPO block
int
250
maximumRowsPerBlock
Value area percentage
float
70.0
valueAreaPercent
Show current profile
bool
true
showCurrentProfile
Show previous profile
bool
true
showPreviousProfile
Profile offset from latest bar
int
5
profileOffsetBars
Maximum TPO width (bars)
int
20
maximumProfileWidthBars
Gap between profiles
int
3
profileGapBars
Show TPO count on rows
bool
false
showRowCounts
Show Current / Previous labels
bool
true
showProfileNames
Highlight value-area rows
bool
true
showValueAreaShading
Show profile midpoint
bool
true
showProfileMidpoint
Show current POC / VAH / VAL
bool
true
showCurrentLevels
Extend previous POC / VAH / VAL
bool
true
extendPreviousLevels
Show Initial Balance
bool
true
showInitialBalance
Show IB range extensions
bool
true
showIbExtensions
First IB extension
float
0.5
firstIbExtension
Second
float
1.0
secondIbExtension
Show session open / close
bool
false
showSessionOpenClose
Closes for prior-value acceptance
int
2
acceptanceCloses
Show previous single-print zones
bool
true
showSinglePrintZones
Show previous poor high / low
bool
true
showPoorHighLow
Level extension beyond latest bar
int
20
levelExtensionBars
Maximum single-print zones
int
12
maximumSinglePrintZones
Level line width
int
1
levelLineWidth
Previous level style
string
Dashed
levelLineStyle
Show estimated volume profile
bool
false
showEstimatedVolume
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.
This indicator calculates and plots a session-based volume profile directly on the price chart. It maps volume distribution across price levels, highlighting key structural reference points including the Point of Control, Value Area High, and Value Area Low. C
The Midnight VWAP indicator calculates and overlays two distinct Volume Weighted Average Price (VWAP) lines on the price chart based on customizable session timeframes. It tracks an overnight or pre-market session alongside regular trading hours, resetting cal
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
This overlay indicator draws a volume profile for a chosen trading session directly on the price chart. It plots the point of control, value area high and low, an optional value area box, a live zone, and a session box with labels. Users select Tokyo, London,
This overlay indicator marks the current day's low and a suggested entry zone a fraction of daily ATR below that low. It compares daily volume with a volume moving average, plots daily SMA and EMA distance, and draws an optional ATR-width price cloud. Labels c
This indicator plots a Market Profile on the price chart using time-price opportunity letters or symbols. It marks the point of control, a configurable value area, and the initial balance, and it can highlight a volume-profile point of control and session VWAP