// Licensed under the Apache License, Version 2.0 // © mihakralj //@version=6 indicator("Close-to-Close Volatility (CCV)", "CCV", overlay=false) //@function Calculates Close-to-Close Volatility using closing price returns //@param length Period for volatility calculations //@param method Smoothing method (1=SMA, 2=EMA, 3=WMA) //@returns float Volatility value //@optimized Beta precomputation for RMA warmup compensation ccv(simple int length, simple int method) => var int p = math.max(1, length) var int head = 0 var int count = 0 var array buffer = array.new_float(p, na) var float sum = 0.0 var float wsum = 0.0 float priceReturn = math.log(close / close[1]) float oldest = array.get(buffer, head) if not na(oldest) sum -= oldest count -= 1 sum += priceReturn count += 1 array.set(buffer, head, priceReturn) head := (head + 1) % p float mean = nz(sum / count) float squaredSum = 0.0 for i = 0 to length - 1 float val = array.get(buffer, (head - i - 1 + p) % p) if not na(val) squaredSum += math.pow(val - mean, 2) float annualizedStdDev = math.sqrt(squaredSum / count) * math.sqrt(252) float alpha = 1.0 / float(length) float beta = 1.0 - alpha var float EPSILON = 1e-10 var float raw_rma = 0.0 var float e = 1.0 float result = na if method == 1 result := annualizedStdDev else if method == 2 raw_rma := (raw_rma * (length - 1) + annualizedStdDev) / length e *= beta result := e > EPSILON ? raw_rma / (1.0 - e) : raw_rma else float sumWeight = length * (length + 1) / 2 float weightedSum = 0.0 float weight = length for i = 0 to length - 1 weightedSum += annualizedStdDev * weight weight -= 1.0 result := weightedSum / sumWeight result // ---------- Main loop ---------- // Inputs i_length = input.int(20, "Length", minval=1, maxval=500, tooltip="Number of bars for volatility calculation") i_method = input.int(1, "Method", minval=1, maxval=3, tooltip="1=SMA, 2=EMA, 3=WMA") // Calculation ccvValue = ccv(i_length, i_method) // Plot plot(ccvValue, "CCV", color=color.yellow, linewidth=2)