mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-02 11:37:42 +00:00
88 lines
3.0 KiB
Plaintext
88 lines
3.0 KiB
Plaintext
|
|
// Licensed under the Apache License, Version 2.0
|
|||
|
|
// © mihakralj
|
|||
|
|
//@version=6
|
|||
|
|
indicator("Autocorrelation Function (ACF)", "ACF", overlay=false)
|
|||
|
|
|
|||
|
|
//@function Calculates autocorrelation at a specified lag using a circular buffer
|
|||
|
|
//@param src series float Input data series
|
|||
|
|
//@param len simple int Lookback period for calculation
|
|||
|
|
//@param lag simple int Lag order for autocorrelation (default 1)
|
|||
|
|
//@returns float Autocorrelation coefficient between -1 and 1
|
|||
|
|
//@optimized for performance using running sums and biased estimator (divide by n)
|
|||
|
|
acf(series float src, simple int len, simple int lag) =>
|
|||
|
|
if len <= 0
|
|||
|
|
runtime.error("Period must be greater than 0")
|
|||
|
|
if lag < 1
|
|||
|
|
runtime.error("Lag must be at least 1")
|
|||
|
|
if len <= lag + 1
|
|||
|
|
runtime.error("Period must be greater than lag + 1")
|
|||
|
|
var int p = math.max(1, len)
|
|||
|
|
var array<float> buffer = array.new_float(p, na)
|
|||
|
|
var int head = 0, var int count = 0
|
|||
|
|
float oldest = array.get(buffer, head)
|
|||
|
|
if not na(oldest)
|
|||
|
|
count -= 1
|
|||
|
|
if not na(src)
|
|||
|
|
array.set(buffer, head, src)
|
|||
|
|
count += 1
|
|||
|
|
else
|
|||
|
|
array.set(buffer, head, na)
|
|||
|
|
head := (head + 1) % p
|
|||
|
|
if count <= lag
|
|||
|
|
na
|
|||
|
|
else
|
|||
|
|
// Calculate mean
|
|||
|
|
float sum = 0.0
|
|||
|
|
int validN = 0
|
|||
|
|
for i = 0 to p - 1
|
|||
|
|
float val = array.get(buffer, i)
|
|||
|
|
if not na(val)
|
|||
|
|
sum += val
|
|||
|
|
validN += 1
|
|||
|
|
if validN <= lag
|
|||
|
|
na
|
|||
|
|
else
|
|||
|
|
float mean = sum / validN
|
|||
|
|
|
|||
|
|
// Calculate variance (population): Σ(x - mean)² / n
|
|||
|
|
float variance = 0.0
|
|||
|
|
for i = 0 to p - 1
|
|||
|
|
float val = array.get(buffer, i)
|
|||
|
|
if not na(val)
|
|||
|
|
float diff = val - mean
|
|||
|
|
variance += diff * diff
|
|||
|
|
variance /= validN
|
|||
|
|
|
|||
|
|
if variance <= 0
|
|||
|
|
0.0
|
|||
|
|
else
|
|||
|
|
// Calculate autocovariance at lag k (biased: divide by n)
|
|||
|
|
// Need to iterate in temporal order through the circular buffer
|
|||
|
|
int startIdx = count < p ? 0 : head
|
|||
|
|
float autocovariance = 0.0
|
|||
|
|
for t = lag to count - 1
|
|||
|
|
int currentIdx = (startIdx + t) % p
|
|||
|
|
int laggedIdx = (startIdx + t - lag) % p
|
|||
|
|
float xt = array.get(buffer, currentIdx)
|
|||
|
|
float xtk = array.get(buffer, laggedIdx)
|
|||
|
|
if not na(xt) and not na(xtk)
|
|||
|
|
autocovariance += (xt - mean) * (xtk - mean)
|
|||
|
|
autocovariance /= validN
|
|||
|
|
|
|||
|
|
// ACF = γ_k / γ_0, clamped to [-1, 1]
|
|||
|
|
float result = autocovariance / variance
|
|||
|
|
math.max(-1.0, math.min(1.0, result))
|
|||
|
|
|
|||
|
|
// ---------- Main loop ----------
|
|||
|
|
|
|||
|
|
// Inputs
|
|||
|
|
i_source = input.source(close, "Source")
|
|||
|
|
i_period = input.int(20, "Period", minval=3)
|
|||
|
|
i_lag = input.int(1, "Lag", minval=1)
|
|||
|
|
|
|||
|
|
// Calculation
|
|||
|
|
acf_value = acf(i_source, i_period, i_lag)
|
|||
|
|
|
|||
|
|
// Plot
|
|||
|
|
plot(acf_value, "ACF", color=color.yellow, linewidth=2)
|