pine files

This commit is contained in:
Miha Kralj
2026-01-31 14:05:53 -08:00
parent 51e885a4a6
commit 5ed4b6c0fc
102 changed files with 2883 additions and 593 deletions
+72
View File
@@ -0,0 +1,72 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Beta Function (BETA)", "BETA", overlay=false)
//@function Calculates the financial Beta indicator comparing src1 volatility to src2
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/statistics/beta.md
//@param src1 series float Series to analyze
//@param src2 series float src2 series to compare against
//@param period simple int Lookback period for calculation
//@returns float Beta value showing src1 volatility relative to src2
//@optimized for performance and dirty data
beta(series float src1, series float src2, simple int period) =>
if period <= 0
runtime.error("Period must be greater than 0")
var float last_src1 = na
var float last_src2 = na
src1_return = last_src1 != 0 and not na(last_src1) ? (src1 - last_src1) / last_src1 : na
bench_return = last_src2 != 0 and not na(last_src2) ? (src2 - last_src2) / last_src2 : na
last_src1 := src1
last_src2 := src2
var int count = 0
var float sum_sr = 0.0, var float sum_br = 0.0
var float sum_sr2 = 0.0, var float sum_br2 = 0.0
var float sum_sbr = 0.0
var sr_buf = array.new_float(period)
var br_buf = array.new_float(period)
var int index = 0
if not na(src1_return) and not na(bench_return)
old_sr = array.get(sr_buf, index)
old_br = array.get(br_buf, index)
if count >= period
sum_sr -= old_sr, sum_br -= old_br
sum_sr2 -= old_sr * old_sr, sum_br2 -= old_br * old_br
sum_sbr -= old_sr * old_br
else
count += 1
sum_sr += src1_return, sum_br += bench_return
sum_sr2 += src1_return * src1_return
sum_br2 += bench_return * bench_return
sum_sbr += src1_return * bench_return
array.set(sr_buf, index, src1_return)
array.set(br_buf, index, bench_return)
index := (index + 1) % period
if count > 0
mean_sr = sum_sr / count
mean_br = sum_br / count
cov = (sum_sbr / count) - (mean_sr * mean_br)
var_bench = (sum_br2 / count) - (mean_br * mean_br)
if var_bench > 1e-10
cov / var_bench
else
na
else
na
// ---------- Main loop ----------
// Inputs
i_symbol = input.symbol("SPY", "src2 Symbol")
i_period = input.int(14, "Period", minval=1)
i_src1 = input.source(close, "src1")
// Get src2 data
src2Price = request.security(i_symbol, timeframe.period, close)
// Calculate beta
beta_value = beta(i_src1, src2Price, i_period)
// Plot
plot(beta_value, "Beta", color=color.yellow, linewidth=2)
+35
View File
@@ -0,0 +1,35 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Cumulative Moving Average", "CMA", overlay=true)
//@function Calculates Cumulative Moving Average (Running Average / Cumulative Mean)
//@doc Calculates the arithmetic mean of ALL data points seen so far.
//@doc Uses Welford's algorithm for numerical stability, O(1) per update.
//@param source Series to calculate CMA from
//@returns CMA value - running mean of all historical values
cma(series float source) =>
// Persistent state
var float mean = 0.0
var int count = 0
float val = nz(source, mean)
count += 1
// Welford's algorithm: M_n = M_(n-1) + alpha * (x_n - M_(n-1))
float alpha = 1.0 / count
float delta = val - mean
mean := mean + alpha * delta
mean
// ---------- Main loop ----------
// Inputs
i_source = input.source(close, "Source")
// Calculation
cma_value = cma(i_source)
// Plot
plot(cma_value, "CMA", color=color.yellow, linewidth=2)
+56
View File
@@ -0,0 +1,56 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Covariance (COVARIANCE)", "COVARIANCE", overlay=false)
//@function Calculates covariance using single pass with circular buffer
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/statistics/covariance.md
//@param src1 series float First series to analyze
//@param src2 series float Second series to analyze
//@param len simple int Lookback period for calculation
//@returns float Covariance between src1 and src2
//@optimized for performance using circular buffer
covariance(series float src1, series float src2, simple int len) =>
if len <= 0
runtime.error("Period must be greater than 0")
var int p = math.max(1, len)
var array<float> buffer1 = array.new_float(p, na)
var array<float> buffer2 = array.new_float(p, na)
var int head = 0, var int count = 0
var float sum1 = 0.0, var float sum2 = 0.0
var float sumProd = 0.0
float oldest1 = array.get(buffer1, head)
float oldest2 = array.get(buffer2, head)
if not na(oldest1) and not na(oldest2)
sum1 -= oldest1
sum2 -= oldest2
sumProd -= oldest1 * oldest2
count -= 1
if not na(src1) and not na(src2)
sum1 += src1
sum2 += src2
sumProd += src1 * src2
count += 1
array.set(buffer1, head, src1)
array.set(buffer2, head, src2)
else
array.set(buffer1, head, na)
array.set(buffer2, head, na)
head := (head + 1) % p
count > 1 ? (sumProd / count) - (sum1 / count) * (sum2 / count) : na
// ---------- Main loop ----------
// Inputs
i_source1 = input.source(close, "Source 1")
i_source2_ticker = input.symbol("SPY", "Source 2 Ticker (e.g., SPY, AAPL)")
i_period = input.int(20, "Period", minval=2)
i_source2 = request.security(i_source2_ticker, timeframe.period, close, lookahead=barmerge.lookahead_off)
// Calculation
variance_value = covariance(i_source1, i_source2, i_period)
// Plot
plot(variance_value, "Covariance", color=color.yellow, linewidth=2)
+59
View File
@@ -0,0 +1,59 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Linear Regression (LINREG)", "LINREG", overlay=false, precision=8)
//@function Calculates linear regression and slope over the specified period
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/statistics/linreg.md
//@param src Source series to calculate linear regression from
//@param len Lookback period for the calculation
//@returns Tuple containing [intercept, slope]
linreg(series float src, simple int len) =>
if len < 2
[na, na]
else
var float lastValid = na
var array<float> buf = array.new_float(len, 0.0)
var int count = 0
var int head = 0
float curr = src
if na(curr) and not na(lastValid)
curr := lastValid
if not na(curr)
lastValid := curr
if not na(curr)
array.set(buf, head, curr)
if count < len
count := count + 1
head := (head + 1) % len
float n = count
if n < 2
[na, na]
else
int start = count < len ? 0 : head
float sumY = 0.0, sumXY = 0.0
for i = 0 to int(n) - 1
int idx = (start + i) % len
float y_val = array.get(buf, idx)
sumY += y_val
sumXY += i * y_val
float sumX = 0.5 * (n - 1) * n
float sumX2 = (n - 1) * n * (2 * n - 1) / 6.0
float D = n * sumX2 - sumX * sumX
float s = D != 0 ? (n * sumXY - sumX * sumY) / D : na
float intercept = (sumY / n) - s * ((n - 1) / 2)
float lr = intercept + s * (n - 1)
[lr, s]
// ---------- Main loop ----------
// Inputs
i_period = input.int(14, "Period", minval=2)
i_source = input.source(close, "Source")
// Calculation: Assign tuple elements.
[lr, slope] = linreg(i_source, i_period)
// Plot
plot(lr, "LinReg", color=color.yellow, linewidth=2)
// plot(slope, "Slope", color=color.orange, linewidth=2, plot.style_histogram)
+42
View File
@@ -0,0 +1,42 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Median", "MEDIAN", overlay=false, precision=8)
//@function Calculates the median of a series over a lookback period.
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/statistics/median.md
//@param src series float Input data series.
//@param len simple int Lookback period (must be > 0).
//@returns series float The median of the series over the period, or na if insufficient valid data.
median(series float src, simple int len) =>
if len <= 0
runtime.error("Length must be greater than 0")
var array<float> values_in_window = array.new_float(0)
array.clear(values_in_window) // Clear from previous bar's calculation
for i = 0 to len - 1
val = src[i]
if not na(val)
array.push(values_in_window, val)
int n = array.size(values_in_window)
float result = na
if n > 0
array.sort(values_in_window) // Sort the array
if n % 2 == 1 // Odd number of elements
result := array.get(values_in_window, n / 2)
else // Even number of elements
float mid1 = array.get(values_in_window, n / 2 - 1)
float mid2 = array.get(values_in_window, n / 2)
result := (mid1 + mid2) / 2.0
result
// ---------- Main loop ----------
// Inputs
i_source = input.source(close, "Source")
i_length = input.int(14, "Period", minval=1)
// Calculation
median_value = median(i_source, i_length)
// Plot
plot(median_value, "Median", color=color.new(color.orange, 0, color=color.yellow, linewidth=2), linewidth=2)
+84
View File
@@ -0,0 +1,84 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Skewness (SKEW)", "SKEW", overlay=false, precision=6)
//@function Calculates the skewness of a source series over a specified period.
// Skewness is a measure of the asymmetry of the probability distribution of a real-valued random variable about its mean.
// This implementation calculates the population skewness (Fisher-Pearson coefficient g1).
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/statistics/skew.md
//@param src The source series.
//@param len The lookback period. Must be > 2.
//@returns The skewness value.
//@optimized Uses efficient rolling calculations for mean, variance, and the third central moment.
skew(series float src, simple int len) =>
if len <= 2
runtime.error("Length must be greater than 2 for Skewness calculation.")
var float sum_m = 0.0
var array<float> buffer_m = array.new_float(len)
var int head_m = 0
if bar_index >= len
sum_m -= array.get(buffer_m, head_m)
float current_src_nz = nz(src)
sum_m += current_src_nz
array.set(buffer_m, head_m, current_src_nz)
head_m := (head_m + 1) % len
float mean_val = na
if bar_index >= len - 1
mean_val := sum_m / len
else
mean_val := sum_m / (bar_index + 1)
float dev = src - mean_val
var float sum_v = 0.0
var array<float> buffer_v = array.new_float(len)
var int head_v = 0
float dev_sq_nz = nz(math.pow(dev, 2))
if bar_index >= len
sum_v -= array.get(buffer_v, head_v)
sum_v += dev_sq_nz
array.set(buffer_v, head_v, dev_sq_nz)
head_v := (head_v + 1) % len
float variance_val = na
if bar_index >= len - 1
variance_val := sum_v / len
else
variance_val := sum_v / (bar_index + 1)
float stddev_val = variance_val > 1e-9 ? math.sqrt(variance_val) : 0.0
var float sum_s3 = 0.0
var array<float> buffer_s3 = array.new_float(len)
var int head_s3 = 0
float dev_cubed_nz = nz(math.pow(dev, 3))
if bar_index >= len
sum_s3 -= array.get(buffer_s3, head_s3)
sum_s3 += dev_cubed_nz
array.set(buffer_s3, head_s3, dev_cubed_nz)
head_s3 := (head_s3 + 1) % len
float m3 = na
if bar_index >= len - 1
m3 := sum_s3 / len
else
m3 := sum_s3 / (bar_index + 1)
float skew_val = na
if not na(m3) and not na(stddev_val)
if stddev_val > 1e-9
float stddev_cubed = math.pow(stddev_val, 3)
if stddev_cubed != 0
skew_val := m3 / stddev_cubed
else
skew_val := 0.0
else
skew_val := 0.0
skew_val
// ---------- Main loop ----------
// Inputs
i_source = input.source(close, "Source")
i_length = input.int(20, "Length", minval=3) // Minval 3 for skewness
// Calculation
skewValue = skew(i_source, i_length)
// Plot
plot(skewValue, "Skewness", color=color.yellow, linewidth=2)
+42
View File
@@ -0,0 +1,42 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Standard Deviation (STDDEV)", "STDDEV", overlay=false)
//@function Calculates the standard deviation using a single pass with a circular buffer.
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/statistics/stddev.md
//@param src {series float} Source series.
//@param len {int} Lookback length. `len` > 0.
//@returns {series float} Standard deviation of `src` for `len` bars back. Returns `na` if not enough data.
stddev(series float src, int len) =>
if len <= 0
runtime.error("Period must be greater than 0")
var int p = math.max(1, len)
var array<float> buffer = array.new_float(p, na)
var int head = 0, var int count = 0
var float sum = 0.0, var float sumSq = 0.0
float oldest = array.get(buffer, head)
if not na(oldest)
sum -= oldest
sumSq -= oldest * oldest
count -= 1
float val = nz(src)
sum += val
sumSq += val * val
count += 1
array.set(buffer, head, val)
head := (head + 1) % p
count > 1 ? math.sqrt(math.max(0.0, (sumSq / count) - math.pow(sum / count, 2))) : 0.0
// ---------- Main loop ----------
// Inputs
i_period = input.int(14, "Period", minval=1) // Default period 14
i_source = input.source(close, "Source")
// Calculation
stddev_value = stddev(i_source, i_period)
// Plot
plot(stddev_value, "StdDev", color=color.yellow, linewidth=2) // Changed color
+59
View File
@@ -0,0 +1,59 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Rolling Sum", "SUM", overlay=false)
//@function Calculates Rolling Sum over a period using Kahan-Babuška algorithm
//@doc Calculates the sum of the last n values with high numerical precision.
//@doc Uses Kahan-Babuška summation for machine-epsilon accuracy.
//@param source Series to calculate sum from
//@param length Number of bars to sum
//@returns Rolling sum of the last 'length' values
rolling_sum(series float source, simple int length) =>
// Persistent state
var float sum = 0.0
var float c = 0.0 // First-order compensation
var float cc = 0.0 // Second-order compensation
float val = nz(source, 0.0)
float oldVal = bar_index >= length ? nz(source[length], 0.0) : 0.0
// Kahan-Babuška subtract old value
if bar_index >= length
float yS = -oldVal - c
float tS = sum + yS
c := tS - sum - yS
sum := tS
float zS = c - cc
float ttS = sum + zS
cc := ttS - sum - zS
sum := ttS
// Kahan-Babuška add new value
float yA = val - c
float tA = sum + yA
c := tA - sum - yA
sum := tA
float zA = c - cc
float ttA = sum + zA
cc := ttA - sum - zA
sum := ttA
sum
// ---------- Main loop ----------
// Inputs
i_length = input.int(14, "Length", minval=1)
i_source = input.source(close, "Source")
// Calculation
sum_value = rolling_sum(i_source, i_length)
// Equivalent built-in for comparison (disabled by default)
// sum_builtin = math.sum(i_source, i_length)
// Plot
plot(sum_value, "Sum", color=color.yellow, linewidth=2)
+41
View File
@@ -0,0 +1,41 @@
// The MIT License (MIT)
// © mihakralj
//@version=6
indicator("Variance, Dispersion or Spread (VARIANCE)", "VARIANCE", overlay=false)
//@function variance
//@doc https://github.com/mihakralj/pinescript/blob/main/indicators/statistics/variance.md
//@param src {series float} Source series.
//@param len {int} Lookback length. `len` > 0.
//@returns {series float} Variance of `src` for `len` bars back. Returns 0 if not enough data.
variance(series float src, int len) =>
if len <= 0
runtime.error("Period must be greater than 0")
var int p = math.max(1, len)
var array<float> buffer = array.new_float(p, na)
var int head = 0, var int count = 0
var float sum = 0.0, var float sumSq = 0.0
float oldest = array.get(buffer, head)
if not na(oldest)
sum -= oldest
sumSq -= oldest * oldest
count -= 1
float val = nz(src)
sum += val
sumSq += val * val
count += 1
array.set(buffer, head, val)
head := (head + 1) % p
count > 1 ? math.max(0.0, (sumSq / count) - math.pow(sum / count, 2)) : 0.0
// ---------- Main loop ----------
// Inputs
i_period = input.int(14, "Period", minval=1)
i_source = input.source(close, "Source")
// Calculation
variance_value = variance(i_source, i_period)
// Plot
plot(variance_value, "Var", color=color.yellow, linewidth=2)