Files
QuanTAlib/lib/numerics/agc/agc.pine
T
Miha Kralj 7253f61299 Add TRAMA implementation and comprehensive tests
- Implemented the TRAMA (Trend Regularity Adaptive Moving Average) class with adaptive EMA logic.
- Added unit tests for TRAMA functionality, including constructor validation, basic calculations, state management, and robustness checks.
- Created validation tests to ensure consistency across different modes of operation (streaming, batch, and static calculations).
- Enhanced documentation for TRAMA, including performance profiles and quality metrics.
- Updated workspace configuration by removing unnecessary folder references.
2026-02-21 20:45:38 -08:00

97 lines
3.8 KiB
Plaintext

// The MIT License (MIT)
// © mihakralj
//@version=6
// Indicator algorithm (C) 2015 John F. Ehlers
indicator("Ehlers Automatic Gain Control (AGC)", "AGC", overlay=false)
//@function Ehlers Automatic Gain Control — amplitude normalization via exponential peak tracking
//@param source Series to normalize (must oscillate around zero — use a filter output, not raw price)
//@param decay Peak decay factor per bar (controls adaptation speed; 0.991 ≈ 110-bar half-life)
//@returns Amplitude-normalized signal in [-1, +1] range
//@optimized O(1) per bar — single division + comparison, zero lookback
agc(series float src, simple float decay) =>
var float peak = 0.0000001
float ssrc = nz(src, 0.0)
// Exponential peak decay — shrinks peak toward zero between excitations
peak := decay * peak
// Track running peak — ratchets up when signal exceeds decayed peak
if math.abs(ssrc) > peak
peak := math.abs(ssrc)
// Guard against zero division (peak initialized to tiny positive value)
float result = peak > 0.0 ? ssrc / peak : 0.0
result
//@function Roofing filter — 2-pole HPF → Super Smoother bandpass for detrending raw price
//@param source Raw price series
//@param hpLength Highpass cutoff period (removes trend below this period)
//@param ssLength Super Smoother cutoff period (removes noise above this period)
//@returns Detrended, smoothed oscillation around zero
roofing(series float src, simple int hpLength, simple int ssLength) =>
var float SQRT2_PI = math.sqrt(2.0) * math.pi
// --- Stage 1: 2-pole Butterworth Highpass ---
int safe_hp = math.max(hpLength, 1)
var float hp_c1 = 0.0
var float hp_c2 = 0.0
var float hp_c3 = 0.0
var int prev_hp = 0
if prev_hp != safe_hp
float hp_arg = SQRT2_PI / float(safe_hp)
float hp_exp = math.exp(-hp_arg)
hp_c2 := 2.0 * hp_exp * math.cos(hp_arg)
hp_c3 := -hp_exp * hp_exp
hp_c1 := (1.0 + hp_c2 - hp_c3) / 4.0
prev_hp := safe_hp
var float hp = 0.0
float ssrc = nz(src, src[1])
float src1 = nz(src[1], ssrc)
float src2 = nz(src[2], src1)
hp := hp_c1 * (ssrc - 2.0 * src1 + src2) + hp_c2 * nz(hp[1], 0.0) + hp_c3 * nz(hp[2], 0.0)
// --- Stage 2: Super Smoother ---
int safe_ss = math.max(ssLength, 1)
var float ss_c1 = 0.0
var float ss_c2 = 0.0
var float ss_c3 = 0.0
var int prev_ss = 0
if prev_ss != safe_ss
float ss_arg = SQRT2_PI / float(safe_ss)
float ss_exp = math.exp(-ss_arg)
ss_c2 := 2.0 * ss_exp * math.cos(ss_arg)
ss_c3 := -ss_exp * ss_exp
ss_c1 := 1.0 - ss_c2 - ss_c3
prev_ss := safe_ss
var float roof = 0.0
roof := ss_c1 * hp + ss_c2 * nz(roof[1], hp) + ss_c3 * nz(roof[2], nz(hp[1], hp))
roof
// ---------- Main loop ----------
// Inputs
i_decay = input.float(0.991, "Decay", minval=0.9, maxval=0.9999, step=0.001,
tooltip="Peak decay factor per bar (0.991 ≈ 110-bar half-life)")
i_hpLength = input.int(48, "HP Length", minval=1,
tooltip="Highpass cutoff period — removes trend cycles longer than this")
i_ssLength = input.int(10, "SS Length", minval=1,
tooltip="Super Smoother cutoff — removes noise cycles shorter than this")
i_source = input.source(close, "Source")
// Preprocessing: Roofing filter detrends raw price into zero-mean oscillation
filt = roofing(i_source, i_hpLength, i_ssLength)
// AGC normalization of the detrended signal
agc_val = agc(filt, i_decay)
// Plot
plot(agc_val, "AGC", color=color.new(color.blue, 0), linewidth=2)
plot(filt, "Filter", color=color.new(color.gray, 60), linewidth=1)
hline(1.0, "+1", color=color.gray, linestyle=hline.style_dotted)
hline(0, "Zero", color=color.gray, linestyle=hline.style_dotted)
hline(-1.0, "-1", color=color.gray, linestyle=hline.style_dotted)