Indicator · Pine Script v6 · On the chart

Compression Fractal Release [WavesUnchained]

by casoon

This indicator detects compressed corrective coils inside a higher-timeframe trend and signals when they resolve into clean directional breaks.

View on GitHub

GitHub stars9on the repository
Pine versionv6current
Lines of code433
Settings30inputs
LicenceMIT
Last update3 days ago27 Oct 2026

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

433 lines of Pine Script v6 from indicators/volatility/compression_fractal_release/compression_fractal_release.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

compression_fractal_release.pine
//@version=6
// ============================================================================
// Compression Fractal Release [WavesUnchained]
// Version: 1.1.4
// Author: WavesUnchained
// Build: 2026-06-30 00:00:00
// ============================================================================
// Detects a compressed, corrective coil inside a higher-timeframe trend and
// trades the moment it resolves into a clean directional break (the "release").
// The band break is the only trigger; coil strength and release dynamics are
// scored as weighted evidence (not a hard AND-chain), and a single choppiness
// veto gates low-quality breaks. The HTF regime decides whether a break is a
// continuation (Release) or a structural change out of a base (Base Break).
//
// Roles (one sensor per role — see indicator-design skill):
//   Trend     HTF EMA slope (continuation vs. counter-trend classification)
//   Location  Compression complexity + box-counting fractal dimension (the coil)
//   Momentum  Release dynamics — complexity falling + efficiency rising
//   Trigger   Break of the prior correction band (timing only)
//   Quality   Choppiness Index veto (no clean break in extreme chop)
//
// Features:
//   ✓ Symbolic-entropy compression proxy + box-counting fractal dimension + efficiency
//   ✓ Multiplicative Setup Score (coil ceiling × release dynamics) — release required, no AND-chain
//   ✓ Regime-classified break: Release (with HTF) vs Base Break (against HTF)
//   ✓ Watch → Setup → Trigger signal staging with per-direction cooldown
//   ✓ Single choppiness quality veto (no per-sensor blocking)
//   ✓ Light-theme dashboard with role-score breakdown
//   ✓ Debug log on every break — including why a break was blocked
//
// ============================================================================

indicator("Compression Fractal Release [WavesUnchained]", "CFR", overlay=true, max_labels_count=500, max_boxes_count=50)

// ============================================================================
// INPUTS
// ============================================================================
g_an = "1 · Analysis"
len           = input.int(34, "Analysis Length", minval=10, maxval=150, group=g_an,
     tooltip="Lookback for compression, fractal dimension and efficiency.")
breakLen      = input.int(12, "Correction Break Length", minval=3, maxval=50, group=g_an,
     tooltip="Window whose high/low forms the break band the release must clear.")
symbolAtrMult = input.float(0.15, "Compression Symbol ATR Step", minval=0.01, maxval=1.0, step=0.01, group=g_an,
     tooltip="Bar return is symbolised in ATR steps; larger = coarser symbols.")

g_tr = "2 · Trend Context"
htf      = input.timeframe("240", "Higher Timeframe", group=g_tr)
emaLen   = input.int(55, "Trend EMA Length", minval=10, maxval=300, group=g_tr)
slopeLen = input.int(5, "EMA Slope Length", minval=1, maxval=30, group=g_tr)
htfSlopeMin = input.float(0.10, "Min HTF Slope (ATR)", minval=0.0, maxval=2.0, step=0.01, group=g_tr,
     tooltip="Minimum EMA slope, normalized by HTF ATR. Values inside +/- this threshold are treated as flat.")

g_rg = "3 · Regime Thresholds"
complexHigh = input.float(62.0, "High Complexity Threshold", minval=40, maxval=90, group=g_rg)
complexLow  = input.float(48.0, "Low Complexity Threshold", minval=20, maxval=70, group=g_rg)
effHigh     = input.float(42.0, "High Efficiency Threshold", minval=20, maxval=80, group=g_rg)
effLow      = input.float(25.0, "Low Efficiency Threshold", minval=5, maxval=60, group=g_rg)

g_sc = "4 · Signal Scoring"
relFloor   = input.float(0.35, "Min Release Share", minval=0.05, maxval=0.9, step=0.05, group=g_sc,
     tooltip="Coil sets the score ceiling; release dynamics scale it from this floor up to 1. Lower = release dynamics matter more — a high coil alone can no longer fire.")
coilNowMin = input.float(0.45, "Current Coil Floor", minval=0.0, maxval=1.0, step=0.05, group=g_sc,
     tooltip="Recent coil evidence is down-weighted when the current bar is no longer compressed.")
thrCont    = input.float(0.45, "Release Score Threshold", minval=0, maxval=1, step=0.05, group=g_sc,
     tooltip="Min Setup Score for a continuation break (with the HTF trend).")
thrBase    = input.float(0.55, "Base Break Score Threshold", minval=0, maxval=1, step=0.05, group=g_sc,
     tooltip="Min Setup Score for a counter-trend base break (needs more evidence).")
baseMinShare = input.float(0.35, "Min Base Bars Share", minval=0.05, maxval=1.0, step=0.05, group=g_sc,
     tooltip="Share of the break window that must have been a base before a counter-trend Base Break is allowed.")
breakAtrMin = input.float(0.10, "Min Break Impulse (ATR)", minval=0.0, maxval=2.0, step=0.01, group=g_sc,
     tooltip="Minimum close distance beyond the broken band, measured in ATR. Filters tiny one-tick band breaks.")
cooldownBars = input.int(8, "Signal Cooldown (bars)", minval=0, maxval=100, group=g_sc,
     tooltip="Minimum bars between two signals in the same direction.")
chopLen    = input.int(14, "Choppiness Length", minval=2, maxval=50, group=g_sc)
chopVetoMax= input.float(61.8, "Choppiness Veto (max)", minval=30, maxval=80, step=0.1, group=g_sc,
     tooltip="Breaks above this choppiness are vetoed as low quality.")

g_di = "5 · Display"
showBg     = input.bool(false, "Show Regime Background", group=g_di)
showTable  = input.bool(true, "Show Dashboard", group=g_di)
showBands  = input.bool(true, "Show Break Bands", group=g_di)
showBox    = input.bool(true, "Show Compression Box", group=g_di)
showLabels = input.bool(true, "Show Signal Labels", group=g_di)
showSetups = input.bool(false, "Show Watch/Setup Markers", group=g_di,
     tooltip="Optional early-stage markers. They are not entry signals; BRK/REV remains the only trigger.")
setupMarkerShare = input.float(0.80, "Setup Marker Score Share", minval=0.0, maxval=1.0, step=0.05, group=g_di,
     tooltip="Show setup markers only when the setup score reaches this share of the release threshold.")

g_db = "6 · Debug"
logBreaks = input.bool(true, "Log Breaks", group=g_db,
     tooltip="Emit one parseable CFR BREAK row per confirmed band break (with raw metrics + block reason).")
logBars   = input.bool(false, "Log Every Bar (distribution)", group=g_db,
     tooltip="Emit one CFR BAR row per confirmed bar — the full metric distribution for offline threshold calibration. Heavy; enable only while exporting logs.")

// ============================================================================
// HELPERS
// ============================================================================
f_clamp(x, lo, hi) =>
    math.max(lo, math.min(hi, x))

f_symbol(r, unit) =>
    r > unit ? 2 : r > 0 ? 1 : r < -unit ? -2 : r < 0 ? -1 : 0

f_entropy_component(c, total) =>
    p = total > 0 ? c / total : 0.0
    p > 0 ? -p * math.log(p) : 0.0

// Compression proxy: symbolise returns into 5 states, combine state entropy
// with the state-transition rate. Not true LZ compression — a Pine-tractable
// approximation of how "busy"/corrective the path is.
f_compression_complexity(n, atrMult) =>
    atrS = ta.atr(14)
    float cM2 = 0.0
    float cM1 = 0.0
    float cZ = 0.0
    float cP1 = 0.0
    float cP2 = 0.0
    float transitions = 0.0
    int prev = na
    float total = math.max(n - 1, 1)
    for i = 0 to n - 2
        r = close[i] - close[i + 1]
        unit = atrS[i] * atrMult
        s = f_symbol(r, unit)
        if s == -2
            cM2 += 1
        else if s == -1
            cM1 += 1
        else if s == 0
            cZ += 1
        else if s == 1
            cP1 += 1
        else
            cP2 += 1
        if not na(prev) and s != prev
            transitions += 1
        prev := s
    entropyRaw = f_entropy_component(cM2, total) + f_entropy_component(cM1, total) + f_entropy_component(cZ, total) + f_entropy_component(cP1, total) + f_entropy_component(cP2, total)
    entropyNorm = entropyRaw / math.log(5)
    transitionRate = transitions / math.max(total - 1, 1)
    f_clamp((entropyNorm * 0.70 + transitionRate * 0.30) * 100, 0, 100)

// Fractal Dimension Index (box-counting, 1..2). High = jagged/range (coiled),
// low = smooth/trend. Robust on price data — does not saturate like Katz FD.
f_fdi(n) =>
    int half = int(math.max(2, n / 2))
    float r1 = (ta.highest(high, half) - ta.lowest(low, half)) / half
    float r2 = (ta.highest(high, half)[half] - ta.lowest(low, half)[half]) / half
    float r3 = (ta.highest(high, n) - ta.lowest(low, n)) / n
    float d = na
    if r1 + r2 > 0.0 and r3 > 0.0
        d := (math.log(r1 + r2) - math.log(r3)) / math.log(2.0)
    f_clamp(nz(d, 1.5), 1.0, 2.0)

f_efficiency(n) =>
    float path = 0.0
    for i = 0 to n - 2
        path += math.abs(close[i] - close[i + 1])
    direct = math.abs(close - close[n - 1])
    path > 0 ? f_clamp(direct / path * 100, 0, 100) : 0.0

// Choppiness Index (range-based) — independent quality sensor.
f_chop(n) =>
    atrSum = math.sum(ta.tr(true), n)
    rng = ta.highest(high, n) - ta.lowest(low, n)
    rng > 0 ? f_clamp(100 * math.log10(atrSum / rng) / math.log10(n), 0, 100) : 0.0

// ============================================================================
// TREND CONTEXT (Trend role)
// ============================================================================
[htfEma, htfEmaPast, htfAtr] = request.security(syminfo.tickerid, htf, [ta.ema(close, emaLen), ta.ema(close, emaLen)[slopeLen], ta.atr(14)], barmerge.gaps_off, barmerge.lookahead_off)
htfSlopeAtr = htfAtr > 0 ? (htfEma - htfEmaPast) / htfAtr : 0.0
htfDir = htfSlopeAtr > htfSlopeMin ? 1 : htfSlopeAtr < -htfSlopeMin ? -1 : 0

emaLocal = ta.ema(close, emaLen)

// ============================================================================
// METRICS (Location + Momentum roles)
// ============================================================================
complexity = f_compression_complexity(len, symbolAtrMult)
fd      = f_fdi(len)
fdScore = (fd - 1.0) * 100.0
eff     = f_efficiency(len)
chop    = f_chop(chopLen)

cmpN = complexity / 100.0
fdN  = fdScore / 100.0
effN = eff / 100.0

// Coil strength (Location): busy + jagged + inefficient = wound spring (0..1).
coilStrength = f_clamp(cmpN * 0.5 + fdN * 0.3 + (1.0 - effN) * 0.2, 0.0, 1.0)
coilRecent   = ta.highest(coilStrength, breakLen)[1]
coilBlend     = f_clamp(coilRecent * 0.70 + coilStrength * 0.30, 0.0, 1.0)
coilCurrentOk = coilStrength >= coilNowMin
coilScore     = coilCurrentOk ? coilBlend : coilBlend * f_clamp(coilStrength / math.max(coilNowMin, 0.01), 0.0, 1.0)

// Release dynamics (Momentum): complexity falling + efficiency rising, smoothed to avoid one-bar jumps (0..1).
cmpDrop    = f_clamp((ta.sma(complexity, 3)[1] - complexity) / 8.0, 0.0, 1.0)
effGain    = f_clamp((eff - ta.sma(eff, 3)[1]) / 8.0, 0.0, 1.0)
releaseDyn = (cmpDrop + effGain) / 2.0

// Setup Score — coil sets the ceiling, release dynamics scale it. Multiplicative
// so a high coil alone cannot fire: a real release is required, not just a coil.
releaseMult = relFloor + (1.0 - relFloor) * releaseDyn
setupScore  = f_clamp(coilScore * releaseMult, 0.0, 1.0)

// ============================================================================
// REGIME STATES (for background + base-break precondition)
// ============================================================================
cleanTrend = complexity < complexLow and fdScore < complexLow and eff > effHigh
base       = complexity > complexHigh and eff < effLow
correction = (htfDir != 0) and not base and coilStrength > 0.55

baseBars = math.sum(base ? 1.0 : 0.0, breakLen)[1]
wasBase = baseBars >= math.round(breakLen * baseMinShare)

// ============================================================================
// TRIGGER + QUALITY VETO  →  SIGNALS
// ============================================================================
corrHigh = ta.highest(high, breakLen)[1]
corrLow  = ta.lowest(low, breakLen)[1]
bandMid  = (corrHigh + corrLow) / 2.0

breakUp = close > corrHigh
breakDn = close < corrLow
firstUp = breakUp and not breakUp[1]
firstDn = breakDn and not breakDn[1]

atrNow = ta.atr(14)
breakAtrUp = atrNow > 0 ? math.abs(close - corrHigh) / atrNow : 0.0
breakAtrDn = atrNow > 0 ? math.abs(close - corrLow) / atrNow : 0.0
breakImpulse = firstUp ? breakAtrUp : firstDn ? breakAtrDn : 0.0
chopOk = chop < chopVetoMax
breakQuality = breakImpulse >= breakAtrMin
quality = chopOk and breakQuality   // Quality veto (§3): structural chop + minimum break impulse.

// Raw candidates (pre-cooldown). Continuation = break with HTF; Base Break = against HTF out of a base.
releaseLongRaw  = firstUp and htfDir == 1  and setupScore >= thrCont and quality
releaseShortRaw = firstDn and htfDir == -1 and setupScore >= thrCont and quality
baseBreakLongRaw  = firstUp and htfDir == -1 and wasBase and setupScore >= thrBase and quality
baseBreakShortRaw = firstDn and htfDir == 1  and wasBase and setupScore >= thrBase and quality

// Cooldown per direction — covers both signal types in that direction.
var int lastUp = na
var int lastDn = na
upCdOk = na(lastUp) or (bar_index - lastUp) >= cooldownBars
dnCdOk = na(lastDn) or (bar_index - lastDn) >= cooldownBars

releaseLong    = releaseLongRaw    and upCdOk
baseBreakLong  = baseBreakLongRaw  and upCdOk
releaseShort   = releaseShortRaw   and dnCdOk
baseBreakShort = baseBreakShortRaw and dnCdOk

if releaseLong or baseBreakLong
    lastUp := bar_index
if releaseShort or baseBreakShort
    lastDn := bar_index

// Watch / Setup staging (§7).
coiled  = coilStrength > 0.60
nearUp  = high >= corrHigh * 0.995
nearDn  = low  <= corrLow  * 1.005
setupUp = coiled and htfDir == 1  and nearUp
setupDn = coiled and htfDir == -1 and nearDn
setupMarkerUp = setupUp and setupScore >= thrCont * setupMarkerShare
setupMarkerDn = setupDn and setupScore >= thrCont * setupMarkerShare

// ============================================================================
// VISUALS
// ============================================================================
plot(emaLocal, "Local EMA", color=color.new(color.gray, 20), linewidth=1)
plot(showBands ? corrHigh : na, "Break High", color=color.new(color.green, 65), style=plot.style_linebr)
plot(showBands ? corrLow  : na, "Break Low",  color=color.new(color.red, 65),   style=plot.style_linebr)
plot(showBands ? bandMid  : na, "Band Mid",   color=color.new(color.gray, 82),  style=plot.style_linebr)

color bgC = na
if cleanTrend
    bgC := color.new(color.green, 97)
else if correction
    bgC := color.new(color.orange, 96)
else if base
    bgC := color.new(color.blue, 97)
bgcolor(showBg ? bgC : na)

boxActive = showBox and (coiled or base or firstUp or firstDn) and not na(corrHigh) and not na(corrLow)
boxBg = base ? color.new(color.blue, 90) : coiled ? color.new(color.orange, 90) : color.new(color.gray, 94)
boxBorder = firstUp ? color.new(color.green, 10) : firstDn ? color.new(color.red, 10) : base ? color.new(color.blue, 62) : color.new(color.orange, 62)
var box compressionBox = na
if boxActive
    if na(compressionBox)
        compressionBox := box.new(left=bar_index - breakLen, top=corrHigh, right=bar_index, bottom=corrLow, bgcolor=boxBg, border_color=boxBorder, border_width=1)
    else
        box.set_left(compressionBox, bar_index - breakLen)
        box.set_right(compressionBox, bar_index)
        box.set_top(compressionBox, corrHigh)
        box.set_bottom(compressionBox, corrLow)
        box.set_bgcolor(compressionBox, boxBg)
        box.set_border_color(compressionBox, boxBorder)
else if not na(compressionBox)
    box.delete(compressionBox)
    compressionBox := na

plotshape(showLabels and releaseLong,  title="Release Long",  style=shape.triangleup,   text="BRK↑",  location=location.belowbar, color=color.new(color.orange, 18), textcolor=color.new(color.orange, 0), size=size.small)
plotshape(showLabels and releaseShort, title="Release Short", style=shape.triangledown, text="BRK↓",  location=location.abovebar, color=color.new(color.orange, 18), textcolor=color.new(color.orange, 0), size=size.small)
plotshape(showLabels and baseBreakLong,  title="Base Break Long",  style=shape.triangleup,   text="REV↑", location=location.belowbar, color=color.new(color.blue, 16), textcolor=color.new(color.blue, 0), size=size.small)
plotshape(showLabels and baseBreakShort, title="Base Break Short", style=shape.triangledown, text="REV↓", location=location.abovebar, color=color.new(color.blue, 16), textcolor=color.new(color.blue, 0), size=size.small)

// Subordinate watch markers — information without logic weight (§9).
plotshape(showLabels and showSetups and coiled and not (setupMarkerUp or setupMarkerDn) and not (releaseLong or releaseShort or baseBreakLong or baseBreakShort), title="Watch", style=shape.circle, location=location.bottom, color=color.new(color.gray, 70), size=size.tiny)
plotshape(showLabels and showSetups and setupMarkerUp and not (releaseLong or baseBreakLong), title="Setup Long", style=shape.triangleup, location=location.belowbar, color=color.new(color.green, 62), size=size.tiny)
plotshape(showLabels and showSetups and setupMarkerDn and not (releaseShort or baseBreakShort), title="Setup Short", style=shape.triangledown, location=location.abovebar, color=color.new(color.red, 62), size=size.tiny)

// ============================================================================
// ALERTS
// ============================================================================
bool alertsOnBarClose = input.bool(true, "Alerts only on bar close (confirmed)", group="Alerts")
bool alertOk = not alertsOnBarClose or barstate.isconfirmed

alertcondition(alertOk and (releaseLong),  "CFR Release Long",  "CFR · RELEASE LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (releaseShort), "CFR Release Short", "CFR · RELEASE SHORT · {{ticker}} {{interval}}")
alertcondition(alertOk and (baseBreakLong),  "CFR Base Break Long",  "CFR · BASE BREAK LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (baseBreakShort), "CFR Base Break Short", "CFR · BASE BREAK SHORT · {{ticker}} {{interval}}")

// ============================================================================
// DEBUG (§10) — parseable key=value rows for offline calibration (scripts/analyze_cfr.py).
//   CFR BREAK …  one row per confirmed band break: raw metrics + block reason.
//   CFR BAR   …  one row per confirmed bar (toggle): full metric distribution.
// ============================================================================
regimeStr = cleanTrend ? "clean" : correction ? "corr" : base ? "base" : "mixed"

if logBreaks and (firstUp or firstDn) and barstate.isconfirmed
    dir = firstUp ? "UP" : "DN"
    aligned = (firstUp and htfDir == 1) or (firstDn and htfDir == -1)
    typeStr = aligned ? "cont" : htfDir == 0 ? "flat" : "base"
    thr = aligned ? thrCont : thrBase
    cdOk = firstUp ? upCdOk : dnCdOk
    reason = not chopOk ? "veto:chop" : not breakQuality ? "veto:weak-break" : htfDir == 0 ? "no-htf-trend" : (not aligned and not wasBase) ? "no-regime" : setupScore < thr ? "score<thr" : not cdOk ? "cooldown" : "ok"
    log.info("CFR BREAK dir={0} type={1} htf={2} htfSlopeAtr={3} score={4} coilScore={5} coilNow={6} coilRecent={7} dyn={8} breakAtr={9} cmp={10} eff={11} fd={12} chop={13} regime={14} baseBars={15} wasBase={16} reason={17}",
         dir, typeStr, htfDir, str.tostring(htfSlopeAtr, "#.00"), str.tostring(setupScore, "#.00"), str.tostring(coilScore, "#.00"),
         str.tostring(coilStrength, "#.00"), str.tostring(coilRecent, "#.00"), str.tostring(releaseDyn, "#.00"), str.tostring(breakImpulse, "#.00"),
         str.tostring(complexity, "#.0"), str.tostring(eff, "#.0"), str.tostring(fd, "#.000"), str.tostring(chop, "#.0"),
         regimeStr, str.tostring(baseBars, "#"), wasBase ? 1 : 0, reason)

if logBars and barstate.isconfirmed
    log.info("CFR BAR cmp={0} eff={1} fd={2} chop={3} coilNow={4} coilScore={5} dyn={6} htf={7} htfSlopeAtr={8} regime={9}",
         str.tostring(complexity, "#.0"), str.tostring(eff, "#.0"), str.tostring(fd, "#.000"), str.tostring(chop, "#.0"),
         str.tostring(coilStrength, "#.00"), str.tostring(coilScore, "#.00"), str.tostring(releaseDyn, "#.00"),
         htfDir, str.tostring(htfSlopeAtr, "#.00"), regimeStr)

// ============================================================================
// DASHBOARD (light-theme style — see CLAUDE.md)
// ============================================================================
color tc = color.new(color.gray, 20)
color hd = color.new(color.gray, 90)
color okBg = color.new(color.green, 82)
color warnBg = color.new(color.orange, 82)
color badBg = color.new(color.red, 86)
color neutralBg = color.new(color.gray, 90)

var table t = table.new(position.top_right, 2, 10,
     bgcolor=color.new(color.white, 5),
     border_color=color.new(color.gray, 60),
     border_width=1,
     frame_color=color.new(color.gray, 40),
     frame_width=1)

string state = "Mixed"
color stateBg = color.new(color.gray, 80)
if releaseLong
    state := "Release ↑"
    stateBg := #00c853
else if releaseShort
    state := "Release ↓"
    stateBg := #d50000
else if baseBreakLong
    state := "Base Break ↑"
    stateBg := color.teal
else if baseBreakShort
    state := "Base Break ↓"
    stateBg := color.purple
else if cleanTrend
    state := "Clean Trend"
    stateBg := color.new(color.gray, 80)
else if coiled
    state := "Coiled (watch)"
    stateBg := #ff6f00
else if base
    state := "Base"
    stateBg := color.new(color.gray, 80)

bool stateActive = releaseLong or releaseShort or baseBreakLong or baseBreakShort or coiled
htfText = htfDir == 1 ? "Up " + str.tostring(htfSlopeAtr, "#.00") : htfDir == -1 ? "Down " + str.tostring(htfSlopeAtr, "#.00") : "Flat " + str.tostring(htfSlopeAtr, "#.00")

longThr = htfDir == 1 ? thrCont : htfDir == -1 and wasBase ? thrBase : na
shortThr = htfDir == -1 ? thrCont : htfDir == 1 and wasBase ? thrBase : na
longText = na(longThr) ? "n/a" : str.tostring(setupScore, "#.00") + "/" + str.tostring(longThr, "#.00")
shortText = na(shortThr) ? "n/a" : str.tostring(setupScore, "#.00") + "/" + str.tostring(shortThr, "#.00")
longBg = na(longThr) ? neutralBg : setupScore >= longThr and chopOk ? okBg : setupScore >= longThr * 0.80 ? warnBg : badBg
shortBg = na(shortThr) ? neutralBg : setupScore >= shortThr and chopOk ? okBg : setupScore >= shortThr * 0.80 ? warnBg : badBg
coilBg = coilCurrentOk ? color.new(color.orange, 80) : warnBg
dynBg = releaseDyn >= 0.35 ? okBg : releaseDyn >= 0.20 ? warnBg : neutralBg
chopBg = chopOk ? neutralBg : badBg

if showTable and barstate.islast
    table.cell(t, 0, 0, "CFR", text_color=tc, bgcolor=hd, text_size=size.small)
    table.cell(t, 1, 0, state, text_color=stateActive ? color.white : tc, bgcolor=stateActive ? stateBg : color.new(color.gray, 80), text_size=size.small)

    table.cell(t, 0, 1, "HTF Trend", text_color=tc, text_size=size.small)
    table.cell(t, 1, 1, htfText, text_color=tc, text_size=size.small)

    table.cell(t, 0, 2, "Long Setup", text_color=tc, text_size=size.small)
    table.cell(t, 1, 2, longText, text_color=tc, bgcolor=longBg, text_size=size.small)

    table.cell(t, 0, 3, "Short Setup", text_color=tc, text_size=size.small)
    table.cell(t, 1, 3, shortText, text_color=tc, bgcolor=shortBg, text_size=size.small)

    table.cell(t, 0, 4, "Coil Score", text_color=tc, text_size=size.small)
    table.cell(t, 1, 4, str.tostring(coilScore, "#.00") + " (" + str.tostring(coilStrength, "#.00") + ")", text_color=tc, bgcolor=coilBg, text_size=size.small)

    table.cell(t, 0, 5, "Release Dyn", text_color=tc, text_size=size.small)
    table.cell(t, 1, 5, str.tostring(releaseDyn, "#.00"), text_color=tc, bgcolor=dynBg, text_size=size.small)

    table.cell(t, 0, 6, "Base Bars", text_color=tc, text_size=size.small)
    table.cell(t, 1, 6, str.tostring(baseBars, "#") + "/" + str.tostring(math.round(breakLen * baseMinShare), "#"), text_color=tc, bgcolor=wasBase ? color.new(color.blue, 84) : neutralBg, text_size=size.small)

    table.cell(t, 0, 7, "Break ATR", text_color=tc, text_size=size.small)
    table.cell(t, 1, 7, str.tostring(breakImpulse, "#.00") + "/" + str.tostring(breakAtrMin, "#.00"), text_color=tc, bgcolor=(firstUp or firstDn) and breakQuality ? okBg : neutralBg, text_size=size.small)

    table.cell(t, 0, 8, "Cmp / FD", text_color=tc, text_size=size.small)
    table.cell(t, 1, 8, str.tostring(complexity, "#.0") + " / " + str.tostring(fd, "#.000"), text_color=tc, text_size=size.small)

    table.cell(t, 0, 9, "Chop", text_color=tc, text_size=size.small)
    table.cell(t, 1, 9, str.tostring(chop, "#.0"), text_color=chopOk ? tc : #d50000, bgcolor=chopBg, text_size=size.small)

Overview

Compression Fractal Release [WavesUnchained] plots compression bands, fractal boxes, efficiency scores, and a light dashboard on the price chart. Signals classify breaks as releases or base breaks according to HTF EMA slope. Main inputs control analysis length, break window, ATR step, trend EMA slope, complexity and efficiency thresholds, and choppiness veto. Optional background, bands, labels, and setup markers are available.

Details

Published by

casoon on GitHub

Draws

On the price chart

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.

Topics

pine-scriptpinescripttechnical-analysistrading-indicatorstrading-strategiestradingview

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Analysis Lengthint34len
Correction Break Lengthint12breakLen
Compression Symbol ATR Stepfloat0.15symbolAtrMult
Higher Timeframetimeframe240htf
Trend EMA Lengthint55emaLen
EMA Slope Lengthint5slopeLen
Min HTF Slope (ATR)float0.10htfSlopeMin
High Complexity Thresholdfloat62.0complexHigh
Low Complexity Thresholdfloat48.0complexLow
High Efficiency Thresholdfloat42.0effHigh
Low Efficiency Thresholdfloat25.0effLow
Min Release Sharefloat0.35relFloor
Show all 30 settings
SettingTypeDefaultVariable
Current Coil Floorfloat0.45coilNowMin
Release Score Thresholdfloat0.45thrCont
Base Break Score Thresholdfloat0.55thrBase
Min Base Bars Sharefloat0.35baseMinShare
Min Break Impulse (ATR)float0.10breakAtrMin
Signal Cooldown (bars)int8cooldownBars
Choppiness Lengthint14chopLen
Choppiness Veto (max)float61.8chopVetoMax
Show Regime BackgroundboolfalseshowBg
Show DashboardbooltrueshowTable
Show Break BandsbooltrueshowBands
Show Compression BoxbooltrueshowBox
Show Signal LabelsbooltrueshowLabels
Show Watch/Setup MarkersboolfalseshowSetups
Setup Marker Score Sharefloat0.80setupMarkerShare
Log BreaksbooltruelogBreaks
Log Every Bar (distribution)boolfalselogBars
Alerts only on bar close (confirmed)booltruealertsOnBarClose
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

Acceleration Bands

by capissimo
114 stars

Acceleration Bands is an overlay indicator that plots a volatility envelope around a central simple moving average. It calculates upper and lower boundary bands scaled by the high-to-low bar range relative to the midpoint price, smoothed by an SMA over a user-

Indicator v4 Volatility & bandsMoving averages
Updated 1 year ago · None stated GitHub repository of Acceleration Bands

MattSuite V3

by mmarbmatt
8 stars

MattSuite V3 is an overlay indicator that combines market-structure and trend tools on the price chart. It identifies bullish and bearish fair value gaps and estimates their strength, flags break of structure and change of character, and marks order blocks. It

Indicator v5 SMC / ICT / price actionVolatility & bands
Updated 1 year ago · None stated GitHub repository of MattSuite V3

This technical indicator combines multiple popular analysis tools into a single price chart overlay to optimize chart space. It calculates and plots the complete Ichimoku Cloud, 50 and 200 Simple Moving Averages, and standard Bollinger Bands. Furthermore, it t

Indicator v5 TrendMoving averages Repaints
Updated 3 years ago · None stated GitHub repository of Ichimoku + MAs + Bollinger + Liquidity Zone

Dereköy-Darvas

by Kalumur
4 stars

The Dereköy-Darvas indicator combines Darvas Box boundary levels with dual Bollinger Bands plotted directly on the main price chart. It calculates top and bottom box lines based on recent high and low extremes to outline price consolidation zones. Simultaneous

Indicator v5 Volatility & bandsSupport & resistance
Updated 2 years ago · None stated GitHub repository of Dereköy-Darvas

Volatility Band Indicator

by mitchell-917
4 stars

The Volatility Band Indicator calculates volatility bands around a customizable moving average basis line directly on the price chart. Users can select between SMA, EMA, RMA, or WMA basis types, alongside adjustable length and standard deviation multiplier set

Indicator v5 Volatility & bandsMoving averages
Updated 11 months ago · Custom licence GitHub repository of Volatility Band Indicator

More Volatility & bands scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers