quant-knowledge-extractor / src /Indicators.jl
cyberkyne's picture
Upload 22 files
094a5f6 verified
"""
Indicators.jl β€” Vectorized technical indicator library.
Standalone module. No includes. No external deps beyond Statistics.
"""
module Indicators
using Statistics
export sma, ema, wma, tema, dema,
rsi, macd, stoch, cci, williams_r,
atr, bbands, keltner, donchian, adx,
vwap, obv, cmf, zscore, std_dev,
momentum, roc, highest, lowest,
crossover, crossunder
# ── Trend ─────────────────────────────────────────────
function sma(x::Vector{Float64}, n::Int)::Vector{Float64}
len = length(x); out = fill(NaN, len); s = 0.0
for i in 1:len
s += x[i]
if i >= n
i > n && (s -= x[i-n])
out[i] = s / n
end
end
return out
end
function ema(x::Vector{Float64}, n::Int)::Vector{Float64}
len = length(x); out = fill(NaN, len)
k = 2.0 / (n + 1.0)
# seed: SMA of first n non-NaN values
s = 0.0; cnt = 0; seed_i = 0
for i in 1:len
isnan(x[i]) && continue
s += x[i]; cnt += 1
if cnt == n
seed_i = i; out[i] = s / n
val = out[i]
for j in (i+1):len
isnan(x[j]) && continue
val = x[j] * k + val * (1.0 - k)
out[j] = val
end
break
end
end
return out
end
function wma(x::Vector{Float64}, n::Int)::Vector{Float64}
len = length(x); out = fill(NaN, len)
ws = n * (n+1) / 2.0
for i in n:len
s = 0.0
for j in 1:n; s += x[i-n+j] * j; end
out[i] = s / ws
end
return out
end
tema(x::Vector{Float64}, n::Int) = let e1=ema(x,n),e2=ema(e1,n),e3=ema(e2,n); 3.0.*e1 .- 3.0.*e2 .+ e3 end
dema(x::Vector{Float64}, n::Int) = let e1=ema(x,n),e2=ema(e1,n); 2.0.*e1 .- e2 end
# ── Oscillators ───────────────────────────────────────
function rsi(close::Vector{Float64}, n::Int=14)::Vector{Float64}
len = length(close); out = fill(NaN, len)
ag = 0.0; al = 0.0
for i in 2:(n+1)
i > len && break
d = close[i] - close[i-1]
d > 0 ? (ag += d) : (al += abs(d))
end
ag /= n; al /= n
n+1 <= len && (out[n+1] = 100.0 - 100.0/(1.0 + (al==0 ? 1e10 : ag/al)))
for i in (n+2):len
d = close[i] - close[i-1]
g = d > 0 ? d : 0.0; l = d < 0 ? abs(d) : 0.0
ag = (ag*(n-1)+g)/n; al = (al*(n-1)+l)/n
out[i] = 100.0 - 100.0/(1.0 + (al==0 ? 1e10 : ag/al))
end
return out
end
function macd(close::Vector{Float64}; fast::Int=12, slow::Int=26, sig::Int=9)
ml = ema(close,fast) .- ema(close,slow)
sl = ema(ml, sig)
return ml, sl, ml .- sl
end
function stoch(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64};
k::Int=14, d::Int=3)
len = length(close); K = fill(NaN, len)
for i in k:len
hh = maximum(high[i-k+1:i]); ll = minimum(low[i-k+1:i])
K[i] = hh==ll ? 50.0 : 100.0*(close[i]-ll)/(hh-ll)
end
return K, sma(K, d)
end
function cci(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64}, n::Int=20)::Vector{Float64}
len = length(close); tp = (high.+low.+close)./3.0; out = fill(NaN, len)
for i in n:len
w = tp[i-n+1:i]; m = mean(w); md = mean(abs.(w.-m))
out[i] = md==0 ? 0.0 : (tp[i]-m)/(0.015*md)
end
return out
end
function williams_r(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64}, n::Int=14)::Vector{Float64}
len = length(close); out = fill(NaN, len)
for i in n:len
hh = maximum(high[i-n+1:i]); ll = minimum(low[i-n+1:i])
out[i] = hh==ll ? -50.0 : -100.0*(hh-close[i])/(hh-ll)
end
return out
end
momentum(x::Vector{Float64}, n::Int=10) = let len=length(x),out=fill(NaN,len); for i in (n+1):len; out[i]=x[i]-x[i-n]; end; out end
roc(x::Vector{Float64}, n::Int=10) = let len=length(x),out=fill(NaN,len); for i in (n+1):len; out[i]=x[i-n]==0 ? 0.0 : 100.0*(x[i]-x[i-n])/x[i-n]; end; out end
# ── Volatility ────────────────────────────────────────
function _tr(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64})::Vector{Float64}
len = length(close); tr = fill(NaN, len)
tr[1] = high[1]-low[1]
for i in 2:len; tr[i] = max(high[i]-low[i], abs(high[i]-close[i-1]), abs(low[i]-close[i-1])); end
return tr
end
atr(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64}, n::Int=14) = ema(_tr(high,low,close), n)
function bbands(close::Vector{Float64}, n::Int=20, k::Float64=2.0)
len = length(close); mid = sma(close,n); sd = fill(NaN, len)
for i in n:len; sd[i] = std(close[i-n+1:i]; corrected=false); end
return mid.+k.*sd, mid, mid.-k.*sd
end
function keltner(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64}, n::Int=20, k::Float64=2.0)
mid = ema(close,n); a = atr(high,low,close,n)
return mid.+k.*a, mid, mid.-k.*a
end
function donchian(high::Vector{Float64}, low::Vector{Float64}, n::Int=20)
len = length(high); u = fill(NaN,len); l = fill(NaN,len)
for i in n:len; u[i]=maximum(high[i-n+1:i]); l[i]=minimum(low[i-n+1:i]); end
return u, (u.+l)./2.0, l
end
function std_dev(x::Vector{Float64}, n::Int=20)::Vector{Float64}
len = length(x); out = fill(NaN, len)
for i in n:len; out[i] = std(x[i-n+1:i]; corrected=false); end
return out
end
function zscore(x::Vector{Float64}, n::Int=20)::Vector{Float64}
mu = sma(x,n); sd = std_dev(x,n); out = fill(NaN, length(x))
for i in eachindex(x)
!isnan(mu[i]) && !isnan(sd[i]) && sd[i]>0 && (out[i]=(x[i]-mu[i])/sd[i])
end
return out
end
# ── Trend strength ────────────────────────────────────
function adx(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64}, n::Int=14)
tr = _tr(high,low,close)
up = diff(vcat(high[1],high)); dn = diff(vcat(low[1],low))
pdm = map((u,d)->u>d&&u>0 ? u : 0.0, up, dn)
ndm = map((u,d)->d>u&&d>0 ? d : 0.0, up, dn)
sm=ema(tr,n); pdi=100.0.*ema(pdm,n)./(sm.+1e-10); ndi=100.0.*ema(ndm,n)./(sm.+1e-10)
dx = 100.0.*abs.(pdi.-ndi)./(pdi.+ndi.+1e-10)
return ema(dx,n), pdi, ndi
end
# ── Volume ────────────────────────────────────────────
function vwap(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64}, volume::Vector{Float64})::Vector{Float64}
tp = (high.+low.+close)./3.0
return cumsum(tp.*volume)./(cumsum(volume).+1e-10)
end
function obv(close::Vector{Float64}, volume::Vector{Float64})::Vector{Float64}
len = length(close); out = zeros(Float64, len); out[1] = volume[1]
for i in 2:len
d = close[i]-close[i-1]
out[i] = out[i-1] + (d>0 ? volume[i] : d<0 ? -volume[i] : 0.0)
end
return out
end
function cmf(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64},
volume::Vector{Float64}, n::Int=20)::Vector{Float64}
len = length(close); out = fill(NaN, len)
hl = high.-low
mfv = map((c,l,h,hl)->hl==0 ? 0.0 : (2c-l-h)/hl, close,low,high,hl).*volume
for i in n:len
sv = sum(volume[i-n+1:i])
out[i] = sv==0 ? 0.0 : sum(mfv[i-n+1:i])/sv
end
return out
end
# ── Utilities ─────────────────────────────────────────
highest(x::Vector{Float64}, n::Int) = let len=length(x),out=fill(NaN,len); for i in n:len; out[i]=maximum(x[i-n+1:i]); end; out end
lowest(x::Vector{Float64}, n::Int) = let len=length(x),out=fill(NaN,len); for i in n:len; out[i]=minimum(x[i-n+1:i]); end; out end
function crossover(a::Vector{Float64}, b::Vector{Float64})::Vector{Bool}
len=length(a); out=fill(false,len)
for i in 2:len; out[i] = a[i]>b[i] && a[i-1]<=b[i-1]; end
return out
end
function crossunder(a::Vector{Float64}, b::Vector{Float64})::Vector{Bool}
len=length(a); out=fill(false,len)
for i in 2:len; out[i] = a[i]<b[i] && a[i-1]>=b[i-1]; end
return out
end
end # module Indicators