Isolated Scroll Container

1. Mô tả chỉ báo Ranked Order Block Zones (Zeiierman)

Chỉ báo phát hiện các nến "Order Block" từ mẫu hình 2 nến đảo chiều đơn giản, chấm điểm và xếp hạng từng vùng, chỉ hiển thị những vùng chất lượng cao nhất trên biểu đồ

1.1 Concept của chỉ báo

Order Block (OB - Khối lệnh) là một khái niệm thuộc trường phái Smart Money Concepts (SMC): cây nến ngược màu cuối cùng ngay trước một cây nến "flip" (đảo chiều) mạnh mẽ, dứt khoát, được cho là đánh dấu chính xác vị trí mà các tay chơi lớn (institutional/smart money) đặt lệnh trước khi đẩy giá mạnh sang hướng ngược lại. Vì một phần các lệnh lớn đó được cho là chưa được khớp hết, giá được kỳ vọng sẽ phản ứng trở lại khi quay về kiểm tra đúng dải giá của cây nến đó sau này.

  • Nhận diện đơn giản bằng 2 nến: khác với các chỉ báo S/R dựa trên swing cần nhiều nến để xác nhận pivot, chỉ báo này đánh dấu một Order Block ngay lập tức khi thấy một mẫu hình đảo chiều nhỏ: một cây nến một màu, ngay sau đó là một cây nến màu ngược lại có thân nến bất thường lớn (đo theo ATR). Cây nến đầu tiên (cây nến ngược màu) chính là vùng Order Block.
  • Chất lượng, không chỉ là sự tồn tại: giống như không phải pivot S/R nào cũng đáng chú ý, không phải mẫu hình 2 nến flip nào cũng là một Order Block có ý nghĩa. Mỗi OB được phát hiện đều được chấm điểm dựa trên kích thước, khối lượng đứng sau nó, và việc giá hiện tại có đang di chuyển theo hướng ủng hộ nó hay không (thuận xu hướng).
  • Xếp hạng, không phải cộng dồn: mỗi nến, toàn bộ Order Block đang lưu trữ được sắp xếp lại theo điểm chất lượng, chỉ Top-N vùng mới thực sự được vẽ ra - các vùng chất lượng thấp hơn vẫn nằm trong bộ nhớ (phòng khi leo hạng trở lại) nhưng bị ẩn đi.
  • Bị "tiêu thụ", không phải bị "vỡ": một Order Block không bị vô hiệu hoá bởi một cú đóng cửa vượt qua nó như cách một vùng S/R bị phá vỡ - nó dần dần bị mitigation (ăn sâu) khi giá quay lại giao dịch trong dải giá của nó, và chỉ biến mất hoàn toàn khi bị lấp đầy 100% (mitigation đầy đủ), phản ánh ý tưởng rằng các lệnh trong khối đó giờ đã được khớp hết.

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

  • Hiển thị Box Order Block:
    • Ô màu xanh lá (mặc định) = Order Block tăng (đóng vai trò như hỗ trợ, kỳ vọng giữ giá đi lên), ô màu đỏ (mặc định) = Order Block giảm (đóng vai trò như kháng cự, kỳ vọng giữ giá đi xuống). Cả hai màu đều có thể tuỳ chỉnh.
    • Mỗi box trải theo toàn bộ dải giá cao-thấp (high-low) của cây nến Order Block và kéo dài theo thời gian khi khối vẫn còn hoạt động.
    • Chỉ những khối xếp hạng cao nhất (điều chỉnh bằng "Show Top Zones") mới được tô màu đầy đủ; phần còn lại vẫn tồn tại trong bộ nhớ nhưng hoàn toàn trong suốt.
  • Thanh sức mạnh (Strength Bars):
    • Bên trong mỗi khối đang hiển thị, nửa trên có thể hiện thanh màu đỏ kèm % phe bán (bear), nửa dưới hiện thanh màu xanh kèm % phe mua (bull). Khác với một "đồng hồ đo độ tin cậy" sống động, hai con số phần trăm này bị cố định ngay tại thời điểm khối được tạo ra và không bao giờ thay đổi sau đó (xem mục 1.4).
    • Chiều dài thanh tỉ lệ thuận với phần trăm.
  • Nhãn chữ trong vùng: chỉ đơn giản là nhãn "Bullish OB" hoặc "Bearish OB" được in bên trong khối. Khác với một số chỉ báo vùng giá khác, nhãn này không thay đổi theo chất lượng hay mức độ mitigation - nó chỉ nêu hướng của khối.
  • Bộ lọc hướng: chỉ báo có thể được đặt để chỉ hiện "All" (tất cả), "Bullish Only" (chỉ khối tăng), hoặc "Bearish Only" (chỉ khối giảm).
  • Alert (cảnh báo): 6 điều kiện cảnh báo - OB tăng/giảm mới hình thành, OB dẫn đầu bảng xếp hạng mới, giá chạm OB tăng/giảm, và OB bị mitigation hoàn toàn - mỗi loại đều có công tắc bật/tắt riêng trong phần Inputs.

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

  • Ưu tiên giao dịch các khối xếp hạng cao nhất: vì khối được xếp hạng lại mỗi nến theo chất lượng, những khối đang được vẽ trên biểu đồ của bạn là những khối mà chỉ báo đánh giá là quan trọng nhất hiện tại - hãy coi đây là các vùng phản ứng chính.
  • Đọc % sức mạnh tại thời điểm tạo: vì bull%/bear% bị đóng băng ngay khi khối hình thành, chúng thể hiện độ mạnh của cây nến flip ban đầu (kích thước, khối lượng, thuận xu hướng, độ "sạch" của nến) - chứ không phải khối đang hoạt động ra sao ở hiện tại. Một OB vừa hình thành với chỉ số 90% là một cú flip rất dứt khoát; một khối 30% là một phản ứng yếu hơn nhiều.
  • Dùng alert "Bullish/Bearish OB Touch" làm cảnh báo sớm: alert này bắn ra ngay khi giá bắt đầu giao dịch trở lại vào dải giá của một khối - hữu ích để chuẩn bị cho một cú bật lại (hoặc một cú thất bại) tại mức giá đó.
  • Dùng "OB Fully Mitigated" để biết một mức giá đã "hết": khi một Order Block bị lấp đầy 100%, nó bị xoá hẳn - không có cảnh báo trực quan kiểu "yếu/đã mitigation một phần" như một số chỉ báo S/R khác; bạn chỉ biết nó đã dùng hết khi nó biến mất (hoặc qua alert này).
  • Dùng "New Top-Ranked OB" để theo dõi mức quan trọng nhất: bắn ra mỗi khi khối #1 theo điểm chất lượng thay đổi.
  • Điều chỉnh theo thị trường/khung thời gian: tăng "Min Flip Body ATR" trên các khung thời gian nhiễu/thấp để chỉ những cây nến flip thực sự quyết liệt mới đạt điều kiện; giảm xuống để bắt được nhiều cú đảo chiều hơn (nhưng yếu hơn). Tắt "Merge Overlapping OBs" nếu bạn muốn thấy mỗi cú flip là một khối riêng biệt thay vì các vùng đã được gộp lại.

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

Đầu vào & vai trò

  • maxZones ("Show Top Zones", số nguyên, mặc định: 10, khoảng 1-20): số lượng Order Block có điểm chất lượng cao nhất thực sự được vẽ lên biểu đồ. Ví dụ: nếu đang lưu 40 khối và maxZones=10, chỉ 10 khối điểm cao nhất được tô màu; phần còn lại vẫn ẩn cho tới khi đủ điểm để lọt vào top. Tăng → nhiều khối hiển thị cùng lúc hơn. Giảm → chỉ những khối tốt nhất được hiện.
  • maxStored ("Max Stored OBs", số nguyên, mặc định: 50, khoảng 10-200): số lượng Order Block tối đa được giữ trong bộ nhớ (cả hiển thị lẫn ẩn). Khi vượt quá, khối điểm chất lượng thấp nhất bị xoá trước. Tăng → nhớ được nhiều khối lịch sử hơn. Giảm → tốn ít bộ nhớ hơn, các khối cũ điểm thấp bị quên nhanh hơn.
  • volLength ("Volume MA Length", số nguyên, mặc định: 20): số nến nhìn lại để tính khối lượng trung bình nền (ta.sma(volume, volLength)) mà khối lượng của nến OB mới sẽ được so sánh vào. Ví dụ: khối lượng trung bình 20 nến = 1.000, khối lượng nến OB = 1.800 → điểm khối lượng = 1.8 (gấp 1.8 lần trung bình), giúp tăng điểm chất lượng và thành phần điểm sức mạnh theo khối lượng của khối. Tăng → đường nền mượt hơn, thay đổi chậm hơn. Giảm → đường nền phản ứng nhanh hơn với biến động khối lượng gần đây.
  • trendLength ("Trend EMA Length", số nguyên, mặc định: 50): độ dài EMA dùng để đánh giá sự thuận xu hướng. Một OB tăng được coi là "thuận" khi giá đóng cửa hiện tại nằm trên EMA này tại thời điểm tạo khối; một OB giảm thuận khi giá đóng cửa nằm dưới EMA. Các khối thuận xu hướng được cộng một khoản điểm thưởng cố định. Tăng → bộ lọc xu hướng phản ứng theo thiên hướng dài hạn hơn. Giảm → phản ứng theo thiên hướng ngắn hạn hơn.
  • minFlipBodyAtr ("Min Flip Body ATR", số thập phân, mặc định: 1.1, tối thiểu 0.05): kích thước thân tối thiểu mà cây nến "flip" (cây nến đảo chiều mạnh, không phải cây nến OB) phải có, tính theo bội số ATR, để một mẫu hình 2 nến đủ điều kiện trở thành Order Block. Ví dụ: ATR=$10, minFlipBodyAtr=1.1 → thân nến flip (|close-open|) phải ít nhất $11; một cây nến đảo chiều nhỏ, nửa vời sẽ bị bỏ qua và không có OB nào được tạo. Tăng → chỉ những cây nến flip rất quyết liệt, dứt khoát mới tạo OB (ít khối hơn, độ tin cậy cao hơn). Giảm → nhiều OB được tạo từ các cú đảo chiều nhẹ hơn (nhiều khối hơn, nhiễu hơn).
  • mergeOverlaps ("Merge Overlapping OBs", boolean, mặc định: true): khi bật, một OB mới phát hiện chồng lấn đáng kể với một khối cùng hướng đang có sẽ được gộp vào thay vì tạo box riêng. true → ít khối hơn nhưng rộng hơn, hợp nhất tại các khu vực có nhiều cú flip lặp lại. false → mỗi cú flip trở thành một khối riêng biệt, kể cả khi nhiều khối nằm chồng lên nhau.
  • maxMergeAtr ("Max Merge Size ATR", số thập phân, mặc định: 2.0, tối thiểu 0.5): chiều cao tối đa (tính theo ATR) mà một khối sau khi gộp được phép đạt tới; nếu việc gộp hai khối chồng lấn tạo ra một vùng cao hơn mức này, chúng sẽ được giữ riêng biệt thay vì gộp. Ví dụ: ATR=$10, maxMergeAtr=2.0 → khối sau khi gộp không được vượt quá $20 chiều cao. Tăng → cho phép các khối gộp lớn hơn. Giảm → giữ các vùng gộp gọn hơn, tách thành khối riêng thường xuyên hơn.
  • directionFilter ("Direction Filter", tuỳ chọn: All / Bullish Only / Bearish Only, mặc định: All): giới hạn những khối nào đủ điều kiện được hiển thị (chúng vẫn tồn tại và vẫn được chấm điểm ngầm dù thế nào).
  • showBars (boolean, mặc định: true): hiện/ẩn các thanh sức mạnh bull%/bear% bên trong mỗi khối đang hiển thị.
  • showBlockText (boolean, mặc định: true): hiện/ẩn nhãn chữ "Bullish OB"/"Bearish OB" bên trong mỗi khối đang hiển thị.
  • strengthTextSize / blockTextSize (tuỳ chọn: auto/tiny/small/normal/large/huge, mặc định: auto): cỡ chữ của nhãn % sức mạnh và nhãn chữ của khối. Thuần tuý về hình thức.
  • bullColor / bearColor: màu tô/màu viền cho Order Block tăng và giảm. Thuần tuý về hình thức.
  • alertNewBull / alertNewBear / alertTopRank / alertBullTouch / alertBearTouch / alertFullyMitigated (boolean, tất cả mặc định: true): bật/tắt riêng từng loại trong 6 loại alert. Lưu ý: tắt một loại không làm nó biến mất khỏi danh sách chọn trong hộp thoại "Create Alert" của TradingView - nó chỉ đơn giản là điều kiện alert đó sẽ không bao giờ thực sự kích hoạt khi đang tắt.
💡 Lưu ý về tooltip mồ côi: code gốc định nghĩa một tooltip ("t9 - Khi bật, vùng Order Block chỉ dùng thân nến. Khi tắt, dùng toàn bộ dải giá kể cả râu nến.") nhưng không hề gắn với bất kỳ input thực tế nào. Trong code hiện tại, vùng OB luôn dùng high[1]/low[1] (toàn bộ dải giá kể cả râu nến) cho cả khối tăng lẫn giảm - chưa có chế độ "chỉ thân nến" nào khả dụng, dù tooltip có mô tả một chế độ như vậy. Đây có vẻ là dấu vết của một tuỳ chọn đã được lên kế hoạch nhưng chưa (hoặc không còn) được cài đặt.

Các khối logic chính

🎯 Flow 1: Những gì bạn thấy — Hiển thị Top-N khối xếp hạng cao nhất

  • Mỗi nến, toàn bộ Order Block đang lưu trong bộ nhớ được sắp xếp từ qualityScore cao nhất tới thấp nhất (xem Flow 2 - điểm số này không phải cùng một con số với bull%/bear% hiển thị trên màn hình, xem Flow 3).
  • Hệ thống duyệt qua danh sách đã sắp xếp này và chỉ đánh dấu một khối là "hiển thị" nếu khối đó (a) vượt qua directionFilter và (b) vẫn còn nằm trong maxZones khối đầu tiên đã đếm được.
  • Một khối đang hiển thị được vẽ thành box màu trải theo toàn bộ dải giá cao-thấp của nến OB. Bên trong: nửa trên có thể hiện thanh sức mạnh bên bán theo bearStrength%, nửa dưới hiện thanh sức mạnh bên mua theo bullStrength%, và (nếu bật) nhãn chữ "Bullish OB"/"Bearish OB".
  • Một khối bị ẩn (rớt hạng) không bị xoá - box của nó chỉ đơn giản được tô hoàn toàn trong suốt và xoá chữ đi, để nó có thể âm thầm xuất hiện trở lại nếu điểm chất lượng leo lại vào top-N.

🔧 Flow 2: Cách hoạt động của điểm xếp hạng nội bộ (Quality Score)

  • Lúc tạo khối: qualityScore = obSize×100 + volScore×10 + trendScore×20 - mitigation×50 (mitigation luôn = 0 lúc mới sinh). Khác với thanh bull%/bear%, điểm số này không bị giới hạn trong khoảng 0-100 - đây là một con số nội bộ thô, chỉ dùng để sắp xếp/so sánh các khối với nhau, và có thể lớn hơn 100 rất nhiều trên các mã có biến động mạnh/giá cao.
  • Mỗi nến sau đó: qualityScore = size×100 + volumeScore×10 + trendScore×20 - mitigation×50 - age×0.1. Chú ý thành phần - age×0.1 thêm vào - mỗi nến trôi qua, một khối đang lưu trữ mất đi 0.1 điểm chất lượng, vĩnh viễn (không có giới hạn cho sự suy giảm này, và khác với chỉ báo S/R zones, một khối không bao giờ bị tự động xoá chỉ vì quá cũ - chỉ có mitigation hoàn toàn hoặc vượt maxStored mới xoá nó). Trên thực tế, sự suy giảm đều đặn này có nghĩa là một khối chưa từng bị chạm, chưa từng được gộp cuối cùng sẽ rớt xuống đáy bảng xếp hạng và biến mất khỏi tầm nhìn (dù không mất khỏi bộ nhớ) chỉ đơn giản vì quá cũ, ngay cả khi giá không bao giờ quay lại kiểm tra nó.

💪 Flow 3: Cách tính thanh sức mạnh Bull%/Bear%

  • Được tính một lần duy nhất, tại đúng thời điểm khối được tạo ra, từ 4 thành phần luôn cộng lại tối đa 100: kích thước so với ATR (tối đa 40 điểm, đạt trần khi dải giá nến OB chạm 2× ATR), khối lượng so với trung bình (tối đa 30 điểm, đạt trần ở 2× khối lượng trung bình), thuận xu hướng (20 điểm cố định nếu thuận), và "độ sạch của nến" - tỉ lệ thân nến thực so với toàn bộ dải giá (kể cả râu) của nến OB (tối đa 10 điểm).
  • Ví dụ: một nến OB có dải giá đúng bằng 2× ATR (40/40), khối lượng đúng bằng 2× trung bình (30/30), thuận xu hướng (20/20), và là một cây nến gần như toàn thân, sạch (9/10) sẽ đạt mainStrength ≈ 99/100.
  • Tách thành 2 con số: bullStrength = mainStrength và bearStrength = 100 - mainStrength cho một khối tăng (ngược lại cho khối giảm) - hai phần trăm này luôn cộng lại đúng bằng 100 lúc mới sinh.
  • Sau đó hai con số này bị đóng băng - khác với điểm xếp hạng ở Flow 2, bull%/bear% không được tính lại khi thời gian trôi qua hay khi khối bị mitigation. Cách duy nhất chúng thay đổi sau đó là khi khối được gộp với một OB chồng lấn mới (xem Flow 4), lúc đó mỗi bên độc lập lấy giá trị cao hơn (giữa giá trị cũ và mới) - nghĩa là sau một lần gộp, hai phần trăm có thể không còn cộng lại đúng bằng 100 nữa.

🌱 Flow 4: Order Block ra đời như thế nào — Nhận diện Flip & Gộp

  • Bước 1 - Nhận diện mẫu hình flip 2 nến: chỉ dùng cây nến hiện tại và cây nến ngay trước đó - không cần chờ các nến tương lai để xác nhận, khác với các chỉ báo dựa trên swing pivot. Tăng: nến trước đóng cửa đỏ (close[1] < open[1]) và nến hiện tại đóng cửa xanh (close > open). Giảm: ngược lại (nến trước xanh, nến hiện tại đỏ).
  • Bước 2 - Yêu cầu một cú flip dứt khoát: kích thước thân của cây nến hiện tại (|close-open|) phải ≥ minFlipBodyAtr×ATR, nếu không mẫu hình sẽ bị bỏ qua hoàn toàn.
  • Bước 3 - Dựng khối ứng viên: vùng giá được đặt bằng toàn bộ dải giá cao-thấp của cây nến trước đó (high[1]/low[1]) - chính cây nến trước, ngược màu đó là "Order Block" thực sự. Điểm khối lượng, điểm xu hướng, điểm chất lượng, và bull%/bear% của nó đều được tính như ở Flow 2-3.
  • Bước 4 - Gộp hoặc tạo mới: nếu mergeOverlaps đang bật, khối ứng viên được so sánh với mọi khối đang có cùng hướng. Nó được gộp nếu chồng lấn ít nhất 50% chiều cao của khối nhỏ hơn và vùng kết hợp không vượt quá maxMergeAtr×ATR về chiều cao; khi gộp, vùng mở rộng ra để bao trùm cả hai, mitigation reset về 0, và mỗi trường điểm số lấy giá trị cao hơn giữa hai bên. Nếu không tìm thấy khối phù hợp (hoặc tính năng gộp đang tắt), một khối hoàn toàn mới được tạo và đẩy vào danh sách đang hoạt động.

🔄 Flow 5: Order Block sống và mất như thế nào — Chạm, Mitigation, Giới hạn lưu trữ

  • Chạm: một khối tăng "bị chạm" khi giá thấp nhất (low) của nến hiện tại chạm xuống mép trên của nó (low ≤ ob.top); một khối giảm bị chạm khi giá cao nhất (high) của nến hiện tại chạm lên mép dưới của nó (high ≥ ob.bottom).
  • Mitigation: khi bị chạm, mitigation được tính lại dựa trên mức giá đã đẩy xuyên qua khối từ mép gần tới mép xa (0 = mới vừa chạm mép, 1 = đã lấp đầy hoàn toàn). Điều này trực tiếp làm giảm điểm chất lượng của khối (qua thành phần - mitigation×50) nhưng không làm thay đổi thanh bull%/bear% đã bị đóng băng từ trước.
  • Mitigation đầy đủ = xoá hẳn: ngay khi mitigation đạt 1.0 (lấp đầy 100%), các box của khối bị xoá và nó bị loại khỏi mảng hoàn toàn - không có "dấu vết vùng đã vỡ" nào để lại (khác với một số chỉ báo S/R), và không có cảnh báo chữ "yếu/đã mitigation một phần" trước đó; khối đơn giản là biến mất khi đã bị "dùng hết".
  • Không hết hạn theo tuổi: một khối chưa từng bị chạm và chưa vượt giới hạn lưu trữ có thể, về nguyên tắc, tồn tại vô thời hạn trong bộ nhớ - nó chỉ liên tục mất điểm chất lượng (Flow 2) cho tới khi rớt hẳn khỏi top-N hiển thị.
  • Cắt bớt theo giới hạn lưu trữ: sau khi xếp hạng lại, nếu tổng số khối đang hoạt động vượt quá maxStored, các khối điểm chất lượng thấp nhất sẽ bị xoá lần lượt cho tới khi số lượng trở lại đúng giới hạn.

🔔 Flow 6: Alert (Cảnh báo)

  • New Bullish/Bearish OB: bắn ra ngay khi một khối hoàn toàn mới (không bị gộp) theo hướng đó được tạo ra.
  • New Top-Ranked OB: bắn ra mỗi khi khối #1 theo điểm chất lượng đổi sang danh tính khác.
  • Bullish/Bearish OB Touch: bắn ra mỗi khi giá chạm vào bất kỳ khối đang hoạt động nào theo hướng đó (gộp chung trên toàn bộ khối trong nến đó).
  • OB Fully Mitigated: bắn ra ngay khi bất kỳ khối nào đạt mitigation 100% và bị xoá.
  • Mỗi điều kiện còn được kiểm soát thêm bởi công tắc bật/tắt riêng của nó (xem phần Đầu vào ở trên).

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

  • Box Order Block xếp hạng cao nhất: đầu ra chính - một danh sách rút gọn, đã xếp hạng các vùng flip gần đây quan trọng nhất để theo dõi phản ứng giá.
  • Thanh sức mạnh Bull%/Bear%: một bức ảnh chụp nhanh mức độ thuyết phục của cây nến flip ban đầu tại thời điểm khối hình thành - hữu ích để chấm điểm độ tin cậy ban đầu của khối, không phải để theo dõi trạng thái hiện tại của nó.
  • Nhãn chữ trong vùng: lời nhắc đơn giản về hướng ("Bullish OB"/"Bearish OB").
  • Alert: cho phép theo dõi việc tạo khối, thay đổi xếp hạng, các lần chạm, và mitigation hoàn toàn mà không cần ngồi canh biểu đồ trực tiếp.
💡 Lưu ý: vì thanh sức mạnh hiển thị được đo kích thước dựa trên toàn bộ tuổi đã trôi qua của khối (right - left, tăng dần mỗi nến) trong khi giá trị bull%/bear% lại giữ nguyên từ lúc tạo, một khối cũ chưa từng bị chạm sẽ có thanh sức mạnh trông dài ra theo thời gian trên màn hình dù phần trăm bên dưới không hề thay đổi - độ dài hiển thị của thanh không phải là một chỉ báo sống động về sức mạnh hiện tại, chỉ có con số % in trên đó mới là chỉ số gốc đã bị đóng băng.
Zoom/Pan Layered Image
Background Overlay
− +
				
					// 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("Ranked Order Block Zones (Zeiierman)", overlay = true, max_boxes_count = 300)
//}

// ~~ Tooltips (Đã Việt hóa) {
t1 = "Điều chỉnh số lượng vùng Order Block chất lượng cao nhất được hiển thị trên biểu đồ."
t2 = "Số lượng vùng Order Block tối đa được lưu trong bộ nhớ trước khi các vùng chất lượng thấp nhất bị xoá bớt."
t3 = "Số nến dùng để tính khối lượng trung bình nền. Điểm khối lượng OB so sánh khối lượng nến gốc với mức trung bình này."
t4 = "Độ dài EMA dùng để xác định mức thuận xu hướng. OB tăng nằm trên EMA và OB giảm nằm dưới EMA sẽ được cộng điểm xu hướng mạnh hơn."
t5 = "Hiện hoặc ẩn các thanh sức mạnh bên mua/bên bán bên trong mỗi vùng Order Block."
t6 = "Hiện hoặc ẩn nhãn chữ bên trong mỗi vùng Order Block."
t7 = "Màu dùng cho các vùng Order Block tăng."
t8 = "Màu dùng cho các vùng Order Block giảm."
t9 = "Khi bật, vùng Order Block chỉ dùng thân nến. Khi tắt, dùng toàn bộ dải giá kể cả râu nến." // Lưu ý: chưa gắn với input nào trong code gốc, vùng OB luôn dùng toàn bộ dải giá kể cả râu nến — giữ nguyên như bản gốc, không bổ sung input mới.
t10 = "Bộ lọc hướng tuỳ chọn cho các Order Block hiển thị."
t11 = "Điều chỉnh cỡ chữ dùng cho nhãn phần trăm sức mạnh."
t12 = "Điều chỉnh cỡ chữ dùng cho nhãn chữ Order Block."
t13 = "Khi bật, các Order Block chồng lấn cùng hướng có thể được gộp thành một vùng."
t14 = "Chiều cao tối đa cho phép của OB sau khi gộp, tính theo ATR. Giúp ngăn các vùng chồng lấn gộp lại thành một khối khổng lồ."
t15 = "Kích thước thân tối thiểu bắt buộc của nến flip, tính theo ATR. Giúp logic OB đơn giản này thích ứng linh hoạt trên nhiều mã và khung thời gian."
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Inputs (Đã Việt hóa) {
gRanking = "🏆 Xếp Hạng"
gStrength = "🧮 Cỗ Máy Tính Điểm"
gDetection = "🕯️ Nhận Diện Order Block"
gDisplay = "👁️ Hiển Thị"
gColors = "🎨 Màu Sắc"
gAlerts = "🔔 Cảnh Báo"

maxZones  = input.int(10, "Hiện Top Bao Nhiêu Vùng", minval = 1, maxval = 20, tooltip = t1, group = gRanking)
maxStored = input.int(50, "Số OB Lưu Tối Đa", minval = 10, maxval = 200, tooltip = t2, group = gRanking)

volLength   = input.int(20, "Độ Dài MA Khối Lượng", minval = 1, tooltip = t3, group = gStrength)
trendLength = input.int(50, "Độ Dài EMA Xu Hướng", minval = 1, tooltip = t4, group = gStrength)

minFlipBodyAtr = input.float(1.1, "Thân Nến Flip Tối Thiểu (ATR)", minval = 0.05, step = 0.05, tooltip = t15, group = gDetection)
mergeOverlaps = input.bool(true, "Gộp OB Chồng Lấn", tooltip = t13, group = gDetection)
maxMergeAtr = input.float(2.0, "Kích Thước Gộp Tối Đa (ATR)", minval = 0.5, step = 0.5, tooltip = t14, group = gDetection)

directionFilter = input.string("All", "Bộ Lọc Hướng", options = ["All", "Bullish Only", "Bearish Only"], tooltip = t10, group = gDisplay)
showBars = input.bool(true, "Hiện Thanh Sức Mạnh", tooltip = t5, group = gDisplay)
showBlockText = input.bool(true, "Hiện Chữ OB", tooltip = t6, group = gDisplay)
strengthTextSize = input.string("auto", "Cỡ Chữ % Sức Mạnh", options = ["auto", "tiny", "small", "normal", "large", "huge"], tooltip = t11, group = gDisplay)
blockTextSize = input.string("auto", "Cỡ Chữ Khối", options = ["auto", "tiny", "small", "normal", "large", "huge"], tooltip = t12, group = gDisplay)

bullColor = input.color(color.rgb(8, 153, 129, 48), "OB Tăng", inline = "colors", tooltip = t7, group = gColors)
bearColor = input.color(color.rgb(242, 54, 69, 48), "OB Giảm", inline = "colors", tooltip = t8, group = gColors)

alertNewBull = input.bool(true, "OB Tăng Mới", group = gAlerts)
alertNewBear = input.bool(true, "OB Giảm Mới", group = gAlerts)
alertTopRank = input.bool(true, "OB Dẫn Đầu Mới", group = gAlerts)
alertBullTouch = input.bool(true, "Chạm OB Tăng", group = gAlerts)
alertBearTouch = input.bool(true, "Chạm OB Giảm", group = gAlerts)
alertFullyMitigated = input.bool(true, "OB Đã Mitigation Hoàn Toàn", group = gAlerts)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ UDT {
type OB
    float qualityScore
    float top
    float bottom
    int direction
    int bornBar
    int leftTime
    float size
    float mitigation
    float volumeScore
    float trendScore
    int bullStrength
    int bearStrength
    box body
    box bullBar
    box bearBar
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Helpers {
volumeMA = ta.sma(volume, volLength)
trendEMA = ta.ema(close, trendLength)
atrValue = ta.atr(14)
barMs = timeframe.in_seconds(timeframe.period) * 1000

method isVisibleDirection(OB ob) =>
    directionFilter == "All" or directionFilter == "Bullish Only" and ob.direction == 1 or directionFilter == "Bearish Only" and ob.direction == -1

method overlapsWith(OB ob, float newTop, float newBottom, int newDirection) =>
    float overlapTop = math.min(ob.top, newTop)
    float overlapBottom = math.max(ob.bottom, newBottom)
    float overlapSize = math.max(overlapTop - overlapBottom, 0)

    float existingSize = math.max(ob.top - ob.bottom, syminfo.mintick)
    float newSize = math.max(newTop - newBottom, syminfo.mintick)
    float smallestSize = math.min(existingSize, newSize)

    float mergedTop = math.max(ob.top, newTop)
    float mergedBottom = math.min(ob.bottom, newBottom)
    float mergedSize = mergedTop - mergedBottom

    bool sameDirection = ob.direction == newDirection
    bool hasOverlap = overlapSize > 0
    bool meaningfulOverlap = overlapSize / smallestSize >= 0.50
    bool mergeNotTooLarge = mergedSize <= atrValue * maxMergeAtr

    sameDirection and hasOverlap and meaningfulOverlap and mergeNotTooLarge
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Simple Candle Flip + ATR Body OB Logic {
flipBodySize = math.abs(close - open)
validFlipBody = flipBodySize >= atrValue * minFlipBodyAtr

bullishOB = close[1] < open[1] and close > open and validFlipBody
bearishOB = close[1] > open[1] and close < open and validFlipBody
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Variables {
var array<OB> obs = array.new<OB>()
var string topObKey = ""

newBullObAlert = false
newBearObAlert = false
newTopRankAlert = false
bullTouchAlert = false
bearTouchAlert = false
fullyMitigatedAlert = false
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Create Order Blocks {
if bullishOB or bearishOB
    int direction = bullishOB ? 1 : -1

    float top = na
    float bottom = na

    if direction == 1
        top := high[1]
        bottom := low[1]
    else
        top := high[1]
        bottom := low[1]

    float obSize = math.abs(top - bottom)
    float volScore = volumeMA != 0 ? volume[1] / volumeMA : 1.0
    float trendScore = direction == 1 and close > trendEMA ? 1.0 : direction == -1 and close < trendEMA ? 1.0 : 0.0
    float mitigation = 0.0

    float qualityScore = obSize * 100 + volScore * 10 + trendScore * 20 - mitigation * 50

    float sizeStrength = math.min(obSize / atrValue, 2.0) / 2.0 * 40
    float volStrength = math.min(volScore, 2.0) / 2.0 * 30
    float trendStrength = trendScore * 20
    float candleStrength = math.abs(close[1] - open[1]) / math.max(high[1] - low[1], syminfo.mintick) * 10

    float totalStrength = sizeStrength + volStrength + trendStrength + candleStrength
    int mainStrength = int(math.max(math.min(totalStrength, 100), 0))

    int bullStrength = direction == 1 ? mainStrength : 100 - mainStrength
    int bearStrength = direction == -1 ? mainStrength : 100 - mainStrength

    newBullObAlert := direction == 1
    newBearObAlert := direction == -1

    bool merged = false

    if mergeOverlaps and obs.size() > 0
        for i = 0 to obs.size() - 1
            OB existing = obs.get(i)

            if existing.overlapsWith(top, bottom, direction) and not merged
                existing.top := math.max(existing.top, top)
                existing.bottom := math.min(existing.bottom, bottom)
                existing.size := math.abs(existing.top - existing.bottom)
                existing.qualityScore := math.max(existing.qualityScore, qualityScore)
                existing.volumeScore := math.max(existing.volumeScore, volScore)
                existing.trendScore := math.max(existing.trendScore, trendScore)
                existing.mitigation := 0.0
                existing.bullStrength := math.max(existing.bullStrength, bullStrength)
                existing.bearStrength := math.max(existing.bearStrength, bearStrength)

                float mergedMid = math.avg(existing.top, existing.bottom)

                box.set_top(existing.body, existing.top)
                box.set_bottom(existing.body, existing.bottom)

                box.set_top(existing.bearBar, existing.top)
                box.set_bottom(existing.bearBar, mergedMid)

                box.set_top(existing.bullBar, mergedMid)
                box.set_bottom(existing.bullBar, existing.bottom)

                obs.set(i, existing)
                merged := true

    if not merged
        color bodyColor = direction == 1 ? bullColor : bearColor

        int leftTime = time[1]
        int rightTime = time + barMs * 5

        box body = box.new(
             left = leftTime,
             right = rightTime,
             top = top,
             bottom = bottom,
             xloc = xloc.bar_time,
             bgcolor = color.new(bodyColor, 78),
             border_color = color.new(bodyColor, 25)
        )

        float mid = math.avg(top, bottom)

        box bearBar = box.new(
             left = leftTime,
             right = leftTime,
             top = top,
             bottom = mid,
             xloc = xloc.bar_time,
             bgcolor = bearColor,
             border_color = na
        )

        box bullBar = box.new(
             left = leftTime,
             right = leftTime,
             top = mid,
             bottom = bottom,
             xloc = xloc.bar_time,
             bgcolor = bullColor,
             border_color = na
        )

        obs.push(OB.new(
             qualityScore,
             top,
             bottom,
             direction,
             bar_index,
             leftTime,
             obSize,
             mitigation,
             volScore,
             trendScore,
             bullStrength,
             bearStrength,
             body,
             bullBar,
             bearBar
        ))
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Update Order Blocks {
if obs.size() > 0
    for i = obs.size() - 1 to 0
        OB ob = obs.get(i)

        int age = bar_index - ob.bornBar

        bool touched = ob.direction == 1 ? low <= ob.top : high >= ob.bottom

        bullTouchAlert := bullTouchAlert or touched and ob.direction == 1
        bearTouchAlert := bearTouchAlert or touched and ob.direction == -1

        if touched
            float fillDistance = ob.direction == 1 ? ob.top - low : high - ob.bottom
            float zoneSize = math.max(ob.top - ob.bottom, syminfo.mintick)
            ob.mitigation := math.min(math.max(fillDistance / zoneSize, 0), 1)

        ob.qualityScore := ob.size * 100 + ob.volumeScore * 10 + ob.trendScore * 20 - ob.mitigation * 50 - age * 0.1

        box.set_right(ob.body, time + barMs * 25)

        if ob.mitigation >= 1
            fullyMitigatedAlert := true
            box.delete(ob.body)
            box.delete(ob.bullBar)
            box.delete(ob.bearBar)
            obs.remove(i)
        else
            obs.set(i, ob)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Sort OBs by quality score {
obs.sort(order.descending, sort_field = "qualityScore")

if obs.size() > 0
    OB topOb = obs.get(0)
    string currentTopKey = str.tostring(topOb.leftTime) + "_" + str.tostring(topOb.direction)

    newTopRankAlert := currentTopKey != topObKey
    topObKey := currentTopKey
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Limit Stored Zones {
while obs.size() > maxStored
    OB removed = obs.pop()
    box.delete(removed.body)
    box.delete(removed.bullBar)
    box.delete(removed.bearBar)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Display Zones {
int visibleCount = 0

if obs.size() > 0
    for i = 0 to obs.size() - 1
        OB ob = obs.get(i)

        bool passDirection = ob.isVisibleDirection()
        bool showZone = passDirection and visibleCount < maxZones

        if showZone
            visibleCount += 1

        color bodyColor = ob.direction == 1 ? bullColor : bearColor

        int left = ob.leftTime
        int right = time + barMs * 25

        float top = box.get_top(ob.body)
        float bot = box.get_bottom(ob.body)
        float mid = math.avg(top, bot)

        int barMaxWidth = int((right - left) * 0.45)
        int bullWidth = int(barMaxWidth * ob.bullStrength / 100)
        int bearWidth = int(barMaxWidth * ob.bearStrength / 100)

        box.set_bgcolor(ob.body, showZone ? color.new(bodyColor, 78) : color.new(color.gray, 100))
        box.set_border_color(ob.body, showZone ? color.new(bodyColor, 25) : color.new(color.gray, 100))
        box.set_right(ob.body, right)

        if showZone and showBars
            box.set_left(ob.bearBar, left)
            box.set_right(ob.bearBar, left + bearWidth)
            box.set_top(ob.bearBar, top)
            box.set_bottom(ob.bearBar, mid)
            box.set_bgcolor(ob.bearBar, color.new(bearColor, 10))
            box.set_border_color(ob.bearBar, na)
            box.set_text(ob.bearBar, str.tostring(ob.bearStrength) + "%")
            box.set_text_color(ob.bearBar, color.white)
            box.set_text_halign(ob.bearBar, text.align_center)
            box.set_text_valign(ob.bearBar, text.align_center)
            box.set_text_size(ob.bearBar, strengthTextSize)

            box.set_left(ob.bullBar, left)
            box.set_right(ob.bullBar, left + bullWidth)
            box.set_top(ob.bullBar, mid)
            box.set_bottom(ob.bullBar, bot)
            box.set_bgcolor(ob.bullBar, color.new(bullColor, 10))
            box.set_border_color(ob.bullBar, na)
            box.set_text(ob.bullBar, str.tostring(ob.bullStrength) + "%")
            box.set_text_color(ob.bullBar, color.white)
            box.set_text_halign(ob.bullBar, text.align_center)
            box.set_text_valign(ob.bullBar, text.align_center)
            box.set_text_size(ob.bullBar, strengthTextSize)

            string labelText = ob.direction == 1 ? "Bullish OB" : "Bearish OB"

            box.set_text(ob.body, showBlockText ? labelText : "")
            box.set_text_halign(ob.body, text.align_right)
            box.set_text_valign(ob.body, text.align_center)
            box.set_text_color(ob.body, chart.fg_color)
            box.set_text_size(ob.body, blockTextSize)
        else
            box.set_bgcolor(ob.bullBar, color.new(color.gray, 100))
            box.set_bgcolor(ob.bearBar, color.new(color.gray, 100))
            box.set_border_color(ob.bullBar, color.new(color.gray, 100))
            box.set_border_color(ob.bearBar, color.new(color.gray, 100))
            box.set_text(ob.bullBar, "")
            box.set_text(ob.bearBar, "")
            box.set_text(ob.body, "")
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Alerts (Đã Việt hóa) {
alertcondition(alertNewBull and newBullObAlert, "OB Tăng Mới", "Một Order Block tăng mới vừa hình thành.")
alertcondition(alertNewBear and newBearObAlert, "OB Giảm Mới", "Một Order Block giảm mới vừa hình thành.")
alertcondition(alertTopRank and newTopRankAlert, "OB Dẫn Đầu Mới", "Một Order Block mới đã trở thành vùng chất lượng cao nhất đang hiển thị.")
alertcondition(alertBullTouch and bullTouchAlert, "Chạm OB Tăng", "Giá đã chạm hoặc bắt đầu ăn sâu vào một Order Block tăng.")
alertcondition(alertBearTouch and bearTouchAlert, "Chạm OB Giảm", "Giá đã chạm hoặc bắt đầu ăn sâu vào một Order Block giảm.")
alertcondition(alertFullyMitigated and fullyMitigatedAlert, "OB Đã Mitigation Hoàn Toàn", "Một Order Block đã bị mitigation hoàn toàn và bị xoá.")
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

				
			

Indicator Insider

Chỉ Báo TP RSI – Strategy

RSI mở rộng: vùng trung tính thích nghi theo biến động riêng của RSI, dải ribbon 5 lớp EMA đắp lên chính RSI, bộ phát hiện phân kỳ Regular/Hidden, và 1 dải Bollinger Bands thứ 2 hiển thị trực quan.

Chỉ Báo ICT Anchored Market Structures with Validation – Strategy

Chỉ báo theo phương pháp ICT (Inner Circle Trader) theo dõi cấu trúc thị trường ở 3 cấp độ lồng nhau (Ngắn/Trung/Dài hạn), xác định BoS/CHoCH tại mỗi cấp, và tự động phát hiện breakout giả (liquidity sweep) trước khi công nhận là phá vỡ cấu trúc thật.

Chỉ Báo Trend Duration Forecast – Strategy

Xác định xu hướng qua độ dốc đường Hull Moving Average (HMA), đo thời lượng thực tế của mỗi xu hướng, lưu lại lịch sử thời lượng theo từng hướng, rồi dùng trung bình lịch sử đó để “dự báo” xu hướng hiện tại có thể kéo dài thêm bao