Indicator · Pine Script v5 · On the chart

Time & volume point of control (TPOC & VPOC)

by clojure-quant· code credited to quantifytools

Calculates Time Point of Control (TPOC) and Volume Point of Control (VPOC) using lower-timeframe data mapped to the current chart resolution.

View on GitHub

GitHub stars2on the repository
Pine versionv5runs as is
Lines of code305
Settings22inputs
LicenceNone statedfile: MPL-2.0
Last update2 years ago6 Jul 2024

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

305 lines of Pine Script v5 from src/quanta/notebook/study/poc/poc.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

poc.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © quantifytools

//@version=5
indicator("Time & volume point of control (TPOC & VPOC)", overlay=true, max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500)

//                      Inputs

//POC inputs
groupPoc = "VPOC & TPOC settings"

i_source = input.source(close, "Source for TPOC/VPOC price", group=groupPoc, tooltip="E.g. With close as source, a point of control is calculated using LTF close.")
i_tpocThreshold = input.int(50, "Threshold % for TPOC highlight", maxval=100, minval=1, group=groupPoc, tooltip="E.g. With 50% threshold, a highlight will occur when >= 50% of total time was traded at point of control.")
i_vpocThreshold = input.int(50, "Threshold % for VPOC highlight", maxval=100, minval=1, group=groupPoc)

i_deltaRule = input.string("Standard VPOC", "Show VPOC as", options=["Standard VPOC", "VPOC with delta indication", "VPOCs separately for each side"], group=groupPoc,  tooltip="Standard VPOC = volume point of control. VPOC with delta indication = volume point of control with direction indicated as color (buy/sell). VPOCs separately for each side = volume point of control for each side separately with direction indicated as color.")
i_buySellPoc = i_deltaRule == "VPOC with delta indication" or i_deltaRule == "VPOCs separately for each side"
i_addPoc = i_deltaRule == "VPOCs separately for each side"

//Timeframe inputs
groupTf = "Timeframe settings"

i_tf1 = input.timeframe("1", "LTF for charts <= 30 min", group=groupTf, tooltip="E.g. Lower timeframe data is fetched from 1 minute timeframe when viewing charts at or below 30 min. The lower the chosen timeframe, the more precision you get, but with the cost of less historical data and slower loading time.")
i_tf2 = input.timeframe("5", "LTF for charts > 30 min & <= 3 hours", group=groupTf)
i_tf3 = input.timeframe("15", "LTF for charts > 3 hours & <= 8 hours", group=groupTf)
i_tf4 = input.timeframe("60", "LTF for charts > 8 hours & <= 1D", group=groupTf)
i_tf5 = input.timeframe("120", "LTF for charts > 1D & <= 3D", group=groupTf)
i_tf6 = input.timeframe("240", "LTF for charts > 3D", group=groupTf)

//TPOC/VPOC visual inputs
groupVisuals = "TPOC/VPOC visuals"

i_hideChart = input.bool(true, "Hide chart", group=groupVisuals, tooltip="")
i_lineCol = input.color(color.gray, "Line", group=groupVisuals, inline="line")
i_lineColThreshold = input.color(color.orange, "Threshold exceeded", group=groupVisuals, inline="line")
i_vpocCol = input.color(color.yellow, "VPOC", group=groupVisuals, inline="vpoc")
i_vpocThresholdCol = input.color(color.orange, "Threshold exceeded", group=groupVisuals, inline="vpoc")
i_tpocCol = input.color(color.yellow, "TPOC", group=groupVisuals, inline="tpoc")
i_tpocThresholdCol = input.color(color.orange, "Threshold exceeded", group=groupVisuals, inline="tpoc")
i_vpocBuyCol = input.color(color.rgb(31, 97, 249), "Buy side POC", group=groupVisuals, inline="vpocdelta")
i_vpocSellCol = input.color(color.rgb(247, 62, 139), "Sell side POC", group=groupVisuals, inline="vpocdelta")



//TPOC/VPOC anomaly visual inputs
groupAnomalyVisuals = "Anomaly visuals"

i_hideAnomalies = input.bool(true, "Hide anomalies", tooltip="", group=groupAnomalyVisuals)
i_trappedTpocVpoc = input.color(color.rgb(232, 74, 74), "Trapped TPOC/VPOC", group=groupAnomalyVisuals)
i_trendInitiationCol = input.color(color.rgb(232, 74, 74), "Trend initiation", group=groupAnomalyVisuals)

//                      LTF data


//Delta rules
upVolRule = i_source >= i_source[1] 
downVolRule = i_source < i_source[1] 

//Current timeframe in minutes
currentTimeframe = timeframe.in_seconds(timeframe.period) / 60

//Dynamic timeframe
dynTf = currentTimeframe <= 30 ? i_tf1 : currentTimeframe > 30 and currentTimeframe <= 180 ? i_tf2 :
 currentTimeframe > 180 and currentTimeframe <= 480 ? i_tf3 : currentTimeframe > 480 and currentTimeframe <= 1440 ? i_tf4 :
  currentTimeframe > 1440 and currentTimeframe <= 4320 ? i_tf5 : currentTimeframe > 4320 ? i_tf6 : na

//Function to fetch LTF data
ltfStats() =>


    vol = na(volume) == false ? volume : 0
    volDelta = upVolRule and na(volume) == false ? volume : downVolRule and na(volume) == false ? 0 - volume : na

    [i_source, vol, volDelta]

[ltfSrc, ltfVolume, ltfDelta]
 = request.security_lower_tf(syminfo.tickerid, dynTf, ltfStats())


//                      TPOC

//Dividing range into 10 blocks/lots
divBlock = (high - low) / 10

//Blocks separately
tpoBlock1 = low + (divBlock * 1)
tpoBlock2 = low + (divBlock * 2)
tpoBlock3 = low + (divBlock * 3)
tpoBlock4 = low + (divBlock * 4)
tpoBlock5 = low + (divBlock * 5)
tpoBlock6 = low + (divBlock * 6)
tpoBlock7 = low + (divBlock * 7)
tpoBlock8 = low + (divBlock * 8)
tpoBlock9 = low + (divBlock * 9)
tpoBlock10 = low + (divBlock * 10)

//Time counters for blocks
int block1Count = 0
int block2Count = 0
int block3Count = 0
int block4Count = 0
int block5Count = 0
int block6Count = 0
int block7Count = 0
int block8Count = 0
int block9Count = 0
int block10Count = 0

//TPO array size
tpoArrSize = array.size(ltfSrc)

//Looping through TPO array and assigning counts to blocks
for i = 0 to (tpoArrSize > 0 ? tpoArrSize - 1 : na)

    recentTpo = array.get(ltfSrc, i)

    if recentTpo < tpoBlock1
        block1Count += 1 
    else
        if recentTpo > tpoBlock1 and recentTpo <= tpoBlock2
            block2Count += 1 
        else   
            if recentTpo > tpoBlock2 and recentTpo <= tpoBlock3
                block3Count += 1 
            else   
                if recentTpo > tpoBlock3 and recentTpo <= tpoBlock4
                    block4Count += 1 
                else   
                    if recentTpo > tpoBlock4 and recentTpo <= tpoBlock5
                        block5Count += 1 
        

    if recentTpo > tpoBlock5 and recentTpo <= tpoBlock6
        block6Count += 1 
    else   
        if recentTpo > tpoBlock6 and recentTpo <= tpoBlock7
            block7Count += 1 
        else   
            if recentTpo > tpoBlock7 and recentTpo <= tpoBlock8
                block8Count += 1 
            else   
                if recentTpo > tpoBlock8 and recentTpo <= tpoBlock9
                    block9Count += 1 
                else
                    if recentTpo > tpoBlock9 and recentTpo <= tpoBlock10
                        block10Count += 1 

//TPOC Array
tpoCountArray = array.new_int(0, 0)

//Populating TPOC array with time counts
array.push(tpoCountArray, block1Count)
array.push(tpoCountArray, block2Count)
array.push(tpoCountArray, block3Count)
array.push(tpoCountArray, block4Count)
array.push(tpoCountArray, block5Count)
array.push(tpoCountArray, block6Count)
array.push(tpoCountArray, block7Count)
array.push(tpoCountArray, block8Count)
array.push(tpoCountArray, block9Count)
array.push(tpoCountArray, block10Count)

//Highest time count (TPOC)
maxTpo = array.max(tpoCountArray)

//Array index of TPOC
indexOfMaxTpo = array.indexof(tpoCountArray, maxTpo)

//Finding TPOC block
relevantTpoBlock = indexOfMaxTpo == 0 ? tpoBlock1 : indexOfMaxTpo == 1 ? tpoBlock2 : indexOfMaxTpo == 2 ? tpoBlock3 : indexOfMaxTpo == 3 ? tpoBlock4 : 
 indexOfMaxTpo == 4 ? tpoBlock5 : indexOfMaxTpo == 5 ? tpoBlock6 : indexOfMaxTpo == 6 ? tpoBlock7 : indexOfMaxTpo == 7 ? tpoBlock8 : 
  indexOfMaxTpo == 8 ? tpoBlock9 : tpoBlock10

//Forming a plot location for TPOC mark (middle point of block)
tpoBlockPos = relevantTpoBlock == tpoBlock1 ? (low + tpoBlock1) / 2 : relevantTpoBlock == tpoBlock2 ? (tpoBlock1 + tpoBlock2) / 2 : relevantTpoBlock == tpoBlock3 ? (tpoBlock2 + tpoBlock3) / 2  : 
 relevantTpoBlock == tpoBlock4 ? (tpoBlock3 + tpoBlock4) / 2  : relevantTpoBlock == tpoBlock5 ? (tpoBlock4 + tpoBlock5) / 2  : relevantTpoBlock == tpoBlock6 ? (tpoBlock5 + tpoBlock6) / 2  : 
  relevantTpoBlock == tpoBlock7 ? (tpoBlock6 + tpoBlock7) / 2  : relevantTpoBlock == tpoBlock8 ? (tpoBlock7 + tpoBlock8) / 2  : relevantTpoBlock == tpoBlock9 ? (tpoBlock8 + tpoBlock9) / 2 : (tpoBlock9 + high) / 2


//                      VPOC

//Max volume from LTF volume/up volume array
maxVolume = array.max(ltfVolume)

//Array index of max volume
indexOfMaxVolume = array.indexof(ltfVolume, maxVolume)

//Price at max volume (VPOC/bVPOC)
maxVol = array.size(ltfSrc) > 0 ? array.get(ltfSrc, indexOfMaxVolume) : na


//Max volume from LTF down volume array
maxVolumeDelta = array.max(ltfDelta)
minVolumeDelta = array.min(ltfDelta)

//Determining additional POC
addVpoc = maxVolumeDelta > math.abs(minVolumeDelta) ? minVolumeDelta : maxVolumeDelta

//Array index of max sell volume
indexOfAddVpoc = array.indexof(ltfDelta, addVpoc)

//Price at max sell volume (sVPOC)
addVpocPrice = array.size(ltfSrc) > 0 ? array.get(ltfSrc, indexOfAddVpoc) : na



//                      TPOC/VPOC anomalies

//Trapped time/volume

//Ensuring sufficient distance between close and TPOC/VPOC, equal to or greater than half of bar range.
vpocCloseSpread = close > maxVol ? close - maxVol : maxVol - close
vpocCloseSpreadCond = vpocCloseSpread >= (hl2 - low)
tpocCloseSpread = close > tpoBlockPos ? close - tpoBlockPos : tpoBlockPos - close
tpocCloseSpreadCond = tpocCloseSpread >= (hl2 - low)

//Trapped volume down scenarios
trappedVpocDown = (close > low[1] and maxVol < low[1]) or (close < low[1] and maxVol < close and vpocCloseSpreadCond)

//Trapped volume up scenarios
trappedVpocUp = (close < high[1] and maxVol > high[1]) or (close > high[1] and maxVol > close and vpocCloseSpreadCond)

//Trapped time down scenarios
trappedTpocDown = (close > low[1] and tpoBlockPos < low[1]) or (close < low[1] and tpoBlockPos < close and tpocCloseSpreadCond)

//Trapped time up scenarios
trappedTpocUp = (close < high[1] and tpoBlockPos > high[1]) or (close > high[1] and tpoBlockPos > close and tpocCloseSpreadCond)

//Trapped volume
trappedVolumeDown = trappedVpocDown and maxVol < hl2 and barstate.isconfirmed
trappedVolumeUp = trappedVpocUp and maxVol > hl2 and barstate.isconfirmed

trappedVolumeAny = trappedVolumeDown or trappedVolumeUp

//Trapped time
trappedTimeDown = trappedTpocDown and tpoBlockPos < hl2 and barstate.isconfirmed
trappedTimeUp = trappedTpocUp and tpoBlockPos > hl2 and barstate.isconfirmed

trappedTimeAny = trappedTimeDown or trappedTimeUp

//Trend initiation

//Defining volatility
volatility = high - low

//Ensuring distance between TPOC and VPOC is sufficient, equal to or greater than half of bar range.
tpocVpocSpread = tpoBlockPos > maxVol ? tpoBlockPos - maxVol : maxVol - tpoBlockPos
tpocVpocSpreadCond = tpocVpocSpread >= (hl2 - low)

//Trend initiation up/down
trendInitiationUp = close > high[1] and tpoBlockPos < hl2 and maxVol > hl2 and volatility > volatility[1] * 1.5 and tpocVpocSpreadCond and barstate.isconfirmed
trendInitiationDown = close < low[1] and (maxVol < hl2 and tpoBlockPos > hl2) and volatility > volatility[1] * 1.5 and tpocVpocSpreadCond and barstate.isconfirmed

trendInitiationAny = trendInitiationUp or trendInitiationDown


//                      Alerts

alertcondition(trappedTimeUp, "Trapped TPOC above", "Potential trapped TPOC above detected.")
alertcondition(trappedTimeDown, "Trapped TPOC below", "Potential trapped TPOC below detected.")

alertcondition(trappedVolumeUp, "Trapped VPOC above", "Potential trapped VPOC above detected.")
alertcondition(trappedVolumeDown, "Trapped VPOC below", "Potential trapped VPOC below detected.")

alertcondition(trappedTimeAny, "Trapped TPOC above/below", "Potential trapped TPOC above/below detected.")
alertcondition(trappedVolumeAny, "Trapped VPOC above/below", "Potential trapped VPOC above/below detected.")
alertcondition(trappedTimeAny or trappedVolumeAny, "Trapped POC (either) above/below", "Potential trapped POC (either) above/below detected.")

alertcondition(trendInitiationUp, "Trend initiation up", "Potential trend initiation up detected.")
alertcondition(trendInitiationDown, "Trend initiation down", "Potential trend initiation down detected.")
alertcondition(trendInitiationAny, "Trend initiation any", "Potential trend initiation up/down detected.")


//                      Colors

vpocThresholdMet = maxVolume >= (volume * (i_vpocThreshold / 100)) and barstate.isconfirmed
tpocThresholdMet = maxTpo >= (tpoArrSize * (i_tpocThreshold / 100)) and barstate.isconfirmed

vpocColor = (trappedVolumeUp or trappedVolumeDown) and i_hideAnomalies == false ? i_trappedTpocVpoc : vpocThresholdMet ? i_vpocThresholdCol : i_vpocCol
tpocColor = (trappedTimeUp or trappedTimeDown) and i_hideAnomalies == false ? i_trappedTpocVpoc : tpocThresholdMet ? i_tpocThresholdCol : i_tpocCol
lineColor = vpocThresholdMet or tpocThresholdMet ? i_lineColThreshold : i_lineCol
boxColor = vpocThresholdMet[500] or tpocThresholdMet[500] ? i_lineColThreshold : i_lineCol


//                      Plots

plotVpocCond = tpoArrSize > 0 and na(volume) == false and volume > 0 
mainVpocCol = i_buySellPoc ? addVpoc == maxVolumeDelta ? i_vpocSellCol : i_vpocBuyCol : vpocColor
addVpocCond = addVpoc == maxVolumeDelta ? i_vpocBuyCol : i_vpocSellCol 

//TPOC and VPOC plots
plotshape(tpoArrSize > 0 ? tpoBlockPos : na, title="TPOC", style=shape.cross, text="", color=tpocColor, location=location.absolute, size=size.small, textcolor=color.white)
plotshape(plotVpocCond ? maxVol : na, title="VPOC #1", style=shape.circle, text="", color=mainVpocCol, location=location.absolute, size=size.tiny, textcolor=color.white)
plotshape(i_buySellPoc and i_addPoc ? addVpocPrice : na, title="VPOC #2", style=shape.cross, text="", color=addVpocCond, location=location.absolute, size=size.small, textcolor=color.white)


plotshape(i_hideAnomalies == false and trendInitiationUp ? low : na, title="Trend initiation up", style=shape.triangleup, text="", color=i_trendInitiationCol, location=location.belowbar, size=size.tiny, textcolor=color.white)
plotshape(i_hideAnomalies == false and trendInitiationDown ? high : na, title="Trend initiation down", style=shape.triangledown, text="", color=i_trendInitiationCol, location=location.abovebar, size=size.tiny, textcolor=color.white)

//Mid-bar line
line.new(bar_index, high, bar_index, low, xloc=xloc.bar_index, color=lineColor, width=3)
box.new(left=bar_index[500], top=high[500], right=bar_index[500], bottom=low[500], bgcolor=boxColor, border_color=boxColor, border_width=2)

//Hiding chart
barcolor(i_hideChart == true ? color.new(color.white, 100) : na)

Overview

Time & volume point of control (TPOC & VPOC) plots control levels directly on the price chart, highlighting bars where the percentage of time or volume exceeds user-defined thresholds. The tool supports standard VPOC, delta-indicated VPOC, and separate buy or sell side VPOCs, alongside anomaly visualisations such as trapped POCs and trend initiations. Settings allow adjusting the price source, threshold percentages, and multi-timeframe resolution mappings.

Details

Published by

clojure-quant on GitHub

Code credit

quantifytools, as written in the script's header

Type

Indicator, Pine Script v5

Draws

On the price chart

Category

Volume, Support & resistance

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
Source for TPOC/VPOC pricesourceclosei_source
Threshold % for TPOC highlightint50i_tpocThreshold
Threshold % for VPOC highlightint50i_vpocThreshold
Show VPOC asstringStandard VPOCi_deltaRule
LTF for charts <= 30 mintimeframe1i_tf1
LTF for charts > 30 min & <= 3 hourstimeframe5i_tf2
LTF for charts > 3 hours & <= 8 hourstimeframe15i_tf3
LTF for charts > 8 hours & <= 1Dtimeframe60i_tf4
LTF for charts > 1D & <= 3Dtimeframe120i_tf5
LTF for charts > 3Dtimeframe240i_tf6
Hide chartbooltruei_hideChart
Linecolorcolor.grayi_lineCol
Show all 22 settings
SettingTypeDefaultVariable
Threshold exceededcolorcolor.orangei_lineColThreshold
VPOCcolorcolor.yellowi_vpocCol
Threshold exceededcolorcolor.orangei_vpocThresholdCol
TPOCcolorcolor.yellowi_tpocCol
Threshold exceededcolorcolor.orangei_tpocThresholdCol
Buy side POCcolorcolor.rgb(31, 97, 249)i_vpocBuyCol
Sell side POCcolorcolor.rgb(247, 62, 139)i_vpocSellCol
Hide anomaliesbooltruei_hideAnomalies
Trapped TPOC/VPOCcolorcolor.rgb(232, 74, 74)i_trappedTpocVpoc
Trend initiationcolorcolor.rgb(232, 74, 74)i_trendInitiationCol
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
218 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 8 weeks ago · Custom licence GitHub repository of Moon Dev GEX Arrows

More indicators

Trade Your Charts With a Regulated Broker

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

Compare brokers