mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-15 17:18: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:
@@ -62,6 +62,8 @@
|
||||
<Compile Include="..\lib\numerics\**\*.cs" Exclude="..\lib\numerics\**\*.Tests.cs;..\lib\numerics\**\*.Validation.Tests.cs;..\lib\numerics\**\*.Quantower.cs;..\lib\numerics\**\obj\**;..\lib\numerics\**\bin\**" />
|
||||
<!-- Include reversals implementations (excluding Quantower adapters - handled separately) -->
|
||||
<Compile Include="..\lib\reversals\**\*.cs" Exclude="..\lib\reversals\**\*.Tests.cs;..\lib\reversals\**\*.Validation.Tests.cs;..\lib\reversals\**\*.Quantower.cs;..\lib\reversals\**\obj\**;..\lib\reversals\**\bin\**" />
|
||||
<!-- Include feeds implementations for GBM test data generation -->
|
||||
<Compile Include="..\lib\feeds\**\*.cs" Exclude="..\lib\feeds\**\*.Tests.cs;..\lib\feeds\**\*.Validation.Tests.cs;..\lib\feeds\**\ValidationTestData.cs;..\lib\feeds\**\ValidationHelper.cs;..\lib\feeds\**\obj\**;..\lib\feeds\**\bin\**" />
|
||||
<!-- Include IndicatorExtensions -->
|
||||
<Compile Include="IndicatorExtensions.cs" />
|
||||
<!-- Include Quantower adapter implementations from quantower folder -->
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<Compile Include="IndicatorExtensions.cs" />
|
||||
<Compile Include="..\lib\core\**\*.cs" Exclude="..\lib\core\**\*.Tests.cs;..\lib\core\**\obj\**;..\lib\core\**\bin\**" />
|
||||
<Compile Include="..\lib\trends_IIR\**\*.cs" Exclude="..\lib\trends_IIR\**\*.Tests.cs;..\lib\trends_IIR\**\obj\**;..\lib\trends_IIR\**\bin\**" />
|
||||
<Compile Include="..\lib\trends_FIR\wma\Wma.cs" />
|
||||
<Reference Include="TradingPlatform.BusinessLayer">
|
||||
<HintPath>..\.github\TradingPlatform.BusinessLayer.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -30,4 +31,4 @@
|
||||
<Copy SourceFiles="$(OutputPath)\Trends_IIR.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Trends_IIR" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Aberration (ABBER)", "ABBER", overlay=true)
|
||||
|
||||
//@function Calculates Aberration bands measuring deviation from a central moving average
|
||||
//@param source Series to calculate aberration from
|
||||
//@param ma_line Pre-calculated moving average line
|
||||
//@param period Lookback period for deviation calculation
|
||||
//@param multiplier Multiplier for deviation bands
|
||||
//@returns [upper_band, lower_band, deviation] Aberration band values and deviation
|
||||
//@optimized Uses simple deviation averaging with O(n) complexity
|
||||
abber(series float source, series float ma_line, simple int period, simple float multiplier) =>
|
||||
if period <= 0 or multiplier <= 0.0
|
||||
runtime.error("Period and multiplier must be greater than 0")
|
||||
float deviation = math.abs(nz(source) - nz(ma_line))
|
||||
float avg_deviation = ta.sma(deviation, period)
|
||||
float upper_band = ma_line + multiplier * avg_deviation
|
||||
float lower_band = ma_line - multiplier * avg_deviation
|
||||
[upper_band, lower_band, avg_deviation]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_period = input.int(20, "Period", minval=1)
|
||||
i_ma_type = input.string("SMA", "Moving Average Type", options=["SMA", "EMA", "WMA", "RMA", "HMA"])
|
||||
i_multiplier = input.float(2.0, "Deviation Multiplier", minval=0.1, step=0.1)
|
||||
i_show_ma = input.bool(true, "Show Moving Average Line")
|
||||
|
||||
// Calculate the moving average based on selected type
|
||||
ma_line = switch i_ma_type
|
||||
"SMA" => ta.sma(i_source, i_period)
|
||||
"EMA" => ta.ema(i_source, i_period)
|
||||
"WMA" => ta.wma(i_source, i_period)
|
||||
"RMA" => ta.rma(i_source, i_period)
|
||||
"HMA" => ta.wma(2 * ta.wma(i_source, i_period / 2) - ta.wma(i_source, i_period), math.round(math.sqrt(i_period)))
|
||||
=> ta.sma(i_source, i_period)
|
||||
|
||||
// Calculation
|
||||
[upper_band, lower_band, deviation] = abber(i_source, ma_line, i_period, i_multiplier)
|
||||
|
||||
// Plots
|
||||
p_upper = plot(upper_band, "Upper Band", color=color.yellow, linewidth=2)
|
||||
p_lower = plot(lower_band, "Lower Band", color=color.yellow, linewidth=2)
|
||||
plot(i_show_ma ? ma_line : na, "MA", color=color.yellow, linewidth=2)
|
||||
fill(p_upper, p_lower, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,64 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Acceleration Bands (ACCBANDS)", "ACCBANDS", overlay=true)
|
||||
|
||||
//@function Calculates Acceleration Bands using SMAs of high, low, close prices
|
||||
//@param high Series of high prices
|
||||
//@param low Series of low prices
|
||||
//@param close Series of close prices
|
||||
//@param period Lookback period for the moving average
|
||||
//@param factor Multiplier for band width calculation
|
||||
//@returns tuple with [middle, upper, lower] band values
|
||||
//@optimized Uses circular buffers with O(1) complexity per bar
|
||||
accbands(series float high, series float low, series float close, simple int period, simple float factor = 2.0) =>
|
||||
if period <= 0 or factor <= 0.0
|
||||
runtime.error("Period and factor must be greater than 0")
|
||||
var int p = math.max(1, period)
|
||||
var int head = 0
|
||||
var int count = 0
|
||||
var array<float> bufferHigh = array.new_float(p, na)
|
||||
var array<float> bufferLow = array.new_float(p, na)
|
||||
var array<float> bufferClose = array.new_float(p, na)
|
||||
var float sumHigh = 0.0
|
||||
var float sumLow = 0.0
|
||||
var float sumClose = 0.0
|
||||
float oldestHigh = array.get(bufferHigh, head)
|
||||
float oldestLow = array.get(bufferLow, head)
|
||||
float oldestClose = array.get(bufferClose, head)
|
||||
if not na(oldestHigh)
|
||||
sumHigh -= oldestHigh
|
||||
sumLow -= oldestLow
|
||||
sumClose -= oldestClose
|
||||
count -= 1
|
||||
float currentHigh = nz(high)
|
||||
float currentLow = nz(low)
|
||||
float currentClose = nz(close)
|
||||
sumHigh += currentHigh
|
||||
sumLow += currentLow
|
||||
sumClose += currentClose
|
||||
count += 1
|
||||
array.set(bufferHigh, head, currentHigh)
|
||||
array.set(bufferLow, head, currentLow)
|
||||
array.set(bufferClose, head, currentClose)
|
||||
head := (head + 1) % p
|
||||
float smaHigh = nz(sumHigh / count)
|
||||
float smaLow = nz(sumLow / count)
|
||||
float smaClose = nz(sumClose / count)
|
||||
float bandWidth = (smaHigh - smaLow) * factor
|
||||
[smaClose, smaHigh + bandWidth, smaLow - bandWidth]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=1)
|
||||
i_factor = input.float(2.0, "Factor", minval=0.001)
|
||||
|
||||
// Calculation
|
||||
[middle, upper, lower] = accbands(high, low, close, i_period, i_factor)
|
||||
|
||||
// Plot
|
||||
plot(middle, "Middle", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lower, "Lower", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,41 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Average Directional Movement Index (ADX)", "ADX", overlay=false)
|
||||
|
||||
//@function Calculates ADX using Wilder's smoothing with compensated RMA
|
||||
//@param period Number of bars used in the calculation
|
||||
//@returns tuple of ADX value, +DI, -DI
|
||||
adx(simple int period = 14) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
float tr = na(close[1]) ? high - low : math.max(high - low, math.max(math.abs(high - close[1]), math.abs(low - close[1])))
|
||||
float plus_dm = na(high[1]) ? 0.0 : high - high[1] > low[1] - low and high - high[1] > 0 ? high - high[1] : 0.0
|
||||
float minus_dm = na(low[1]) ? 0.0 : low[1] - low > high - high[1] and low[1] - low > 0 ? low[1] - low : 0.0
|
||||
var float tr_sum = 0.0
|
||||
var float plus_dm_sum = 0.0
|
||||
var float minus_dm_sum = 0.0
|
||||
|
||||
tr_sum := nz(tr_sum) - nz(tr_sum[period]) + tr
|
||||
plus_dm_sum := nz(plus_dm_sum) - nz(plus_dm_sum[period]) + plus_dm
|
||||
minus_dm_sum := nz(minus_dm_sum) - nz(minus_dm_sum[period]) + minus_dm
|
||||
|
||||
float plus_di = tr_sum != 0.0 ? math.min(100 * plus_dm_sum / tr_sum, 50.0) : 0.0
|
||||
float minus_di = tr_sum != 0.0 ? math.min(100 * minus_dm_sum / tr_sum, 50.0) : 0.0
|
||||
float dx = plus_di + minus_di != 0.0 ? 100 * math.abs(plus_di - minus_di) / (plus_di + minus_di) : 0.0
|
||||
|
||||
var float dx_sum = 0.0
|
||||
dx_sum := nz(dx_sum) - nz(dx_sum[period]) + dx
|
||||
float adx_value = dx_sum / period
|
||||
[adx_value, plus_di, minus_di]
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(14, "Period", minval=1, tooltip="Number of bars used in the calculation")
|
||||
|
||||
// Calculate ADX
|
||||
[adx_value, plus_di, minus_di] = adx(i_period)
|
||||
|
||||
// Plot
|
||||
plot(adx_value, "ADX", color=color.yellow, linewidth=2)
|
||||
plot(plus_di, "+DI", color=color.yellow, linewidth=2)
|
||||
plot(minus_di, "-DI", color=color.yellow, linewidth=2)
|
||||
@@ -1,54 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Average Directional Movement Index Rating (ADXR)", "ADXR", overlay=false)
|
||||
|
||||
//@function Calculates ADX Rating (ADXR) using current and historical ADX values
|
||||
//@param period Number of bars used in ADX calculation
|
||||
//@param rating_period Number of bars between current and historical ADX
|
||||
//@returns tuple of ADXR value, ADX value, +DI, -DI
|
||||
adxr(simple int period, simple int rating_period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
if rating_period <= 0
|
||||
runtime.error("Rating period must be greater than 0")
|
||||
var float EPSILON = 1e-10
|
||||
float alpha = 1.0/float(period)
|
||||
float tr = na(close[1]) ? high - low : math.max(high - low, math.max(math.abs(high - close[1]), math.abs(low - close[1])))
|
||||
float plus_dm = na(high[1]) ? 0.0 : high - high[1] > low[1] - low and high - high[1] > 0 ? high - high[1] : 0.0
|
||||
float minus_dm = na(low[1]) ? 0.0 : low[1] - low > high - high[1] and low[1] - low > 0 ? low[1] - low : 0.0
|
||||
var float e = 1.0
|
||||
var float tr_raw = na
|
||||
tr_raw := na(tr_raw) ? tr : (tr_raw * (period - 1) + tr) / period
|
||||
float tr_smooth = e > EPSILON ? tr_raw / (1.0 - e) : tr_raw
|
||||
var float pdm_raw = na
|
||||
pdm_raw := na(pdm_raw) ? plus_dm : (pdm_raw * (period - 1) + plus_dm) / period
|
||||
float plus_dm_smooth = e > EPSILON ? pdm_raw / (1.0 - e) : pdm_raw
|
||||
var float mdm_raw = na
|
||||
mdm_raw := na(mdm_raw) ? minus_dm : (mdm_raw * (period - 1) + minus_dm) / period
|
||||
float minus_dm_smooth = e > EPSILON ? mdm_raw / (1.0 - e) : mdm_raw
|
||||
float plus_di = tr_smooth != 0.0 ? math.min(100 * plus_dm_smooth / tr_smooth, 50.0) : 0.0
|
||||
float minus_di = tr_smooth != 0.0 ? math.min(100 * minus_dm_smooth / tr_smooth, 50.0) : 0.0
|
||||
float dx = plus_di + minus_di != 0.0 ? 100 * math.abs(plus_di - minus_di) / (plus_di + minus_di) : 0.0
|
||||
var float adx_raw = na
|
||||
adx_raw := na(adx_raw) ? 0.0 : (adx_raw * (period - 1) + dx) / period
|
||||
float adx_value = e > EPSILON ? adx_raw / (1.0 - e) : adx_raw
|
||||
e *= (1 - alpha)
|
||||
float historical_adx = adx_value[math.min(rating_period, bar_index)]
|
||||
float adxr_value = (adx_value + nz(historical_adx,0)) / 2.0
|
||||
[adxr_value, adx_value, plus_di, minus_di]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(14, "ADX Period", minval=1, tooltip="Number of bars used in ADX calculation")
|
||||
i_rating_period = input.int(14, "Rating Period", minval=1, tooltip="Number of bars between current and historical ADX")
|
||||
|
||||
// Calculate ADXR
|
||||
[adxr_value, adx_value, plus_di, minus_di] = adxr(i_period, i_rating_period)
|
||||
|
||||
// Plot
|
||||
plot(adxr_value, "ADXR", color=color.yellow, linewidth=2)
|
||||
plot(adx_value, "ADX", color=color.yellow, linewidth=2)
|
||||
plot(plus_di, "+DI", color=color.yellow, linewidth=2)
|
||||
plot(minus_di, "-DI", color=color.yellow, linewidth=2)
|
||||
@@ -1,120 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Autoregressive FIR Moving Average (AFIRMA)", "AFIRMA", overlay=true)
|
||||
|
||||
//@function Calculates AFIRMA using various windowing functions with optional least squares cubic spline fitting
|
||||
//@param source Series to calculate AFIRMA from
|
||||
//@param period Lookback period - window size
|
||||
//@param windowType Window function type (1:Hanning, 2:Hamming, 3:Blackman, 4:Blackman-Harris)
|
||||
//@param leastSquares Apply least squares cubic polynomial fitting for autoregressive prediction
|
||||
//@returns AFIRMA value, calculates from first bar using available data
|
||||
//@optimized Uses windowing functions with O(n) complexity; least squares adds O(n) for polynomial fitting
|
||||
afirma(series float src, simple int period, simple int windowType=4, simple bool leastSquares=false) =>
|
||||
float result = src
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
if windowType < 1 or windowType > 4
|
||||
runtime.error("WindowType should be in range [1-4]")
|
||||
int p = math.min(bar_index + 1, period)
|
||||
|
||||
if p > 1
|
||||
var array<float> coefs = array.new_float(1, 0.0)
|
||||
var int prevPeriod = 0
|
||||
var int prevWindowType = -1
|
||||
if p != prevPeriod or windowType != prevWindowType
|
||||
coefs := array.new_float(p, 0.0)
|
||||
float a0 = 0.35875
|
||||
float a1 = -0.48829
|
||||
float a2 = 0.14128
|
||||
float a3 = -0.01168
|
||||
if windowType == 1
|
||||
a0 := 0.50
|
||||
a1 := -0.50
|
||||
else if windowType == 2
|
||||
a0 := 0.54
|
||||
a1 := -0.46
|
||||
else if windowType == 3
|
||||
a0 := 0.42
|
||||
a1 := -0.50
|
||||
a2 := 0.08
|
||||
float TWO_PI = 6.28318530718
|
||||
float twoPiDivP = TWO_PI / p
|
||||
for k = 0 to p - 1
|
||||
float kTwoPiDivP = k * twoPiDivP
|
||||
float coef = a0 + a1 * math.cos(kTwoPiDivP)
|
||||
if a2 != 0.0
|
||||
coef += a2 * math.cos(2.0 * kTwoPiDivP)
|
||||
if a3 != 0.0
|
||||
coef += a3 * math.cos(3.0 * kTwoPiDivP)
|
||||
array.set(coefs, k, coef)
|
||||
prevPeriod := p
|
||||
prevWindowType := windowType
|
||||
float sum = 0.0
|
||||
float weightSum = 0.0
|
||||
int validCount = 0
|
||||
for i = 0 to p - 1
|
||||
float price = src[i]
|
||||
if not na(price)
|
||||
float coef = array.get(coefs, i)
|
||||
sum += price * coef
|
||||
weightSum += coef
|
||||
validCount += 1
|
||||
result := validCount > 0 and weightSum > 0 ? sum / weightSum : src
|
||||
|
||||
if leastSquares and p > 2
|
||||
int n = math.min(math.floor((p - 1) / 2), 50)
|
||||
if n >= 2
|
||||
var float sx = 0.0
|
||||
var float sx2 = 0.0
|
||||
var int prevN = 0
|
||||
|
||||
if n != prevN
|
||||
sx := 0.0
|
||||
sx2 := 0.0
|
||||
for i = 0 to n - 1
|
||||
sx += i
|
||||
sx2 += i * i
|
||||
prevN := n
|
||||
|
||||
float sy = 0.0
|
||||
float sxy = 0.0
|
||||
for i = 0 to n - 1
|
||||
float yi = nz(src[i])
|
||||
sy += yi
|
||||
sxy += i * yi
|
||||
|
||||
float denom = n * sx2 - sx * sx
|
||||
if math.abs(denom) > 1e-10
|
||||
float slope = (n * sxy - sx * sy) / denom
|
||||
float intercept = (sy - slope * sx) / n
|
||||
|
||||
var array<float> fittedBuffer = array.new_float(p, na)
|
||||
for i = 0 to n - 1
|
||||
float fitted = intercept + slope * i
|
||||
array.set(fittedBuffer, i, fitted)
|
||||
|
||||
float lsSum = 0.0
|
||||
float lsCount = 0.0
|
||||
for i = 0 to p - 1
|
||||
float val = i < n ? array.get(fittedBuffer, i) : nz(src[i])
|
||||
if not na(val)
|
||||
lsSum += val
|
||||
lsCount += 1.0
|
||||
|
||||
result := lsCount > 0 ? lsSum / lsCount : result
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=1)
|
||||
i_source = input.source(close, "Source")
|
||||
i_windowType = input.int(4, "Window Function", minval=1, maxval=4, tooltip="1:Hanning, 2:Hamming, 3:Blackman, 4:Blackman-Harris")
|
||||
i_leastSquares = input.bool(false, "Least Squares Method", tooltip="Enable cubic polynomial fitting for autoregressive prediction")
|
||||
|
||||
// Calculation
|
||||
afirma_value = afirma(i_source, i_period, i_windowType, i_leastSquares)
|
||||
|
||||
// Plot
|
||||
plot(afirma_value, "AFIRMA", color=color.yellow, linewidth=2)
|
||||
@@ -1,56 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Andrews' Pitchfork (AP)", "AP", overlay=true)
|
||||
|
||||
//@function Calculates Andrews' Pitchfork lines based on three pivot points
|
||||
//@param p1_back Bars back to first pivot point (leftmost)
|
||||
//@param p2_back Bars back to second pivot point (middle)
|
||||
//@param p3_back Bars back to third pivot point (rightmost)
|
||||
//@returns tuple of [median, upper, lower] lines for current bar
|
||||
//@optimized Geometric projection with O(1) complexity per bar
|
||||
apchannel(simple int p1_back, simple int p2_back, simple int p3_back) =>
|
||||
if p1_back <= 0 or p2_back <= 0 or p3_back <= 0 or not (p1_back > p2_back and p2_back > p3_back)
|
||||
runtime.error("Use P1 oldest, P2 newer, P3 newest — all >0")
|
||||
[na, na, na]
|
||||
int p1_b = math.min(p1_back, bar_index)
|
||||
int p2_b = math.min(p2_back, bar_index)
|
||||
int p3_b = math.min(p3_back, bar_index)
|
||||
int p1_time = bar_index - p1_b
|
||||
int p2_time = bar_index - p2_b
|
||||
int p3_time = bar_index - p3_b
|
||||
float p1_price = nz(close[p1_b])
|
||||
float p2_price = nz(high[p2_b])
|
||||
float p3_price = nz(low[p3_b])
|
||||
if na(close[p1_b]) or na(high[p2_b]) or na(low[p3_b])
|
||||
[float(na), float(na), float(na)]
|
||||
float mid_time_float = (float(p2_time) + float(p3_time)) / 2.0
|
||||
float mid_price = (p2_price + p3_price) / 2.0
|
||||
float time_diff = mid_time_float - float(p1_time)
|
||||
float median_slope = math.abs(time_diff) > 1e-10 ? (mid_price - p1_price) / time_diff : 0.0
|
||||
float median_value = p1_price + median_slope * (float(bar_index) - float(p1_time))
|
||||
float upper_value = p2_price + median_slope * (float(bar_index) - float(p2_time))
|
||||
float lower_value = p3_price + median_slope * (float(bar_index) - float(p3_time))
|
||||
if math.abs(median_value) > 1e9 or math.abs(upper_value) > 1e9 or math.abs(lower_value) > 1e9
|
||||
[float(na), float(na), float(na)]
|
||||
[median_value, upper_value, lower_value]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_p1_back = input.int(45, "Point 1 (Leftmost)", minval=1)
|
||||
i_p2_back = input.int(30, "Point 2 (Second)", minval=1)
|
||||
i_p3_back = input.int(15, "Point 3 (Third)", minval=1)
|
||||
|
||||
// Validation
|
||||
if i_p1_back <= i_p2_back or i_p2_back <= i_p3_back
|
||||
runtime.error("Points must be in chronological order (P1 > P2 > P3)")
|
||||
|
||||
// Calculation
|
||||
[median, upper, lower] = apchannel(i_p1_back, i_p2_back, i_p3_back)
|
||||
|
||||
// Plot
|
||||
plot(median, "Median", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.new(color.blue, 50), linewidth=1)
|
||||
p2 = plot(lower, "Lower", color=color.new(color.blue, 50), linewidth=1)
|
||||
fill(p1, p2, color=color.new(color.blue, 90))
|
||||
@@ -1,70 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Adaptive Price Zone", "APZ", overlay=true)
|
||||
|
||||
//@function Calculates Adaptive Price Zone using double-smoothed EMA
|
||||
//@param source Series to calculate middle line from
|
||||
//@param period Lookback period (sqrt applied internally for smoothing)
|
||||
//@param bandPct Band width multiplier
|
||||
//@returns tuple with [middle, upper, lower] band values
|
||||
//@optimized Uses compound warmup compensation for nested EMAs, O(1) complexity per bar
|
||||
apz(series float source, simple int period, simple float bandPct) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
if period > 5000
|
||||
runtime.error("Period exceeds maximum of 5000")
|
||||
if bandPct <= 0.0
|
||||
runtime.error("Band multiplier must be greater than 0")
|
||||
|
||||
float smoothPeriod = math.sqrt(period)
|
||||
float alpha = 2.0 / (smoothPeriod + 1.0)
|
||||
float beta = 1.0 - alpha
|
||||
|
||||
var float ema1_price = 0.0
|
||||
var float ema2_price = 0.0
|
||||
var float ema1_range = 0.0
|
||||
var float ema2_range = 0.0
|
||||
var float e = 1.0
|
||||
var bool warmup = true
|
||||
|
||||
float current_price = nz(source)
|
||||
float current_range = nz(high - low)
|
||||
|
||||
ema1_price := alpha * current_price + beta * ema1_price
|
||||
ema2_price := alpha * ema1_price + beta * ema2_price
|
||||
|
||||
ema1_range := alpha * current_range + beta * ema1_range
|
||||
ema2_range := alpha * ema1_range + beta * ema2_range
|
||||
|
||||
float middle = ema2_price
|
||||
float adaptiveRange = ema2_range
|
||||
|
||||
if warmup
|
||||
e *= beta * beta
|
||||
float compensator = 1.0 / (1.0 - e)
|
||||
middle := compensator * ema2_price
|
||||
adaptiveRange := compensator * ema2_range
|
||||
warmup := e > 1e-10
|
||||
|
||||
float width = bandPct * adaptiveRange
|
||||
float upper = middle + width
|
||||
float lower = middle - width
|
||||
|
||||
[middle, upper, lower]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=1, maxval=5000)
|
||||
i_bandPct = input.float(2.0, "Band Multiplier", minval=0.001, step=0.1)
|
||||
i_source = input.source(close, "Source")
|
||||
|
||||
// Calculation
|
||||
[middle, upper, lower] = apz(i_source, i_period, i_bandPct)
|
||||
|
||||
// Plot
|
||||
plot(middle, "Middle", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.new(color.yellow, 50), linewidth=1)
|
||||
p2 = plot(lower, "Lower", color=color.new(color.yellow, 50), linewidth=1)
|
||||
fill(p1, p2, color=color.new(color.yellow, 90), title="Band Fill")
|
||||
@@ -1,69 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("ATR Bands (ATRBANDS)", "ATRBANDS", overlay=true)
|
||||
|
||||
//@function Calculates ATR Bands using ATR for width
|
||||
//@param source Source series for the center line
|
||||
//@param length Period for ATR and MA calculations
|
||||
//@param multiplier ATR multiplier for band width
|
||||
//@returns tuple with [middle, upper, lower] band values
|
||||
//@optimized Uses RMA with warmup compensator, O(1) complexity per bar
|
||||
atrbands(series float source, simple int length, simple float multiplier) =>
|
||||
if length <= 0 or multiplier <= 0.0
|
||||
runtime.error("Length and multiplier must be greater than 0")
|
||||
var float prevClose = close
|
||||
float tr1 = high - low
|
||||
float tr2 = math.abs(high - prevClose)
|
||||
float tr3 = math.abs(low - prevClose)
|
||||
float trueRange = math.max(tr1, tr2, tr3)
|
||||
prevClose := close
|
||||
var int p = math.max(1, length)
|
||||
var int head = 0
|
||||
var int count = 0
|
||||
var array<float> bufferSource = array.new_float(p, na)
|
||||
var array<float> bufferTR = array.new_float(p, na)
|
||||
var float sumSource = 0.0
|
||||
var float sumTR = 0.0
|
||||
float oldestSource = array.get(bufferSource, head)
|
||||
float oldestTR = array.get(bufferTR, head)
|
||||
if not na(oldestSource)
|
||||
sumSource -= oldestSource
|
||||
sumTR -= oldestTR
|
||||
count -= 1
|
||||
float currentSource = nz(source)
|
||||
float currentTR = nz(trueRange)
|
||||
sumSource += currentSource
|
||||
sumTR += currentTR
|
||||
count += 1
|
||||
array.set(bufferSource, head, currentSource)
|
||||
array.set(bufferTR, head, currentTR)
|
||||
head := (head + 1) % p
|
||||
var float EPSILON = 1e-10
|
||||
var float raw_rma = 0.0
|
||||
var float e = 1.0
|
||||
float atrValue = na
|
||||
if not na(trueRange)
|
||||
float alpha = 1.0 / float(length)
|
||||
raw_rma := (raw_rma * (length - 1) + trueRange) / length
|
||||
e := (1 - alpha) * e
|
||||
atrValue := e > EPSILON ? raw_rma / (1.0 - e) : raw_rma
|
||||
float middleBand = nz(sumSource / count, source)
|
||||
float width = nz(atrValue * multiplier)
|
||||
[middleBand, middleBand + width, middleBand - width]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_length = input.int(20, "Length", minval=1)
|
||||
i_mult = input.float(2.0, "ATR Multiplier", minval=0.001)
|
||||
|
||||
// Calculation
|
||||
[middle, upper, lower] = atrbands(i_source, i_length, i_mult)
|
||||
|
||||
// Plot
|
||||
plot(middle, "Middle", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lower, "Lower", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,50 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Bollinger Bands (BBANDS)", "BBANDS", overlay=true)
|
||||
|
||||
//@function Calculates Bollinger Bands with adjustable period and multiplier
|
||||
//@param source Series to calculate Bollinger Bands from
|
||||
//@param period Lookback period for calculations
|
||||
//@param multiplier Standard deviation multiplier for band width
|
||||
//@returns tuple with [middle, upper, lower] band values
|
||||
//@optimized Uses circular buffer with running sums, O(1) complexity per bar
|
||||
bbands(series float source, simple int period, simple float multiplier) =>
|
||||
if period <= 0 or multiplier <= 0.0
|
||||
runtime.error("Period and multiplier must be greater than 0")
|
||||
var int p = math.max(1, period)
|
||||
var int head = 0
|
||||
var int count = 0
|
||||
var array<float> buffer = array.new_float(p, na)
|
||||
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 current_val = nz(source)
|
||||
sum += current_val
|
||||
sumSq += current_val * current_val
|
||||
count += 1
|
||||
array.set(buffer, head, current_val)
|
||||
head := (head + 1) % p
|
||||
float basis = nz(sum / count, source)
|
||||
float dev = count > 1 ? multiplier * math.sqrt(math.max(0.0, sumSq / count - basis * basis)) : 0.0
|
||||
[basis, basis + dev, basis - dev]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=1)
|
||||
i_source = input.source(close, "Source")
|
||||
i_multiplier = input.float(2.0, "StdDev Multiplier", minval=0.001)
|
||||
|
||||
// Calculation
|
||||
[basis, upper, lower] = bbands(i_source, i_period, i_multiplier)
|
||||
|
||||
// Plot
|
||||
plot(basis, "Basis", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lower, "Lower", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,33 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Center of Gravity (CG)", "CG", overlay=false)
|
||||
|
||||
//@function Calculates Ehlers' Center of Gravity indicator
|
||||
//@param src Series to calculate Center of Gravity from
|
||||
//@param length Period for the Center of Gravity calculation
|
||||
//@returns Center of Gravity value identifying cycle turning points
|
||||
//@optimized for performance and dirty data
|
||||
cg(series float src, simple int length) =>
|
||||
if length <= 0
|
||||
runtime.error("Length must be greater than 0")
|
||||
float num = 0.0, float den = 0.0
|
||||
for count = 1 to length
|
||||
float price = nz(src[count - 1])
|
||||
num += count * price
|
||||
den += price
|
||||
float result = den != 0 ? num / den : (length + 1) / 2.0
|
||||
result - (length + 1) / 2.0
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(10, "Length", minval=1, tooltip="Period for Center of Gravity calculation")
|
||||
i_source = input.source(close, "Source")
|
||||
|
||||
// Calculation
|
||||
cg_value = cg(i_source, i_length)
|
||||
|
||||
// Plot
|
||||
plot(cg_value, "CG", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero Line", color.gray, linestyle=hline.style_dashed)
|
||||
@@ -1,50 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Donchian Channels (DCHANNEL)", "DCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates the Donchian Channel (DC) efficiently using monotonic deques
|
||||
//@param hi Source series for the highest high calculation (usually high)
|
||||
//@param lo Source series for the lowest low calculation (usually low)
|
||||
//@param p Lookback period (p > 0)
|
||||
//@returns Tuple containing [basis, upper_band, lower_band]
|
||||
//@optimized Uses monotonic deque for O(1) amortized complexity per bar
|
||||
dchannel(series float hi, series float lo, simple int p) =>
|
||||
if p <= 0
|
||||
runtime.error("Period must be > 0")
|
||||
var float[] hbuf = array.new_float(p, na)
|
||||
var float[] lbuf = array.new_float(p, na)
|
||||
var int[] hq = array.new_int()
|
||||
var int[] lq = array.new_int()
|
||||
int idx = bar_index % p
|
||||
array.set(hbuf, idx, hi)
|
||||
array.set(lbuf, idx, lo)
|
||||
while array.size(hq) > 0 and array.get(hq, 0) <= bar_index - p
|
||||
array.shift(hq)
|
||||
while array.size(hq) > 0 and array.get(hbuf, array.get(hq, -1) % p) <= hi
|
||||
array.pop(hq)
|
||||
array.push(hq, bar_index)
|
||||
while array.size(lq) > 0 and array.get(lq, 0) <= bar_index - p
|
||||
array.shift(lq)
|
||||
while array.size(lq) > 0 and array.get(lbuf, array.get(lq, -1) % p) >= lo
|
||||
array.pop(lq)
|
||||
array.push(lq, bar_index)
|
||||
float top = array.get(hbuf, array.get(hq, 0) % p)
|
||||
float bot = array.get(lbuf, array.get(lq, 0) % p)
|
||||
[math.avg(top, bot), top, bot]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=1)
|
||||
i_high = input.source(high, "High Source")
|
||||
i_low = input.source(low, "Low Source")
|
||||
|
||||
// Calculation
|
||||
[basis, upper, lower] = dchannel(i_high, i_low, i_period)
|
||||
|
||||
// Plot
|
||||
plot(basis, "Basis", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lower, "Lower", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,77 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Decay Min-Max Channel (DECAYCHANNEL)", "DECAYCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates the Decaying Min-Max Channel with decay towards midpoint
|
||||
//@param period Lookback period (period > 0)
|
||||
//@param hi Source series for the highest high calculation (usually high)
|
||||
//@param lo Source series for the lowest low calculation (usually low)
|
||||
//@returns Tuple containing [decaying_highest_high, decaying_lowest_low]
|
||||
//@optimized Uses exponential decay with O(n) complexity per bar
|
||||
decaychannel(simple int period, series float hi = high, series float lo = low) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be > 0")
|
||||
float decayLambda = math.log(2.0) / period
|
||||
var float[] hbuf = array.new_float(period, na)
|
||||
var float[] lbuf = array.new_float(period, na)
|
||||
var float currentMax = na
|
||||
var float currentMin = na
|
||||
var int timeSinceNewMax = 0
|
||||
var int timeSinceNewMin = 0
|
||||
int idx = bar_index % period
|
||||
array.set(hbuf, idx, hi)
|
||||
array.set(lbuf, idx, lo)
|
||||
float periodMax = na
|
||||
float periodMin = na
|
||||
float periodSum = 0.0
|
||||
int validCount = 0
|
||||
for i = 0 to period - 1
|
||||
float hVal = array.get(hbuf, i)
|
||||
float lVal = array.get(lbuf, i)
|
||||
if not na(hVal) and not na(lVal)
|
||||
periodMax := na(periodMax) ? hVal : math.max(periodMax, hVal)
|
||||
periodMin := na(periodMin) ? lVal : math.min(periodMin, lVal)
|
||||
periodSum := periodSum + (hVal + lVal) / 2.0
|
||||
validCount := validCount + 1
|
||||
float periodAverage = validCount > 0 ? periodSum / validCount : (hi + lo) / 2.0
|
||||
if na(currentMax) or na(currentMin)
|
||||
currentMax := na(periodMax) ? hi : periodMax
|
||||
currentMin := na(periodMin) ? lo : periodMin
|
||||
timeSinceNewMax := 0
|
||||
timeSinceNewMin := 0
|
||||
else
|
||||
if hi >= currentMax
|
||||
currentMax := hi
|
||||
timeSinceNewMax := 0
|
||||
else
|
||||
timeSinceNewMax := timeSinceNewMax + 1
|
||||
if lo <= currentMin
|
||||
currentMin := lo
|
||||
timeSinceNewMin := 0
|
||||
else
|
||||
timeSinceNewMin := timeSinceNewMin + 1
|
||||
if validCount > 0
|
||||
float midpoint = (currentMax + currentMin) / 2.0
|
||||
float maxDecayRate = 1 - math.exp(-decayLambda * timeSinceNewMax)
|
||||
float minDecayRate = 1 - math.exp(-decayLambda * timeSinceNewMin)
|
||||
currentMax := currentMax - maxDecayRate * (currentMax - midpoint)
|
||||
currentMin := currentMin - minDecayRate * (currentMin - midpoint)
|
||||
if not na(periodMax)
|
||||
currentMax := math.min(currentMax, periodMax)
|
||||
if not na(periodMin)
|
||||
currentMin := math.max(currentMin, periodMin)
|
||||
[currentMax, currentMin]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(100, "Period", minval=1)
|
||||
|
||||
// Calculation
|
||||
[highest, lowest] = decaychannel(i_period)
|
||||
|
||||
// Plot
|
||||
p1 = plot(highest, "Decaying High", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lowest, "Decaying Low", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Channel Fill")
|
||||
@@ -1,50 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Detrended Synthetic Price (DSP)", "DSP", overlay=false)
|
||||
|
||||
//@function Calculates Detrended Synthetic Price using Ehlers dual-EMA algorithm
|
||||
//@param source Series to detrend
|
||||
//@param period Dominant cycle period for quarter/half-cycle EMA calculation
|
||||
//@returns Detrended synthetic price (difference between quarter-cycle and half-cycle EMAs)
|
||||
dsp(series float source, simple int period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int fast_period = math.max(2, int(math.round(period / 4.0)))
|
||||
int slow_period = math.max(3, int(math.round(period / 2.0)))
|
||||
float alpha_fast = 2.0 / (fast_period + 1)
|
||||
float alpha_slow = 2.0 / (slow_period + 1)
|
||||
var float ema_fast_raw = 0.0
|
||||
var float ema_slow_raw = 0.0
|
||||
float current = nz(source)
|
||||
ema_fast_raw += alpha_fast * (current - ema_fast_raw)
|
||||
ema_slow_raw += alpha_slow * (current - ema_slow_raw)
|
||||
var bool warmup = true
|
||||
var float e_fast = 1.0
|
||||
var float e_slow = 1.0
|
||||
float ema_fast = ema_fast_raw
|
||||
float ema_slow = ema_slow_raw
|
||||
if warmup
|
||||
e_fast *= (1.0 - alpha_fast)
|
||||
e_slow *= (1.0 - alpha_slow)
|
||||
float c_fast = 1.0 / (1.0 - e_fast)
|
||||
float c_slow = 1.0 / (1.0 - e_slow)
|
||||
ema_fast := c_fast * ema_fast_raw
|
||||
ema_slow := c_slow * ema_slow_raw
|
||||
warmup := e_fast > 1e-10 or e_slow > 1e-10
|
||||
|
||||
// Return difference (detrended synthetic price)
|
||||
ema_fast - ema_slow
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(hlc3, "Source")
|
||||
i_period = input.int(40, "Dominant Cycle Period", minval=4, maxval=200, tooltip="Dominant cycle period. Quarter-cycle and half-cycle EMAs calculated from this value.")
|
||||
|
||||
// Calculation
|
||||
dsp_val = dsp(i_source, i_period)
|
||||
|
||||
// Plot
|
||||
plot(dsp_val, "DSP", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero Line", color=color.gray, linestyle=hline.style_solid)
|
||||
@@ -1,145 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Autocorrelation Periodogram (EACP)","EACP",overlay=false)
|
||||
//@function Autocorrelation periodogram dominant cycle estimator
|
||||
//@param source Price input series
|
||||
//@param minPeriod Minimum period to evaluate
|
||||
//@param maxPeriod Maximum period to evaluate
|
||||
//@param avgLength Averaging length for Pearson correlation (0 uses lag length)
|
||||
//@param enhance Apply cubic emphasis to highlight dominant peaks
|
||||
//@returns Smoothed dominant cycle estimate
|
||||
//@optimized Removed buffer complexity, uses native PineScript historical operator for O(n) correlation
|
||||
//@validation wolfram:"Wiener-Khinchin theorem","Pearson correlation coefficient" external:"TradingView TASC 2025.02 Autocorrelation","ImmortalFreedom Ehlers ACP","QuantStrat autocorrPeriodogram"
|
||||
eacp(series float source,simple int minPeriod,simple int maxPeriod,simple int avgLength,simple bool enhance)=>
|
||||
if minPeriod<3
|
||||
runtime.error("Min period must be at least 3")
|
||||
if maxPeriod<=minPeriod
|
||||
runtime.error("Max period must be greater than min period")
|
||||
if avgLength<0
|
||||
runtime.error("Average length must be non-negative")
|
||||
int size=maxPeriod+1
|
||||
var array<float> corr=array.new_float(0)
|
||||
var array<float> power=array.new_float(0)
|
||||
var array<float> smooth=array.new_float(0)
|
||||
var int storedSize=0
|
||||
var int storedMin=0
|
||||
var int storedMax=0
|
||||
var bool configured=false
|
||||
var float hp=0.0
|
||||
var float filt=0.0
|
||||
var float dom=0.0
|
||||
var float domPower=0.0
|
||||
var float maxPwr=0.0
|
||||
var float e=1.0
|
||||
var bool warmup=true
|
||||
if not configured or storedSize!=size or storedMin!=minPeriod or storedMax!=maxPeriod
|
||||
corr:=array.new_float(size,0.0)
|
||||
power:=array.new_float(size,0.0)
|
||||
smooth:=array.new_float(size,0.0)
|
||||
storedSize:=size
|
||||
storedMin:=minPeriod
|
||||
storedMax:=maxPeriod
|
||||
configured:=true
|
||||
hp:=0.0
|
||||
filt:=0.0
|
||||
dom:=(minPeriod+maxPeriod)*0.5
|
||||
domPower:=0.0
|
||||
maxPwr:=0.0
|
||||
e:=1.0
|
||||
warmup:=true
|
||||
float price=nz(source)
|
||||
float alphaHP=(math.cos(math.sqrt(2.0)*math.pi/float(maxPeriod))+math.sin(math.sqrt(2.0)*math.pi/float(maxPeriod))-1.0)/math.cos(math.sqrt(2.0)*math.pi/float(maxPeriod))
|
||||
hp:=math.pow(1.0-alphaHP/2.0,2.0)*(price-2.0*nz(price[1])+nz(price[2]))+2.0*(1.0-alphaHP)*nz(hp[1])-math.pow(1.0-alphaHP,2.0)*nz(hp[2])
|
||||
float a1=math.exp(-math.sqrt(2.0)*math.pi/float(minPeriod))
|
||||
float b1=2.0*a1*math.cos(math.sqrt(2.0)*math.pi/float(minPeriod))
|
||||
float c2=b1
|
||||
float c3=-(a1*a1)
|
||||
float c1=1.0-c2-c3
|
||||
filt:=c1*(hp+nz(hp[1]))*0.5+c2*nz(filt[1])+c3*nz(filt[2])
|
||||
for lag=0 to maxPeriod
|
||||
if lag<2
|
||||
array.set(corr,lag,0.0)
|
||||
else
|
||||
int window=avgLength==0?lag:avgLength
|
||||
if window<2
|
||||
window:=2
|
||||
float sx=0.0
|
||||
float sy=0.0
|
||||
float sxx=0.0
|
||||
float syy=0.0
|
||||
float sxy=0.0
|
||||
int valid=0
|
||||
for k=0 to window-1
|
||||
float x=nz(filt[k])
|
||||
float y=nz(filt[lag+k])
|
||||
sx+=x
|
||||
sy+=y
|
||||
sxx+=x*x
|
||||
syy+=y*y
|
||||
sxy+=x*y
|
||||
valid+=1
|
||||
float corrVal=0.0
|
||||
if valid>1
|
||||
float denomX=float(valid)*sxx-sx*sx
|
||||
float denomY=float(valid)*syy-sy*sy
|
||||
float denom=denomX*denomY
|
||||
corrVal:=denom>0.0?(float(valid)*sxy-sx*sy)/math.sqrt(denom):0.0
|
||||
array.set(corr,lag,corrVal)
|
||||
for period=minPeriod to maxPeriod
|
||||
float cosAcc=0.0
|
||||
float sinAcc=0.0
|
||||
for n=2 to maxPeriod
|
||||
float corrVal=array.get(corr,n)
|
||||
float angle=2.0*math.pi*float(n)/float(period)
|
||||
cosAcc+=corrVal*math.cos(angle)
|
||||
sinAcc+=corrVal*math.sin(angle)
|
||||
float sq=cosAcc*cosAcc+sinAcc*sinAcc
|
||||
array.set(smooth,period,0.2*sq*sq+0.8*array.get(smooth,period))
|
||||
float localMaxPwr=0.0
|
||||
for period=minPeriod to maxPeriod
|
||||
float smoothVal=array.get(smooth,period)
|
||||
if smoothVal>localMaxPwr
|
||||
localMaxPwr:=smoothVal
|
||||
float diff=float(maxPeriod-minPeriod)
|
||||
float K=diff>0?math.pow(10.0,-0.15/diff):1.0
|
||||
if localMaxPwr>maxPwr
|
||||
maxPwr:=localMaxPwr
|
||||
else
|
||||
maxPwr:=K*maxPwr
|
||||
float weighted=0.0
|
||||
float sumWeight=0.0
|
||||
float peakPwr=0.0
|
||||
for period=minPeriod to maxPeriod
|
||||
float smoothVal=array.get(smooth,period)
|
||||
float pwr=maxPwr>0.0?smoothVal/maxPwr:0.0
|
||||
if enhance
|
||||
pwr:=math.pow(pwr,3.0)
|
||||
array.set(power,period,pwr)
|
||||
if pwr>peakPwr
|
||||
peakPwr:=pwr
|
||||
if pwr>=0.5
|
||||
weighted+=float(period)*pwr
|
||||
sumWeight+=pwr
|
||||
float base=sumWeight>=0.25?weighted/sumWeight:dom
|
||||
float alpha=0.2
|
||||
float beta=1.0-alpha
|
||||
dom:=alpha*(base-dom)+dom
|
||||
if warmup
|
||||
e*=beta
|
||||
float c=1.0/(1.0-e)
|
||||
dom:=c*dom
|
||||
warmup:=e>1e-10
|
||||
int domIdx=math.min(math.max(int(math.round(dom)),minPeriod),maxPeriod)
|
||||
domPower:=array.get(power,domIdx)
|
||||
[dom,domPower]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
i_source=input.source(close,"Source")
|
||||
i_minPeriod=input.int(8,"Min Period",minval=3,maxval=500)
|
||||
i_maxPeriod=input.int(48,"Max Period",minval=4,maxval=500)
|
||||
i_avgLength=input.int(3,"Autocorrelation Length",minval=0,maxval=500)
|
||||
i_enhance=input.bool(true,"Enhance Resolution")
|
||||
[dominantCycle,normalizedPower]=eacp(i_source,i_minPeriod,i_maxPeriod,i_avgLength,i_enhance)
|
||||
plot(dominantCycle,"Dominant Cycle",color=color.yellow,linewidth=2)
|
||||
plot(normalizedPower,"Normalized Power",color=color.orange,linewidth=2)
|
||||
@@ -1,43 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Even Better Sinewave (EBSW)", "EBSW", overlay=false)
|
||||
|
||||
//@function Calculates Ehlers Even Better Sinewave using HPF, SSF, and AGC
|
||||
//@param src Series to calculate EBSW from
|
||||
//@param hpLength int Period for the High-Pass Filter
|
||||
//@param ssfLength int Period for the Super Smoother Filter
|
||||
//@returns single normalized sinewave value
|
||||
//@optimized for performance and dirty data
|
||||
ebsw(series float src, simple int hpLength, simple int ssfLength) =>
|
||||
if hpLength <= 0 or ssfLength <= 0
|
||||
runtime.error("Periods must be greater than 0")
|
||||
float pi = 2 * math.asin(1)
|
||||
float angle_hp = 2 * pi / hpLength
|
||||
float alpha1_hp = (1 - math.sin(angle_hp)) / math.cos(angle_hp)
|
||||
var float hp = 0.0
|
||||
hp := (0.5 * (1 + alpha1_hp) * (src - nz(src[1]))) + (alpha1_hp * nz(hp[1]))
|
||||
float angle_ssf = math.sqrt(2) * pi / ssfLength
|
||||
float alpha2_ssf = math.exp(-angle_ssf)
|
||||
float beta_ssf = 2 * alpha2_ssf * math.cos(angle_ssf)
|
||||
float c2 = beta_ssf, c3 = -alpha2_ssf * alpha2_ssf, c1 = 1 - c2 - c3
|
||||
var float filt = 0.0
|
||||
filt := c1 * ((hp + nz(hp[1])) / 2) + c2 * nz(filt[1]) + c3 * nz(filt[2])
|
||||
float waveVal = (filt + nz(filt[1]) + nz(filt[2])) / 3.0
|
||||
float pwr = (math.pow(filt, 2) + math.pow(nz(filt[1]), 2) + math.pow(nz(filt[2]), 2)) / 3.0
|
||||
float sineWave = pwr == 0 ? 0 : waveVal / math.sqrt(pwr)
|
||||
math.min(1, math.max(-1, sineWave))
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_hpLength = input.int(40, "High-Pass Filter Length", minval=1, tooltip="Period for detrending the price data.")
|
||||
i_ssfLength = input.int(10, "Super Smoother Filter Length", minval=1, tooltip="Period for smoothing the cycle component.")
|
||||
|
||||
// Calculation
|
||||
ebsw_wave = ebsw(i_source, i_hpLength, i_ssfLength)
|
||||
|
||||
// Plot
|
||||
plot(ebsw_wave, "EBSW", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero Line", color.gray, linestyle=hline.style_dashed)
|
||||
@@ -1,90 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Homodyne Discriminator (HOMOD)","HOMOD",overlay=false)
|
||||
|
||||
//@function Quadrant-aware angle calculation using stable atan2
|
||||
//@param y Imaginary component
|
||||
//@param x Real component
|
||||
//@returns Angle in radians from -π to π
|
||||
atan2(series float y,series float x)=>
|
||||
if y==0.0 and x==0.0
|
||||
runtime.error("atan2: y and x cannot both be zero")
|
||||
float ay=math.abs(y)
|
||||
float ax=math.abs(x)
|
||||
float angle=0.0
|
||||
if ax>ay
|
||||
angle:=math.atan(ay/ax)
|
||||
else
|
||||
angle:=(math.pi/2.0)-math.atan(ax/ay)
|
||||
if x<0.0
|
||||
angle:=math.pi-angle
|
||||
if y<0.0
|
||||
angle:=-angle
|
||||
angle
|
||||
|
||||
//@function Measures dominant cycle period using Ehlers homodyne discriminator
|
||||
//@param source Price input series
|
||||
//@param minPeriod Minimum dominant cycle length
|
||||
//@param maxPeriod Maximum dominant cycle length
|
||||
//@returns Smoothed dominant cycle estimate
|
||||
//@optimized Exponential warmup compensation for dominant cycle smoothing
|
||||
//@validation wolfram:"atan2(y,x)" external:"TradingView Homodyne Discriminator","Forex-Station Homodyne Discriminator","MQL5 Adaptive Lookback Homodyne","tindicators hd.cc"
|
||||
homod(series float source,simple float minPeriod,simple float maxPeriod)=>
|
||||
if minPeriod<=0
|
||||
runtime.error("Min period must be greater than 0")
|
||||
if maxPeriod<=minPeriod
|
||||
runtime.error("Max period must be greater than min period")
|
||||
var float smooth_price=0.0
|
||||
var float detrender=0.0
|
||||
var float i1=0.0
|
||||
var float q1=0.0
|
||||
var float ji=0.0
|
||||
var float jq=0.0
|
||||
var float i2=0.0
|
||||
var float q2=0.0
|
||||
var float re=0.0
|
||||
var float im=0.0
|
||||
var float period=15.0
|
||||
var float smooth_period=15.0
|
||||
var float warm_decay=1.0
|
||||
var bool warmup=true
|
||||
float price=nz(source)
|
||||
float bandwidth=0.075*smooth_period+0.54
|
||||
smooth_price:=(4.0*price+3.0*nz(price[1])+2.0*nz(price[2])+nz(price[3]))/10.0
|
||||
detrender:=(0.0962*smooth_price+0.5769*nz(smooth_price[2])-0.5769*nz(smooth_price[4])-0.0962*nz(smooth_price[6]))*bandwidth
|
||||
q1:=(0.0962*detrender+0.5769*nz(detrender[2])-0.5769*nz(detrender[4])-0.0962*nz(detrender[6]))*bandwidth
|
||||
i1:=nz(detrender[3])
|
||||
ji:=(0.0962*i1+0.5769*nz(i1[2])-0.5769*nz(i1[4])-0.0962*nz(i1[6]))*bandwidth
|
||||
jq:=(0.0962*q1+0.5769*nz(q1[2])-0.5769*nz(q1[4])-0.0962*nz(q1[6]))*bandwidth
|
||||
float i2_raw=i1-jq
|
||||
float q2_raw=q1+ji
|
||||
i2:=0.2*i2_raw+0.8*nz(i2[1])
|
||||
q2:=0.2*q2_raw+0.8*nz(q2[1])
|
||||
float re_raw=i2*nz(i2[1])+q2*nz(q2[1])
|
||||
float im_raw=i2*nz(q2[1])-q2*nz(i2[1])
|
||||
re:=0.2*re_raw+0.8*nz(re[1])
|
||||
im:=0.2*im_raw+0.8*nz(im[1])
|
||||
float magnitude=math.abs(re)+math.abs(im)
|
||||
if magnitude>1e-10
|
||||
float angle=atan2(im,re)
|
||||
if math.abs(angle)>1e-10
|
||||
float candidate=2.0*math.pi/angle
|
||||
float clamped=math.max(minPeriod,math.min(maxPeriod,math.abs(candidate)))
|
||||
period:=0.2*clamped+0.8*period
|
||||
float alpha=0.33
|
||||
smooth_period:=smooth_period+alpha*(period-smooth_period)
|
||||
float result=smooth_period
|
||||
if warmup
|
||||
warm_decay*=1.0-alpha
|
||||
float denom=1.0-warm_decay
|
||||
result:=denom>1e-10?result/denom:result
|
||||
warmup:=warm_decay>1e-10
|
||||
result
|
||||
|
||||
// ---------- Main loop ----------
|
||||
i_source=input.source(hlc3,"Source")
|
||||
i_minPeriod=input.float(6,"Min Period",minval=1,maxval=5000,step=0.5)
|
||||
i_maxPeriod=input.float(50,"Max Period",minval=2,maxval=5000,step=0.5)
|
||||
homodPeriod=homod(i_source,i_minPeriod,i_maxPeriod)
|
||||
plot(homodPeriod,"Dominant Cycle Period",color=color.yellow,linewidth=2)
|
||||
@@ -1,77 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Hilbert Transform Dominant Cycle Period (HT_DCPERIOD)", "HT_DCPERIOD", overlay=false)
|
||||
|
||||
//@function Numerically stable atan2 implementation for quadrant-aware angle calculation
|
||||
//@param y Y-coordinate (imaginary/quadrature component)
|
||||
//@param x X-coordinate (real/in-phase component)
|
||||
//@returns Angle in radians from -π to π
|
||||
atan2(series float y, series float x) =>
|
||||
if y == 0.0 and x == 0.0
|
||||
runtime.error("atan2: Both y and x cannot be zero")
|
||||
ay = math.abs(y)
|
||||
ax = math.abs(x)
|
||||
angle = 0.0
|
||||
if ax > ay
|
||||
angle := math.atan(ay / ax)
|
||||
else
|
||||
angle := (math.pi / 2.0) - math.atan(ax / ay)
|
||||
if x < 0.0
|
||||
angle := math.pi - angle
|
||||
if y < 0.0
|
||||
angle := -angle
|
||||
angle
|
||||
|
||||
//@function Calculates Hilbert Transform Dominant Cycle Period using Ehlers algorithm
|
||||
//@param source Series to analyze for dominant cycle
|
||||
//@returns Dominant cycle period in bars (typically 6-50)
|
||||
ht_dcperiod(series float source) =>
|
||||
var float smooth_price = 0.0
|
||||
var float detrender = 0.0
|
||||
var float i1 = 0.0
|
||||
var float q1 = 0.0
|
||||
var float ji = 0.0
|
||||
var float jq = 0.0
|
||||
var float i2 = 0.0
|
||||
var float q2 = 0.0
|
||||
var float re = 0.0
|
||||
var float im = 0.0
|
||||
var float period = 15.0
|
||||
var float smooth_period = 15.0
|
||||
float price = nz(source)
|
||||
float bandwidth = 0.075 * smooth_period + 0.54
|
||||
smooth_price := (4.0 * price + 3.0 * nz(price[1]) + 2.0 * nz(price[2]) + nz(price[3])) / 10.0
|
||||
detrender := (0.0962 * smooth_price + 0.5769 * nz(smooth_price[2]) - 0.5769 * nz(smooth_price[4]) - 0.0962 * nz(smooth_price[6])) * bandwidth
|
||||
q1 := (0.0962 * detrender + 0.5769 * nz(detrender[2]) - 0.5769 * nz(detrender[4]) - 0.0962 * nz(detrender[6])) * bandwidth
|
||||
i1 := nz(detrender[3])
|
||||
ji := (0.0962 * i1 + 0.5769 * nz(i1[2]) - 0.5769 * nz(i1[4]) - 0.0962 * nz(i1[6])) * bandwidth
|
||||
jq := (0.0962 * q1 + 0.5769 * nz(q1[2]) - 0.5769 * nz(q1[4]) - 0.0962 * nz(q1[6])) * bandwidth
|
||||
i2 := i1 - jq
|
||||
q2 := q1 + ji
|
||||
i2 := 0.2 * i2 + 0.8 * nz(i2[1])
|
||||
q2 := 0.2 * q2 + 0.8 * nz(q2[1])
|
||||
re := i2 * nz(i2[1]) + q2 * nz(q2[1])
|
||||
im := i2 * nz(q2[1]) - q2 * nz(i2[1])
|
||||
re := 0.2 * re + 0.8 * nz(re[1])
|
||||
im := 0.2 * im + 0.8 * nz(im[1])
|
||||
if im != 0.0 or re != 0.0
|
||||
float angle = atan2(im, re)
|
||||
if angle != 0.0
|
||||
period := 2.0 * math.pi / angle
|
||||
period := math.max(6.0, math.min(50.0, period))
|
||||
smooth_period := 0.33 * period + 0.67 * smooth_period
|
||||
smooth_period
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(hlc3, "Source")
|
||||
|
||||
// Calculation
|
||||
dcperiod = ht_dcperiod(i_source)
|
||||
|
||||
// Plot
|
||||
plot(dcperiod, "Dominant Cycle Period", color=color.yellow, linewidth=2)
|
||||
hline(15, "Short Cycle", color=color.new(color.gray, 70), linestyle=hline.style_dashed)
|
||||
hline(30, "Long Cycle", color=color.new(color.gray, 70), linestyle=hline.style_dashed)
|
||||
@@ -1,81 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Hilbert Transform Dominant Cycle Phase (HT_DCPHASE)", "HT_DCPHASE", overlay=false)
|
||||
|
||||
//@function Numerically stable atan2 implementation for quadrant-aware angle calculation
|
||||
//@param y Y-coordinate (imaginary/quadrature component)
|
||||
//@param x X-coordinate (real/in-phase component)
|
||||
//@returns Angle in radians from -π to π
|
||||
atan2(series float y, series float x) =>
|
||||
if y == 0.0 and x == 0.0
|
||||
runtime.error("atan2: Both y and x cannot be zero")
|
||||
ay = math.abs(y)
|
||||
ax = math.abs(x)
|
||||
angle = 0.0
|
||||
if ax > ay
|
||||
angle := math.atan(ay / ax)
|
||||
else
|
||||
angle := (math.pi / 2.0) - math.atan(ax / ay)
|
||||
if x < 0.0
|
||||
angle := math.pi - angle
|
||||
if y < 0.0
|
||||
angle := -angle
|
||||
angle
|
||||
|
||||
//@function Calculates Hilbert Transform Dominant Cycle Phase using Ehlers algorithm
|
||||
//@param source Series to analyze for dominant cycle phase
|
||||
//@returns Phase angle in radians (-π to π)
|
||||
ht_dcphase(series float source) =>
|
||||
var float smooth_price = 0.0
|
||||
var float detrender = 0.0
|
||||
var float i1 = 0.0
|
||||
var float q1 = 0.0
|
||||
var float ji = 0.0
|
||||
var float jq = 0.0
|
||||
var float i2 = 0.0
|
||||
var float q2 = 0.0
|
||||
var float re = 0.0
|
||||
var float im = 0.0
|
||||
var float period = 15.0
|
||||
var float smooth_period = 15.0
|
||||
var float phase = 0.0
|
||||
float price = nz(source)
|
||||
float bandwidth = 0.075 * smooth_period + 0.54
|
||||
smooth_price := (4.0 * price + 3.0 * nz(price[1]) + 2.0 * nz(price[2]) + nz(price[3])) / 10.0
|
||||
detrender := (0.0962 * smooth_price + 0.5769 * nz(smooth_price[2]) - 0.5769 * nz(smooth_price[4]) - 0.0962 * nz(smooth_price[6])) * bandwidth
|
||||
q1 := (0.0962 * detrender + 0.5769 * nz(detrender[2]) - 0.5769 * nz(detrender[4]) - 0.0962 * nz(detrender[6])) * bandwidth
|
||||
i1 := nz(detrender[3])
|
||||
ji := (0.0962 * i1 + 0.5769 * nz(i1[2]) - 0.5769 * nz(i1[4]) - 0.0962 * nz(i1[6])) * bandwidth
|
||||
jq := (0.0962 * q1 + 0.5769 * nz(q1[2]) - 0.5769 * nz(q1[4]) - 0.0962 * nz(q1[6])) * bandwidth
|
||||
i2 := i1 - jq
|
||||
q2 := q1 + ji
|
||||
i2 := 0.2 * i2 + 0.8 * nz(i2[1])
|
||||
q2 := 0.2 * q2 + 0.8 * nz(q2[1])
|
||||
re := i2 * nz(i2[1]) + q2 * nz(q2[1])
|
||||
im := i2 * nz(q2[1]) - q2 * nz(i2[1])
|
||||
re := 0.2 * re + 0.8 * nz(re[1])
|
||||
im := 0.2 * im + 0.8 * nz(im[1])
|
||||
if im != 0.0 or re != 0.0
|
||||
float angle = atan2(im, re)
|
||||
if angle != 0.0
|
||||
period := 2.0 * math.pi / angle
|
||||
period := math.max(6.0, math.min(50.0, period))
|
||||
smooth_period := 0.33 * period + 0.67 * smooth_period
|
||||
if i2 != 0.0 or q2 != 0.0
|
||||
phase := atan2(q2, i2)
|
||||
phase
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(hlc3, "Source")
|
||||
|
||||
// Calculation
|
||||
dcphase = ht_dcphase(i_source)
|
||||
|
||||
// Plot
|
||||
plot(dcphase, "Dominant Cycle Phase", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero Phase", color=color.gray, linestyle=hline.style_solid)
|
||||
hline(1.5708, "π/2", color=color.new(color.gray, 70), linestyle=hline.style_dashed)
|
||||
hline(-1.5708, "-π/2", color=color.new(color.gray, 70), linestyle=hline.style_dashed)
|
||||
@@ -1,77 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Hilbert Transform Phasor Components (HT_PHASOR)", "HT_PHASOR", overlay=false)
|
||||
|
||||
//@function Numerically stable atan2 implementation for quadrant-aware angle calculation
|
||||
//@param y Y-coordinate (imaginary/quadrature component)
|
||||
//@param x X-coordinate (real/in-phase component)
|
||||
//@returns Angle in radians from -π to π
|
||||
atan2(series float y, series float x) =>
|
||||
if y == 0.0 and x == 0.0
|
||||
runtime.error("atan2: Both y and x cannot be zero")
|
||||
ay = math.abs(y)
|
||||
ax = math.abs(x)
|
||||
angle = 0.0
|
||||
if ax > ay
|
||||
angle := math.atan(ay / ax)
|
||||
else
|
||||
angle := (math.pi / 2.0) - math.atan(ax / ay)
|
||||
if x < 0.0
|
||||
angle := math.pi - angle
|
||||
if y < 0.0
|
||||
angle := -angle
|
||||
angle
|
||||
|
||||
//@function Calculates Hilbert Transform Phasor Components (InPhase and Quadrature)
|
||||
//@param source Series to analyze for phasor components
|
||||
//@returns Tuple [inphase, quadrature] components
|
||||
ht_phasor(series float source) =>
|
||||
var float smooth_price = 0.0
|
||||
var float detrender = 0.0
|
||||
var float i1 = 0.0
|
||||
var float q1 = 0.0
|
||||
var float ji = 0.0
|
||||
var float jq = 0.0
|
||||
var float i2 = 0.0
|
||||
var float q2 = 0.0
|
||||
var float re = 0.0
|
||||
var float im = 0.0
|
||||
var float period = 15.0
|
||||
var float smooth_period = 15.0
|
||||
float price = nz(source)
|
||||
float bandwidth = 0.075 * smooth_period + 0.54
|
||||
smooth_price := (4.0 * price + 3.0 * nz(price[1]) + 2.0 * nz(price[2]) + nz(price[3])) / 10.0
|
||||
detrender := (0.0962 * smooth_price + 0.5769 * nz(smooth_price[2]) - 0.5769 * nz(smooth_price[4]) - 0.0962 * nz(smooth_price[6])) * bandwidth
|
||||
q1 := (0.0962 * detrender + 0.5769 * nz(detrender[2]) - 0.5769 * nz(detrender[4]) - 0.0962 * nz(detrender[6])) * bandwidth
|
||||
i1 := nz(detrender[3])
|
||||
ji := (0.0962 * i1 + 0.5769 * nz(i1[2]) - 0.5769 * nz(i1[4]) - 0.0962 * nz(i1[6])) * bandwidth
|
||||
jq := (0.0962 * q1 + 0.5769 * nz(q1[2]) - 0.5769 * nz(q1[4]) - 0.0962 * nz(q1[6])) * bandwidth
|
||||
i2 := i1 - jq
|
||||
q2 := q1 + ji
|
||||
i2 := 0.2 * i2 + 0.8 * nz(i2[1])
|
||||
q2 := 0.2 * q2 + 0.8 * nz(q2[1])
|
||||
re := i2 * nz(i2[1]) + q2 * nz(q2[1])
|
||||
im := i2 * nz(q2[1]) - q2 * nz(i2[1])
|
||||
re := 0.2 * re + 0.8 * nz(re[1])
|
||||
im := 0.2 * im + 0.8 * nz(im[1])
|
||||
if im != 0.0 or re != 0.0
|
||||
float angle = atan2(im, re)
|
||||
if angle != 0.0
|
||||
period := 2.0 * math.pi / angle
|
||||
period := math.max(6.0, math.min(50.0, period))
|
||||
smooth_period := 0.33 * period + 0.67 * smooth_period
|
||||
[i2, q2]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(hlc3, "Source")
|
||||
|
||||
// Calculation
|
||||
[inphase, quadrature] = ht_phasor(i_source)
|
||||
|
||||
// Plot
|
||||
plot(inphase, "InPhase", color=color.yellow, linewidth=2)
|
||||
plot(quadrature, "Quadrature", color=color.blue, linewidth=2)
|
||||
hline(0, "Zero", color=color.gray, linestyle=hline.style_solid)
|
||||
@@ -1,84 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Hilbert Transform SineWave (HT_SINE)", "HT_SINE", overlay=false)
|
||||
|
||||
//@function Numerically stable atan2 implementation for quadrant-aware angle calculation
|
||||
//@param y Y-coordinate (imaginary/quadrature component)
|
||||
//@param x X-coordinate (real/in-phase component)
|
||||
//@returns Angle in radians from -π to π
|
||||
atan2(series float y, series float x) =>
|
||||
if y == 0.0 and x == 0.0
|
||||
runtime.error("atan2: Both y and x cannot be zero")
|
||||
ay = math.abs(y)
|
||||
ax = math.abs(x)
|
||||
angle = 0.0
|
||||
if ax > ay
|
||||
angle := math.atan(ay / ax)
|
||||
else
|
||||
angle := (math.pi / 2.0) - math.atan(ax / ay)
|
||||
if x < 0.0
|
||||
angle := math.pi - angle
|
||||
if y < 0.0
|
||||
angle := -angle
|
||||
angle
|
||||
|
||||
//@function Calculates Hilbert Transform SineWave and LeadSine
|
||||
//@param source Series to analyze for dominant cycle
|
||||
//@returns Tuple [sine, leadsine] - sine wave and lead sine wave
|
||||
ht_sine(series float source) =>
|
||||
var float smooth_price = 0.0
|
||||
var float detrender = 0.0
|
||||
var float i1 = 0.0
|
||||
var float q1 = 0.0
|
||||
var float ji = 0.0
|
||||
var float jq = 0.0
|
||||
var float i2 = 0.0
|
||||
var float q2 = 0.0
|
||||
var float re = 0.0
|
||||
var float im = 0.0
|
||||
var float period = 15.0
|
||||
var float smooth_period = 15.0
|
||||
var float phase = 0.0
|
||||
var float sine = 0.0
|
||||
var float leadsine = 0.0
|
||||
float price = nz(source)
|
||||
float bandwidth = 0.075 * smooth_period + 0.54
|
||||
smooth_price := (4.0 * price + 3.0 * nz(price[1]) + 2.0 * nz(price[2]) + nz(price[3])) / 10.0
|
||||
detrender := (0.0962 * smooth_price + 0.5769 * nz(smooth_price[2]) - 0.5769 * nz(smooth_price[4]) - 0.0962 * nz(smooth_price[6])) * bandwidth
|
||||
q1 := (0.0962 * detrender + 0.5769 * nz(detrender[2]) - 0.5769 * nz(detrender[4]) - 0.0962 * nz(detrender[6])) * bandwidth
|
||||
i1 := nz(detrender[3])
|
||||
ji := (0.0962 * i1 + 0.5769 * nz(i1[2]) - 0.5769 * nz(i1[4]) - 0.0962 * nz(i1[6])) * bandwidth
|
||||
jq := (0.0962 * q1 + 0.5769 * nz(q1[2]) - 0.5769 * nz(q1[4]) - 0.0962 * nz(q1[6])) * bandwidth
|
||||
i2 := i1 - jq
|
||||
q2 := q1 + ji
|
||||
i2 := 0.2 * i2 + 0.8 * nz(i2[1])
|
||||
q2 := 0.2 * q2 + 0.8 * nz(q2[1])
|
||||
re := i2 * nz(i2[1]) + q2 * nz(q2[1])
|
||||
im := i2 * nz(q2[1]) - q2 * nz(i2[1])
|
||||
re := 0.2 * re + 0.8 * nz(re[1])
|
||||
im := 0.2 * im + 0.8 * nz(im[1])
|
||||
if im != 0.0 or re != 0.0
|
||||
float angle = atan2(im, re)
|
||||
if angle != 0.0
|
||||
period := 2.0 * math.pi / angle
|
||||
period := math.max(6.0, math.min(50.0, period))
|
||||
smooth_period := 0.33 * period + 0.67 * smooth_period
|
||||
if i2 != 0.0 or q2 != 0.0
|
||||
phase := atan2(q2, i2)
|
||||
sine := math.sin(phase)
|
||||
leadsine := math.sin(phase + math.pi / 4.0)
|
||||
[sine, leadsine]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(hlc3, "Source")
|
||||
|
||||
// Calculation
|
||||
[sine, leadsine] = ht_sine(i_source)
|
||||
|
||||
// Plot
|
||||
plot(sine, "Sine", color=color.yellow, linewidth=2)
|
||||
plot(leadsine, "LeadSine", color=color.blue, linewidth=2)
|
||||
hline(0, "Zero", color=color.gray, linestyle=hline.style_solid)
|
||||
@@ -1,51 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Jurik Volatility Bands (JBANDS)", "JBANDS", overlay=true)
|
||||
|
||||
//@function Calculates JBANDS using adaptive techniques to adjust width to market volatility
|
||||
//@param source Series to calculate Jvolty from
|
||||
//@param period Number of bars used in the calculation
|
||||
//@returns JBANDS volatility bands
|
||||
//@optimized Uses adaptive volatility weighting with O(1) complexity per bar
|
||||
jbands(series float source, simple int period) =>
|
||||
var simple float LEN1 = math.max((math.log(math.sqrt(0.5 * (period - 1))) / math.log(2.0)) + 2.0, 0.0)
|
||||
var simple float POW1 = math.max(LEN1 - 2.0, 0.5)
|
||||
var simple float LEN2 = math.sqrt(0.5 * (period - 1)) * LEN1
|
||||
var simple float AVG_VOLTY_ALPHA = 2.0 / (math.max(4.0 * period, 65.0) + 1.0)
|
||||
var simple float DIV = 1.0 / (10.0 + 10.0 * (math.min(math.max(period - 10, 0), 100) / 100.0))
|
||||
var float upperBand = nz(source)
|
||||
var float lowerBand = nz(source)
|
||||
var float vSum = 0.0
|
||||
var float avgVolty = 0.0
|
||||
if na(source)
|
||||
na
|
||||
else
|
||||
float del1 = (low + high) * 0.5 - upperBand
|
||||
float del2 = (low + high) * 0.5 - lowerBand
|
||||
float volty = math.max(math.abs(del1), math.abs(del2))
|
||||
float past_volty = na(volty[10]) ? 0.0 : volty[10]
|
||||
vSum := vSum + (volty - past_volty) * DIV
|
||||
avgVolty := na(avgVolty) ? vSum : avgVolty + AVG_VOLTY_ALPHA * (vSum - avgVolty)
|
||||
float rvolty = 1.0
|
||||
if avgVolty > 0.0
|
||||
rvolty := volty / avgVolty
|
||||
rvolty := math.min(math.max(rvolty, 1.0), math.pow(LEN1, 1.0 / POW1))
|
||||
float Kv = math.pow(LEN2 / (LEN2 + 1.0), math.sqrt(math.pow(rvolty, POW1)))
|
||||
upperBand := del1 > 0.0 ? high : high - Kv * del1
|
||||
lowerBand := del2 < 0.0 ? low : low - Kv * del2
|
||||
[upperBand, lowerBand]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(10, "Period", minval=1)
|
||||
i_source = input.source(close, "Source")
|
||||
|
||||
// Calculation
|
||||
[upperBand, lowerBand] = jbands(i_source, i_period)
|
||||
|
||||
// Plot
|
||||
p1 = plot(upperBand, "Upper", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lowerBand, "Lower", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,57 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Keltner Channel (KCHANNEL)", "KCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates Keltner Channel using EMA and ATR
|
||||
//@param source Series to calculate middle line from
|
||||
//@param length Lookback period for calculations
|
||||
//@param mult ATR multiplier for band width
|
||||
//@returns tuple with [middle, upper, lower] band values
|
||||
//@optimized Uses EMA with warmup and ATR with compensator, O(1) complexity per bar
|
||||
kchannel(series float source, simple int length, simple float mult) =>
|
||||
if length <= 0 or mult <= 0.0
|
||||
runtime.error("Length and multiplier must be greater than 0")
|
||||
var float alpha = 2.0 / (length + 1)
|
||||
var float sum = 0.0
|
||||
var float weight = 0.0
|
||||
float ema = na
|
||||
if na(sum)
|
||||
sum := source
|
||||
weight := 1.0
|
||||
sum := sum * (1.0 - alpha) + source * alpha
|
||||
weight := weight * (1.0 - alpha) + alpha
|
||||
ema := sum / weight
|
||||
var float prevClose = close
|
||||
float tr1 = high - low
|
||||
float tr2 = math.abs(high - prevClose)
|
||||
float tr3 = math.abs(low - prevClose)
|
||||
float trueRange = math.max(tr1, tr2, tr3)
|
||||
prevClose := close
|
||||
var float EPSILON = 1e-10
|
||||
var float raw_rma = 0.0
|
||||
var float e = 1.0
|
||||
float atrValue = na
|
||||
if not na(trueRange)
|
||||
float alpha_atr = 1.0 / float(length)
|
||||
raw_rma := (raw_rma * (length - 1) + trueRange) / length
|
||||
e := (1.0 - alpha_atr) * e
|
||||
atrValue := e > EPSILON ? raw_rma / (1.0 - e) : raw_rma
|
||||
float width = mult * nz(atrValue, 0.0)
|
||||
[ema, ema + width, ema - width]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_length = input.int(20, "Length", minval=1)
|
||||
i_mult = input.float(2.0, "ATR Multiplier", minval=0.001)
|
||||
|
||||
// Calculation
|
||||
[middle, upper, lower] = kchannel(i_source, i_length, i_mult)
|
||||
|
||||
// Plot
|
||||
plot(middle, "Middle", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lower, "Lower", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,56 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Lunar Phase (LUNAR)", "LUNAR", overlay=false)
|
||||
|
||||
//@function Calculates precise lunar phase using orbital mechanics
|
||||
//@param none Uses timestamp of open (start of the bar) for calculations
|
||||
//@returns float Lunar phase from 0.0 (new moon) through 1.0 (full moon)
|
||||
//@Includes orbital perturbation terms and epoch corrections
|
||||
lunar() =>
|
||||
jd = (time / 86400000.0) + 2440587.5
|
||||
T = (jd - 2451545.0) / 36525.0
|
||||
Lp = (218.3164477 + 481267.88123421 * T - 0.0015786 * T * T + T * T * T / 538841.0 - T * T * T * T / 65194000.0) % 360.0
|
||||
D = (297.8501921 + 445267.1114034 * T - 0.0018819 * T * T + T * T * T / 545868.0 - T * T * T * T / 113065000.0) % 360.0
|
||||
M = (357.5291092 + 35999.0502909 * T - 0.0001536 * T * T + T * T * T / 24490000.0) % 360.0
|
||||
Mp = (134.9633964 + 477198.8675055 * T + 0.0087414 * T * T + T * T * T / 69699.0 - T * T * T * T / 14712000.0) % 360.0
|
||||
F = (93.2720950 + 483202.0175233 * T - 0.0036539 * T * T - T * T * T / 3526000.0 + T * T * T * T / 863310000.0) % 360.0
|
||||
Lp_rad = Lp * math.pi / 180.0
|
||||
D_rad = D * math.pi / 180.0
|
||||
M_rad = M * math.pi / 180.0
|
||||
Mp_rad = Mp * math.pi / 180.0
|
||||
F_rad = F * math.pi / 180.0
|
||||
dL = 6288.016 * math.sin(Mp_rad) + 1274.242 * math.sin(2.0 * D_rad - Mp_rad) +
|
||||
658.314 * math.sin(2.0 * D_rad) + 214.818 * math.sin(2.0 * Mp_rad) +
|
||||
186.986 * math.sin(M_rad) + 109.154 * math.sin(2.0 * F_rad)
|
||||
L_moon = Lp + dL / 1000000.0
|
||||
M_sun = (357.5291092 + 35999.0502909 * T - 0.0001536 * T * T + T * T * T / 24490000.0) % 360.0
|
||||
L_sun = (280.46646 + 36000.76983 * T + 0.0003032 * T * T) % 360.0
|
||||
phase_angle = ((L_moon - L_sun) % 360.0) * math.pi / 180.0
|
||||
phase = (1.0 - math.cos(phase_angle)) / 2.0
|
||||
phase
|
||||
|
||||
// Calculation
|
||||
lunarPhase = lunar()
|
||||
|
||||
// Plot
|
||||
plot(lunarPhase, "Lunar Phase", color=color.yellow, linewidth=2)
|
||||
|
||||
// Calculate derivatives to find local maxima/minima and inflection points
|
||||
delta1 = lunarPhase - lunarPhase[1]
|
||||
|
||||
// New Moon detection (at the trough)
|
||||
newMoonCondition = lunarPhase < 0.1 and lunarPhase[1] < 0.1 and delta1 > 0 and delta1[1] < 0
|
||||
plotchar(newMoonCondition ? lunarPhase : na, "New Moon", "🌑", location.absolute, color.white, size = size.small)
|
||||
|
||||
// First Quarter detection (crossing 0.5 going up)
|
||||
firstQuarterCondition = lunarPhase[1] < 0.5 and lunarPhase >= 0.5 and delta1 > 0
|
||||
plotchar(firstQuarterCondition ? lunarPhase : na, "First Quarter", "🌓", location.absolute, color.white, size = size.small)
|
||||
|
||||
// Full Moon detection (at the peak)
|
||||
fullMoonCondition = lunarPhase > 0.9 and lunarPhase[1] > 0.9 and delta1 < 0 and delta1[1] > 0
|
||||
plotchar(fullMoonCondition ? lunarPhase : na, "Full Moon", "🌕", location.absolute, color.white, size = size.small)
|
||||
|
||||
// Last Quarter detection (crossing 0.5 going down)
|
||||
lastQuarterCondition = lunarPhase[1] > 0.5 and lunarPhase <= 0.5 and delta1 < 0
|
||||
plotchar(lastQuarterCondition ? lunarPhase : na, "Last Quarter", "🌗", location.absolute, color.white, size = size.small)
|
||||
@@ -1,70 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("MA Envelope (MAE)", "MAE", overlay=true)
|
||||
|
||||
//@function Calculates MA Envelope bands using a fixed percentage
|
||||
//@param source Series to calculate moving average from
|
||||
//@param length Lookback period for MA calculation
|
||||
//@param percentage Distance of bands from MA as percentage
|
||||
//@param ma_type Type of moving average (0:SMA, 1:EMA, 2:WMA)
|
||||
//@returns tuple with [middle, upper, lower] band values
|
||||
//@optimized SMA uses circular buffer O(1), EMA uses warmup O(1), WMA is O(n)
|
||||
mae(series float source, simple int length, simple float percentage, simple int ma_type = 1) =>
|
||||
if length <= 0 or percentage <= 0.0
|
||||
runtime.error("Length and percentage must be greater than 0")
|
||||
float middle = na
|
||||
if ma_type == 0
|
||||
var int head = 0
|
||||
var int count = 0
|
||||
var array<float> buffer = array.new_float(length, na)
|
||||
var float sum = 0.0
|
||||
float oldest = array.get(buffer, head)
|
||||
if not na(oldest)
|
||||
sum -= oldest
|
||||
count -= 1
|
||||
float current = nz(source)
|
||||
sum += current
|
||||
count += 1
|
||||
array.set(buffer, head, current)
|
||||
head := (head + 1) % length
|
||||
middle := sum / count
|
||||
else if ma_type == 1
|
||||
var float alpha = 2.0 / (length + 1)
|
||||
var float sum = 0.0
|
||||
var float weight = 0.0
|
||||
if na(sum)
|
||||
sum := source
|
||||
weight := 1.0
|
||||
sum := sum * (1.0 - alpha) + source * alpha
|
||||
weight := weight * (1.0 - alpha) + alpha
|
||||
middle := sum / weight
|
||||
else if ma_type == 2
|
||||
float norm = 0.0
|
||||
float sum = 0.0
|
||||
for i = 0 to length - 1
|
||||
float w = float((length - i) * length)
|
||||
norm += w
|
||||
sum += nz(source[i]) * w
|
||||
middle := sum / norm
|
||||
else
|
||||
runtime.error("MA type must be 0 (SMA), 1 (EMA), or 2 (WMA)")
|
||||
float dist = middle * percentage / 100.0
|
||||
[middle, middle + dist, middle - dist]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_length = input.int(20, "Length", minval=1)
|
||||
i_percentage = input.float(1.0, "Percentage", minval=0.001)
|
||||
i_ma_type = input.int(1, "MA Type", minval=0, maxval=2, tooltip="0:SMA, 1:EMA, 2:WMA")
|
||||
|
||||
// Calculation
|
||||
[middle, upper, lower] = mae(i_source, i_length, i_percentage, i_ma_type)
|
||||
|
||||
// Plot
|
||||
plot(middle, "Middle", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lower, "Lower", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,49 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Min-Max Channel (MMCHANNEL)", "MMCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates the Min-Max Channel efficiently using monotonic deques
|
||||
//@param hi Source series for the highest high calculation (usually high)
|
||||
//@param lo Source series for the lowest low calculation (usually low)
|
||||
//@param period Lookback period (period > 0)
|
||||
//@returns Tuple containing [highest_high, lowest_low]
|
||||
//@optimized Uses monotonic deque for O(1) amortized complexity per bar
|
||||
mmchannel(series float hi, series float lo, simple int period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be > 0")
|
||||
var float[] hbuf = array.new_float(period, na)
|
||||
var float[] lbuf = array.new_float(period, na)
|
||||
var int[] hq = array.new_int()
|
||||
var int[] lq = array.new_int()
|
||||
int idx = bar_index % period
|
||||
array.set(hbuf, idx, hi)
|
||||
array.set(lbuf, idx, lo)
|
||||
while array.size(hq) > 0 and array.get(hq, 0) <= bar_index - period
|
||||
array.shift(hq)
|
||||
while array.size(hq) > 0 and array.get(hbuf, array.get(hq, -1) % period) <= hi
|
||||
array.pop(hq)
|
||||
array.push(hq, bar_index)
|
||||
while array.size(lq) > 0 and array.get(lq, 0) <= bar_index - period
|
||||
array.shift(lq)
|
||||
while array.size(lq) > 0 and array.get(lbuf, array.get(lq, -1) % period) >= lo
|
||||
array.pop(lq)
|
||||
array.push(lq, bar_index)
|
||||
float highest = array.get(hbuf, array.get(hq, 0) % period)
|
||||
float lowest = array.get(lbuf, array.get(lq, 0) % period)
|
||||
[highest, lowest]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=1)
|
||||
i_high = input.source(high, "High Source")
|
||||
i_low = input.source(low, "Low Source")
|
||||
|
||||
// Calculation
|
||||
[highest, lowest] = mmchannel(i_high, i_low, i_period)
|
||||
|
||||
// Plot
|
||||
p1 = plot(highest, "Highest High", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lowest, "Lowest Low", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,64 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Price Channel (PCHANNEL)", "PCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates Price Channel
|
||||
//@param length_param Lookback period for determining the highest high and lowest low
|
||||
//@returns tuple [upperChannel, middleChannel, lowerChannel]
|
||||
//@optimized Uses monotonic deque for O(1) amortized complexity per bar
|
||||
pchannel(simple int length_param) =>
|
||||
if length_param <= 0
|
||||
runtime.error("Length must be greater than 0")
|
||||
var deque_hi = array.new_int(0)
|
||||
var src_buffer_hi = array.new_float(0, na)
|
||||
var int current_index_hi = 0
|
||||
var deque_lo = array.new_int(0)
|
||||
var src_buffer_lo = array.new_float(0, na)
|
||||
var int current_index_lo = 0
|
||||
if array.size(src_buffer_hi) != length_param
|
||||
src_buffer_hi := array.new_float(length_param, na)
|
||||
current_index_hi := 0
|
||||
array.clear(deque_hi)
|
||||
src_buffer_lo := array.new_float(length_param, na)
|
||||
current_index_lo := 0
|
||||
array.clear(deque_lo)
|
||||
float cv_hi = nz(high)
|
||||
array.set(src_buffer_hi, current_index_hi, cv_hi)
|
||||
float cv_lo = nz(low)
|
||||
array.set(src_buffer_lo, current_index_lo, cv_lo)
|
||||
while array.size(deque_hi) > 0 and array.get(deque_hi, 0) <= bar_index - length_param
|
||||
array.shift(deque_hi)
|
||||
while array.size(deque_lo) > 0 and array.get(deque_lo, 0) <= bar_index - length_param
|
||||
array.shift(deque_lo)
|
||||
while array.size(deque_hi) > 0
|
||||
if array.get(src_buffer_hi, array.get(deque_hi, array.size(deque_hi) - 1) % length_param) <= cv_hi
|
||||
array.pop(deque_hi)
|
||||
else
|
||||
break
|
||||
array.push(deque_hi, bar_index)
|
||||
while array.size(deque_lo) > 0
|
||||
if array.get(src_buffer_lo, array.get(deque_lo, array.size(deque_lo) - 1) % length_param) >= cv_lo
|
||||
array.pop(deque_lo)
|
||||
else
|
||||
break
|
||||
array.push(deque_lo, bar_index)
|
||||
float highestHigh = array.get(src_buffer_hi, array.get(deque_hi, 0) % length_param)
|
||||
current_index_hi := (current_index_hi + 1) % length_param
|
||||
float lowestLow = array.get(src_buffer_lo, array.get(deque_lo, 0) % length_param)
|
||||
current_index_lo := (current_index_lo + 1) % length_param
|
||||
[highestHigh, (highestHigh + lowestLow) / 2.0, lowestLow]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_length = input.int(20, "Length", minval=1)
|
||||
|
||||
// Calculation
|
||||
[upperCh, middleCh, lowerCh] = pchannel(i_length)
|
||||
|
||||
// Plot
|
||||
plot(middleCh, "Middle Channel", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upperCh, "Upper Channel", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lowerCh, "Lower Channel", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,118 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Hilbert Transform Phasor Components (HT_PHASOR)", shorttitle="HT_PHASOR", overlay=false)
|
||||
|
||||
//@function Calculates the Ehlers Phasor Angle, Derived Period, and Trend State.
|
||||
//@param src The source series to analyze.
|
||||
//@param period The fixed cycle period to correlate against. Default is 28.
|
||||
//@returns A tuple: `[float finalPhasorAngle, float derivedPeriod, int trendState]`.
|
||||
phasor(series float src, simple int period = 28) =>
|
||||
float sx_corr = 0.0
|
||||
float sy_cos_corr = 0.0
|
||||
float sxx_corr = 0.0
|
||||
float sxy_cos_corr = 0.0
|
||||
float syy_cos_corr = 0.0
|
||||
for i = 0 to period - 1
|
||||
float x_val = nz(src[i])
|
||||
float y_val_cos = math.cos(2 * math.pi * i / period)
|
||||
sx_corr += x_val
|
||||
sy_cos_corr += y_val_cos
|
||||
sxx_corr += x_val * x_val
|
||||
sxy_cos_corr += x_val * y_val_cos
|
||||
syy_cos_corr += y_val_cos * y_val_cos
|
||||
float real_part = 0.0
|
||||
float den_cos = (period * sxx_corr - sx_corr * sx_corr) * (period * syy_cos_corr - sy_cos_corr * sy_cos_corr)
|
||||
if den_cos > 0
|
||||
real_part := (period * sxy_cos_corr - sx_corr * sy_cos_corr) / math.sqrt(den_cos)
|
||||
sx_corr := 0.0
|
||||
sxx_corr := 0.0
|
||||
float sy_sin_corr = 0.0
|
||||
float sxy_sin_corr = 0.0
|
||||
float syy_sin_corr = 0.0
|
||||
for i = 0 to period - 1
|
||||
float x_val = nz(src[i])
|
||||
float y_val_sin = -math.sin(2 * math.pi * i / period) // Negative sine as per Ehlers
|
||||
sx_corr += x_val
|
||||
sxx_corr += x_val * x_val
|
||||
sy_sin_corr += y_val_sin
|
||||
sxy_sin_corr += x_val * y_val_sin
|
||||
syy_sin_corr += y_val_sin * y_val_sin
|
||||
float imag_part = 0.0
|
||||
float den_sin = (period * sxx_corr - sx_corr * sx_corr) * (period * syy_sin_corr - sy_sin_corr * sy_sin_corr)
|
||||
if den_sin > 0
|
||||
imag_part := (period * sxy_sin_corr - sx_corr * sy_sin_corr) / math.sqrt(den_sin)
|
||||
float current_raw_phase = 0.0
|
||||
if real_part != 0.0
|
||||
current_raw_phase := 90.0 - math.atan(imag_part / real_part) * 180.0 / math.pi
|
||||
if real_part < 0.0
|
||||
current_raw_phase -= 180.0
|
||||
else if imag_part != 0.0
|
||||
current_raw_phase := imag_part > 0.0 ? 0.0 : 180.0
|
||||
var float core_Phasor_unwrapped_state = na
|
||||
if not na(core_Phasor_unwrapped_state[1])
|
||||
float diff = current_raw_phase - core_Phasor_unwrapped_state[1]
|
||||
if diff > 180.0
|
||||
current_raw_phase -= 360.0
|
||||
else if diff < -180.0
|
||||
current_raw_phase += 360.0
|
||||
core_Phasor_unwrapped_state := na(core_Phasor_unwrapped_state[1]) ? current_raw_phase : core_Phasor_unwrapped_state[1] + (current_raw_phase - core_Phasor_unwrapped_state[1])
|
||||
float calculated_Phasor_val = core_Phasor_unwrapped_state
|
||||
var float final_Phasor_state = na
|
||||
if na(final_Phasor_state[1])
|
||||
final_Phasor_state := calculated_Phasor_val
|
||||
else
|
||||
if calculated_Phasor_val < final_Phasor_state[1] and ((calculated_Phasor_val > -135 and final_Phasor_state[1] < 135) or (calculated_Phasor_val < -90 and final_Phasor_state[1] < -90))
|
||||
final_Phasor_state := final_Phasor_state[1]
|
||||
else
|
||||
final_Phasor_state := calculated_Phasor_val
|
||||
var float derivedPeriod_calc_state = na
|
||||
float angle_Change_For_Period = final_Phasor_state - nz(final_Phasor_state[1], final_Phasor_state)
|
||||
if nz(angle_Change_For_Period) == 0 and not na(derivedPeriod_calc_state[1])
|
||||
if derivedPeriod_calc_state[1] != 0
|
||||
angle_Change_For_Period := 360.0 / derivedPeriod_calc_state[1]
|
||||
else
|
||||
angle_Change_For_Period := 0.0
|
||||
if nz(angle_Change_For_Period) <= 0 and not na(derivedPeriod_calc_state[1])
|
||||
if derivedPeriod_calc_state[1] != 0
|
||||
angle_Change_For_Period := 360.0 / derivedPeriod_calc_state[1]
|
||||
else
|
||||
angle_Change_For_Period := 0.0
|
||||
if nz(angle_Change_For_Period) != 0.0
|
||||
derivedPeriod_calc_state := 360.0 / angle_Change_For_Period
|
||||
else if not na(derivedPeriod_calc_state[1])
|
||||
derivedPeriod_calc_state := derivedPeriod_calc_state[1]
|
||||
else
|
||||
derivedPeriod_calc_state := 60.0
|
||||
derivedPeriod_calc_state := math.max(1.0, math.min(derivedPeriod_calc_state, 60.0))
|
||||
var int trendState_calc_state = 0
|
||||
float angle_Change_For_State = final_Phasor_state - nz(final_Phasor_state[1], final_Phasor_state)
|
||||
int currentTrendState_calc = 0
|
||||
if angle_Change_For_State <= 6.0
|
||||
if final_Phasor_state >= 90.0 or final_Phasor_state <= -90.0
|
||||
currentTrendState_calc := 1
|
||||
else if final_Phasor_state > -90.0 and final_Phasor_state < 90.0
|
||||
currentTrendState_calc := -1
|
||||
trendState_calc_state := currentTrendState_calc
|
||||
[final_Phasor_state, derivedPeriod_calc_state, trendState_calc_state]
|
||||
|
||||
// ---------- Inputs ----------
|
||||
i_period = input.int(28, "Period", minval=1, group="Phasor Settings")
|
||||
i_source = input.source(close, "Source", group="Phasor Settings")
|
||||
showDerivedPeriod = input.bool(false, "Show Derived Period", group="Optional Plots", inline="derived_period")
|
||||
showTrendState = input.bool(false, "Show Trend State Variable", group="Optional Plots", inline="trend_state")
|
||||
|
||||
// ---------- Calculations ----------
|
||||
// Call the main function to get all values
|
||||
[phasorAngle, derivedPeriodValue, trendStateValue] = phasor(i_source, i_period)
|
||||
|
||||
// ---------- Plotting Phasor Angle ----------
|
||||
plot(phasorAngle, "Phasor Angle", color=color.yellow, linewidth=2)
|
||||
|
||||
|
||||
// ---------- Optional Plots ----------
|
||||
// Plot for Derived Period
|
||||
plot(showDerivedPeriod ? derivedPeriodValue : na, "Derived Period", color=color.yellow, linewidth=2)
|
||||
|
||||
// Plot for Trend State
|
||||
plot(showTrendState ? trendStateValue : na, "Trend State", color=color.yellow, linewidth=2, style=plot.style_histogram)
|
||||
@@ -1,60 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Regression Channels (REGCHANNEL)", "REGCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates Regression Channels with parallel lines equidistant from a central linear regression line
|
||||
//@param period Lookback period for regression calculation (period > 1)
|
||||
//@param source Source series for regression calculation (usually close)
|
||||
//@param multiplier Distance multiplier for channel bands (multiplier > 0)
|
||||
//@returns Tuple containing [upper_band, regression_line, lower_band]
|
||||
//@optimized Uses linear regression with O(n) complexity per bar
|
||||
regchannel(simple int period, series float source = close, simple float multiplier = 2.0) =>
|
||||
if period <= 1
|
||||
runtime.error("Period must be > 1")
|
||||
if multiplier <= 0.0
|
||||
runtime.error("Multiplier must be > 0")
|
||||
float sumX = 0.0
|
||||
float sumY = 0.0
|
||||
float sumXY = 0.0
|
||||
float sumX2 = 0.0
|
||||
for i = 0 to period - 1
|
||||
float x = float(i)
|
||||
float y = source[period - 1 - i]
|
||||
sumX := sumX + x
|
||||
sumY := sumY + y
|
||||
sumXY := sumXY + x * y
|
||||
sumX2 := sumX2 + x * x
|
||||
float n = float(period)
|
||||
float slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX)
|
||||
float intercept = (sumY - slope * sumX) / n
|
||||
float currentX = float(period - 1)
|
||||
float regression = slope * currentX + intercept
|
||||
float sumResiduals2 = 0.0
|
||||
for i = 0 to period - 1
|
||||
float x = float(i)
|
||||
float y = source[period - 1 - i]
|
||||
float predicted = slope * x + intercept
|
||||
float residual = y - predicted
|
||||
sumResiduals2 := sumResiduals2 + residual * residual
|
||||
float stdDev = math.sqrt(sumResiduals2 / n)
|
||||
float upperBand = regression + multiplier * stdDev
|
||||
float lowerBand = regression - multiplier * stdDev
|
||||
[upperBand, regression, lowerBand]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=2)
|
||||
i_source = input.source(close, "Source")
|
||||
i_multiplier = input.float(2.0, "Standard Deviation Multiplier", minval=0.1, step=0.1)
|
||||
|
||||
// Calculation
|
||||
[upperBand, midLine, lowerBand] = regchannel(i_period, i_source, i_multiplier)
|
||||
|
||||
// Plot
|
||||
p1 = plot(upperBand, "Upper Band", color=color.yellow, linewidth=2)
|
||||
p2 = plot(midLine, "Regression Line", color=color.yellow, linewidth=2)
|
||||
p3 = plot(lowerBand, "Lower Band", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.red, 90), title="Upper Fill")
|
||||
fill(p2, p3, color=color.new(color.green, 90), title="Lower Fill")
|
||||
@@ -1,60 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Standard Deviation Channel (SDCHANNEL)", "SDCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates Standard Deviation Channel with lines N standard deviations above and below a linear regression line
|
||||
//@param period Lookback period for regression and standard deviation calculation (period > 1)
|
||||
//@param source Source series for analysis (usually close)
|
||||
//@param multiplier Standard deviation multiplier for channel distance (multiplier > 0)
|
||||
//@returns Tuple containing [upper_channel, regression_line, lower_channel]
|
||||
//@optimized Uses linear regression with O(n) complexity per bar
|
||||
sdchannel(simple int period, series float source = close, simple float multiplier = 2.0) =>
|
||||
if period <= 1
|
||||
runtime.error("Period must be > 1")
|
||||
if multiplier <= 0.0
|
||||
runtime.error("Multiplier must be > 0")
|
||||
float sumX = 0.0
|
||||
float sumY = 0.0
|
||||
float sumXY = 0.0
|
||||
float sumX2 = 0.0
|
||||
for i = 0 to period - 1
|
||||
float x = float(i)
|
||||
float y = source[period - 1 - i]
|
||||
sumX := sumX + x
|
||||
sumY := sumY + y
|
||||
sumXY := sumXY + x * y
|
||||
sumX2 := sumX2 + x * x
|
||||
float n = float(period)
|
||||
float slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX)
|
||||
float intercept = (sumY - slope * sumX) / n
|
||||
float currentX = float(period - 1)
|
||||
float regressionLine = slope * currentX + intercept
|
||||
float sumSquaredResiduals = 0.0
|
||||
for i = 0 to period - 1
|
||||
float x = float(i)
|
||||
float y = source[period - 1 - i]
|
||||
float predicted = slope * x + intercept
|
||||
float residual = y - predicted
|
||||
sumSquaredResiduals := sumSquaredResiduals + residual * residual
|
||||
float stdDev = math.sqrt(sumSquaredResiduals / n)
|
||||
float upperChannel = regressionLine + multiplier * stdDev
|
||||
float lowerChannel = regressionLine - multiplier * stdDev
|
||||
[upperChannel, regressionLine, lowerChannel]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(20, "Period", minval=2)
|
||||
i_source = input.source(close, "Source")
|
||||
i_multiplier = input.float(2.0, "Standard Deviation Multiplier", minval=0.1, step=0.1)
|
||||
|
||||
// Calculation
|
||||
[upperLine, midLine, lowerLine] = sdchannel(i_period, i_source, i_multiplier)
|
||||
|
||||
// Plot
|
||||
p1 = plot(upperLine, "Upper Channel", color=color.yellow, linewidth=2)
|
||||
p2 = plot(midLine, "Regression Line", color=color.yellow, linewidth=2)
|
||||
p3 = plot(lowerLine, "Lower Channel", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.red, 90), title="Upper Fill")
|
||||
fill(p2, p3, color=color.new(color.green, 90), title="Lower Fill")
|
||||
@@ -1,47 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers Sine Wave (SINE)", "SINE", overlay=false)
|
||||
|
||||
//@function Calculates Ehlers’ original Sine Wave using a two‑pole High‑Pass, a Super‑Smoother,
|
||||
// and a Hilbert‑transform FIR pair (In‑phase I / Quadrature Q).
|
||||
//@param src Series to calculate the Sine Wave from
|
||||
//@param hpLength High‑Pass filter length (detrending period)
|
||||
//@param ssfLength Super‑Smoother filter length (cycle smoothing period)
|
||||
//@returns single normalized sine‑wave value in [‑1 … +1]
|
||||
sine(series float src, simple int hpLength, simple int ssfLength) =>
|
||||
if hpLength <= 0 or ssfLength <= 0
|
||||
runtime.error("Periods must be > 0")
|
||||
float pi = 2 * math.asin(1)
|
||||
float angHP = 2 * pi / hpLength
|
||||
float aHP = (1 - math.sin(angHP)) / math.cos(angHP)
|
||||
var float hp = 0.0
|
||||
hp := 0.5 * (1 + aHP) * (src - nz(src[1])) + aHP * nz(hp[1])
|
||||
float angSSF = math.sqrt(2) * pi / ssfLength
|
||||
float aSSF = math.exp(-angSSF)
|
||||
float bSSF = 2 * aSSF * math.cos(angSSF)
|
||||
float c2 = bSSF
|
||||
float c3 = -aSSF * aSSF
|
||||
float c1 = 1 - c2 - c3
|
||||
var float filt = 0.0
|
||||
filt := c1 * (hp + nz(hp[1])) / 2 + c2 * nz(filt[1]) + c3 * nz(filt[2])
|
||||
float Q = 0.0962 * nz(filt[3]) + 0.5769 * nz(filt[1])
|
||||
- 0.5769 * nz(filt[5]) - 0.0962 * nz(filt[7])
|
||||
float I = filt
|
||||
float pwr = I*I + Q*Q
|
||||
float sineWave = pwr == 0 ? 0 : I / math.sqrt(pwr)
|
||||
math.min(1, math.max(-1, sineWave))
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_hpLength = input.int(40, "High‑Pass Filter Length", minval=1)
|
||||
i_ssfLength = input.int(10, "Super‑Smoother Filter Length", minval=1)
|
||||
|
||||
// Calculation
|
||||
sine_wave = sine(i_source, i_hpLength, i_ssfLength)
|
||||
|
||||
// Plot
|
||||
plot(sine_wave, "SINE", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero Line", color.gray, linestyle=hline.style_dashed)
|
||||
@@ -1,44 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Solar Cycle (SOLAR)", "SOLAR", overlay=false)
|
||||
|
||||
//@function Calculates precise solar cycle value using Sun's ecliptic longitude.
|
||||
//@param barTime int The timestamp of the bar (open time) in milliseconds.
|
||||
//@returns float Solar cycle value from -1.0 (winter solstice) through 0.0 (equinoxes) to +1.0 (summer solstice).
|
||||
//@optimized for performance and dirty data
|
||||
solar(int barTime) =>
|
||||
float jd = (barTime / 86400000.0) + 2440587.5
|
||||
float T = (jd - 2451545.0) / 36525.0
|
||||
float l0DegRaw = 280.46646 + 36000.76983 * T + 0.0003032 * T * T
|
||||
float l0Deg = (l0DegRaw % 360.0 + 360.0) % 360.0
|
||||
float mDegRaw = 357.52911 + 35999.05029 * T - 0.0001537 * T * T - 0.00000025 * T * T * T
|
||||
float mDeg = (mDegRaw % 360.0 + 360.0) % 360.0
|
||||
float mRad = mDeg * math.pi / 180.0
|
||||
float cDeg = (1.914602 - 0.004817 * T - 0.000014 * T * T) * math.sin(mRad) +
|
||||
(0.019993 - 0.000101 * T) * math.sin(2.0 * mRad) +
|
||||
0.000289 * math.sin(3.0 * mRad)
|
||||
float lambdaSunDegRaw = l0Deg + cDeg
|
||||
float lambdaSunDeg = (lambdaSunDegRaw % 360.0 + 360.0) % 360.0
|
||||
float lambdaSunRad = lambdaSunDeg * math.pi / 180.0
|
||||
float valueRaw = math.sin(lambdaSunRad)
|
||||
valueRaw
|
||||
|
||||
// ---------- Main loop ----------
|
||||
// Calculation
|
||||
float solarCycleValue = solar(time)
|
||||
float delta1 = solarCycleValue - solarCycleValue[1]
|
||||
|
||||
bool summerSolsticeCondition = solarCycleValue > 0.985 and solarCycleValue[1] > 0.985 and delta1 < 0 and delta1[1] > 0
|
||||
bool vernalEquinoxCondition = solarCycleValue[1] < 0.0 and solarCycleValue >= 0.0 and delta1 > 0
|
||||
bool winterSolsticeCondition = solarCycleValue < -0.985 and solarCycleValue[1] < -0.985 and delta1 > 0 and delta1[1] < 0
|
||||
bool autumnalEquinoxCondition = solarCycleValue[1] > 0.0 and solarCycleValue <= 0.0 and delta1 < 0
|
||||
|
||||
// Plot
|
||||
plot(solarCycleValue, "Solar Cycle", color=color.yellow, linewidth=2)
|
||||
|
||||
// Plotchars
|
||||
plotchar(summerSolsticeCondition ? solarCycleValue : na, "Peak Summer", "•", location.absolute, color.new(color.red,0), size = size.small)
|
||||
plotchar(vernalEquinoxCondition ? 0.0 : na, "Spring Rise", "•", location.absolute, color.new(color.yellow,0), size = size.small)
|
||||
plotchar(winterSolsticeCondition ? solarCycleValue : na, "Peak Winter", "•", location.absolute, color.new(color.blue,0), size = size.small)
|
||||
plotchar(autumnalEquinoxCondition ? 0.0 : na, "Autumn Fall", "•", location.absolute, color.new(color.yellow,0), size = size.small)
|
||||
@@ -1,63 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Ehlers SSF Detrended Synthetic Price (SSFDSP)", "SSF-DSP", overlay=false)
|
||||
|
||||
//@function Calculates SSF-based Detrended Synthetic Price using dual Super Smooth Filters
|
||||
//@param source Series to detrend
|
||||
//@param period Dominant cycle period for quarter/half-cycle SSF calculation
|
||||
//@returns Detrended synthetic price (difference between quarter-cycle and half-cycle SSFs)
|
||||
ssfdsp(series float source, simple int period) =>
|
||||
if period <= 0
|
||||
runtime.error("Period must be greater than 0")
|
||||
int fast_period = math.max(2, int(math.round(period / 4.0)))
|
||||
int slow_period = math.max(3, int(math.round(period / 2.0)))
|
||||
float SQRT2_PI = math.sqrt(2.0) * math.pi
|
||||
float arg_fast = SQRT2_PI / float(fast_period)
|
||||
float exp_fast = math.exp(-arg_fast)
|
||||
float c2_fast = 2.0 * exp_fast * math.cos(arg_fast)
|
||||
float c3_fast = -exp_fast * exp_fast
|
||||
float c1_fast = 1.0 - c2_fast - c3_fast
|
||||
float arg_slow = SQRT2_PI / float(slow_period)
|
||||
float exp_slow = math.exp(-arg_slow)
|
||||
float c2_slow = 2.0 * exp_slow * math.cos(arg_slow)
|
||||
float c3_slow = -exp_slow * exp_slow
|
||||
float c1_slow = 1.0 - c2_slow - c3_slow
|
||||
var float ssf_fast_1 = 0.0
|
||||
var float ssf_fast_2 = 0.0
|
||||
var int prev_fast_period = 0
|
||||
var float ssf_slow_1 = 0.0
|
||||
var float ssf_slow_2 = 0.0
|
||||
var int prev_slow_period = 0
|
||||
float current = nz(source)
|
||||
float src_1 = nz(source[1], current)
|
||||
float input = (current + src_1) * 0.5
|
||||
if prev_fast_period != fast_period
|
||||
ssf_fast_1 := input
|
||||
ssf_fast_2 := input
|
||||
prev_fast_period := fast_period
|
||||
if prev_slow_period != slow_period
|
||||
ssf_slow_1 := input
|
||||
ssf_slow_2 := input
|
||||
prev_slow_period := slow_period
|
||||
float ssf_fast = c1_fast * input + c2_fast * ssf_fast_1 + c3_fast * ssf_fast_2
|
||||
ssf_fast_2 := ssf_fast_1
|
||||
ssf_fast_1 := ssf_fast
|
||||
float ssf_slow = c1_slow * input + c2_slow * ssf_slow_1 + c3_slow * ssf_slow_2
|
||||
ssf_slow_2 := ssf_slow_1
|
||||
ssf_slow_1 := ssf_slow
|
||||
ssf_fast - ssf_slow
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(hlc3, "Source")
|
||||
i_period = input.int(40, "Dominant Cycle Period", minval=4, maxval=200,
|
||||
tooltip="Dominant cycle period. Quarter-cycle and half-cycle SSFs calculated from this value.")
|
||||
|
||||
// Calculation
|
||||
ssfdsp_val = ssfdsp(i_source, i_period)
|
||||
|
||||
// Plot
|
||||
plot(ssfdsp_val, "SSF-DSP", color=color.yellow, linewidth=2)
|
||||
hline(0, "Zero Line", color=color.gray, linestyle=hline.style_solid)
|
||||
@@ -1,69 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Stoller Average Range Channel (STARCHANNEL)", "STARCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates Stoller Average Range Channel using ATR for width and SMA for center
|
||||
//@param source Source series for the center line
|
||||
//@param length Period for ATR and SMA calculations
|
||||
//@param multiplier ATR multiplier for band width
|
||||
//@returns tuple with [middle, upper, lower] band values
|
||||
//@optimized Uses circular buffer for SMA and ATR with compensator, O(1) complexity
|
||||
starchannel(series float source, simple int length, simple float multiplier) =>
|
||||
if length <= 0 or multiplier <= 0.0
|
||||
runtime.error("Length and multiplier must be greater than 0")
|
||||
var float prevClose = close
|
||||
float tr1 = high - low
|
||||
float tr2 = math.abs(high - prevClose)
|
||||
float tr3 = math.abs(low - prevClose)
|
||||
float trueRange = math.max(tr1, tr2, tr3)
|
||||
prevClose := close
|
||||
var int p = math.max(1, length)
|
||||
var int head = 0
|
||||
var int count = 0
|
||||
var array<float> bufferSource = array.new_float(p, na)
|
||||
var array<float> bufferTR = array.new_float(p, na)
|
||||
var float sumSource = 0.0
|
||||
var float sumTR = 0.0
|
||||
float oldestSource = array.get(bufferSource, head)
|
||||
float oldestTR = array.get(bufferTR, head)
|
||||
if not na(oldestSource)
|
||||
sumSource -= oldestSource
|
||||
sumTR -= oldestTR
|
||||
count -= 1
|
||||
float currentSource = nz(source)
|
||||
float currentTR = nz(trueRange)
|
||||
sumSource += currentSource
|
||||
sumTR += currentTR
|
||||
count += 1
|
||||
array.set(bufferSource, head, currentSource)
|
||||
array.set(bufferTR, head, currentTR)
|
||||
head := (head + 1) % p
|
||||
var float EPSILON = 1e-10
|
||||
var float raw_rma = 0.0
|
||||
var float e = 1.0
|
||||
float atrValue = na
|
||||
if not na(trueRange)
|
||||
float alpha = 1.0 / float(length)
|
||||
raw_rma := (raw_rma * (length - 1) + trueRange) / length
|
||||
e := (1.0 - alpha) * e
|
||||
atrValue := e > EPSILON ? raw_rma / (1.0 - e) : raw_rma
|
||||
float middleBand = nz(sumSource / count, source)
|
||||
float width = nz(atrValue * multiplier)
|
||||
[middleBand, middleBand + width, middleBand - width]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_length = input.int(20, "Length", minval=1)
|
||||
i_mult = input.float(2.0, "ATR Multiplier", minval=0.001)
|
||||
|
||||
// Calculation
|
||||
[middle, upper, lower] = starchannel(i_source, i_length, i_mult)
|
||||
|
||||
// Plot
|
||||
plot(middle, "Middle", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upper, "Upper", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lower, "Lower", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,67 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Super Trend Bands (STBANDS)", "STBANDS", overlay=true)
|
||||
|
||||
//@function Calculates Super Trend Bands using ATR-based dynamic support/resistance
|
||||
//@param source Series to calculate bands from
|
||||
//@param period Lookback period for ATR calculation
|
||||
//@param multiplier ATR multiplier for band distance
|
||||
//@returns [upper_band, lower_band, trend] Super Trend band values and trend direction
|
||||
//@optimized for performance and dirty data
|
||||
stbands(series float source, simple int period, simple float multiplier) =>
|
||||
if period <= 0 or multiplier <= 0.0
|
||||
runtime.error("Period and multiplier must be greater than 0")
|
||||
var int p = math.max(1, period), var int head = 0, var int count = 0
|
||||
var array<float> tr_buffer = array.new_float(p, na)
|
||||
var float tr_sum = 0.0
|
||||
float high_val = nz(high), float low_val = nz(low), float close_val = nz(source)
|
||||
float prev_close = nz(source[1], source)
|
||||
float tr = math.max(high_val - low_val, math.max(math.abs(high_val - prev_close), math.abs(low_val - prev_close)))
|
||||
float oldest_tr = array.get(tr_buffer, head)
|
||||
if not na(oldest_tr)
|
||||
tr_sum -= oldest_tr
|
||||
count -= 1
|
||||
tr_sum += tr
|
||||
count += 1
|
||||
array.set(tr_buffer, head, tr)
|
||||
head := (head + 1) % p
|
||||
float atr = count > 0 ? tr_sum / count : tr
|
||||
float hl2_val = (high_val + low_val) / 2
|
||||
float basic_upper = hl2_val + multiplier * atr
|
||||
float basic_lower = hl2_val - multiplier * atr
|
||||
var float final_upper = na, var float final_lower = na
|
||||
var int trend = 1
|
||||
|
||||
// Initialize on first bar
|
||||
if bar_index == 0
|
||||
final_upper := basic_upper
|
||||
final_lower := basic_lower
|
||||
trend := 1
|
||||
else
|
||||
prev_upper = nz(final_upper[1], basic_upper)
|
||||
prev_lower = nz(final_lower[1], basic_lower)
|
||||
prev_close_val = nz(source[1], source)
|
||||
|
||||
final_upper := basic_upper < prev_upper or prev_close_val > prev_upper ? basic_upper : prev_upper
|
||||
final_lower := basic_lower > prev_lower or prev_close_val < prev_lower ? basic_lower : prev_lower
|
||||
|
||||
prev_trend = nz(trend[1], 1)
|
||||
trend := close_val <= prev_lower ? 1 : close_val >= prev_upper ? -1 : prev_trend
|
||||
|
||||
[final_upper, final_lower, trend]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_period = input.int(10, "ATR Period", minval=1)
|
||||
i_source = input.source(close, "Source")
|
||||
i_multiplier = input.float(3.0, "ATR Multiplier", minval=0.001)
|
||||
|
||||
// Calculation
|
||||
[upper_band, lower_band, trend] = stbands(i_source, i_period, i_multiplier)
|
||||
|
||||
// Plot
|
||||
p_upper = plot(upper_band, "Upper Band", color=color.yellow, linewidth=2)
|
||||
p_lower = plot(lower_band, "Lower Band", color=color.yellow, linewidth=2)
|
||||
fill(p_upper, p_lower, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,73 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("Schaff Trend Cycle (STC)", "STC", overlay=false)
|
||||
|
||||
ema(series float source,simple int period=0,simple float alpha=0)=>
|
||||
if alpha<=0 and period<=0
|
||||
runtime.error("Alpha or period must be provided")
|
||||
float a=alpha>0?alpha:2.0/math.max(period,1)
|
||||
var float raw_ema=na
|
||||
var float ema=na
|
||||
var float e=1.0
|
||||
var bool warmup=true
|
||||
if not na(source)
|
||||
if na(raw_ema)
|
||||
raw_ema:=0
|
||||
ema:=source
|
||||
else
|
||||
raw_ema:=a*(source-raw_ema)+raw_ema
|
||||
if warmup
|
||||
e*=(1-a)
|
||||
float c=1.0/(1.0-e)
|
||||
ema:=c*raw_ema
|
||||
if e<=1e-10
|
||||
warmup:=false
|
||||
else
|
||||
ema:=raw_ema
|
||||
ema
|
||||
|
||||
//@function Calculates the Schaff Trend Cycle (STC) indicator
|
||||
//@param source Input price series
|
||||
//@param cycleLength Main cycle length parameter for lookback periods
|
||||
//@param fastLength Period for fast EMA calculation
|
||||
//@param slowLength Period for slow EMA calculation
|
||||
//@param smoothingType Type of smoothing (0:none, 1:ema, 2:sigmoid, 3:digital)
|
||||
//@returns Smoothed STC value
|
||||
stc(series float source, simple int cycleLength, simple int fastLength, simple int slowLength, simple int smoothingType = 2) =>
|
||||
float fast_ema = ema(source, fastLength)
|
||||
float slow_ema = ema(source, slowLength)
|
||||
float macdLine = fast_ema - slow_ema
|
||||
|
||||
h1 = ta.highest(macdLine, cycleLength)
|
||||
l1 = ta.lowest(macdLine, cycleLength)
|
||||
float stoch1_raw = (h1 - l1) > 0 ? 100 * (macdLine - l1) / (h1 - l1) : 0
|
||||
float stoch1 = ema(stoch1_raw, 3)
|
||||
h2 = ta.highest(stoch1, cycleLength)
|
||||
l2 = ta.lowest(stoch1, cycleLength)
|
||||
float stoch2 = (h2 - l2) > 0 ? 100 * (stoch1 - l2) / (h2 - l2) : 0
|
||||
|
||||
|
||||
float stcValue = stoch2
|
||||
if smoothingType == 1
|
||||
stcValue := ema(stoch2, 3)
|
||||
else if smoothingType == 2
|
||||
stcValue := 100 / (1 + math.exp(-0.1 * (stcValue - 50)))
|
||||
else if smoothingType == 3
|
||||
stcValue := stcValue > 75 ? 100 : stcValue < 25 ? 0 : stcValue[1]
|
||||
stcValue
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, title="Source")
|
||||
i_cycleLength = input.int(12, title="Cycle Length", minval=2)
|
||||
i_fastLength = input.int(26, title="Fast Length", minval=2)
|
||||
i_slowLength = input.int(50, title="Slow Length", minval=2)
|
||||
i_smoothingType = input.int(2, title="Smoothing", minval=0, maxval=3, tooltip="0: none, 1:ema, 2:sigmoid, 3:digital")
|
||||
|
||||
// Calculation
|
||||
stcValue = stc(i_source, i_cycleLength, i_fastLength, i_slowLength, i_smoothingType)
|
||||
|
||||
// Plot
|
||||
plot(stcValue, "STC", color=color.yellow, linewidth=2)
|
||||
@@ -1,54 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
// Ultimate Bands logic based on work by John F. Ehlers (c) 2024
|
||||
indicator("Ehlers Ultimate Bands (UBANDS)", "UBANDS", overlay=true)
|
||||
|
||||
//@function Calculates Ultimate Bands
|
||||
//@param src Source series for the bands
|
||||
//@param length Lookback period for the Ehlers Ultrasmooth Filter and RMS
|
||||
//@param mult RMS multiplier for band width
|
||||
//@returns tuple [upperBand, middleBand, lowerBand]
|
||||
ubands(series float src, simple int length, simple float mult) =>
|
||||
var float usf_state = na, var float c1=0.0, var float c2=0.0, var float c3=0.0, var int prev_len = 0
|
||||
if prev_len != length or na(c1)
|
||||
float arg = (math.sqrt(2)*math.pi)/math.max(1,float(length))
|
||||
float exp_arg = math.exp(-arg)
|
||||
c2 := 2*exp_arg*math.cos(arg)
|
||||
c3 := -exp_arg*exp_arg
|
||||
c1 := (1+c2-c3)/4.0
|
||||
prev_len := length
|
||||
usf_state := na
|
||||
float s = nz(src,src[1]), s1 = nz(src[1],s), s2 = nz(src[2],s1)
|
||||
float current_usf = na(usf_state) or na(usf_state[1]) or na(usf_state[2]) ? s :
|
||||
(1-c1)*s + (2*c1-c2)*s1 - (c1+c3)*s2 + c2*nz(usf_state[1],s1) + c3*nz(usf_state[2],s2)
|
||||
usf_state := current_usf
|
||||
float smooth = usf_state
|
||||
series float residuals = src - smooth
|
||||
float rms = 0.0
|
||||
if length > 0
|
||||
float sumSq_r = 0.0, int count_r = 0
|
||||
for i = 0 to length - 1
|
||||
float val_r = residuals[i]
|
||||
if not na(val_r)
|
||||
sumSq_r += val_r*val_r
|
||||
count_r += 1
|
||||
if count_r > 0
|
||||
rms := math.sqrt(sumSq_r/count_r)
|
||||
[smooth + mult*rms, smooth, smooth - mult*rms]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source")
|
||||
i_length = input.int(20, "Length", minval=1, tooltip="Lookback period for smoothing and RMS calculation.")
|
||||
i_mult = input.float(1.0, "RMS Multiplier", minval=0.01, tooltip="Band width as multiple of RMS value.")
|
||||
|
||||
// Calculation
|
||||
[upperBand, middleBand, lowerBand] = ubands(i_source, i_length, i_mult)
|
||||
|
||||
// Plot
|
||||
plot(middleBand, "Middle Band", color=color.yellow, linewidth=2)
|
||||
p1 = plot(upperBand, "Upper Band", color=color.yellow, linewidth=2)
|
||||
p2 = plot(lowerBand, "Lower Band", color=color.yellow, linewidth=2)
|
||||
fill(p1, p2, color=color.new(color.blue, 90), title="Band Fill")
|
||||
@@ -1,68 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
// Ultimate Channel logic based on work by John F. Ehlers (c) 2024
|
||||
indicator("Ehlers Ultimate Channel (UCHANNEL)", "UCHANNEL", overlay=true)
|
||||
|
||||
//@function Calculates Ultimate Channel
|
||||
//@param src Source series for the centerline (typically close)
|
||||
//@param high_src Source series for high prices
|
||||
//@param low_src Source series for low prices
|
||||
//@param strLength Lookback period for smoothing the True Range
|
||||
//@param length Lookback period for smoothing the centerline
|
||||
//@param numSTRs Multiplier for the Smoothed True Range to define channel width
|
||||
//@returns tuple [upperChannel, middleChannel, lowerChannel]
|
||||
uchannel(series float src_centerline, series float high_src, series float low_src, simple int strLength_param, simple int length_param, simple float numSTRs_param) =>
|
||||
if strLength_param <= 0 or length_param <= 0 or numSTRs_param <= 0
|
||||
runtime.error("strLength, numSTR and length must be greater than 0")
|
||||
var float usf_s = na, var float usf_c = na
|
||||
var float c1_s = 0.0, var float c2_s = 0.0, var float c3_s = 0.0
|
||||
var float c1_c = 0.0, var float c2_c = 0.0, var float c3_c = 0.0
|
||||
var int prev_sLen = 0, var int prev_cLen = 0
|
||||
float th = math.max(high_src, nz(src_centerline[1], high_src))
|
||||
float tl = math.min(low_src, nz(src_centerline[1], low_src))
|
||||
series float tr_s = th - tl // true_range_series
|
||||
if prev_sLen != strLength_param or na(c1_s)
|
||||
float arg = (math.sqrt(2)*math.pi)/float(strLength_param)
|
||||
float exp_arg = math.exp(-arg)
|
||||
c2_s := 2*exp_arg*math.cos(arg)
|
||||
c3_s := -exp_arg*exp_arg
|
||||
c1_s := (1+c2_s-c3_s)/4.0
|
||||
prev_sLen := strLength_param
|
||||
usf_s := na
|
||||
float s_str = nz(tr_s, tr_s[1]), s1_str = nz(tr_s[1], s_str), s2_str = nz(tr_s[2], s1_str)
|
||||
float cur_usf_s = na(usf_s) or na(usf_s[1]) or na(usf_s[2]) ? s_str : (1-c1_s)*s_str + (2*c1_s-c2_s)*s1_str - (c1_s+c3_s)*s2_str + c2_s*nz(usf_s[1],s1_str) + c3_s*nz(usf_s[2],s2_str)
|
||||
usf_s := cur_usf_s
|
||||
float str_val = usf_s
|
||||
if prev_cLen != length_param or na(c1_c)
|
||||
float arg = (math.sqrt(2)*math.pi)/float(length_param)
|
||||
float exp_arg = math.exp(-arg)
|
||||
c2_c := 2*exp_arg*math.cos(arg)
|
||||
c3_c := -exp_arg*exp_arg
|
||||
c1_c := (1+c2_c-c3_c)/4.0
|
||||
prev_cLen := length_param
|
||||
usf_c := na
|
||||
float s_cen = nz(src_centerline,src_centerline[1]), s1_cen = nz(src_centerline[1],s_cen), s2_cen = nz(src_centerline[2],s1_cen)
|
||||
float cur_usf_c = na(usf_c) or na(usf_c[1]) or na(usf_c[2]) ? s_cen : (1-c1_c)*s_cen + (2*c1_c-c2_c)*s1_cen - (c1_c+c3_c)*s2_cen + c2_c*nz(usf_c[1],s1_cen) + c3_c*nz(usf_c[2],s2_cen)
|
||||
usf_c := cur_usf_c
|
||||
float centerline = usf_c
|
||||
[centerline + numSTRs_param*str_val, centerline, centerline - numSTRs_param*str_val]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(close, "Source for Centerline")
|
||||
i_high = input.source(high, "Source for High")
|
||||
i_low = input.source(low, "Source for Low")
|
||||
i_strLength = input.int(20, "STR Length", minval=1, tooltip="Lookback period for smoothing the True Range.")
|
||||
i_length = input.int(20, "Centerline Length", minval=1, tooltip="Lookback period for smoothing the centerline (close price).")
|
||||
i_numSTRs = input.float(1.0, "STR Multiplier", minval=0.01, tooltip="Number of Smoothed True Ranges for channel width.")
|
||||
|
||||
// Calculation
|
||||
[upperCh, middleCh, lowerCh] = uchannel(i_source, i_high, i_low, i_strLength, i_length, i_numSTRs)
|
||||
|
||||
// Plot
|
||||
plot(middleCh, "Middle Channel", color=color.yellow, linewidth=2)
|
||||
p_upper = plot(upperCh, "Upper Channel", color=color.yellow, linewidth=2)
|
||||
p_lower = plot(lowerCh, "Lower Channel", color=color.yellow, linewidth=2)
|
||||
fill(p_upper, p_lower, color=color.new(color.blue, 90), title="Channel Fill")
|
||||
@@ -1,92 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("VWAP Bands (VWAPBANDS)", "VWAPBANDS", overlay=true)
|
||||
|
||||
//@function Calculates VWAP Bands with standard deviation bands
|
||||
//@param src Source price series (typically hlc3)
|
||||
//@param vol Volume series
|
||||
//@param reset_condition Condition to reset VWAP calculation
|
||||
//@param multiplier Standard deviation multiplier for bands
|
||||
//@returns [vwap_value, upper_band1, lower_band1, upper_band2, lower_band2, stdev] VWAP and band values
|
||||
//@optimized for performance and dirty data
|
||||
vwapbands(series float src, series float vol, series bool reset_condition, series float multiplier) =>
|
||||
var float sum_pv = 0.0, var float sum_vol = 0.0, var float sum_pv2 = 0.0, var int count = 0
|
||||
float current_price = nz(src), float current_vol = nz(vol, 0.0)
|
||||
if reset_condition
|
||||
if current_vol > 0.0
|
||||
sum_pv := current_price * current_vol
|
||||
sum_vol := current_vol
|
||||
sum_pv2 := current_price * current_price * current_vol
|
||||
count := 1
|
||||
else
|
||||
sum_pv := 0.0, sum_vol := 0.0, sum_pv2 := 0.0, count := 0
|
||||
else
|
||||
if current_vol > 0.0
|
||||
sum_pv += current_price * current_vol
|
||||
sum_vol += current_vol
|
||||
sum_pv2 += current_price * current_price * current_vol
|
||||
count += 1
|
||||
float vwap_val = sum_vol > 0.0 ? sum_pv / sum_vol : src
|
||||
float variance = 0.0
|
||||
if sum_vol > 0.0 and count > 1
|
||||
mean_p2 = sum_pv2 / sum_vol
|
||||
vwap_squared = vwap_val * vwap_val
|
||||
variance := math.max(0.0, mean_p2 - vwap_squared)
|
||||
float stdev = math.sqrt(variance)
|
||||
float upper1 = vwap_val + multiplier * stdev
|
||||
float lower1 = vwap_val - multiplier * stdev
|
||||
float upper2 = vwap_val + 2.0 * multiplier * stdev
|
||||
float lower2 = vwap_val - 2.0 * multiplier * stdev
|
||||
[vwap_val, upper1, lower1, upper2, lower2, stdev]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(hlc3, "Source")
|
||||
i_session_type = input.string("1D", "Session Reset", options=["1m", "2m", "3m", "5m", "10m", "15m", "30m", "45m", "1H", "2H", "3H", "4H", "1D", "1W", "1M", "3M", "6M", "12M", "Never"])
|
||||
i_multiplier = input.float(1.0, "Standard Deviation Multiplier", minval=0.1, step=0.1)
|
||||
i_show_bands2 = input.bool(true, "Show 2nd Standard Deviation Bands")
|
||||
|
||||
// Calculate reset condition
|
||||
reset_condition = switch i_session_type
|
||||
"1m" => ta.change(time("1")) != 0
|
||||
"2m" => ta.change(time("2")) != 0
|
||||
"3m" => ta.change(time("3")) != 0
|
||||
"5m" => ta.change(time("5")) != 0
|
||||
"10m" => ta.change(time("10")) != 0
|
||||
"15m" => ta.change(time("15")) != 0
|
||||
"30m" => ta.change(time("30")) != 0
|
||||
"45m" => ta.change(time("45")) != 0
|
||||
"1H" => ta.change(time("60")) != 0
|
||||
"2H" => ta.change(time("120")) != 0
|
||||
"3H" => ta.change(time("180")) != 0
|
||||
"4H" => ta.change(time("240")) != 0
|
||||
"1D" => ta.change(time("1D")) != 0
|
||||
"1W" => ta.change(time("1W")) != 0
|
||||
"1M" => ta.change(time("1M")) != 0
|
||||
"3M" => ta.change(time("3M")) != 0
|
||||
"6M" => ta.change(time("6M")) != 0
|
||||
"12M" => ta.change(time("12M")) != 0
|
||||
"Never" => bar_index == 0
|
||||
=> false
|
||||
|
||||
// Calculation
|
||||
[vwap_value, upper_band1, lower_band1, upper_band2, lower_band2, stdev] = vwapbands(i_source, volume, reset_condition, i_multiplier)
|
||||
|
||||
// Colors
|
||||
vwap_color = color.yellow
|
||||
band1_color = color.blue
|
||||
band2_color = color.purple
|
||||
fill_color1 = color.blue
|
||||
fill_color2 = color.purple
|
||||
|
||||
// Plot
|
||||
p_vwap = plot(vwap_value, "VWAP", color=color.yellow, linewidth=2)
|
||||
p_upper1 = plot(upper_band1, "Upper Band 1σ", color=color.yellow, linewidth=2)
|
||||
p_lower1 = plot(lower_band1, "Lower Band 1σ", color=color.yellow, linewidth=2)
|
||||
p_upper2 = plot(i_show_bands2 ? upper_band2 : na, "Upper Band 2σ", color=color.yellow, linewidth=2)
|
||||
p_lower2 = plot(i_show_bands2 ? lower_band2 : na, "Lower Band 2σ", color=color.yellow, linewidth=2)
|
||||
fill(p_upper1, p_lower1, color=color.new(color.blue, 90), title="1σ Band Fill")
|
||||
fill(p_upper2, p_upper1, color=i_show_bands2 ? color.new(color.purple, 90) : na, title="Upper 2σ Fill")
|
||||
fill(p_lower1, p_lower2, color=i_show_bands2 ? color.new(color.purple, 90) : na, title="Lower 2σ Fill")
|
||||
@@ -1,80 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
// © mihakralj
|
||||
//@version=6
|
||||
indicator("VWAP with Standard Deviation Bands", "VWAPSD", overlay=true)
|
||||
|
||||
//@function Calculate VWAP with Standard Deviation Bands
|
||||
//@param src Source price series (typically hlc3)
|
||||
//@param vol Volume series
|
||||
//@param reset_condition Condition to reset VWAP calculation
|
||||
//@param num_devs Number of standard deviations for bands
|
||||
//@returns [vwap, upper_band, lower_band]
|
||||
vwapsd(series float src, series float vol, series bool reset_condition, simple float num_devs) =>
|
||||
if num_devs <= 0
|
||||
runtime.error("Number of deviations must be greater than 0")
|
||||
if num_devs > 5
|
||||
runtime.error("Number of deviations exceeds maximum of 5")
|
||||
|
||||
var float sum_pv = 0.0, var float sum_vol = 0.0, var float sum_pv2 = 0.0
|
||||
float current_price = nz(src), float current_vol = nz(vol, 0.0)
|
||||
|
||||
if reset_condition
|
||||
sum_pv := current_vol > 0.0 ? current_price * current_vol : 0.0
|
||||
sum_vol := current_vol > 0.0 ? current_vol : 0.0
|
||||
sum_pv2 := current_vol > 0.0 ? current_price * current_price * current_vol : 0.0
|
||||
else
|
||||
if current_vol > 0.0
|
||||
sum_pv += current_price * current_vol
|
||||
sum_vol += current_vol
|
||||
sum_pv2 += current_price * current_price * current_vol
|
||||
|
||||
float vwap = sum_vol > 0.0 ? sum_pv / sum_vol : src
|
||||
float variance = sum_vol > 0.0 ? (sum_pv2 / sum_vol) - math.pow(vwap, 2) : 0.0
|
||||
float stddev = math.sqrt(math.max(0.0, variance))
|
||||
|
||||
float upper = vwap + (num_devs * stddev)
|
||||
float lower = vwap - (num_devs * stddev)
|
||||
|
||||
[vwap, upper, lower]
|
||||
|
||||
// ---------- Main loop ----------
|
||||
|
||||
// Inputs
|
||||
i_source = input.source(hlc3, "Source")
|
||||
i_session_type = input.string("1D", "Session Reset", options=["1m", "2m", "3m", "5m", "10m", "15m", "30m", "45m", "1H", "2H", "3H", "4H", "1D", "1W", "1M", "3M", "6M", "12M", "Never"])
|
||||
i_num_devs = input.float(2.0, "Standard Deviations", minval=0.1, maxval=5.0, step=0.1, tooltip="Number of standard deviations for bands")
|
||||
|
||||
// Calculate reset condition
|
||||
reset_condition = switch i_session_type
|
||||
"1m" => ta.change(time("1")) != 0
|
||||
"2m" => ta.change(time("2")) != 0
|
||||
"3m" => ta.change(time("3")) != 0
|
||||
"5m" => ta.change(time("5")) != 0
|
||||
"10m" => ta.change(time("10")) != 0
|
||||
"15m" => ta.change(time("15")) != 0
|
||||
"30m" => ta.change(time("30")) != 0
|
||||
"45m" => ta.change(time("45")) != 0
|
||||
"1H" => ta.change(time("60")) != 0
|
||||
"2H" => ta.change(time("120")) != 0
|
||||
"3H" => ta.change(time("180")) != 0
|
||||
"4H" => ta.change(time("240")) != 0
|
||||
"1D" => ta.change(time("1D")) != 0
|
||||
"1W" => ta.change(time("1W")) != 0
|
||||
"1M" => ta.change(time("1M")) != 0
|
||||
"3M" => ta.change(time("3M")) != 0
|
||||
"6M" => ta.change(time("6M")) != 0
|
||||
"12M" => ta.change(time("12M")) != 0
|
||||
"Never" => bar_index == 0
|
||||
=> false
|
||||
|
||||
// Calculation
|
||||
[vwap, upper, lower] = vwapsd(i_source, volume, reset_condition, i_num_devs)
|
||||
|
||||
// Plot
|
||||
plot(vwap, "VWAP", color=color.yellow, linewidth=2)
|
||||
plot(upper, "Upper Band", color=color.red, linewidth=1, style=plot.style_line)
|
||||
plot(lower, "Lower Band", color=color.green, linewidth=1, style=plot.style_line)
|
||||
|
||||
// Fill between bands
|
||||
fill_color = color.new(color.gray, 90)
|
||||
fill(plot(upper), plot(lower), color=fill_color, title="Band Fill")
|
||||
Reference in New Issue
Block a user