mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-02 03:37:42 +00:00
35a6702b06
Deep review of all indicator categories verified .md headers against .cs WarmupPeriod, parameters, inputs, and outputs. Fixes include warmup corrections, parameter documentation, output type accuracy, and Pine Script alignment.
127 lines
4.3 KiB
Plaintext
127 lines
4.3 KiB
Plaintext
// Licensed under the Apache License, Version 2.0
|
|
// © mihakralj
|
|
//@version=6
|
|
indicator("KDJ Oscillator (KDJ)", "KDJ", overlay=false)
|
|
|
|
//@function Calculates KDJ (K, D, J) lines - enhanced Stochastic Oscillator
|
|
//@param high Series of high prices
|
|
//@param low Series of low prices
|
|
//@param close Series of close prices
|
|
//@param length Lookback period for highest/lowest calculation
|
|
//@param signal Smoothing period for K and D lines
|
|
//@returns Tuple [K line, D line, J line]
|
|
//@optimized Uses Wilder's RMA smoothing and deque min/max for highest/lowest, O(n) amortized
|
|
kdj(series float high, series float low, series float close, simple int length, simple int signal) =>
|
|
if length <= 0 or signal <= 0
|
|
runtime.error("Length and signal must be greater than 0")
|
|
if length > 500
|
|
runtime.error("Length exceeds maximum of 500")
|
|
|
|
float alpha = 1.0 / signal
|
|
float beta = 1.0 - alpha
|
|
var bool warmupK = true
|
|
var bool warmupD = true
|
|
var float eK = 1.0
|
|
var float eD = 1.0
|
|
var float k = 0.0
|
|
var float d = 0.0
|
|
var float resultK = 50.0
|
|
var float resultD = 50.0
|
|
|
|
var int lastLength = 0
|
|
var array<int> maxDeque = array.new_int(0)
|
|
var array<int> minDeque = array.new_int(0)
|
|
var array<float> highBuffer = array.new_float(0)
|
|
var array<float> lowBuffer = array.new_float(0)
|
|
|
|
if length != lastLength
|
|
lastLength := length
|
|
maxDeque := array.new_int(0)
|
|
minDeque := array.new_int(0)
|
|
highBuffer := array.new_float(length, na)
|
|
lowBuffer := array.new_float(length, na)
|
|
warmupK := true
|
|
warmupD := true
|
|
eK := 1.0
|
|
eD := 1.0
|
|
k := 0.0
|
|
d := 0.0
|
|
resultK := 50.0
|
|
resultD := 50.0
|
|
|
|
if not na(high) and not na(low) and not na(close)
|
|
int currentBar = bar_index
|
|
int slot = currentBar % length
|
|
|
|
array.set(highBuffer, slot, high)
|
|
array.set(lowBuffer, slot, low)
|
|
|
|
while array.size(maxDeque) > 0 and array.get(maxDeque, 0) <= currentBar - length
|
|
array.shift(maxDeque)
|
|
|
|
while array.size(minDeque) > 0 and array.get(minDeque, 0) <= currentBar - length
|
|
array.shift(minDeque)
|
|
|
|
while array.size(maxDeque) > 0 and array.get(highBuffer, array.get(maxDeque, array.size(maxDeque) - 1) % length) <= high
|
|
array.pop(maxDeque)
|
|
|
|
while array.size(minDeque) > 0 and array.get(lowBuffer, array.get(minDeque, array.size(minDeque) - 1) % length) >= low
|
|
array.pop(minDeque)
|
|
|
|
array.push(maxDeque, currentBar)
|
|
array.push(minDeque, currentBar)
|
|
|
|
float highest = high
|
|
float lowest = low
|
|
if array.size(maxDeque) > 0
|
|
highest := array.get(highBuffer, array.get(maxDeque, 0) % length)
|
|
if array.size(minDeque) > 0
|
|
lowest := array.get(lowBuffer, array.get(minDeque, 0) % length)
|
|
|
|
float price_range = highest - lowest
|
|
|
|
float rsv = price_range > 0 ? 100.0 * (close - lowest) / price_range : 50.0
|
|
|
|
k := alpha * rsv + beta * k
|
|
d := alpha * k + beta * d
|
|
|
|
if warmupK
|
|
eK *= beta
|
|
float cK = 1.0 / (1.0 - eK)
|
|
resultK := math.max(0.0, math.min(100.0, cK * k))
|
|
warmupK := eK > 1e-10
|
|
else
|
|
resultK := math.max(0.0, math.min(100.0, k))
|
|
|
|
if warmupD
|
|
eD *= beta
|
|
float cD = 1.0 / (1.0 - eD)
|
|
resultD := math.max(0.0, math.min(100.0, cD * d))
|
|
warmupD := eD > 1e-10
|
|
else
|
|
resultD := math.max(0.0, math.min(100.0, d))
|
|
|
|
float j = 3.0 * resultK - 2.0 * resultD
|
|
|
|
[resultK, resultD, j]
|
|
|
|
// ---------- Main loop ----------
|
|
|
|
// Inputs
|
|
i_length = input.int(9, "Length", minval=1, maxval=500)
|
|
i_signal = input.int(3, "Signal", minval=1, maxval=50)
|
|
|
|
// Calculation
|
|
[k, d, j] = kdj(high, low, close, i_length, i_signal)
|
|
|
|
// Plot
|
|
plot(k, "K", color=color.blue, linewidth=2)
|
|
plot(d, "D", color=color.red, linewidth=2)
|
|
plot(j, "J", color=color.yellow, linewidth=2)
|
|
|
|
hline(80, "Overbought K", color=color.red, linestyle=hline.style_dotted)
|
|
hline(70, "Overbought D", color=color.orange, linestyle=hline.style_dotted)
|
|
hline(50, "Midline", color=color.gray, linestyle=hline.style_solid)
|
|
hline(30, "Oversold D", color=color.lime, linestyle=hline.style_dotted)
|
|
hline(20, "Oversold K", color=color.green, linestyle=hline.style_dotted)
|