Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |