Files

125 lines
4.6 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Beta Distribution CDF (BETADIST)", "BETADIST", overlay=false, precision=6)
//@function Log-gamma via Lanczos approximation (g=7, 9 coefficients)
//@param z Input value (z > 0)
//@returns ln(Gamma(z))
lnGamma(simple float z) =>
float g = 7.0
array<float> c = array.from(
0.99999999999980993,
676.5203681218851,
-1259.1392167224028,
771.32342877765313,
-176.61502916214059,
12.507343278686905,
-0.13857109526572012,
9.9843695780195716e-6,
1.5056327351493116e-7)
float zz = z - 1.0
float x = array.get(c, 0)
for i = 1 to 8
x += array.get(c, i) / (zz + i)
float t = zz + g + 0.5
0.5 * math.log(2.0 * math.pi) + (zz + 0.5) * math.log(t) - t + math.log(x)
//@function Regularized incomplete beta function I_x(a,b) via continued fraction (Lentz)
//@param x Evaluation point (0 <= x <= 1)
//@param a Shape parameter alpha (a > 0)
//@param b Shape parameter beta (b > 0)
//@returns CDF value P(X <= x) for Beta(a,b)
betaReg(series float x, simple float a, simple float b) =>
if x <= 0.0
0.0
else if x >= 1.0
1.0
else
float lnPfx = a * math.log(x) + b * math.log(1.0 - x) - math.log(a) - lnGamma(a) - lnGamma(b) + lnGamma(a + b)
float front = math.exp(lnPfx)
bool flip = x > (a + 1.0) / (a + b + 2.0)
float xx = flip ? 1.0 - x : x
float aa = flip ? b : a
float bb = flip ? a : b
float lnPfx2 = aa * math.log(xx) + bb * math.log(1.0 - xx) - math.log(aa) - lnGamma(aa) - lnGamma(bb) + lnGamma(aa + bb)
float front2 = math.exp(lnPfx2)
float TINY = 1e-30
float EPS = 1e-10
int MAXITER = 200
float f = TINY
float C = TINY
float D = 0.0
float delta = 0.0
for m = 0 to MAXITER - 1
float d_val = 0.0
int mm = m / 2
if m == 0
d_val := 1.0
else if m % 2 == 0
float mf = float(mm)
d_val := mf * (bb - mf) * xx / ((aa + 2.0 * mf - 1.0) * (aa + 2.0 * mf))
else
float mf = float(mm) + 1.0
d_val := -(aa + mf - 1.0) * (aa + bb + mf - 1.0) * xx / ((aa + 2.0 * mf - 2.0) * (aa + 2.0 * mf - 1.0))
D := 1.0 + d_val * D
if math.abs(D) < TINY
D := TINY
D := 1.0 / D
C := 1.0 + d_val / C
if math.abs(C) < TINY
C := TINY
delta := C * D
f *= delta
if math.abs(delta - 1.0) < EPS
break
float result = front2 * f
flip ? 1.0 - result : result
//@function Computes Beta Distribution CDF for a normalized price series
//@param source Series to transform
//@param period Lookback period for min-max normalization to [0,1]
//@param alpha Shape parameter alpha (controls left skew)
//@param beta_param Shape parameter beta (controls right skew)
//@returns Beta CDF value in [0,1]
//@optimized Lentz continued fraction converges in ~10-20 iterations for typical parameters
betadist(series float source, simple int period, simple float alpha, simple float beta_param) =>
if period <= 0
runtime.error("Period must be greater than 0")
if alpha <= 0.0
runtime.error("Alpha must be greater than 0")
if beta_param <= 0.0
runtime.error("Beta must be greater than 0")
float minVal = source
float maxVal = source
for i = 1 to period - 1
float v = source[i]
if not na(v)
if v < minVal
minVal := v
if v > maxVal
maxVal := v
float range = maxVal - minVal
float x = range > 0.0 ? (source - minVal) / range : 0.5
betaReg(x, alpha, beta_param)
// ---------- Main loop ----------
// Inputs
i_source = input.source(close, "Source")
i_period = input.int(50, "Lookback Period", minval=2, maxval=5000, tooltip="Min-max normalization window")
i_alpha = input.float(2.0, "Alpha (α)", minval=0.01, step=0.1, tooltip="Left shape; α<1 weight toward 0, α>1 weight toward center")
i_beta = input.float(2.0, "Beta (β)", minval=0.01, step=0.1, tooltip="Right shape; β<1 weight toward 1, β>1 weight toward center")
// Calculation
result = betadist(i_source, i_period, i_alpha, i_beta)
// Plot
plot(result, "BETADIST", color=color.yellow, linewidth=2)
hline(0.5, "Midline", color=color.gray, linestyle=hline.style_dotted)
hline(0.95, "Upper", color=color.red, linestyle=hline.style_dashed)
hline(0.05, "Lower", color=color.green, linestyle=hline.style_dashed)