OPEN-SOURCE SCRIPT
엘리어트 파동 3의 3파

//version=5
indicator("Elliott Wave — 3 of 3 Detector (v5)", overlay=true, timeframe_gaps=true)
// === Inputs ===
pivotLen = input.int(5, "Pivot length (bars each side)", minval=2)
subPivotLen = input.int(3, "Sub-pivot length (internal waves)", minval=2)
retrMin = input.float(0.382, "Wave 2 retracement min", minval=0.1, maxval=0.9, step=0.001)
retrMax = input.float(0.786, "Wave 2 retracement max", minval=0.1, maxval=0.95, step=0.001)
subRetrMin = input.float(0.382, "Subwave 2 retracement min", minval=0.1, maxval=0.9, step=0.001)
subRetrMax = input.float(0.618, "Subwave 2 retracement max", minval=0.1, maxval=0.95, step=0.001)
useRSI = input.bool(true, "Require RSI > threshold")
rsiPeriod = input.int(14, "RSI period", minval=2)
rsiThresh = input.float(55.0, "RSI threshold", minval=40, maxval=70)
useMACD = input.bool(true, "Require MACD histogram > 0")
fastLen = input.int(12, "MACD fast EMA", minval=2)
slowLen = input.int(26, "MACD slow EMA", minval=2)
signalLen = input.int(9, "MACD signal", minval=1)
useEMAFilter = input.bool(true, "Require trend filter (EMA50 > EMA200)")
useVolFilter = input.bool(false, "Require volume > volume MA")
volMaLen = input.int(20, "Volume MA length", minval=1)
plotZigs = input.bool(true, "Plot swing lines")
showLabels = input.bool(true, "Show wave labels")
// === Helpers ===
rsi = ta.rsi(close, rsiPeriod)
macdLine = ta.ema(close, fastLen) - ta.ema(close, slowLen)
signal = ta.ema(macdLine, signalLen)
hist = macdLine - signal
ema50 = ta.ema(close, 50)
ema200 = ta.ema(close, 200)
volMa = ta.sma(volume, volMaLen)
bool momentumOK = (not useRSI or rsi > rsiThresh) and (not useMACD or hist > 0)
bool trendOK = (not useEMAFilter or ema50 > ema200)
bool volOK = (not useVolFilter or volume > volMa)
// === Swing detection (basic pivot-based zigzag) ===
var float[] pivPrice = array.new_float()
var int[] pivIndex = array.new_int()
var int[] pivType = array.new_int() // +1 = swing high, -1 = swing low
ph = ta.pivothigh(high, pivotLen, pivotLen)
pl = ta.pivotlow(low, pivotLen, pivotLen)
f_addPivot(_price, _index, _type) =>
// avoid duplicate consecutive types
if array.size(pivType) > 0 and array.get(pivType, array.size(pivType)-1) == _type
// replace last pivot if same type and is more extreme in same direction
lastPrice = array.get(pivPrice, array.size(pivPrice)-1)
replace = (_type == 1 and _price > lastPrice) or (_type == -1 and _price < lastPrice)
if replace
array.set(pivPrice, array.size(pivPrice)-1, _price)
array.set(pivIndex, array.size(pivIndex)-1, _index)
else
array.push(pivPrice, _price)
array.push(pivIndex, _index)
array.push(pivType, _type)
if not na(ph)
f_addPivot(ph, bar_index - pivotLen, 1)
if not na(pl)
f_addPivot(pl, bar_index - pivotLen, -1)
// Keep arrays from growing unbounded
maxKeep = 200
if array.size(pivPrice) > maxKeep
for _i = 0 to array.size(pivPrice) - maxKeep - 1
array.shift(pivPrice)
array.shift(pivIndex)
array.shift(pivType)
// === Utility to get recent Nth pivot from the end ===
getPrice(n) => array.get(pivPrice, array.size(pivPrice) - 1 - n)
getIndex(n) => array.get(pivIndex, array.size(pivIndex) - 1 - n)
getType(n) => array.get(pivType, array.size(pivType) - 1 - n)
haveAtLeast(n) => array.size(pivPrice) >= n
// === Identify bullish 1-2 structure ===
bool has12 = false
float L0 = na, H1 = na, L2 = na
int L0i = na, H1i = na, L2i = na
if haveAtLeast(3)
// We want last three alternating pivots to be: low (L0), high (H1), low (L2)
t0 = getType(2)
t1 = getType(1)
t2 = getType(0)
if t0 == -1 and t1 == 1 and t2 == -1
L0 := getPrice(2)
H1 := getPrice(1)
L2 := getPrice(0)
L0i := getIndex(2)
H1i := getIndex(1)
L2i := getIndex(0)
// Retracement check for wave 2
wave1 = H1 - L0
retr = wave1 != 0 ? (H1 - L2) / wave1 : na
has12 := wave1 > 0 and not na(retr) and retr >= retrMin and retr <= retrMax
// === Wave 3 start (break above H1) ===
bool wave3Start = has12 and close > H1 and bar_index > H1i
// === Internal subwave 1-2 inside wave 3 using tighter sub-pivots ===
// We'll compute a separate list of sub-pivots since L2 to now, using smaller length
var float[] spPrice = array.new_float()
var int[] spIndex = array.new_int()
var int[] spType = array.new_int()
sph = ta.pivothigh(high, subPivotLen, subPivotLen)
spl = ta.pivotlow(low, subPivotLen, subPivotLen)
f_addSubPivot(_price, _index, _type) =>
if array.size(spType) > 0 and array.get(spType, array.size(spType)-1) == _type
lastPrice = array.get(spPrice, array.size(spPrice)-1)
replace = (_type == 1 and _price > lastPrice) or (_type == -1 and _price < lastPrice)
if replace
array.set(spPrice, array.size(spPrice)-1, _price)
array.set(spIndex, array.size(spIndex)-1, _index)
else
array.push(spPrice, _price)
array.push(spIndex, _index)
array.push(spType, _type)
// Reset sub-pivots after L2 to only track internal wave structure of current move
var int lastL2iTracked = na
if not na(L2i)
if na(lastL2iTracked) or L2i != lastL2iTracked
array.clear(spPrice)
array.clear(spIndex)
array.clear(spType)
lastL2iTracked := L2i
if not na(sph) and (na(L2i) or (bar_index - subPivotLen) >= L2i)
f_addSubPivot(sph, bar_index - subPivotLen, 1)
if not na(spl) and (na(L2i) or (bar_index - subPivotLen) >= L2i)
f_addSubPivot(spl, bar_index - subPivotLen, -1)
// Find subwave 1-2 (sL0 -> sH1 -> sL2) after L2
bool hasSub12 = false
float sL0 = na, sH1 = na, sL2 = na
int sL0i = na, sH1i = na, sL2i = na
if array.size(spPrice) >= 3
st0 = array.get(spType, array.size(spType) - 3)
st1 = array.get(spType, array.size(spType) - 2)
st2 = array.get(spType, array.size(spType) - 1)
if st0 == -1 and st1 == 1 and st2 == -1
sL0 := array.get(spPrice, array.size(spPrice) - 3)
sH1 := array.get(spPrice, array.size(spPrice) - 2)
sL2 := array.get(spPrice, array.size(spPrice) - 1)
sL0i := array.get(spIndex, array.size(spIndex) - 3)
sH1i := array.get(spIndex, array.size(spIndex) - 2)
sL2i := array.get(spIndex, array.size(spIndex) - 1)
// Sub retracement check
sw1 = sH1 - sL0
sRetr = sw1 != 0 ? (sH1 - sL2) / sw1 : na
hasSub12 := sw1 > 0 and not na(sRetr) and sRetr >= subRetrMin and sRetr <= subRetrMax
// === 3 of 3 trigger ===
bool threeOfThree = wave3Start and hasSub12 and close > sH1 and bar_index > sH1i and momentumOK and trendOK and volOK
// === Plotting ===
color upCol = color.new(color.lime, 0)
color dnCol = color.new(color.red, 0)
color neutCol = color.new(color.gray, 60)
plotshape(threeOfThree, title="3 of 3 Buy Signal", style=shape.triangleup, location=location.belowbar, size=size.large, color=upCol, text="3/3")
// Mark the 1-2 and sub 1-2 swings
if showLabels and has12
label.new(H1i, H1, text="1", style=label.style_label_up, color=color.new(color.green, 0))
label.new(L2i, L2, text="2", style=label.style_label_down, color=color.new(color.orange, 0))
if showLabels and hasSub12
label.new(sH1i, sH1, text="(i)", style=label.style_label_up, color=color.new(color.green, 60))
label.new(sL2i, sL2, text="(ii)", style=label.style_label_down, color=color.new(color.orange, 60))
// Draw swing lines
f_plotZigzag() =>
if array.size(pivPrice) >= 2 and plotZigs
for i = 1 to 1
x1 = array.get(pivIndex, array.size(pivIndex) - 2)
y1 = array.get(pivPrice, array.size(pivPrice) - 2)
x2 = array.get(pivIndex, array.size(pivIndex) - 1)
y2 = array.get(pivPrice, array.size(pivPrice) - 1)
line.new(x1, y1, x2, y2, extend=extend.none, color=neutCol, width=1)
f_plotZigzag()
// Visual filters
plot(ema50, color=color.new(color.teal, 0), title="EMA50")
plot(ema200, color=color.new(color.blue, 0), title="EMA200")
bgcolor(threeOfThree ? color.new(color.lime, 85) : na)
// === Alerts ===
alertcondition(threeOfThree, title="3 of 3 long", message="3 of 3 long setup on {{ticker}} {{interval}} — price has broken above subwave (i) high with momentum.")
// === Notes ===
// Heuristic detector:
// 1) Find L0-H1-L2 with L2 retracing 38.2%–78.6% of wave 1.
// 2) Confirm wave 3 start when price breaks above H1.
// 3) Inside wave 3, find sub L0-H1-L2 using tighter sub-pivots with 38.2%–61.8% retracement.
// 4) Trigger 3 of 3 when price breaks above subwave (i) high with momentum/trend/volume filters.
// Tune pivotLen/subPivotLen and thresholds to match your instrument and timeframe.
indicator("Elliott Wave — 3 of 3 Detector (v5)", overlay=true, timeframe_gaps=true)
// === Inputs ===
pivotLen = input.int(5, "Pivot length (bars each side)", minval=2)
subPivotLen = input.int(3, "Sub-pivot length (internal waves)", minval=2)
retrMin = input.float(0.382, "Wave 2 retracement min", minval=0.1, maxval=0.9, step=0.001)
retrMax = input.float(0.786, "Wave 2 retracement max", minval=0.1, maxval=0.95, step=0.001)
subRetrMin = input.float(0.382, "Subwave 2 retracement min", minval=0.1, maxval=0.9, step=0.001)
subRetrMax = input.float(0.618, "Subwave 2 retracement max", minval=0.1, maxval=0.95, step=0.001)
useRSI = input.bool(true, "Require RSI > threshold")
rsiPeriod = input.int(14, "RSI period", minval=2)
rsiThresh = input.float(55.0, "RSI threshold", minval=40, maxval=70)
useMACD = input.bool(true, "Require MACD histogram > 0")
fastLen = input.int(12, "MACD fast EMA", minval=2)
slowLen = input.int(26, "MACD slow EMA", minval=2)
signalLen = input.int(9, "MACD signal", minval=1)
useEMAFilter = input.bool(true, "Require trend filter (EMA50 > EMA200)")
useVolFilter = input.bool(false, "Require volume > volume MA")
volMaLen = input.int(20, "Volume MA length", minval=1)
plotZigs = input.bool(true, "Plot swing lines")
showLabels = input.bool(true, "Show wave labels")
// === Helpers ===
rsi = ta.rsi(close, rsiPeriod)
macdLine = ta.ema(close, fastLen) - ta.ema(close, slowLen)
signal = ta.ema(macdLine, signalLen)
hist = macdLine - signal
ema50 = ta.ema(close, 50)
ema200 = ta.ema(close, 200)
volMa = ta.sma(volume, volMaLen)
bool momentumOK = (not useRSI or rsi > rsiThresh) and (not useMACD or hist > 0)
bool trendOK = (not useEMAFilter or ema50 > ema200)
bool volOK = (not useVolFilter or volume > volMa)
// === Swing detection (basic pivot-based zigzag) ===
var float[] pivPrice = array.new_float()
var int[] pivIndex = array.new_int()
var int[] pivType = array.new_int() // +1 = swing high, -1 = swing low
ph = ta.pivothigh(high, pivotLen, pivotLen)
pl = ta.pivotlow(low, pivotLen, pivotLen)
f_addPivot(_price, _index, _type) =>
// avoid duplicate consecutive types
if array.size(pivType) > 0 and array.get(pivType, array.size(pivType)-1) == _type
// replace last pivot if same type and is more extreme in same direction
lastPrice = array.get(pivPrice, array.size(pivPrice)-1)
replace = (_type == 1 and _price > lastPrice) or (_type == -1 and _price < lastPrice)
if replace
array.set(pivPrice, array.size(pivPrice)-1, _price)
array.set(pivIndex, array.size(pivIndex)-1, _index)
else
array.push(pivPrice, _price)
array.push(pivIndex, _index)
array.push(pivType, _type)
if not na(ph)
f_addPivot(ph, bar_index - pivotLen, 1)
if not na(pl)
f_addPivot(pl, bar_index - pivotLen, -1)
// Keep arrays from growing unbounded
maxKeep = 200
if array.size(pivPrice) > maxKeep
for _i = 0 to array.size(pivPrice) - maxKeep - 1
array.shift(pivPrice)
array.shift(pivIndex)
array.shift(pivType)
// === Utility to get recent Nth pivot from the end ===
getPrice(n) => array.get(pivPrice, array.size(pivPrice) - 1 - n)
getIndex(n) => array.get(pivIndex, array.size(pivIndex) - 1 - n)
getType(n) => array.get(pivType, array.size(pivType) - 1 - n)
haveAtLeast(n) => array.size(pivPrice) >= n
// === Identify bullish 1-2 structure ===
bool has12 = false
float L0 = na, H1 = na, L2 = na
int L0i = na, H1i = na, L2i = na
if haveAtLeast(3)
// We want last three alternating pivots to be: low (L0), high (H1), low (L2)
t0 = getType(2)
t1 = getType(1)
t2 = getType(0)
if t0 == -1 and t1 == 1 and t2 == -1
L0 := getPrice(2)
H1 := getPrice(1)
L2 := getPrice(0)
L0i := getIndex(2)
H1i := getIndex(1)
L2i := getIndex(0)
// Retracement check for wave 2
wave1 = H1 - L0
retr = wave1 != 0 ? (H1 - L2) / wave1 : na
has12 := wave1 > 0 and not na(retr) and retr >= retrMin and retr <= retrMax
// === Wave 3 start (break above H1) ===
bool wave3Start = has12 and close > H1 and bar_index > H1i
// === Internal subwave 1-2 inside wave 3 using tighter sub-pivots ===
// We'll compute a separate list of sub-pivots since L2 to now, using smaller length
var float[] spPrice = array.new_float()
var int[] spIndex = array.new_int()
var int[] spType = array.new_int()
sph = ta.pivothigh(high, subPivotLen, subPivotLen)
spl = ta.pivotlow(low, subPivotLen, subPivotLen)
f_addSubPivot(_price, _index, _type) =>
if array.size(spType) > 0 and array.get(spType, array.size(spType)-1) == _type
lastPrice = array.get(spPrice, array.size(spPrice)-1)
replace = (_type == 1 and _price > lastPrice) or (_type == -1 and _price < lastPrice)
if replace
array.set(spPrice, array.size(spPrice)-1, _price)
array.set(spIndex, array.size(spIndex)-1, _index)
else
array.push(spPrice, _price)
array.push(spIndex, _index)
array.push(spType, _type)
// Reset sub-pivots after L2 to only track internal wave structure of current move
var int lastL2iTracked = na
if not na(L2i)
if na(lastL2iTracked) or L2i != lastL2iTracked
array.clear(spPrice)
array.clear(spIndex)
array.clear(spType)
lastL2iTracked := L2i
if not na(sph) and (na(L2i) or (bar_index - subPivotLen) >= L2i)
f_addSubPivot(sph, bar_index - subPivotLen, 1)
if not na(spl) and (na(L2i) or (bar_index - subPivotLen) >= L2i)
f_addSubPivot(spl, bar_index - subPivotLen, -1)
// Find subwave 1-2 (sL0 -> sH1 -> sL2) after L2
bool hasSub12 = false
float sL0 = na, sH1 = na, sL2 = na
int sL0i = na, sH1i = na, sL2i = na
if array.size(spPrice) >= 3
st0 = array.get(spType, array.size(spType) - 3)
st1 = array.get(spType, array.size(spType) - 2)
st2 = array.get(spType, array.size(spType) - 1)
if st0 == -1 and st1 == 1 and st2 == -1
sL0 := array.get(spPrice, array.size(spPrice) - 3)
sH1 := array.get(spPrice, array.size(spPrice) - 2)
sL2 := array.get(spPrice, array.size(spPrice) - 1)
sL0i := array.get(spIndex, array.size(spIndex) - 3)
sH1i := array.get(spIndex, array.size(spIndex) - 2)
sL2i := array.get(spIndex, array.size(spIndex) - 1)
// Sub retracement check
sw1 = sH1 - sL0
sRetr = sw1 != 0 ? (sH1 - sL2) / sw1 : na
hasSub12 := sw1 > 0 and not na(sRetr) and sRetr >= subRetrMin and sRetr <= subRetrMax
// === 3 of 3 trigger ===
bool threeOfThree = wave3Start and hasSub12 and close > sH1 and bar_index > sH1i and momentumOK and trendOK and volOK
// === Plotting ===
color upCol = color.new(color.lime, 0)
color dnCol = color.new(color.red, 0)
color neutCol = color.new(color.gray, 60)
plotshape(threeOfThree, title="3 of 3 Buy Signal", style=shape.triangleup, location=location.belowbar, size=size.large, color=upCol, text="3/3")
// Mark the 1-2 and sub 1-2 swings
if showLabels and has12
label.new(H1i, H1, text="1", style=label.style_label_up, color=color.new(color.green, 0))
label.new(L2i, L2, text="2", style=label.style_label_down, color=color.new(color.orange, 0))
if showLabels and hasSub12
label.new(sH1i, sH1, text="(i)", style=label.style_label_up, color=color.new(color.green, 60))
label.new(sL2i, sL2, text="(ii)", style=label.style_label_down, color=color.new(color.orange, 60))
// Draw swing lines
f_plotZigzag() =>
if array.size(pivPrice) >= 2 and plotZigs
for i = 1 to 1
x1 = array.get(pivIndex, array.size(pivIndex) - 2)
y1 = array.get(pivPrice, array.size(pivPrice) - 2)
x2 = array.get(pivIndex, array.size(pivIndex) - 1)
y2 = array.get(pivPrice, array.size(pivPrice) - 1)
line.new(x1, y1, x2, y2, extend=extend.none, color=neutCol, width=1)
f_plotZigzag()
// Visual filters
plot(ema50, color=color.new(color.teal, 0), title="EMA50")
plot(ema200, color=color.new(color.blue, 0), title="EMA200")
bgcolor(threeOfThree ? color.new(color.lime, 85) : na)
// === Alerts ===
alertcondition(threeOfThree, title="3 of 3 long", message="3 of 3 long setup on {{ticker}} {{interval}} — price has broken above subwave (i) high with momentum.")
// === Notes ===
// Heuristic detector:
// 1) Find L0-H1-L2 with L2 retracing 38.2%–78.6% of wave 1.
// 2) Confirm wave 3 start when price breaks above H1.
// 3) Inside wave 3, find sub L0-H1-L2 using tighter sub-pivots with 38.2%–61.8% retracement.
// 4) Trigger 3 of 3 when price breaks above subwave (i) high with momentum/trend/volume filters.
// Tune pivotLen/subPivotLen and thresholds to match your instrument and timeframe.
Skrip sumber terbuka
Dalam semangat sebenar TradingView, pencipta skrip ini telah menjadikannya sumber terbuka supaya pedagang dapat menilai dan mengesahkan kefungsiannya. Terima kasih kepada penulis! Walaupun anda boleh menggunakannya secara percuma, ingat bahawa menerbitkan semula kod ini adalah tertakluk kepada Peraturan Dalaman kami.
Penafian
Maklumat dan penerbitan adalah tidak dimaksudkan untuk menjadi, dan tidak membentuk, nasihat untuk kewangan, pelaburan, perdagangan dan jenis-jenis lain atau cadangan yang dibekalkan atau disahkan oleh TradingView. Baca dengan lebih lanjut di Terma Penggunaan.
Skrip sumber terbuka
Dalam semangat sebenar TradingView, pencipta skrip ini telah menjadikannya sumber terbuka supaya pedagang dapat menilai dan mengesahkan kefungsiannya. Terima kasih kepada penulis! Walaupun anda boleh menggunakannya secara percuma, ingat bahawa menerbitkan semula kod ini adalah tertakluk kepada Peraturan Dalaman kami.
Penafian
Maklumat dan penerbitan adalah tidak dimaksudkan untuk menjadi, dan tidak membentuk, nasihat untuk kewangan, pelaburan, perdagangan dan jenis-jenis lain atau cadangan yang dibekalkan atau disahkan oleh TradingView. Baca dengan lebih lanjut di Terma Penggunaan.