Files
Miha Kralj 6f0a339c9b fix: resolve build and test errors
- Sar.Quantower.Tests.cs: add missing opening quote on string literal (line 48)
- Exports.cs: rename Correlation.Batch → Correl.Batch (CS0103)
- Ad.Validation.Tests.cs: fix Ooples OutputValues key "Ad" → "Adl"
2026-03-16 12:45:13 -07:00

58 lines
2.0 KiB
Plaintext

// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Keltner Channel (KC)", "KC", overlay=true)
//@function Calculates Keltner Channel using EMA and ATR
//@param source Series to calculate middle line from
//@param length Lookback period for calculations
//@param mult ATR multiplier for band width
//@returns tuple with [middle, upper, lower] band values
//@optimized Uses EMA with warmup and ATR with compensator, O(1) complexity per bar
kc(series float source, simple int length, simple float mult) =>
if length <= 0 or mult <= 0.0
runtime.error("Length and multiplier must be greater than 0")
var float alpha = 2.0 / (length + 1)
var float sum = 0.0
var float weight = 0.0
float ema = na
if na(sum)
sum := source
weight := 1.0
sum := sum * (1.0 - alpha) + source * alpha
weight := weight * (1.0 - alpha) + alpha
ema := sum / weight
var float prevClose = close
float tr1 = high - low
float tr2 = math.abs(high - prevClose)
float tr3 = math.abs(low - prevClose)
float trueRange = math.max(tr1, tr2, tr3)
prevClose := close
var float EPSILON = 1e-10
var float raw_rma = 0.0
var float e = 1.0
float atrValue = na
if not na(trueRange)
float alpha_atr = 1.0 / float(length)
raw_rma := (raw_rma * (length - 1) + trueRange) / length
e := (1.0 - alpha_atr) * e
atrValue := e > EPSILON ? raw_rma / (1.0 - e) : raw_rma
float width = mult * nz(atrValue, 0.0)
[ema, ema + width, ema - width]
// ---------- Main loop ----------
// Inputs
i_source = input.source(close, "Source")
i_length = input.int(20, "Length", minval=1)
i_mult = input.float(2.0, "ATR Multiplier", minval=0.001)
// Calculation
[middle, upper, lower] = kc(i_source, i_length, i_mult)
// Plot
plot(middle, "Middle", color=color.yellow, linewidth=2)
p1 = plot(upper, "Upper", color=color.yellow, linewidth=2)
p2 = plot(lower, "Lower", color=color.yellow, linewidth=2)
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")