1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
631 lines of Pine Script v5 from indicator.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.
indicator.pine
//@version=5
indicator("Pivot Waves + 8-Pattern + CHoCH (upfate ver.)", overlay=true, max_lines_count=500, max_labels_count=500)
// ========== Inputs ==========
left = input.int(1, "Pivot Left", minval=1)
right = input.int(1, "Pivot Right", minval=1)
drawWaves = input.bool(true, "Vẽ sóng (nối line giữa các pivot)")
useBodyBreak= input.bool(true, "CHoCH theo thân nến (close)")
touchTicks = input.int(2, "Độ dung sai 'chạm' (số tick)", minval=0)
keepPivots = input.int(200, "Số pivot tối đa lưu", minval=20)
plotPvtMark = input.bool(true, "Hiển thị PH/PL")
plotEightLbl = input.bool(true, "Ghi nhãn P1..P8 gần nhất")
showTable = input.bool(true, "Bảng thông tin")
// --- Variant filters (BẮT BUỘC - chỉ detect pivot có variant hợp lệ)
allowPH1 = input.bool(true, "Cho phép PH loại 1")
allowPH2 = input.bool(true, "Cho phép PH loại 2 (con là nến #1)")
allowPH3 = input.bool(true, "Cho phép PH loại 3 (biến thể khác)")
allowPH4 = input.bool(true, "Cho phép PH loại 4")
allowPH5 = input.bool(true, "Cho phép PH loại 5")
allowPL1 = input.bool(true, "Cho phép PL loại 1")
allowPL2 = input.bool(true, "Cho phép PL loại 2 (con là nến #1)")
allowPL3 = input.bool(true, "Cho phép PL loại 3 (biến thể khác)")
allowPL4 = input.bool(true, "Cho phép PL loại 4")
allowPL5 = input.bool(true, "Cho phép PL loại 5")
showVariantLabel = input.bool(false, "Hiển thị mã biến thể trên chart")
var bool chochLocked = false
// Store CHoCH bar for persistent label
var int chochUpBar = na
var int chochDownBar = na
// ========== Storage ==========
var prices = array.new_float()
var bars = array.new_int()
var highs = array.new_bool()
var volumes = array.new_float() // Store pivot volumes
storePivot(_bar, _price, _isHigh) =>
array.push(bars, _bar)
array.push(prices, _price)
array.push(highs, _isHigh)
// Store volume at pivot bar
off = math.max(0, bar_index - _bar)
array.push(volumes, volume[off])
while array.size(bars) > keepPivots
array.shift(bars)
array.shift(prices)
array.shift(highs)
array.shift(volumes)
pCount() => array.size(prices)
getPivotFromEnd(idxFromEnd) =>
sz = array.size(prices)
[ array.get(bars, sz-1-idxFromEnd),
array.get(prices, sz-1-idxFromEnd),
array.get(highs, sz-1-idxFromEnd) ]
getVolumeFromEnd(idxFromEnd) =>
sz = array.size(volumes)
array.get(volumes, sz-1-idxFromEnd)
// Dung sai tick
tol = syminfo.mintick * touchTicks
// ========== Helpers cho phân loại biến thể ==========
getHLTripletAroundMid() =>
// Timeline: [LEFT/h3] → [CENTER/h2/PIVOT] → [RIGHT/h1]
// Pine indexing: [right+1] → [right] → [right-1]
float h2 = high[right] // CENTER (pivot bar)
float l2 = low[right]
float h3 = na // LEFT side
float l3 = na
float h1 = na // RIGHT side
float l1 = na
if bar_index > right + 1
h3 := high[right + 1] // LEFT side
l3 := low[right + 1]
if right - 1 >= 0
h1 := high[right - 1] // RIGHT side
l1 := low[right - 1]
[h1,h2,h3,l1,l2,l3]
isPH_type1() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (h2 > h1 and h2 > h3) and (l2 > l1 and l2 > l3)
isPH_type2() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (h2 >= h1 and h2 > h3) and (l2 > l3 and l2 < l1)
isPH_type3() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (h2 > h1 and h2 >= h3) and (l2 < l3 and l2 > l1)
isPH_type4() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (h2 >= h3 and h2 > h1) and (l2 <= l3 and l2 > l1)
isPH_type5() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (h2 >= h3 and h2 >= h1) and (l2 <= l3 and l2 > l1)
isPL_type1() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (l2 < l1 and l2 < l3) and (h2 < h1 and h2 < h3)
isPL_type2() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (h2 >= h1 and h2 < h3) and (l2 < l3 and l2 <= l1)
isPL_type3() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (l2 < l1 and l2 < l3) and (h2 < h1 and h2 >= h3)
isPL_type4() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (h2 <= h3 and h2 < h1) and (l2 >= l3 and l2 <= l1)
isPL_type5() =>
[h1,h2,h3,l1,l2,l3] = getHLTripletAroundMid()
not na(h1) and not na(h3) and (h2 >= h3 and h2 < h1) and (l2 <= l3 and l2 <= l1)
classifyPivotVariant(isHighPivot) =>
var string t = "NA"
if isHighPivot
t := isPH_type1() ? "PH1" :
isPH_type2() ? "PH2" :
isPH_type3() ? "PH3" :
isPH_type4() ? "PH4" :
isPH_type5() ? "PH5" : "NA"
else
t := isPL_type1() ? "PL1" :
isPL_type2() ? "PL2" :
isPL_type3() ? "PL3" :
isPL_type4() ? "PL4" :
isPL_type5() ? "PL5" : "NA"
t
allowedByUser(t) =>
switch t
"PH1" => allowPH1
"PH2" => allowPH2
"PH3" => allowPH3
"PH4" => allowPH4
"PH5" => allowPH5
"PL1" => allowPL1
"PL2" => allowPL2
"PL3" => allowPL3
"PL4" => allowPL4
"PL5" => allowPL5
=> false
// ========== Custom Pivot Detection (không dùng ta.pivot*) ==========
// Detect pivot manually based on variant classification only
var bool isPH = false
var bool isPL = false
var float newPivotPrice = na
var int newPivotBar = na
var bool newPivotHigh = false
// Reset detection flags
isPH := false
isPL := false
newPivotPrice := na
newPivotBar := na
// Chỉ check khi có đủ bars 2 bên
if bar_index > right + 1 and right - 1 >= 0
// Check Pivot High variants
if isPH_type1() and allowPH1
isPH := true
else if isPH_type2() and allowPH2
isPH := true
else if isPH_type3() and allowPH3
isPH := true
else if isPH_type4() and allowPH4
isPH := true
else if isPH_type5() and allowPH5
isPH := true
// Check Pivot Low variants
if isPL_type1() and allowPL1
isPL := true
else if isPL_type2() and allowPL2
isPL := true
else if isPL_type3() and allowPL3
isPL := true
else if isPL_type4() and allowPL4
isPL := true
else if isPL_type5() and allowPL5
isPL := true
// Set pivot data if detected
if isPH
newPivotPrice := high[right]
newPivotBar := bar_index - right
newPivotHigh := true
else if isPL
newPivotPrice := low[right]
newPivotBar := bar_index - right
newPivotHigh := false
// ========== State vars ==========
var int lastPivotBar = na
var float lastPivotPrice = na
var bool lastEightUp = false
var bool lastEightDown = false
var int lastEightBarIdx = na
var float p2_price = na
var bool acceptedPH_event = false
var bool acceptedPL_event = false
// Pattern group order flags (needed for CHoCH confirmation)
var bool g1_upOrder = false
var bool g1_downOrder = false
var bool g2_upOrder = false
var bool g2_downOrder = false
var bool g3_upOrder = false
var bool g3_downOrder = false
// Additional groups G4, G5, G6 removed - only G1..G3 are used now
// ========== Khi có pivot mới ==========
acceptedPH_event := false
acceptedPL_event := false
if (isPH or isPL)
// ========== BẮT BUỘC lọc theo variant - KHÔNG detect pivot thuần ==========
tvar = classifyPivotVariant(isPH)
accept = (tvar != "NA") and allowedByUser(tvar)
if accept
// Check if we need to replace the last pivot (if same type and new one is better)
shouldReplaceLast = false
shouldSkipNew = false // Flag to skip adding new pivot if old one is better
if pCount() > 0
[lastBar, lastPrice, lastHigh] = getPivotFromEnd(0)
newHigh = isPH
// If last pivot is same type, check if new one is better
if lastHigh == newHigh
gap = newPivotBar - lastBar - 1
// If consecutive (gap = 0, truly adjacent), choose the better one
if gap == 0
if newHigh
// Both are PH → keep the higher one
if newPivotPrice > lastPrice
shouldReplaceLast := true // New is better → replace old
else
shouldSkipNew := true // Old is better → skip new
else
// Both are PL → keep the lower one
if newPivotPrice < lastPrice
shouldReplaceLast := true // New is better → replace old
else
shouldSkipNew := true // Old is better → skip new
// Replace last pivot if new one is better
if shouldReplaceLast
// Remove last pivot
array.pop(bars)
array.pop(prices)
array.pop(highs)
array.pop(volumes)
// Store new pivot (will replace the removed one)
storePivot(newPivotBar, newPivotPrice, newPivotHigh)
if drawWaves and pCount() > 1
// Redraw wave from previous pivot
[prevBar, prevPrice, _] = getPivotFromEnd(1)
line.new(prevBar, prevPrice, newPivotBar, newPivotPrice, color=color.new(color.blue, 0), width=2)
lastPivotBar := newPivotBar
lastPivotPrice := newPivotPrice
else if not shouldSkipNew
// Normal case: insert fake pivot if needed, then add new pivot
if pCount() > 0
[lastBar, lastPrice, lastHigh] = getPivotFromEnd(0)
newHigh = isPH
if lastHigh == newHigh
gap = newPivotBar - lastBar - 1
if gap > 0
firstBarInGap = lastBar + 1
lastBarInGap = newPivotBar - 1
offsetFirst = bar_index - firstBarInGap
offsetLast = bar_index - lastBarInGap
if offsetFirst >= 0 and offsetLast >= 0
float insertPrice = newHigh ? low[offsetFirst] : high[offsetFirst]
int insertBar = firstBarInGap
bool insertHigh = not newHigh
for i = offsetFirst to offsetLast
float candidate = newHigh ? low[i] : high[i]
if (newHigh and candidate < insertPrice) or (not newHigh and candidate > insertPrice)
insertPrice := candidate
insertBar := bar_index - i
if insertBar > lastBar and insertBar < newPivotBar
storePivot(insertBar, insertPrice, insertHigh)
if drawWaves and not na(lastPivotBar)
line.new(lastPivotBar, lastPivotPrice, insertBar, insertPrice, color=color.new(color.blue, 0), width=2)
lastPivotBar := insertBar
lastPivotPrice := insertPrice
// Lưu pivot mới
storePivot(newPivotBar, newPivotPrice, newPivotHigh)
if drawWaves and not na(lastPivotBar)
line.new(lastPivotBar, lastPivotPrice, newPivotBar, newPivotPrice, color=color.new(color.blue, 0), width=2)
lastPivotBar := newPivotBar
lastPivotPrice := newPivotPrice
// reset khóa CHoCH khi đã hình thành pivot sau cụm 8 trước đó
if not na(lastEightBarIdx) and newPivotBar > lastEightBarIdx
chochLocked := false
acceptedPH_event := isPH
acceptedPL_event := isPL
if showVariantLabel and tvar != "NA"
label.new(newPivotBar, newPivotPrice, tvar, style=label.style_label_center,
textcolor=color.white, color=color.new(color.black, 10), size=size.tiny)
// ========== 8-Pattern ==========
var float pivot6 = na // p6 (reference cho CHoCH)
var float pivot5 = na // p5 (additional reference cho CHoCH)
var float pivot4 = na // p4 (additional reference cho CHoCH)
var float saved_p5 = na // Store p5 for pattern group confirmation
var float saved_p7 = na // Store p7 for pattern group confirmation (G2)
eightReady = pCount() >= 8
if eightReady
[b8,p8,h8] = getPivotFromEnd(0)
[b7,p7,h7] = getPivotFromEnd(1)
[b6,p6,h6] = getPivotFromEnd(2)
[b5,p5,h5] = getPivotFromEnd(3)
[b4,p4,h4] = getPivotFromEnd(4)
[b3,p3,h3] = getPivotFromEnd(5)
[b2,p2,h2] = getPivotFromEnd(6)
[b1,p1,h1] = getPivotFromEnd(7)
// cấu trúc xen kẽ 8 pivot
upStruct = (not h1) and h2 and (not h3) and h4 and (not h5) and h6 and (not h7) and h8
downStruct = h1 and (not h2) and h3 and (not h4) and h5 and (not h6) and h7 and (not h8)
// Retest P7 ↔ P4 (thay vì P5 ↔ P2)
off7 = math.max(0, bar_index - b7)
off4 = math.max(0, bar_index - b4)
hi7 = high[off7]
lo7 = low[off7]
hi4 = high[off4]
lo4 = low[off4]
touchRetest = (upStruct and (lo7 < hi4)) or (downStruct and (hi7 > lo4))
// P8 là cực trị trong cụm 1–8
highsArr = array.from(p1, p2, p3, p4, p5, p6, p7, p8)
lowsArr = array.from(p1, p2, p3, p4, p5, p6, p7, p8)
isHighest8 = p8 == array.max(highsArr)
isLowest8 = p8 == array.min(lowsArr)
// Ràng buộc thứ tự 8-pivot - 3 nhóm (G1, G2, G3)
// Ràng buộc thứ tự - nhóm (G1..G3)
// We intentionally removed the 10-pivot groups (G4/G5/G6). Always use the most recent 8 pivots (p1..p8).
// G1 (Original):
g1_upOrder := (p2 < p4 and p4 < p6 and p6 < p8) and (p3 < p5 and p5 < p7)
g1_downOrder := (p3 > p5 and p5 > p7) and (p2 > p4 and p4 > p6 and p6 > p8)
// G2:
// Uptrend: p3 < p7 < p5, p2 < p6 < p4 < p8, p2 < p5
g2_upOrder := (p3 < p7 and p7 < p5) and (p2 < p6 and p6 < p4 and p4 < p8) and (p2 < p5)
// Downtrend: p3 > p7 > p5, p2 > p6 > p4 > p8, p2 > p5
g2_downOrder := (p3 > p7 and p7 > p5) and (p2 > p6 and p6 > p4 and p4 > p8) and (p2 > p5)
// G3:
// Uptrend: p3 < p5 < p7, p2 < p6 < p4 < p8, p2 < p5
g3_upOrder := (p3 < p5 and p5 < p7) and (p2 < p6 and p6 < p4 and p4 < p8) and (p2 < p5)
// Downtrend: p3 > p5 > p7, p2 > p6 > p4 > p8, p2 > p5
g3_downOrder := (p3 > p5 and p5 > p7) and (p2 > p6 and p6 > p4 and p4 > p8) and (p2 > p5)
// Combined order check (any valid group) - only G1..G3 are considered
upOrderOK = g1_upOrder or g2_upOrder or g3_upOrder
downOrderOK = g1_downOrder or g2_downOrder or g3_downOrder
// Điều kiện breakout mới
off5 = math.max(0, bar_index - b5)
off3 = math.max(0, bar_index - b3)
off2 = math.max(0, bar_index - b2)
off1 = math.max(0, bar_index - b1)
lo5 = low[off5]
hi2 = high[off2]
lo3 = low[off3]
lo1 = low[off1]
hi5 = high[off5]
lo2 = low[off2]
hi3 = high[off3]
hi1 = high[off1]
upBreakout = (lo5 > hi2)
downBreakout = (hi5 < lo2)
// Mẫu hợp lệ với điều kiện breakout phù hợp
// Uptrend pattern + UP breakout conditions
lastEightUp := upStruct and upOrderOK and touchRetest and isHighest8 and upBreakout
// Downtrend pattern + DOWN breakout conditions
lastEightDown := downStruct and downOrderOK and touchRetest and isLowest8 and downBreakout
// Lưu pivots khi mẫu hợp lệ
if (lastEightDown or lastEightUp)
pivot5 := p5
pivot6 := p6
pivot4 := p4
// Store pivot values for confirmation logic
saved_p5 := p5
saved_p7 := p7
// saved_p8 removed (G4/G5/G6 groups removed)
lastEightBarIdx := b8
p2_price := p2
// ---- Sau khi đã có eightReady ----
isAfterEight = eightReady and (bar_index > nz(lastEightBarIdx))
var bool fireChochUp = false
var bool fireChochDn = false
var string matchedGroup = na // Track which pattern group matched
if eightReady and not na(pivot6) and not na(pivot5) and not na(pivot4)
// ========== 3-CANDLE CHoCH CONFIRMATION (matching Python logic) ==========
// Pre-CHoCH [2] → CHoCH Bar [1] → Confirmation [0]
// CHoCH Bar conditions (nến [1] - previous bar)
// CHoCH Up: low[1] > low[2] AND close[1] > high[2] AND close[1] > pivot6 AND close[1] < pivot2 AND close[1] > pivot4
chochUpBar = (low[1] > low[2]) and (close[1] > high[2]) and
(close[1] > pivot6) and (close[1] < p2_price) and (close[1] > pivot4)
// CHoCH Down: high[1] < high[2] AND close[1] < low[2] AND close[1] < pivot6 AND close[1] > pivot2 AND close[1] < pivot4
chochDownBar = (high[1] < high[2]) and (close[1] < low[2]) and
(close[1] < pivot6) and (close[1] > p2_price) and (close[1] < pivot4)
// Confirmation conditions - Basic (nến [0] - current bar)
// CHoCH Up confirmed: close của confirmation > high của nến pre-CHoCH [2]
confirmUpBasic = (close > high[2])
// CHoCH Down confirmed: close của confirmation < low của nến pre-CHoCH [2]
confirmDownBasic = (close < low[2])
// Check nếu nến CHoCH (bar[1]) là pivot mới nhất → lấy pivot trước đó
// để tránh fail điều kiện isAfterEight
chochBarIsPivot = (bar_index - 1 == lastEightBarIdx)
nineReady = pCount() >= 9
var int effectiveEightBarIdx = na
var bool effectiveLastEightUp = false
var bool effectiveLastEightDown = false
if chochBarIsPivot and nineReady
// Nến CHoCH là pivot → lấy pivot trước đó (offset 1)
[b9,_,_] = getPivotFromEnd(1)
effectiveEightBarIdx := b9
// Lấy giá trị lastEightUp/Down từ nến trước đó (bar[1])
effectiveLastEightUp := lastEightUp[1]
effectiveLastEightDown := lastEightDown[1]
else
// Nến CHoCH không là pivot → dùng lastEightBarIdx bình thường
effectiveEightBarIdx := lastEightBarIdx
effectiveLastEightUp := lastEightUp
effectiveLastEightDown := lastEightDown
// Recalculate isAfterEight với effectiveEightBarIdx
isAfterEightSafe = eightReady and (bar_index > nz(effectiveEightBarIdx))
// Match with pattern direction - sử dụng isAfterEightSafe và effective values
baseUp = isAfterEightSafe and effectiveLastEightDown and chochUpBar and confirmUpBasic
baseDown = isAfterEightSafe and effectiveLastEightUp and chochDownBar and confirmDownBasic
// ========== PATTERN GROUP SPECIFIC CONDITIONS for Confirmation Candle ==========
// Determine which group matched
if lastEightUp
if g1_upOrder
matchedGroup := "G1"
else if g2_upOrder
matchedGroup := "G2"
else if g3_upOrder
matchedGroup := "G3"
else
matchedGroup := na
else if lastEightDown
if g1_downOrder
matchedGroup := "G1"
else if g2_downOrder
matchedGroup := "G2"
else if g3_downOrder
matchedGroup := "G3"
else
matchedGroup := na
// Get volumes at pivot bars
vol8 = getVolumeFromEnd(0)
vol7 = getVolumeFromEnd(1)
vol6 = getVolumeFromEnd(2)
vol5 = getVolumeFromEnd(3)
vol4 = getVolumeFromEnd(4)
vol_choch = volume[1] // Volume of CHoCH candle (previous bar)
chochBarIsPivotVol = (bar_index - 1 == lastEightBarIdx)
if chochBarIsPivotVol and pCount() >= 9
// CHoCH bar is p8 → shift indices by 1
vol8 := getVolumeFromEnd(1) // p9 → p8
vol7 := getVolumeFromEnd(2) // p8 → p7
vol6 := getVolumeFromEnd(3) // p7 → p6
vol5 := getVolumeFromEnd(4) // p6 → p5
vol4 := getVolumeFromEnd(5) // p5 → p4
// label.new(bar_index, high, str.tostring(volume[0]),style=label.style_label_down, textcolor=color.white,color=color.new(color.orange, 0), size=size.tiny)
// Apply pattern group conditions with volume
confirmUp = false
confirmDown = false
if baseUp and not na(matchedGroup)
// Downtrend → CHoCH Up
priceCondition = false
volumeCondition = false
if matchedGroup == "G1"
// G1: Close_CF <= HIGH_5
priceCondition := close <= saved_p5
// Volume condition G1: 678_ok AND 456_ok
// 1. (Vol8 OR Vol6 OR Vol_CHoCH) là lớn nhất cụm 678
max_678 = math.max(vol6, math.max(vol7, vol8))
vol_678_ok = (vol8 == max_678) or (vol6 == max_678) or (vol_choch >= max_678)
// 2. (Vol4 OR Vol6) là lớn nhất cụm 456
max_456 = math.max(vol4, math.max(vol5, vol6))
vol_456_ok = (vol4 == max_456) or (vol6 == max_456)
volumeCondition := vol_678_ok and vol_456_ok
confirmUp := priceCondition and volumeCondition
else if matchedGroup == "G2"
// G2: Close_CF <= HIGH_7
priceCondition := close <= saved_p7
// Volume condition G2/G3:
// (Vol4 OR Vol8 OR Vol_CHoCH) là lớn nhất cụm 45678
max_45678 = math.max(vol4, math.max(vol5, math.max(vol6, math.max(vol7, vol8))))
volumeCondition := (vol4 == max_45678) or (vol8 == max_45678) or (vol_choch >= max_45678)
confirmUp := priceCondition and volumeCondition
else if matchedGroup == "G3"
// G3: Close_CF <= HIGH_5
priceCondition := close <= saved_p5
// Volume condition G2/G3:
// (Vol4 OR Vol8 OR Vol_CHoCH) là lớn nhất cụm 45678
max_45678 = math.max(vol4, math.max(vol5, math.max(vol6, math.max(vol7, vol8))))
volumeCondition := (vol4 == max_45678) or (vol8 == max_45678) or (vol_choch >= max_45678)
confirmUp := priceCondition and volumeCondition
// Additional groups for Up confirmation (Downtrend -> CHoCH Up): G4', G5', G6'
// Groups G4/G5/G6 removed - no additional group-specific Up rules
if baseDown and not na(matchedGroup)
// Uptrend → CHoCH Down
priceCondition = false
volumeCondition = false
if matchedGroup == "G1"
// G1: Close_CF >= LOW_5
priceCondition := close >= saved_p5
// Volume condition G1: 678_ok AND 456_ok
// 1. (Vol8 OR Vol6 OR Vol_CHoCH) là lớn nhất cụm 678
max_678 = math.max(vol6, math.max(vol7, vol8))
vol_678_ok = (vol8 == max_678) or (vol6 == max_678) or (vol_choch >= max_678)
// 2. (Vol4 OR Vol6) là lớn nhất cụm 456
max_456 = math.max(vol4, math.max(vol5, vol6))
vol_456_ok = (vol4 == max_456) or (vol6 == max_456)
volumeCondition := vol_678_ok and vol_456_ok
confirmDown := priceCondition and volumeCondition
else if matchedGroup == "G2"
// G2: Close_CF >= LOW_7
priceCondition := close >= saved_p7
// Volume condition G2/G3:
// (Vol4 OR Vol8 OR Vol_CHoCH) là lớn nhất cụm 45678
max_45678 = math.max(vol4, math.max(vol5, math.max(vol6, math.max(vol7, vol8))))
volumeCondition := (vol4 == max_45678) or (vol8 == max_45678) or (vol_choch >= max_45678)
confirmDown := priceCondition and volumeCondition
else if matchedGroup == "G3"
// G3: Close_CF >= LOW_5
priceCondition := close >= saved_p5
// Volume condition G2/G3:
// (Vol4 OR Vol8 OR Vol_CHoCH) là lớn nhất cụm 45678
max_45678 = math.max(vol4, math.max(vol5, math.max(vol6, math.max(vol7, vol8))))
volumeCondition := (vol4 == max_45678) or (vol8 == max_45678) or (vol_choch >= max_45678)
confirmDown := priceCondition and volumeCondition
// New groups for Down confirmation (Uptrend -> CHoCH Down)
// Groups G4/G5/G6 removed - no additional group-specific Down rules
// Fire signal với locking
fireChochUp := (not chochLocked) and confirmUp
fireChochDn := (not chochLocked) and confirmDown
if fireChochUp or fireChochDn
chochLocked := true
// Store bar index for persistent label
if fireChochUp
chochUpBar := bar_index
if fireChochDn
chochDownBar := bar_index
// ========== Plot ==========
plotshape(fireChochUp, title="CHoCH Up", style=shape.labelup, text="CH↑",
color=color.new(color.teal, 0), textcolor=color.white, location=location.belowbar, size=size.tiny)
plotshape(fireChochDn, title="CHoCH Down", style=shape.labeldown, text="CH↓",
color=color.new(color.orange, 0), textcolor=color.white, location=location.abovebar, size=size.tiny)
// ========== Alert Conditions ==========
alertcondition(fireChochUp, title="CHoCH Up Alert", message="CHoCH Up detected on {{ticker}} {{interval}}")
alertcondition(fireChochDn, title="CHoCH Down Alert", message="CHoCH Down detected on {{ticker}} {{interval}}")
alertcondition(fireChochUp or fireChochDn, title="CHoCH Alert (Any)", message="CHoCH detected on {{ticker}} {{interval}}")
Overview
Pivot Waves + 8-Pattern + CHoCH connects pivot points with wave lines, plots PH and PL marks, and labels the latest P1 to P8 patterns. A table shows pivot details including volumes. The script detects CHoCH signals based on body breaks or candle closes, with options to enable or disable each of five pattern variants. All elements draw directly on the price chart with customizable persistence of up to 200 pivots and tick-based touch tolerance.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Pivot Left
int
1
left
Pivot Right
int
1
right
Vẽ sóng (nối line giữa các pivot)
bool
true
drawWaves
CHoCH theo thân nến (close)
bool
true
useBodyBreak
Độ dung sai 'chạm' (số tick)
int
2
touchTicks
Số pivot tối đa lưu
int
200
keepPivots
Hiển thị PH/PL
bool
true
plotPvtMark
Ghi nhãn P1..P8 gần nhất
bool
true
plotEightLbl
Bảng thông tin
bool
true
showTable
Cho phép PH loại 1
bool
true
allowPH1
Cho phép PH loại 2 (con là nến #1)
bool
true
allowPH2
Cho phép PH loại 3 (biến thể khác)
bool
true
allowPH3
Show all 20 settings
Setting
Type
Default
Variable
Cho phép PH loại 4
bool
true
allowPH4
Cho phép PH loại 5
bool
true
allowPH5
Cho phép PL loại 1
bool
true
allowPL1
Cho phép PL loại 2 (con là nến #1)
bool
true
allowPL2
Cho phép PL loại 3 (biến thể khác)
bool
true
allowPL3
Cho phép PL loại 4
bool
true
allowPL4
Cho phép PL loại 5
bool
true
allowPL5
Hiển thị mã biến thể trên chart
bool
false
showVariantLabel
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
This overlay indicator detects tweezer tops and bottoms and kangaroo-tail pin bars, then draws supply and demand zones from those patterns. It scores each zone, decays freshness, merges overlapping zones, and can invalidate a zone after a close-through or afte
This indicator plots Smart Money Concepts and price action components directly on the price chart. It highlights bullish and bearish order blocks, multi-timeframe pivot points, market structure swings with key Fibonacci retracement levels (0.618 and 0.786), zo
This Pine Script indicator plots Smart Money Concepts market structure directly on the price chart. It identifies swing highs and lows, internal market structures, Break of Structure events, Change of Character shifts, and EQH/EQL levels. Breaker blocs are hig
The Price Action Toolkit is an indicator that combines several price action tools on the chart. It calculates the ATR with user-defined length and shows it in a table. It plots multiple Kaufman Adaptive Moving Averages (KAMA) with different lengths and sources
This indicator plots Smart Money Concepts elements on the price chart including internal and swing order blocks, bullish and bearish structures with BOS and CHoCH breaks, fair value gaps, equal highs and lows, and swing points. It can color candles according t
This indicator plots ZigZag lines connecting swing highs and lows to map price structure. It draws candle body and shadow boxes on the price chart and detects CHoCH for potential trend shifts and BOS for continuation signals. The main setting toggles candle di