Accumulation Zone Profiles [UAlgo]Accumulation Zone Profiles is an overlay indicator that detects low volatility accumulation phases and automatically builds a fixed range volume profile for each confirmed zone. The script uses a statistical volatility model based on log returns, identifies periods where volatility compresses into an unusually quiet regime, then verifies that price action remains sufficiently sideways before confirming the zone.
When a zone is confirmed, the indicator draws two complementary views:
A highlighted accumulation range on the chart
A fixed range volume profile drawn to the right of the zone, including Point of Control and Value Area levels
The intent is to turn consolidation into a structured map. Instead of treating ranges as vague rectangles, the script assigns a volume distribution to the range so you can see where the market accepted price, where it rejected price, and which levels are most likely to matter during expansion.
🔹 Features
1) Statistical Accumulation Detection Using Log Volatility
The detection engine starts from log returns r = ln(C / C ) and builds an EWMA based volatility estimate. Volatility is converted into log space and evaluated with a rolling distribution model. A z score determines whether the current volatility is unusually low relative to its own history.
Zones begin when the z score falls below a configurable entry threshold and they end after volatility recovers above an exit threshold for a configurable number of confirmation bars.
2) Sideways Quality Filter With Trend Score
Not every low volatility period is true accumulation. The script measures drift using a trend score defined as:
absolute sum of returns divided by sum of absolute returns
Lower values indicate more rotation and less directional drift. A maximum trend score filter ensures zones remain meaningfully sideways before a profile is created.
3) Non Repainting Option Using Confirmed Bars
When enabled, the system only updates on confirmed bars. This reduces repainting behavior and makes zone start and zone end decisions more stable for live trading workflows.
4) Fixed Range Volume Profile Per Zone
For each confirmed accumulation zone, the script builds a histogram of volume across a user selected number of price rows. Each bar’s volume is distributed into bins according to how much of that candle range overlaps each bin. This produces a true fixed range profile for the zone.
The profile is drawn to the right of the zone so it does not cover price action.
5) Point of Control and Value Area Levels
The highest volume row is treated as the Point of Control. Value Area High and Value Area Low are computed by expanding outward from POC until a target percent of total volume is captured. This creates a practical acceptance framework inside the zone.
Optional plotting allows:
Highlighting the POC row
Drawing VAH and VAL lines
Drawing zone boundary guides
Showing an information label that summarizes zone statistics
6) Tick Alignment For Clean Levels
If enabled, the zone range, bin step size, and derived levels are aligned to the instrument tick size. This produces cleaner price levels and reduces floating point noise in labels and lines.
7) Object Budget Management
Volume profiles use many boxes. The script computes an effective rows value based on the number of profiles you choose to keep, then limits total box usage so you are less likely to hit platform object limits. Older profiles are deleted automatically when new ones are created.
8) Alerts
Two alerts are available:
Accumulation Zone Started
Accumulation Zone Confirmed and Profile Created
This supports automation such as watchlist monitoring and breakout preparation.
🔹 Calculations
1) Log Return and EWMA Variance
The script defines log return as ln(C / C ) and uses an EWMA of squared returns as a variance proxy.
Alpha for EWMA:
f_alpha(int len) =>
2.0 / (len + 1.0)
Log return:
lr = math.log(close / close )
lr := na(close ) or close == 0.0 ? 0.0 : lr
EWMA variance and volatility:
alphaFast = f_alpha(fastLen)
var float ewmaVarR = na
ewmaVarR := na(ewmaVarR ) ? lr * lr : alphaFast * (lr * lr) + (1.0 - alphaFast) * ewmaVarR
vol = math.sqrt(math.max(ewmaVarR, 0.0))
2) Log Volatility Distribution and z Score
Volatility is transformed into log space to stabilize distribution behavior. The script maintains EWMA estimates of mean and second moment, then computes standard deviation and z score.
eps = 1e-10
logVol = math.log(vol + eps)
alphaDist = f_alpha(distLen)
var float m1 = na
var float m2 = na
m1 := na(m1 ) ? logVol : alphaDist * logVol + (1.0 - alphaDist) * m1
m2 := na(m2 ) ? logVol * logVol : alphaDist * (logVol * logVol) + (1.0 - alphaDist) * m2
sigma = math.sqrt(math.max(m2 - m1 * m1, eps))
z = (logVol - m1) / sigma
Interpretation:
Negative z means volatility is below its typical level
More negative z means stronger compression
3) Zone Start and Zone End Conditions
Entry and exit logic uses the z score relative to thresholds:
Start when z is less than or equal to minus Enter Threshold
End when z is greater than or equal to minus Exit Threshold for Exit Confirm Bars
lowNow = not na(z) and z <= -enterZ
highNow = not na(z) and z >= -exitZ
Exit confirmation counter:
zb.exitCount := highNow ? zb.exitCount + 1 : 0
bool exitByConfirm = zb.exitCount >= exitBars
A maximum zone duration also forces closure:
bool exitByMax = zb.barCount() >= maxZoneBars
If exit is triggered via confirmation bars, the script removes those last bars from the zone before profiling to avoid contaminating the accumulation range with the volatility recovery phase.
4) Sideways Drift Score Filter
During zone building, returns are accumulated:
sumR is the signed drift
sumAbsR is total movement magnitude
Trend score:
method driftScore(ZoneBuilder this) =>
math.abs(this.sumR) / math.max(this.sumAbsR, 1e-10)
A zone is accepted only if:
Bar count is at least Min Zone Bars
Drift score is less than or equal to Max Trend Score
5) Profile Range and Row Step
Once accepted, the profile range is built from the min low and max high of the zone. The range is optionally aligned to tick.
Step size equals range divided by rows, with optional tick alignment:
float stepRaw = (hi - lo) / rows
float step = stepRaw
if alignTick
step := math.max(syminfo.mintick, f_roundToTick(stepRaw))
6) Volume Histogram Construction
For each bar in the zone, volume is assigned into bins.
If the candle range is very small, volume goes to a single nearest bin.
Otherwise, volume is distributed proportionally by overlap between candle range and each bin range.
Core proportional distribution logic:
float overlap = math.max(0.0, math.min(bh, binHi) - math.max(bl, binLo))
if overlap > 0
float frac = overlap / br
array.set(binVol, b, array.get(binVol, b) + bv * frac)
This produces binVol, a per row volume distribution.
7) POC Calculation
POC is the index of the maximum bin volume. POC price is the center of that row.
if v > maxV
maxV := v
pocIdx := b
float poc = lo + (pocIdx + 0.5) * step
8) Value Area Computation
Value Area is derived by expanding outward from POC until the cumulative volume reaches Value Area percent of total volume.
Target volume:
float target = totV * (valueAreaPct / 100.0)
Expand left and right by choosing the side with higher next volume until the target is met. This creates a contiguous value area band.
VAL and VAH mapping to prices:
float valP = lo + left * step
float vahP = lo + (right + 1) * step
9) Rendering Logic
Zone highlight is drawn directly over the accumulation period using a box with configurable fill and border transparency.
The volume profile is drawn as a stack of boxes to the right of the zone. Each row width is proportional to row volume relative to max row volume. Transparency is mapped so high volume rows appear more prominent.
POC row can be highlighted using a dedicated color and transparency configuration.
VAH and VAL can be drawn as horizontal lines across the profile region, and optional boundary lines can mark the start and end of the detected zone.
10) Profile Retention and Cleanup
Profiles are stored in an array. When the number of stored profiles exceeds Keep Last Profiles, the oldest profile is deleted and all of its objects are removed. This keeps the chart responsive and prevents reaching the platform maximum object counts.
Pine Script® インジケーター






















