mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-18 18:48:05 +00:00
Remove multiple Pine Script indicators: SSFDSP, STARCHANNEL, STBANDS, STC, UBANDS, UCHANNEL, VWAPBANDS, and VWAPSD. These indicators were deleted to streamline the library and remove unused or redundant code.
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Polynomial Fitting (POLYFIT)", "POLYFIT", overlay=true, precision=8)
|
||||
|
||||
//@function Rolling polynomial regression fit of degree d over a lookback window
|
||||
//@param source Series to fit
|
||||
//@param period Lookback period (number of data points)
|
||||
//@param degree Polynomial degree (1=linear, 2=quadratic, 3=cubic, etc.)
|
||||
//@returns Fitted value at the current bar (polynomial endpoint)
|
||||
//@description Fits a polynomial P(x) = a_0 + a_1*x + ... + a_d*x^d to the most
|
||||
// recent `period` data points using the normal equations (X'X)a = X'y.
|
||||
// The x-values are normalized to [0,1] for numerical stability.
|
||||
// Solves via Gauss-Jordan elimination with partial pivoting.
|
||||
// Output is P(1.0) — the polynomial evaluated at the current bar.
|
||||
// Degree 1 = linear regression (endpoint), degree 2 = quadratic fit, etc.
|
||||
// Complexity: O(period * degree + degree^3) per bar.
|
||||
polyfit(series float source, simple int period, simple int degree) =>
|
||||
if period < 2
|
||||
runtime.error("Period must be at least 2")
|
||||
if degree < 1
|
||||
runtime.error("Degree must be at least 1")
|
||||
int d = math.min(degree, period - 1)
|
||||
int m = d + 1
|
||||
|
||||
var array<float> buf = array.new_float(period, na)
|
||||
var int head = 0
|
||||
var int count = 0
|
||||
var float lastValid = na
|
||||
|
||||
float curr = source
|
||||
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 < period
|
||||
count += 1
|
||||
head := (head + 1) % period
|
||||
|
||||
int n = count
|
||||
if n < m + 1
|
||||
na
|
||||
else
|
||||
int start = n < period ? 0 : head
|
||||
float nf = float(n)
|
||||
float invN = 1.0 / (nf - 1.0)
|
||||
|
||||
int matSize = m * m
|
||||
array<float> mat = array.new_float(matSize, 0.0)
|
||||
array<float> rhs = array.new_float(m, 0.0)
|
||||
|
||||
for i = 0 to n - 1
|
||||
int idx = (start + i) % period
|
||||
float y = array.get(buf, idx)
|
||||
float x = float(i) * invN
|
||||
|
||||
float xp = 1.0
|
||||
for r = 0 to d
|
||||
float val_r = array.get(rhs, r)
|
||||
array.set(rhs, r, val_r + xp * y)
|
||||
|
||||
float xq = xp
|
||||
for c = r to d
|
||||
int pos = r * m + c
|
||||
float val_m = array.get(mat, pos)
|
||||
array.set(mat, pos, val_m + xp * xq)
|
||||
if c != r
|
||||
int pos2 = c * m + r
|
||||
array.set(mat, pos2, val_m + xp * xq)
|
||||
xq *= x
|
||||
xp *= x
|
||||
|
||||
for col = 0 to d
|
||||
int pivRow = col
|
||||
float pivMax = math.abs(array.get(mat, col * m + col))
|
||||
for row = col + 1 to d
|
||||
float absVal = math.abs(array.get(mat, row * m + col))
|
||||
if absVal > pivMax
|
||||
pivMax := absVal
|
||||
pivRow := row
|
||||
if pivRow != col
|
||||
for k = 0 to d
|
||||
int p1 = col * m + k
|
||||
int p2 = pivRow * m + k
|
||||
float tmp = array.get(mat, p1)
|
||||
array.set(mat, p1, array.get(mat, p2))
|
||||
array.set(mat, p2, tmp)
|
||||
float tmpR = array.get(rhs, col)
|
||||
array.set(rhs, col, array.get(rhs, pivRow))
|
||||
array.set(rhs, pivRow, tmpR)
|
||||
|
||||
float piv = array.get(mat, col * m + col)
|
||||
if math.abs(piv) < 1e-30
|
||||
break
|
||||
|
||||
float invPiv = 1.0 / piv
|
||||
for k = col to d
|
||||
int pos = col * m + k
|
||||
array.set(mat, pos, array.get(mat, pos) * invPiv)
|
||||
array.set(rhs, col, array.get(rhs, col) * invPiv)
|
||||
|
||||
for row = 0 to d
|
||||
if row != col
|
||||
float factor = array.get(mat, row * m + col)
|
||||
for k = col to d
|
||||
int p1 = row * m + k
|
||||
int p2 = col * m + k
|
||||
array.set(mat, p1, array.get(mat, p1) - factor * array.get(mat, p2))
|
||||
array.set(rhs, row, array.get(rhs, row) - factor * array.get(rhs, col))
|
||||
|
||||
float result = 0.0
|
||||
float xp = 1.0
|
||||
for r = 0 to d
|
||||
result += array.get(rhs, r) * xp
|
||||
xp *= 1.0
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=2, tooltip="Number of data points in the fitting window")
|
||||
i_degree = input.int(2, "Degree", minval=1, maxval=6, tooltip="Polynomial degree: 1=linear, 2=quadratic, 3=cubic")
|
||||
i_source = input.source(close, "Source")
|
||||
|
||||
// Calculation
|
||||
fit_value = polyfit(i_source, i_period, i_degree)
|
||||
|
||||
// Plot
|
||||
plot(fit_value, "POLYFIT", color=color.yellow, linewidth=2)
|
||||
@@ -0,0 +1,46 @@
|
||||
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0
|
||||
// https://mozilla.org/MPL/2.0/
|
||||
// © QuanTAlib
|
||||
|
||||
//@version=6
|
||||
indicator("TRIM: Trimmed Mean Moving Average", shorttitle="TRIM", overlay=true)
|
||||
|
||||
// @function Calculates the Trimmed Mean Moving Average.
|
||||
// Sorts the lookback window, discards the lowest and highest trimPct%
|
||||
// of values, and averages the remaining middle portion.
|
||||
// This robust estimator reduces the influence of outliers/spikes
|
||||
// while preserving more information than a pure median.
|
||||
// trimPct=0 → SMA, trimPct=50 → Median.
|
||||
// @param src Series to smooth.
|
||||
// @param period Window length. Must be >= 3.
|
||||
// @param trimPct Percentage of values to trim from each tail (0-49). Default 10.
|
||||
// @returns The trimmed mean value.
|
||||
export trim(series float src, simple int period, simple int trimPct) =>
|
||||
// Number of values to discard from each end
|
||||
int trimCount = math.max(int(period * trimPct / 100.0), 0)
|
||||
int keepCount = period - 2 * trimCount
|
||||
if keepCount < 1
|
||||
keepCount := 1
|
||||
trimCount := (period - 1) / 2
|
||||
|
||||
// Collect values into array and sort
|
||||
float[] vals = array.new_float(period)
|
||||
for i = 0 to period - 1
|
||||
array.set(vals, i, nz(src[i]))
|
||||
array.sort(vals, order.ascending)
|
||||
|
||||
// Average the middle portion
|
||||
float sum = 0.0
|
||||
for i = trimCount to trimCount + keepCount - 1
|
||||
sum += array.get(vals, i)
|
||||
sum / keepCount
|
||||
|
||||
// ── Inputs ──────────────────────────────────────────────
|
||||
p = input.int(20, "Period", minval=3)
|
||||
t = input.int(10, "Trim %", minval=0, maxval=49, tooltip="Percentage trimmed from each tail. 0=SMA, 50=Median")
|
||||
|
||||
// ── Calculation ─────────────────────────────────────────
|
||||
result = trim(close, p, t)
|
||||
|
||||
// ── Plot ────────────────────────────────────────────────
|
||||
plot(result, "TRIM", color=color.yellow, linewidth=2)
|
||||
@@ -0,0 +1,60 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Weighted Average (WAVG)", "WAVG", overlay=true)
|
||||
|
||||
//@function Calculates rolling linearly-weighted average over a lookback window
|
||||
//@param source Series to evaluate (typically close)
|
||||
//@param period Lookback period (number of bars)
|
||||
//@returns Weighted average where weight[i] = position from oldest (1) to newest (period)
|
||||
//@description WAVG assigns linearly increasing weights to the lookback window:
|
||||
// weight_i = i + 1 for i = 0 (oldest) to period-1 (newest)
|
||||
// WAVG = Σ(weight_i × value_i) / Σ(weight_i)
|
||||
// Σ(weight_i) = period × (period + 1) / 2
|
||||
// Uses a circular buffer for O(1) updates per bar. On each new bar:
|
||||
// 1. Subtract the departing oldest value's contribution from weightedSum
|
||||
// 2. Shift all existing weights down by 1 (subtract runningSum from weightedSum)
|
||||
// 3. Add the new value with weight = count (current fill level)
|
||||
// runningSum tracks the unweighted sum for the shift operation.
|
||||
// §3 count-based warmup: during filling, actual count < period, and
|
||||
// denominator = count × (count + 1) / 2.
|
||||
// This is mathematically identical to WMA but categorized as a statistical measure.
|
||||
wavg(series float source, simple int period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
|
||||
var array<float> buffer = array.new_float(period, na)
|
||||
var int head = 0
|
||||
var float weightedSum = 0.0
|
||||
var float runningSum = 0.0
|
||||
var int count = 0
|
||||
|
||||
float srcVal = nz(source)
|
||||
float oldest = array.get(buffer, head)
|
||||
|
||||
if not na(oldest)
|
||||
runningSum -= oldest
|
||||
else
|
||||
count += 1
|
||||
|
||||
weightedSum -= runningSum
|
||||
runningSum += srcVal
|
||||
weightedSum += float(count) * srcVal
|
||||
|
||||
array.set(buffer, head, srcVal)
|
||||
head := (head + 1) % period
|
||||
|
||||
float denom = float(count) * float(count + 1) / 2.0
|
||||
denom > 0.0 ? weightedSum / denom : srcVal
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_period = input.int(14, "Period", minval=1)
|
||||
|
||||
// Calculation
|
||||
wavg_value = wavg(i_source, i_period)
|
||||
|
||||
// Plot
|
||||
plot(wavg_value, "WAVG", color=color.yellow, linewidth=2)
|
||||
@@ -0,0 +1,51 @@
|
||||
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0
|
||||
// https://mozilla.org/MPL/2.0/
|
||||
// © QuanTAlib
|
||||
|
||||
//@version=6
|
||||
indicator("WINS: Winsorized Mean Moving Average", shorttitle="WINS", overlay=true)
|
||||
|
||||
// @function Calculates the Winsorized Mean Moving Average.
|
||||
// Sorts the lookback window, then replaces (not discards) the lowest and
|
||||
// highest winPct% of values with the boundary values at the trim point.
|
||||
// This robust estimator reduces outlier influence while retaining the
|
||||
// full sample size (unlike trimmed mean which discards).
|
||||
// winPct=0 → SMA, winPct=50 → all values equal the median pair.
|
||||
// @param src Series to smooth.
|
||||
// @param period Window length. Must be >= 3.
|
||||
// @param winPct Percentage of values to winsorize from each tail (0-49). Default 10.
|
||||
// @returns The winsorized mean value.
|
||||
export wins(series float src, simple int period, simple int winPct) =>
|
||||
// Number of values to winsorize from each end
|
||||
int winCount = math.max(int(period * winPct / 100.0), 0)
|
||||
if winCount >= period / 2
|
||||
winCount := (period - 1) / 2
|
||||
|
||||
// Collect values into array and sort
|
||||
float[] vals = array.new_float(period)
|
||||
for i = 0 to period - 1
|
||||
array.set(vals, i, nz(src[i]))
|
||||
array.sort(vals, order.ascending)
|
||||
|
||||
// Replace tail values with boundary values
|
||||
float lowerBound = array.get(vals, winCount)
|
||||
float upperBound = array.get(vals, period - 1 - winCount)
|
||||
for i = 0 to winCount - 1
|
||||
array.set(vals, i, lowerBound)
|
||||
array.set(vals, period - 1 - i, upperBound)
|
||||
|
||||
// Average all values (including replaced ones)
|
||||
float sum = 0.0
|
||||
for i = 0 to period - 1
|
||||
sum += array.get(vals, i)
|
||||
sum / period
|
||||
|
||||
// ── Inputs ──────────────────────────────────────────────
|
||||
p = input.int(20, "Period", minval=3)
|
||||
w = input.int(10, "Winsorize %", minval=0, maxval=49, tooltip="Percentage winsorized from each tail. 0=SMA")
|
||||
|
||||
// ── Calculation ─────────────────────────────────────────
|
||||
result = wins(close, p, w)
|
||||
|
||||
// ── Plot ────────────────────────────────────────────────
|
||||
plot(result, "WINS", color=color.yellow, linewidth=2)
|
||||
Reference in New Issue
Block a user