Isolated Scroll Container

1. Mô tả chỉ báo Cup & Handle (Zeiierman)

Chỉ báo nhận diện mô hình Cup & Handle (Cốc và Quai Cốc) - một trong những mô hình biểu đồ kinh điển nhất trong phân tích kỹ thuật

1.1 Concept của chỉ báo

Chỉ báo Cup & Handle dựa trên lý thuyết về mô hình tâm lý thị trường được phát hiện bởi William O'Neil. Mô hình này tin rằng:

  • Giai đoạn "Cốc" (Cup): Thể hiện quá trình giá giảm dần, tạo đáy, rồi phục hồi trở lại mức ban đầu. Đây là giai đoạn thị trường "lắng đọng" sau một đợt tăng trước đó, người bán yếu dần thoát ra, và người mua mạnh tích lũy.
  • Giai đoạn "Quai cốc" (Handle): Sau khi hình thành cốc, giá có một đợt điều chỉnh nhỏ (pullback) tạo thành quai cốc. Đây là lần "test" cuối cùng trước khi bứt phá.
  • Breakout (Bứt phá): Khi giá vượt qua mức "miệng cốc" (rim), đây là tín hiệu xu hướng tăng mạnh sắp diễn ra, với mục tiêu giá bằng độ sâu của cốc.

Chỉ báo này tự động phát hiện cả mô hình tăng (Cup & Handle)mô hình giảm (Inverted Cup & Handle) dựa trên các điểm pivot (đỉnh/đáy dao động) của giá.

1.2 Chức năng của chỉ báo

  • Hiển thị mô hình Cup & Handle:
    • Đường cong "Cốc": Vẽ bằng đường polyline cong (curved) nối 3 điểm: miệng trái → đáy cốc → miệng phải. Màu xanh lá cho mô hình tăng, đỏ cho mô hình giảm.
    • Đường cong "Quai cốc": Vẽ từ miệng phải → điểm thấp nhất của quai → điểm dự kiến breakout. Cùng màu với cốc.
    • Vùng tô màu: Tô màu nhạt bên trong cốc và quai cốc để dễ nhận diện.
    • Nhãn tên mô hình: Hiển thị chữ "Cup+Handle" hoặc "Inv Cup+Handle" tại giữa cốc.
    • Đường mục tiêu (Target line): Đường ngang chỉ mức giá mục tiêu sau khi breakout, được tính bằng: Rim Price ± (Cup Depth × Target Multiplier)
  • Alert (Cảnh báo):
    • "Cup & Handle (Brewed)": Kích hoạt khi phát hiện mô hình tăng hợp lệ.
    • "Inverted Cup & Handle (Brewed)": Kích hoạt khi phát hiện mô hình giảm hợp lệ.

1.3 Cách dùng chỉ báo

  • Xác định cơ hội vào lệnh: Khi chỉ báo vẽ mô hình Cup & Handle và giá breakout khỏi rim (nếu bật "Require breakout confirmation"), đây là tín hiệu vào lệnh BUY (mô hình tăng) hoặc SELL (mô hình giảm).
  • Đặt mục tiêu Take Profit: Sử dụng đường Target Line làm mức chốt lời. Mặc định là 0.5× độ sâu cốc, có thể điều chỉnh qua "Target multiplier".
  • Đặt Stop Loss: Đặt SL dưới mức "Invalidation" (mặc định 61.8% độ sâu quai cốc tính từ rim). Nếu giá chạm mức này, mô hình bị phá vỡ.
  • Lọc tín hiệu chất lượng: Điều chỉnh các tham số như "Rim similarity tolerance", "Handle retrace min/max" để lọc ra các mô hình chuẩn xác hơn, giảm nhiễu.
  • Kết hợp với khung thời gian lớn: Mô hình Cup & Handle hiệu quả nhất trên khung H4, Daily, Weekly. Tránh dùng trên khung quá nhỏ (M1, M5) vì nhiễu.

1.4 Cách hoạt động của chỉ báo

Đầu vào & vai trò

  • pivotSpan (Số nguyên, mặc định: 10): Số nến cần để xác nhận một đỉnh/đáy dao động (pivot). Ví dụ pivotSpan=10 nghĩa là cần 10 nến bên trái và 10 nến bên phải đều thấp hơn (đối với đỉnh) hoặc cao hơn (đối với đáy) thì mới xác nhận pivot. Tăng pivotSpan → ít pivot hơn nhưng mạnh hơn, mô hình ít hơn nhưng chất lượng cao. Giảm pivotSpan → nhiều pivot hơn, nhiều tín hiệu nhưng nhiều nhiễu.
  • rimTolPct (Số nguyên %, mặc định: 18%): Độ sai lệch cho phép giữa miệng trái và miệng phải của cốc. Ví dụ nếu miệng trái ở 100$, miệng phải ở 110$, sai lệch = 10%. Nếu rimTolPct=18% thì chấp nhận, nếu rimTolPct=5% thì loại bỏ. Giảm rimTolPct → cốc phải cân đối hơn, ít mô hình hơn nhưng chuẩn xác. Tăng rimTolPct → chấp nhận cốc lệch nhiều hơn, nhiều mô hình hơn.
  • hMin (Số thập phân, mặc định: 0.12): Độ sâu tối thiểu của quai cốc so với độ sâu cốc. Ví dụ nếu cốc sâu 100$, quai cốc phải sâu ít nhất 12$ (0.12×100). Quá nhỏ → quai cốc quá nông, không đủ "test" lại, tín hiệu yếu.
  • hMax (Số thập phân, mặc định: 0.55): Độ sâu tối đa của quai cốc. Ví dụ nếu cốc sâu 100$, quai cốc không được sâu quá 55$. Quá lớn → quai cốc quá sâu, mô hình lộn xộn, không còn là Cup & Handle chuẩn.
  • killAtFib (Số thập phân, mặc định: 61.8%): Mức "vô hiệu hoá" mô hình. Nếu giá rơi xuống quá 61.8% độ sâu quai cốc (tính từ rim), mô hình bị phá vỡ. Đây là mức Stop Loss an toàn. Giảm → SL chặt hơn, ít rủi ro nhưng dễ bị dừng sớm. Tăng → SL lỏng hơn, chấp nhận dao động lớn hơn.
  • needConfirm (Tick chọn true/false, mặc định: false): Có yêu cầu giá phải breakout qua rim hay không. false → phát hiện sớm ngay khi mô hình hình thành. true → chỉ kích hoạt khi giá đã vượt qua rim, tín hiệu chất lượng cao hơn nhưng muộn hơn.
  • targetMult (Số thập phân, mặc định: 0.5): Hệ số nhân để tính mục tiêu giá. Ví dụ cốc sâu 100$, rim ở 200$, target = 200 + (100×0.5) = 250$. Tăng → mục tiêu xa hơn, lợi nhuận lớn nhưng khó đạt. Giảm → mục tiêu gần hơn, dễ đạt nhưng lợi nhuận nhỏ.
  • showName, bgBull, lnBull, bgBear, lnBear, wOk, stOk, showTarget, targetWidth, targetStyle: Các tuỳ chọn hiển thị (bật/tắt nhãn, màu sắc, độ dày đường, kiểu đường). Không ảnh hưởng logic phát hiện, chỉ ảnh hưởng giao diện.

Các khối logic chính

🎯 Flow 1: Phát hiện mô hình Cup & Handle

  • Bước 1 - Thu thập Pivot (Đỉnh/Đáy dao động):
    • Chỉ báo sử dụng hàm ta.pivothigh()ta.pivotlow() để tìm các đỉnh và đáy dao động dựa trên pivotSpan.
    • Mỗi khi tìm thấy pivot, nó được lưu vào một "danh sách pivot" (array) với 3 thông tin: giá (p), vị trí nến (i), hướng (d) (+1 = đỉnh, -1 = đáy).
    • Danh sách này chỉ giữ tối đa 12 pivot gần nhất (nếu quá 12, pivot cũ nhất bị xoá).
    • Ví dụ: Nếu pivotSpan=10, giá tạo đỉnh tại nến 100 với giá 50$, và 10 nến trước + 10 nến sau đều thấp hơn 50$, thì pivot này được lưu: {p: 50, i: 100, d: +1}.
  • Bước 2 - Nhận diện chuỗi 4 pivot hợp lệ:
    • Chỉ báo lấy 4 pivot gần nhất từ danh sách: s0, s1, s2, s3.
    • Mô hình tăng (Cup & Handle): Chuỗi phải là: Đỉnh → Đáy → Đỉnh → Đáy (s0.d=+1, s1.d=-1, s2.d=+1, s3.d=-1).
    • Mô hình giảm (Inverted Cup & Handle): Chuỗi phải là: Đáy → Đỉnh → Đáy → Đỉnh (s0.d=-1, s1.d=+1, s2.d=-1, s3.d=+1).
    • Ví dụ mô hình tăng: s0 = đỉnh 100$ (miệng trái), s1 = đáy 80$ (đáy cốc), s2 = đỉnh 98$ (miệng phải), s3 = đáy 92$ (đáy quai cốc).
  • Bước 3 - Kiểm tra điều kiện "Cốc" hợp lệ:
    • Rim similarity (Độ tương đồng miệng cốc): So sánh giá của s0 (miệng trái) và s2 (miệng phải). Nếu sai lệch ≤ rimTolPct% thì hợp lệ.
    • Ví dụ: s0=100$, s2=110$, sai lệch = |100-110|/100 = 10%. Nếu rimTolPct=18% → hợp lệ. Nếu rimTolPct=5% → loại bỏ.
    • Tính độ sâu cốc (depth): depth = |rimPrice - Bp|, trong đó rimPrice = min(s0, s2) đối với mô hình tăng, hoặc max(s0, s2) đối với mô hình giảm.
    • Ví dụ mô hình tăng: s0=100$, s2=98$, s1=80$ → rimPrice=98$ (lấy thấp hơn), depth=|98-80|=18$.
  • Bước 4 - Kiểm tra điều kiện "Quai cốc" hợp lệ:
    • Tính độ sâu quai cốc (hRet): hRet = |rimPrice - Hp| / depth, trong đó Hp là giá của s3 (đáy quai cốc).
    • Ví dụ: rimPrice=98$, Hp=92$, depth=18$ → hRet = |98-92|/18 = 6/18 = 0.33 (33%).
    • Kiểm tra hRet nằm trong khoảng [hMin, hMax]: Nếu hMin=0.12, hMax=0.55, thì 0.33 nằm trong khoảng → hợp lệ.
    • Kiểm tra vị trí quai cốc: Đối với mô hình tăng, Hp phải < rimPrice (quai cốc phải thấp hơn miệng cốc). Đối với mô hình giảm, Hp phải > rimPrice.
  • Bước 5 - Kiểm tra Breakout (nếu bật needConfirm):
    • Nếu needConfirm=true, chỉ báo chỉ kích hoạt khi giá đóng cửa (close) vượt qua rimPrice.
    • Mô hình tăng: close > rimPrice.
    • Mô hình giảm: close < rimPrice.
    • Nếu needConfirm=false, bỏ qua bước này, kích hoạt ngay khi mô hình hình thành.
  • Bước 6 - Loại bỏ trùng lặp:
    • Kiểm tra xem mô hình mới có trùng với mô hình trước đó không (dựa trên vị trí rim và giá rim).
    • Nếu trùng (sai lệch < 0.15%), bỏ qua để tránh vẽ nhiều lần.

🎨 Flow 2: Vẽ mô hình lên biểu đồ

  • Vẽ đường cong "Cốc":
    • Tạo 3 điểm: điểm 1 (Li, rimPrice), điểm 2 (midBar, Bp), điểm 3 (Ri, rimPrice).
    • Dùng hàm polyline.new() với tham số curved=true để vẽ đường cong nối 3 điểm này.
    • Màu đường và màu tô được chọn dựa trên mô hình tăng/giảm (bgBull/lnBull hoặc bgBear/lnBear).
  • Vẽ đường cong "Quai cốc":
    • Tạo 3 điểm: điểm 1 (Ri, rimPrice), điểm 2 (Hi, Hp), điểm 3 (projBar, rimPrice).
    • projBar = Hi + (Hi - Ri), tức là kéo dài quai cốc về phía trước để tạo hình quai hoàn chỉnh.
    • Vẽ tương tự như cốc, dùng polyline cong.
  • Vẽ đường mục tiêu (Target Line):
    • Nếu showTarget=true, vẽ đường ngang từ Ri đến Hi+25 nến, tại mức giá tgtPrice.
    • tgtPrice = Rp + (depth × targetMult) đối với mô hình tăng, hoặc Rp - (depth × targetMult) đối với mô hình giảm.
    • Ví dụ: Rp=98$, depth=18$, targetMult=0.5 → tgtPrice = 98 + (18×0.5) = 107$.
  • Vẽ nhãn tên mô hình:
    • Nếu showName=true, vẽ nhãn "Cup+Handle" hoặc "Inv Cup+Handle" tại giữa cốc (midBar, rimPrice).

🔔 Flow 3: Kích hoạt Alert

  • Khi mô hình hợp lệ được phát hiện, biến newSignal = true và newSide = +1 (tăng) hoặc -1 (giảm).
  • Hàm alertcondition() kiểm tra điều kiện này và kích hoạt alert tương ứng.
  • Alert "Cup & Handle (Brewed)": Kích hoạt khi newSignal=true và newSide=+1.
  • Alert "Inverted Cup & Handle (Brewed)": Kích hoạt khi newSignal=true và newSide=-1.

Đầu ra & vai trò trong sử dụng

  • Đường cong Cup & Handle: Giúp trader nhận diện trực quan mô hình trên biểu đồ, xác định vùng tích lũy và điểm breakout tiềm năng.
  • Đường Target Line: Chỉ mức giá mục tiêu để chốt lời, dựa trên lý thuyết độ sâu cốc.
  • Mức Invalidation (invPrice): Mức giá mà nếu chạm vào, mô hình bị phá vỡ. Dùng để đặt Stop Loss. Ví dụ: invPrice = Rp - (depth × 0.618) cho mô hình tăng.
  • Alert: Thông báo kịp thời khi mô hình xuất hiện, giúp trader không bỏ lỡ cơ hội.
💡 Lưu ý: Chỉ báo này hoạt động tốt nhất trên khung thời gian lớn (H4, Daily, Weekly) và trên các tài sản có thanh khoản tốt. Nên kết hợp với các chỉ báo khác (volume, RSI, MACD) để xác nhận tín hiệu.
Zoom/Pan Layered Image
Background Overlay
+
Isolated Scroll Container

Kết Quả Backtest - Chiến Lược Cup & Handle

Phân tích hiệu suất và cài đặt tối ưu cho chiến lược giao dịch

Phát Triển Chiến Lược

Để tìm ra phương án tối ưu nhất khi vào lệnh và quản lý rủi ro cho chỉ báo này, mình đã chuyển đổi chỉ báo thành chiến lược/strategy trên TradingView với đầy đủ các chức năng quản lý vốn và rủi ro chuyên nghiệp.

Các Tính Năng Chính Của Strategy

🎯 Quản Lý Vào Lệnh

  • Bật/Tắt Strategy: Bật hoặc tắt thực thi chiến lược
  • Kiểm Soát Hướng: Cho phép vị thế LONG, SHORT, hoặc cả hai
  • Loại Lệnh Vào: Chọn giữa lệnh Market hoặc Limit
  • Thoát Khi Tín Hiệu Ngược: Tự động đóng vị thế khi có tín hiệu ngược chiều xuất hiện

💰 Quản Lý Kích Thước Vị Thế

  • Loại Kích Thước: Fixed Contracts (số lượng cố định) hoặc Percentage of Equity (phần trăm vốn)
  • Phần Trăm Vốn: Đặt phần trăm vốn sử dụng cho mỗi giao dịch

🛡️ Quản Lý Stop Loss

  • Loại Stop Loss: Swing Point (dựa trên chu kỳ swing lookback)
  • Nhìn Lại Swing: Số nến nhìn lại để phát hiện điểm swing
  • Spread Điều Chỉnh: Khoảng cách đệm bổ sung cho stop loss

🎯 Quản Lý Take Profit

  • Tỷ Lệ Risk:Reward: Đặt TP dựa trên khoảng cách SL (ví dụ: RR = 4 nghĩa là TP gấp 4 lần SL)

⚡ Bảo Vệ Break Even

  • Kích Hoạt BE Tại RR: Tự động chuyển SL về break even khi giá đạt mức RR nhất định
  • Offset Từ Entry: Khoảng cách đệm từ giá vào lệnh

📊 Trực Quan Hóa

  • Hiển Thị Box TP/SL: Hiển thị các hộp trực quan cho mức take profit và stop loss
  • Màu Tùy Chỉnh: Màu riêng biệt cho hộp TP và SL
  • Độ Dài Box: Thời lượng hiển thị hộp trực quan tính bằng số nến

Kết Quả Hiệu Suất

Sau khi backtest kỹ lưỡng, chiến lược đã đạt được các chỉ số hiệu suất ấn tượng:

Tỷ Lệ Thắng
39.20%
Tổng P&L
+$38,269.22
Lợi Nhuận %
+3.82%
Max Drawdown
$3,814.09
Drawdown %
0.37%
Tổng Giao Dịch
199
Giao Dịch Có Lời
78/199
Profit Factor
1.812

Cài Đặt Tối Ưu

Các cài đặt sau đây đã chứng minh hiệu quả nhất qua quá trình backtest:

Phát Hiện Cup & Handle

Pivot Span
2
Dung Sai Miệng Cốc
18%
Độ Sâu Tối Thiểu Quai Cốc
0.15
Độ Sâu Tối Đa Quai Cốc
0.6
Vô Hiệu Hóa
77%
Yêu Cầu Xác Nhận Breakout
✅ Có
Hệ Số Mục Tiêu
0.5

Cài Đặt Strategy

Enable Strategy
✅ Có
Allow LONG
✅ Có
Allow SHORT
✅ Có
Exit on Opposite Signal
❌ Không
Entry Type
Market

Kích Thước Vị Thế

Position Size Type
Percentage of Equity
Equity Percentage
10%

Stop Loss

Enable Stop Loss
✅ Có
Stop Loss Type
Swing Point
Swing Lookback
10 bars
SL Spread Adjustment
0

Take Profit

Enable Take Profit
✅ Có
Risk:Reward Ratio
4.0

Break Even

Enable Break Even
✅ Có
BE Activation at RR
1.0
Offset from Entry
0

Trực Quan Hóa

Show TP/SL Boxes
✅ Có
Box Length
10 bars

Tài Nguyên

⚠️ Lưu ý quan trọng: Hiệu suất trong quá khứ không đảm bảo kết quả trong tương lai. Luôn kiểm tra chiến lược kỹ lưỡng và sử dụng quản lý rủi ro phù hợp trước khi giao dịch thực.
Resources

📚 Useful Resources

Discover more tools and courses to enhance your trading skills

💡 Tip: Always backtest thoroughly and use proper risk management before applying any strategy to live trading.
				
					// This work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
// https://creativecommons.org/licenses/by-nc-sa/4.0/
// © Zeiierman {
//@version=6
indicator("Cup & Handle (Zeiierman)", overlay=true, max_lines_count=500, max_labels_count=500, max_polylines_count=100)
//}

// ~~ Tooltips {
var string t1  = "Pivot span:\nControls how many bars are used to confirm swing highs/lows.\nHigher = fewer but stronger pivots.\nLower = more signals but more noise."
var string t2  = "Rim similarity tolerance (%):\nHow close the left rim and right rim must be.\nLower = stricter, cleaner cups.\nHigher = more patterns detected."
var string t3  = "Handle retrace min:\nMinimum depth of the handle pullback relative to cup depth.\nToo small = weak handle.\nTypical: 0.10–0.15."
var string t4  = "Handle retrace max:\nMaximum depth of the handle pullback.\nToo large = messy structure.\nTypical: 0.45–0.60."
var string t5  = "Invalidation (handle max retrace %):\nDefines when the pattern fails.\nLower = tighter stop.\nHigher = looser stop."
var string t6  = "Require breakout confirmation:\nIf enabled, pattern triggers only after price breaks above (bull) or below (bear) the rim.\nOff = earlier detection.\nOn = higher quality confirmation."
var string t7  = "Target multiplier (× cup depth):\nControls how far the projection target is.\n1.0 = classic rim ± depth.\n0.3–0.7 = closer targets."
var string t8  = "Show pattern name:\nDisplays a label on the cup structure."
var string t9  = "Bullish fill:\nBackground fill color for bullish cup & handle."
var string t10 = "Bullish border:\nOutline color for bullish cup & handle."
var string t11 = "Bearish fill:\nBackground fill color for inverted (bearish) cup & handle."
var string t12 = "Bearish border:\nOutline color for inverted (bearish) cup & handle."
var string t13 = "Width:\nLine thickness of the cup and handle curves."
var string t14 = "Style:\nLine style of the cup and handle curves."
var string t15 = "Show target line:\nDraws the projected price target line for the pattern."
var string t16 = "Target width:\nThickness of the target projection line."
var string t17 = "Target style:\nLine style of the target projection line."
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Settings {
pivotSpan    = input.int(10, "Pivot span", minval=2, step=1, group="Cup & Handle", tooltip=t1)
rimTolPct    = input.int(18, "Rim similarity tolerance (%)", minval=0, maxval=50, step=1, group="Cup & Handle", tooltip=t2)
hMin         = input.float(0.12, "Handle retrace min", minval=0.01, step=0.01, group="Cup & Handle", tooltip=t3)
hMax         = input.float(0.55, "Handle retrace max", minval=0.05, step=0.01, group="Cup & Handle", tooltip=t4)
killAtFib    = input.float(61.8, "Invalidation (handle max retrace %) ", minval=10, maxval=100, step=0.1, group="Cup & Handle", tooltip=t5) / 100.0
needConfirm  = input.bool(false, "Require breakout confirmation", group="Cup & Handle", tooltip=t6)
targetMult   = input.float(0.5, "Target multiplier (× cup depth)", minval=0.1, maxval=3.0, step=0.05, group="Cup & Handle", tooltip=t7)

showName     = input.bool(true, "Show pattern name", group="Style", tooltip=t8)

bgBull       = input.color(color.new(color.lime, 82), "Bullish fill", group="Style", inline="bull", tooltip=t9)
lnBull       = input.color(color.new(color.lime, 35), "Bullish border", group="Style", inline="bull", tooltip=t10)

bgBear       = input.color(color.new(color.red, 82), "Bearish fill", group="Style", inline="bear", tooltip=t11)
lnBear       = input.color(color.new(color.red, 35), "Bearish border", group="Style", inline="bear", tooltip=t12)

wOk          = input.int(2, "Width", minval=1, group="Style", inline="w1", tooltip=t13)
stOk         = input.string("Solid", "Style", options=["Solid","Dashed","Dotted"], group="Style", inline="w1", tooltip=t14)

showTarget   = input.bool(true, "Show target line", group="Style", tooltip=t15)
targetWidth  = input.int(2, "Target width", minval=1, group="Style", inline="t1", tooltip=t16)
targetStyle  = input.string("Dashed", "Target style", options=["Solid","Dashed","Dotted"], group="Style", inline="t1", tooltip=t17)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Helpers {
f_lineStyle(string s) =>
    switch s
        "Solid"  => line.style_solid
        "Dashed" => line.style_dashed
        "Dotted" => line.style_dotted
        => line.style_solid

f_near(float a, float b, float tolPct) =>
    a == 0 or b == 0 ? false : math.abs(a - b) / math.abs(a) <= tolPct / 100.0

f_depth(float rim, float bowl) =>
    math.abs(rim - bowl)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~  Pivot stream {
type Swing
    float p
    int   i
    int   d   // +1 = high pivot, -1 = low pivot

var Swing[] swings = array.new<Swing>()

f_pushSwing(float price, int idx, int dir) =>
    if array.size(swings) == 0
        array.push(swings, Swing.new(price, idx, dir))
    else
        last = array.get(swings, array.size(swings) - 1)
        if last.d == dir
            better = dir == 1 ? price > last.p : price < last.p
            if better
                array.set(swings, array.size(swings) - 1, Swing.new(price, idx, dir))
        else
            array.push(swings, Swing.new(price, idx, dir))
    if array.size(swings) > 12
        array.shift(swings)

ph = ta.pivothigh(high, pivotSpan, pivotSpan)
pl = ta.pivotlow(low, pivotSpan, pivotSpan)

if not na(ph)
    f_pushSwing(ph, bar_index - pivotSpan, +1)
if not na(pl)
    f_pushSwing(pl, bar_index - pivotSpan, -1)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~  Pattern record + storage {
type BrewPattern
    chart.point[] cupPts
    chart.point[] hdlPts
    polyline cupLine
    polyline hdlLine
    line tgtLine
    label nameTag
    int   side
    int   state      // 1 active, -1 invalid, 2 target hit
    float rimPrice
    float invPrice
    float tgtPrice
    int   rimBar

var BrewPattern[] found = array.new<BrewPattern>()

f_kill(BrewPattern pat) =>
    polyline.delete(pat.cupLine)
    polyline.delete(pat.hdlLine)
    if not na(pat.tgtLine)
        line.delete(pat.tgtLine)
    if not na(pat.nameTag)
        label.delete(pat.nameTag)

f_isDuplicate(int rimIdx, float rimPx) =>
    if array.size(found) == 0
        false
    else
        last = array.get(found, array.size(found) - 1)
        rimIdx == last.rimBar or f_near(rimPx, last.rimPrice, 0.15)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~  Detection {
Swing s0 = na
Swing s1 = na
Swing s2 = na
Swing s3 = na

int n = array.size(swings)
if n >= 4
    s0 := array.get(swings, n - 4)
    s1 := array.get(swings, n - 3)
    s2 := array.get(swings, n - 2)
    s3 := array.get(swings, n - 1)

bool newSignal = false
int  newSide   = 0

if not na(s0) and not na(s1) and not na(s2) and not na(s3)
    isSeqNorm = (s0.d == +1 and s1.d == -1 and s2.d == +1 and s3.d == -1)
    isSeqInv  = (s0.d == -1 and s1.d == +1 and s2.d == -1 and s3.d == +1)

    if isSeqNorm or isSeqInv
        side = isSeqNorm ? +1 : -1

        Lp = s0.p, Li = s0.i
        Bp = s1.p, Bi = s1.i
        Rp = s2.p, Ri = s2.i
        Hp = s3.p, Hi = s3.i

        useFill = side == +1 ? bgBull : bgBear
        useLine = side == +1 ? lnBull : lnBear

        rimPrice = side == +1 ? math.min(Lp, Rp) : math.max(Lp, Rp)
        depth    = f_depth(rimPrice, Bp)
        rimOk    = f_near(Lp, Rp, rimTolPct)
        hRet     = depth == 0 ? 10.0 : math.abs(rimPrice - Hp) / depth
        handleOk = (hRet >= hMin and hRet <= hMax)
        sideOk   = side == +1 ? Hp < rimPrice : Hp > rimPrice
        invPrice = side == +1 ? (Rp - depth * killAtFib) : (Rp + depth * killAtFib)
        tgtPrice  = side == +1 ? (Rp + depth * targetMult) : (Rp - depth * targetMult)
        confirmOk = not needConfirm ? true : (side == +1 ? close > rimPrice : close < rimPrice)
        ok        = rimOk and handleOk and sideOk and confirmOk

        if ok and not f_isDuplicate(Li, rimPrice)
            midBar = Ri - (Ri - Li) / 2

            cupPts = array.new<chart.point>()
            array.push(cupPts, chart.point.from_index(Li, rimPrice))
            array.push(cupPts, chart.point.from_index(midBar, Bp))
            array.push(cupPts, chart.point.from_index(Ri, rimPrice))

            hdlPts = array.new<chart.point>()
            projBar = Hi + (Hi - Ri)
            array.push(hdlPts, chart.point.from_index(Ri, rimPrice))
            array.push(hdlPts, chart.point.from_index(Hi, Hp))
            array.push(hdlPts, chart.point.from_index(projBar, rimPrice))

            cupPL = polyline.new(cupPts, line_color=useLine, fill_color=useFill, line_width=wOk, line_style=f_lineStyle(stOk), curved=true)
            hdlPL = polyline.new(hdlPts, line_color=useLine, fill_color=useFill, line_width=wOk, line_style=f_lineStyle(stOk), curved=true)

            // target line
            extendBars = 25
            line tgtLn = na
            if showTarget
                tgtCol = side == +1 ? color.new(color.lime, 0) : color.new(color.red, 0)
                tgtLn := line.new(Ri, tgtPrice, Hi + extendBars, tgtPrice, color=tgtCol, width=targetWidth, style=f_lineStyle(targetStyle))

            label tag = na
            if showName
                txt = side == +1 ? "Cup+Handle" : "Inv Cup+Handle"
                tagCol = side == +1 ? color.new(color.lime, 70) : color.new(color.red, 70)
                tag := label.new(midBar, rimPrice, txt, style=label.style_label_center, textcolor=color.white, color=tagCol, size=size.small)

            rec = BrewPattern.new(
                 cupPts, hdlPts,
                 cupPL, hdlPL,
                 tgtLn,
                 tag,
                 side, 1,
                 rimPrice, invPrice, tgtPrice,
                 Li
            )
            array.push(found, rec)

            newSignal := true
            newSide   := side

if array.size(found) > 0
    last = array.get(found, array.size(found)-1)

    if last.state == 1
        invHit = (last.side == +1 and low <= last.invPrice) or
                 (last.side == -1 and high >= last.invPrice)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~  Alerts {
alertcondition(newSignal and newSide == +1, title="Cup & Handle (Brewed)", message="Cup & Handle detected on {{ticker}}")
alertcondition(newSignal and newSide == -1, title="Inverted Cup & Handle (Brewed)", message="Inverted Cup & Handle detected on {{ticker}}")
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
				
			

Indicator Insider

04/02/026 Cup & Handle

Cup & Handle Strategy – Advanced TradingView Trading Bot with Automated Risk Management

Discover the Cup & Handle Pattern Strategy, a professional PineScript trading bot for TradingView that automatically identifies classic cup and handle chart patterns. This automated trading strategy combines technical pattern recognition with comprehensive risk management features including

27/01/026 Spring & Upthrust Trap

Phân tích backtest chiến lược Spring & Upthrust Trap với winrate 34.9%, lợi nhuận +4.95%, profit factor 2.021. Hướng dẫn cài đặt tối ưu và quản lý rủi ro hiệu quả.