Dynamic Point of Control (POC)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tkarolak
//@version=5

// ====================
// ==== Background ====
// ====================

// The Dynamic POC Indicator is a potent tool designed to provide traders with real-time market insights.
// It helps identify critical price levels (POC), analyze volume distribution, and gauge market sentiment.

// Key Features:
// 1. Instant Updates: The POC recalculates with every new bar, keeping you well-informed about evolving market conditions.
// 2. Market Sentiment: Assess sentiment by examining bullish volume share. This metric indicates whether the market leans towards bullish or bearish tendencies.
// 3. Customization: Tailor inputs to align with your unique trading strategy. Adjust source, bar count, and row size for fine-tuned analysis.
// 4. Chart Visualization: Graphically view POC and related data on your price chart, enhancing your understanding of critical price levels and volume insights.
//
// How to Use:
// - POC Identification: Locate the POC price level, representing the price point with the highest trading volume in the historical range.
//   It often serves as pivotal support or resistance.
// - Volume Analysis: Study volume distribution across different price segments to pinpoint potential trade entry and exit zones.
// - Sentiment Assessment: Gauge market sentiment via the "Bias of Volume Share at POC."  - insight available in Data Window
//   A bullish bias suggests a positive sentiment, while a bearish bias indicates negativity.
//
// The Dynamic POC Indicator equips traders with a dynamic, adaptable tool, enhancing their ability to navigate markets effectively. 
// To maximize effectiveness, combine Dynamic POC insights with other technical tools and sound risk management strategies for comprehensive trading decisions.

indicator("Dynamic Point of Control (POC)", shorttitle = "Dynamic POC", overlay=true, format = format.price)

// Custom types
type SettingsPoc
    float   source
    int     bars
    int     slots

// Tooltips for parameter explanations
gPoc           = "Dynamic Point of Control"
ttSourcePoc    = "Select the price source Dynami POC calculations"
ttBarsPoc      = "Define the number of historical bars to include in the analysis. A larger value encompasses more historical data, while a smaller value focuses on recent price action."
ttSlotsPoc     = "Specify the number of rows or price segments within the selected historical range. A smaller row size provides a more detailed analysis, while a larger row size simplifies the view."

// Input parameters
SettingsPoc settingsPoc =
 SettingsPoc.new(
   input.source  (close,      "Source",                         group = gPoc, tooltip = ttSourcePoc),
   input.int     (150,        "Number of Bars / MA length",     group = gPoc, minval = 1, maxval = 2000, tooltip = ttBarsPoc),
   input.int     (10,         "Row Size",                       group = gPoc, minval = 5, maxval = 100, tooltip = ttSlotsPoc)
 )

// Runtime error indicating the absence of volume data from the data vendor
if barstate.islast and ta.cum(volume) == 0
    runtime.error("No volume is provided by the data vendor.")

// Confirmation condition
bool  confirmed = barstate.isconfirmed

// Calculate the highest and lowest price within the specified number of bars
float top       = ta.highest(settingsPoc.bars)
float bot       = ta.lowest(settingsPoc.bars)

// Calculate the price step for each row/slot
float step      = (top - bot) / settingsPoc.slots

// Calculate & keep rows/slots middle price levels
levels = array.new_float(settingsPoc.slots)
for x = 0 to settingsPoc.slots - 1 by 1
    array.set(levels, x, bot + step * x + step/2)

// Initialize arrays to store volumes
volumes         = array.new_float(settingsPoc.slots, 0.0)
volumesBull     = array.new_float(settingsPoc.slots, 0.0)

int index = na

// Loop through historical bars
for bars = 0 to settingsPoc.bars - 1 by 1
    if confirmed and bar_index >= settingsPoc.bars
        // Calculate the index of the row for the current bar
        index := int((settingsPoc.source[bars] - bot) / step)
        index := index == settingsPoc.slots ? settingsPoc.slots - 1 : index
        
        // Accumulate volume within the row/slot
        float currentVol        = array.get(volumes, index)
        float currentVolBull    = array.get(volumesBull, index)
        array.set(volumes, index, currentVol + volume[bars])
        
        // Check if the current bar is bullish and accumulate volume within the row/slot
        array.set(volumesBull, index, close[bars] >= open[bars] ? currentVolBull + volume[bars] : currentVolBull)

// Find the index of the Point of Control (POC) based on the maximum volume
int pocIndex = array.indexof(volumes, array.max(volumes))

// Calculate the percentage of bullish volume share at POC
float bullishShare = array.get(volumesBull, pocIndex) / array.get(volumes, pocIndex) * 100

// Calculate Volume Weighted Moving Average (VWMA)
ma = ta.vwma(settingsPoc.source, settingsPoc.bars)

// Get calculated Point of Control (POC) level
poc_level = array.get(levels, pocIndex)

////////////////////////////////////////////////////////////////////////////////
//                     ====== DRAWING and PLOTTING ======                     //
////////////////////////////////////////////////////////////////////////////////

// Determine the color of the line based on bullish sentiment
colorLine = bullishShare >= 50 ? color.green : color.red

// Plot POC and other data on the chart
plot(poc_level, "POC", color=colorLine, linewidth=2)
plot(bullishShare, "Bias of Volume Share at POC", display=display.data_window)