📊 Chấm Điểm Chất Lượng FVG | Flux Charts
Phân Tích Chỉ Báo · Phiên Bản Tiếng Việt · PineScript v6
1.1 Khái Niệm Cốt Lõi
FVG Quality Scorer đánh giá mỗi vùng Fair Value Gap (FVG / vùng mất cân bằng) qua 4 trục chấm điểm độc lập, mỗi trục tối đa 25 điểm, tổng hợp thành điểm tổng 0–100 và xếp hạng A/B/C/D. Thay vì coi mọi FVG ngang bằng, chỉ báo lọc ra các vùng FVG chất lượng cao nhất — chỉ hiển thị các setup xứng đáng được giao dịch.
FVG tăng hình thành khi low > high[2] (khoảng trống giữa đáy nến hiện tại và đỉnh nến cách 2 nến). FVG giảm khi high < low[2]. Đây là cấu trúc mất cân bằng 3 nến tiêu chuẩn theo phương pháp ICT/SMC.
1.2 Tính Năng Chính
- 📐 4 trục chấm điểm — Độ Dịch Chuyển · Delta Khối Lượng · Bối Cảnh · Cấu Trúc, mỗi trục 0–25 điểm
- 🏆 Xếp hạng A/B/C/D — điểm tối thiểu và trục bắt buộc có thể cấu hình riêng từng cấp
- ⚖️ Tổng hợp có trọng số — trọng số do người dùng định nghĩa, tự động chuẩn hóa về 100
- 📊 Delta khối lượng nội nến (LTF) — tỷ lệ khối lượng tăng/giảm trong nến via request.security_lower_tf
- 🕰️ Lồng ghép FVG đa khung — kiểm tra FVG hiện tại có nằm trong FVG khung thời gian cao hơn không
- 🎯 Nhận diện Killzone — cộng điểm cho FVG hình thành trong phiên Á/London/NY
- 📈 Theo dõi BOS/CHoCH — thưởng điểm cho FVG cùng hướng với phá vỡ cấu trúc gần nhất
- 🔄 Máy trạng thái — Fresh → Tested / PartiallyFilled → Mitigated, hiển thị lịch sử tùy chọn
- 🖼️ Vẽ trì hoãn — toàn bộ box/nhãn chỉ vẽ tại barstate.islast để tối ưu hiệu năng
- 📋 Bảng thông tin — số FVG hoạt động/tổng/thời gian lấp trung bình per cấp + khoảng cách FVG-A gần nhất
- 🚨 Cảnh báo — phát hiện FVG mới cấp A và cấp B
1.3 Hướng Dẫn Sử Dụng
⚙️ Các Tham Số Đầu Vào| Tham Số | Mặc Định | Ý Nghĩa |
|---|---|---|
| swingLen | 5 | Số nến mỗi bên để xác nhận swing cao/thấp cho cấu trúc thị trường |
| atrLen | 14 | Chu kỳ ATR cho chấm điểm độ dịch chuyển |
| ltfTf | "1" | Khung thời gian thấp hơn để lấy delta khối lượng nội nến |
| htfTf | "60" | Khung thời gian cao hơn để kiểm tra lồng ghép FVG |
| rangeLb | 50 | Số nến lookback để tính vùng premium/discount |
| sweepWin | 5 | Cửa sổ (nến) nhìn lại để tìm sự kiện quét thanh khoản |
| bosLb | 20 | Số nến tối đa kể từ BOS/CHoCH gần nhất để cộng điểm cấu trúc |
| emaLen | 50 | Chu kỳ EMA cho kiểm tra căn chỉnh xu hướng |
| dispW/volW/ctxW/strW | 25 mỗi trục | Trọng số tương đối cho từng trục (tự chuẩn hóa về 100) |
| minGrade | "C" | Ẩn FVG thấp hơn cấp này khỏi biểu đồ |
| mitMethod | "Wick" | Wick: vô hiệu khi bóng nến vượt vùng; Close: khi giá đóng cửa vượt |
| showHist | false | Hiện FVG đã bị vô hiệu hóa dưới dạng box xám |
- Box xanh lá (Grade A) — FVG chất lượng cao nhất, cả 4 trục mạnh, ưu tiên giao dịch
- Box xanh ngọc (Grade B) — setup tốt, độ dịch chuyển + khối lượng mạnh, cấu trúc/bối cảnh hơi yếu
- Box vàng (Grade C) — chất lượng trung bình, cần thêm xác nhận
- Box xám (Grade D) — chất lượng thấp, nên bỏ qua (bộ lọc minGrade sẽ ẩn)
- Nhãn điểm số — điểm tổng 0–100 hiển thị bên phải mỗi box; hover để xem chi tiết từng trục
- Box mờ hơn — trạng thái PartiallyFilled (giá đã vào vùng nhưng chưa thoát)
- Box xám nhạt — trạng thái Mitigated (chỉ hiện khi showHist = true)
- Đặt minGrade = "B" để chỉ hiện setup độ tin cậy cao — giảm nhiễu đáng kể
- Bật cảnh báo "New A-Grade FVG" để không bỏ lỡ các FVG xuất sắc theo thời gian thực
- Dùng cột "Avg Fill Time" trong dashboard để ước tính thời gian hold theo từng cấp xếp hạng
- FVG cấp A lồng ghép trong FVG đa khung (HTF) được cộng đủ 8 điểm bối cảnh — đây là setup có độ hội tụ cao nhất
- Trên khung thời gian thấp (M5), đặt ltfTf = "1" để có dữ liệu LTF phù hợp
1.4 Nguyên Lý Hoạt Động (Từ Đầu Ra Đến Đầu Vào)
🖥️ Đầu Ra — Những Gì Hiển Thị Trên Biểu Đồ- Box màu (A=xanh lá / B=xanh ngọc / C=vàng / D=xám) trải dài vùng giá FVG
- Chữ cái xếp hạng canh giữa trong box
- Nhãn điểm số (0–100) ở cạnh phải box kèm tooltip chi tiết từng trục khi hover
- Bảng thông tin: số FVG hoạt động/tổng/thời gian lấp trung bình theo cấp + khoảng cách FVG-A gần nhất
Toàn bộ đối tượng đồ họa được vẽ lại từ đầu mỗi tick barstate.islast: xóa hết box/nhãn cũ, sau đó duyệt toàn bộ fvgArr. FVG thấp hơn minGrade bị bỏ qua; FVG đã mitigated bị bỏ qua trừ khi showHist = true. Box mờ đi khi PartiallyFilled, chuyển xám hoàn toàn khi Mitigated.
🔄 Tầng 5 — Máy Trạng Thái (Vòng Đời FVG)Mỗi nến, tất cả FVG chưa bị vô hiệu trong fvgArr được kiểm tra:
| Chuyển Trạng Thái | Điều Kiện |
|---|---|
| → Mitigated | Phương pháp Wick: low < fvg.bottom (tăng) / high > fvg.top (giảm); Close: giá đóng vượt vùng |
| → PartiallyFilled | Giá vào vùng nhưng chưa vượt qua hoàn toàn: close < top và close > bottom |
| → Tested | Giá chạm biên vùng từ bên ngoài: low ≤ top và low[1] ≥ top (FVG tăng) |
| PartiallyFilled → Tested | Giá thoát vùng mà không vô hiệu hoàn toàn |
FVG mới được push vào fvgArr (mảng các đối tượng FVG UDT). Khi mảng vượt 200 phần tử, phần tử cũ nhất bị shift ra để tránh tràn bộ nhớ. Các bộ đếm dashboard (aTotal, aBarsSum...) tích lũy thống kê suốt vòng đời và không bao giờ reset.
🏆 Tầng 3 — Xếp Hạng & Tổng Điểm Có Trọng Số
Công thức tổng điểm:
weighted = (dsp/25)×dW + (vol/25)×vW + (ctx/25)×cW + (str/25)×sW
total = min(round(weighted × 100/wSum), 100)
Xếp hạng — kiểm tra theo thứ tự A → B → C → D; cấp đầu tiên đạt được sẽ được gán. Mỗi cấp kiểm tra cả điểm tối thiểu từng trục VÀ cờ "bắt buộc" (required):
| Cấp | Điểm Tối Thiểu Mặc Định | Trục Bắt Buộc |
|---|---|---|
| A | 12/25 mỗi trục | Cả 4 trục bắt buộc |
| B | 12/25 mỗi trục | Dịch Chuyển + Khối Lượng bắt buộc |
| C | 8/25 mỗi trục | Dịch Chuyển + Khối Lượng bắt buộc |
| D | (dự phòng) | Không có trục bắt buộc |
| Thành Phần | Điểm Max | Logic |
|---|---|---|
| Tỷ lệ thân nến/biên độ | 10 | ≥85% → 10 · ≥70% → 7 · ≥55% → 4 · còn lại → 0 |
| Bội số ATR | 10 | Kích thước thân ÷ ATR: ≥2× → 10 · ≥1.5× → 7 · ≥1× → 4 · còn lại → 0 |
| Liên tiếp cùng chiều | 5 | Nến tại bar[2] và bar[0] đều cùng hướng FVG: 5 · một nến: 2 · không có: 0 |
| Thành Phần | Điểm Max | Logic |
|---|---|---|
| Tỷ lệ tăng/giảm LTF | 12 | Tỷ lệ khối lượng hướng tính trên nến dịch chuyển: >75% → 12 · ≥60% → 8 · ≥50% → 4 |
| Khối lượng tương đối | 8 | Khối lượng nến ÷ SMA 20: >2× → 8 · ≥1.5× → 5 · ≥1× → 2 |
| Khối lượng tăng dần | 5 | vol[1] > vol[2] > vol[3] → 5 điểm |
| Thành Phần | Điểm Max | Logic |
|---|---|---|
| Vùng Premium / Discount | 8 | FVG tăng trong vùng discount (dưới 50% biên độ rangeLb) = 8 điểm; trên midpoint = gradient 4→0 |
| Lồng ghép FVG đa khung | 8 | FVG hiện tại nằm hoàn toàn trong FVG HTF cùng hướng = 8 điểm |
| Gần sự kiện quét thanh khoản | 5 | Quét thanh khoản (wick vượt swing rồi đóng cửa lại) trong sweepWin nến = 5 điểm |
| Thời điểm Killzone | 4 | FVG hình thành trong phiên Á / London / NY sáng / NY chiều = 4 điểm |
| Thành Phần | Điểm Max | Logic |
|---|---|---|
| Xu hướng cấu trúc thị trường | 10 | HH+HL (cấu trúc tăng) + FVG tăng → 10 · cấu trúc ngược chiều → 0 · trung tính → 2 |
| Căn chỉnh EMA | 8 | FVG tăng và close > EMA50 → 8 · FVG giảm và close < EMA50 → 8 · ngược → 0 |
| BOS/CHoCH gần đây | 7 | BOS trong vòng bosLb nến, đúng hướng → 7 · sai hướng → 0 · không có BOS gần → 2 |
- atrVal = ta.atr(atrLen) — ATR cho chấm điểm độ dịch chuyển
- ema50 = ta.ema(close, emaLen) — đường chuẩn xu hướng
- volSma20 = ta.sma(volume, 20) — baseline khối lượng tương đối
- pivHi / pivLo — swing cao/thấp để theo dõi cấu trúc và BOS
- request.security_lower_tf — thu thập mảng khối lượng tăng/giảm mỗi nến LTF, cộng lại theo nến hiện tại
- request.security(htfTf, [high, low, high[2], low[2]]) — OHLC khung cao để kiểm tra lồng ghép
- inKillzone() — nhận diện phiên theo giờ New York (Á ≥20:00, London 02:00–05:00, NY AM 09:30–11:00, NY PM 13:30–16:00)
- rangeHigh / rangeLow / rangeMid — cao nhất/thấp nhất rolling trong rangeLb nến cho vùng premium/discount
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © fluxchart
//@version=6
indicator("FVG Quality Scorer | Flux Charts", overlay = true, max_boxes_count = 500, max_labels_count = 500, max_bars_back = 5000)
//#region CONSTANTS
GP_SCORE = "⚙️ Cài Đặt"
GP_WEIGHT = "⚖️ Trọng Số Trục"
GP_DISP = "🎨 Hiển Thị"
GP_DASH = "📋 Bảng Thông Tin"
TT_LTF = "Khung thời gian thấp hơn để tính delta khối lượng nội nến. Dùng '1' cho 1 phút."
TT_HTF = "Khung thời gian cao hơn để kiểm tra FVG lồng ghép."
TT_WEIGHT = "Trọng số tương đối cho trục chấm điểm này. Tất cả trọng số được chuẩn hóa về 100."
TT_GRADE = "Ẩn các FVG có cấp thấp hơn ngưỡng này."
TT_MIT = "Wick: vô hiệu khi bóng nến vượt vùng. Close: vô hiệu khi giá đóng cửa vượt vùng."
TT_HIST = "Khi bật, các FVG đã vô hiệu vẫn hiện dưới dạng box xám."
STATE_FRESH = "Fresh"
STATE_TESTED = "Tested"
STATE_PARTIAL = "PartiallyFilled"
STATE_MIT = "Mitigated"
EXTEND_BARS = 20
GREEN = #00C853
TEAL = #00BFA5
AMBER = #FFC107
GRAY = #9E9E9E
DASH_DARK = #1a1a2e
DASH_MID = #16213e
DASH_ROW = #0f3460
//#endregion CONSTANTS
//#region INPUTS
//#region Settings
swingLen = input.int(5, "Độ Dài Swing", minval = 2, maxval = 50, group = GP_SCORE, display = display.none)
atrLen = input.int(14, "Chu Kỳ ATR", minval = 5, maxval = 50, group = GP_SCORE, display = display.none)
ltfTf = input.timeframe("1", "LTF cho Delta Khối Lượng", group = GP_SCORE, display = display.none, tooltip = TT_LTF)
htfTf = input.timeframe("60", "HTF cho Lồng Ghép FVG", group = GP_SCORE, display = display.none, tooltip = TT_HTF)
rangeLb = input.int(50, "Lookback Vùng Giá", minval = 10, maxval = 200, group = GP_SCORE, display = display.none)
sweepWin = input.int(5, "Cửa Sổ Quét Thanh Khoản", minval = 1, maxval = 20, group = GP_SCORE, display = display.none)
bosLb = input.int(20, "Lookback BOS/CHoCH", minval = 5, maxval = 50, group = GP_SCORE, display = display.none)
emaLen = input.int(50, "Chu Kỳ EMA", minval = 10, maxval = 200, group = GP_SCORE, display = display.none)
//#endregion Settings
//#region Axis Weights
dispW = input.int(25, "Trọng Số Độ Dịch Chuyển", minval = 0, maxval = 100, group = GP_WEIGHT, display = display.none, tooltip = TT_WEIGHT)
volW = input.int(25, "Trọng Số Delta Khối Lượng", minval = 0, maxval = 100, group = GP_WEIGHT, display = display.none, tooltip = TT_WEIGHT)
ctxW = input.int(25, "Trọng Số Bối Cảnh", minval = 0, maxval = 100, group = GP_WEIGHT, display = display.none, tooltip = TT_WEIGHT)
strW = input.int(25, "Trọng Số Cấu Trúc", minval = 0, maxval = 100, group = GP_WEIGHT, display = display.none, tooltip = TT_WEIGHT)
//#endregion Axis Weights
//#region Display
minGrade = input.string("C", "Cấp Tối Thiểu Hiển Thị", options = ["A", "B", "C", "D"], group = GP_DISP, display = display.none, tooltip = TT_GRADE)
showHist = input.bool(false, "Hiện FVG Đã Vô Hiệu", group = GP_DISP, display = display.none, tooltip = TT_HIST)
showLabels = input.bool(true, "Hiện Nhãn Điểm Số", group = GP_DISP, display = display.none)
colA = input.color(GREEN, "Màu Cấp (A → D)", inline = "gradeColors", group = GP_DISP, display = display.none)
colB = input.color(TEAL, "", inline = "gradeColors", group = GP_DISP, display = display.none)
colC = input.color(AMBER, "", inline = "gradeColors", group = GP_DISP, display = display.none)
colD = input.color(GRAY, "", inline = "gradeColors", group = GP_DISP, display = display.none)
//#endregion Display
mitMethod = input.string("Wick", "Phương Pháp Vô Hiệu", options = ["Close", "Wick"], group = GP_SCORE, display = display.none, tooltip = TT_MIT)
//#region Dashboard
showDash = input.bool(true, "Hiện Bảng Thông Tin", group = GP_DASH)
dashPos = input.string("Top Right", "Vị Trí Bảng", options = ["Top Right", "Top Center", "Top Left", "Middle Right", "Middle Center", "Middle Left", "Bottom Right", "Bottom Center", "Bottom Left"], group = GP_DASH, display = display.none)
dashSize = input.string("Small", "Cỡ Chữ Bảng", options = ["Tiny", "Small", "Normal", "Large", "Huge"], group = GP_DASH, display = display.none)
//#endregion Dashboard
//#region A-Grade Requirements
aMinDisp = input.int(12, "A: Diem Dich Chuyen Toi Thieu", minval = 0, maxval = 25, group = "🏆 Yêu Cầu Cấp A", display = display.none)
aMinVol = input.int(12, "A: Diem Khoi Luong Toi Thieu", minval = 0, maxval = 25, group = "🏆 Yêu Cầu Cấp A", display = display.none)
aMinCtx = input.int(12, "A: Diem Boi Canh Toi Thieu", minval = 0, maxval = 25, group = "🏆 Yêu Cầu Cấp A", display = display.none)
aMinStr = input.int(12, "A: Diem Cau Truc Toi Thieu", minval = 0, maxval = 25, group = "🏆 Yêu Cầu Cấp A", display = display.none)
aReqDisp = input.bool(true, "A: Bat Buoc Dich Chuyen", group = "🏆 Yêu Cầu Cấp A", display = display.none)
aReqVol = input.bool(true, "A: Bat Buoc Khoi Luong", group = "🏆 Yêu Cầu Cấp A", display = display.none)
aReqCtx = input.bool(true, "A: Bat Buoc Boi Canh", group = "🏆 Yêu Cầu Cấp A", display = display.none)
aReqStr = input.bool(true, "A: Bat Buoc Cau Truc", group = "🏆 Yêu Cầu Cấp A", display = display.none)
//#endregion A-Grade Requirements
//#region B-Grade Requirements
bMinDisp = input.int(12, "B: Diem Dich Chuyen Toi Thieu", minval = 0, maxval = 25, group = "🥈 Yêu Cầu Cấp B", display = display.none)
bMinVol = input.int(12, "B: Diem Khoi Luong Toi Thieu", minval = 0, maxval = 25, group = "🥈 Yêu Cầu Cấp B", display = display.none)
bMinCtx = input.int(12, "B: Diem Boi Canh Toi Thieu", minval = 0, maxval = 25, group = "🥈 Yêu Cầu Cấp B", display = display.none)
bMinStr = input.int(12, "B: Diem Cau Truc Toi Thieu", minval = 0, maxval = 25, group = "🥈 Yêu Cầu Cấp B", display = display.none)
bReqDisp = input.bool(true, "B: Bat Buoc Dich Chuyen", group = "🥈 Yêu Cầu Cấp B", display = display.none)
bReqVol = input.bool(true, "B: Bat Buoc Khoi Luong", group = "🥈 Yêu Cầu Cấp B", display = display.none)
bReqCtx = input.bool(false, "B: Bat Buoc Boi Canh", group = "🥈 Yêu Cầu Cấp B", display = display.none)
bReqStr = input.bool(false, "B: Bat Buoc Cau Truc", group = "🥈 Yêu Cầu Cấp B", display = display.none)
//#endregion B-Grade Requirements
//#region C-Grade Requirements
cMinDisp = input.int(8, "C: Diem Dich Chuyen Toi Thieu", minval = 0, maxval = 25, group = "🥉 Yêu Cầu Cấp C", display = display.none)
cMinVol = input.int(8, "C: Diem Khoi Luong Toi Thieu", minval = 0, maxval = 25, group = "🥉 Yêu Cầu Cấp C", display = display.none)
cMinCtx = input.int(8, "C: Diem Boi Canh Toi Thieu", minval = 0, maxval = 25, group = "🥉 Yêu Cầu Cấp C", display = display.none)
cMinStr = input.int(8, "C: Diem Cau Truc Toi Thieu", minval = 0, maxval = 25, group = "🥉 Yêu Cầu Cấp C", display = display.none)
cReqDisp = input.bool(true, "C: Bat Buoc Dich Chuyen", group = "🥉 Yêu Cầu Cấp C", display = display.none)
cReqVol = input.bool(true, "C: Bat Buoc Khoi Luong", group = "🥉 Yêu Cầu Cấp C", display = display.none)
cReqCtx = input.bool(false, "C: Bat Buoc Boi Canh", group = "🥉 Yêu Cầu Cấp C", display = display.none)
cReqStr = input.bool(false, "C: Bat Buoc Cau Truc", group = "🥉 Yêu Cầu Cấp C", display = display.none)
//#endregion C-Grade Requirements
//#endregion INPUTS
//#region DECLARATIONS
//#region USER-DEFINED TYPES
type FVG
int formedAt
float top
float bottom
bool isBullish
string state
int dispScore
int volScore
int ctxScore
int strScore
int totalScore
string grade
int mitigatedAt = na
//#endregion USER-DEFINED TYPES
//#endregion DECLARATIONS
//#region CALCULATIONS
//#region GLOBAL CALCULATIONS
atrVal = ta.atr(atrLen)
ema50 = ta.ema(close, emaLen)
volSma20 = ta.sma(volume, 20)
//#region Swing Highs / Lows for Market Structure
pivHi = ta.pivothigh(high, swingLen, swingLen)
pivLo = ta.pivotlow(low, swingLen, swingLen)
var float lastSwingHigh = na
var float prevSwingHigh = na
var int lastSwingHighBar = na
var float lastSwingLow = na
var float prevSwingLow = na
var int lastSwingLowBar = na
if not na(pivHi)
prevSwingHigh := lastSwingHigh
lastSwingHigh := pivHi
lastSwingHighBar := bar_index - swingLen
if not na(pivLo)
prevSwingLow := lastSwingLow
lastSwingLow := pivLo
lastSwingLowBar := bar_index - swingLen
//#endregion Swing Highs / Lows for Market Structure
//#region BOS Tracking
var int lastBosBar = na
var bool lastBosBull = false
var float brokenSwingHigh = na
var float brokenSwingLow = na
if not na(lastSwingHigh) and close > lastSwingHigh and close[1] <= lastSwingHigh and lastSwingHigh != brokenSwingHigh
lastBosBar := bar_index
lastBosBull := true
brokenSwingHigh := lastSwingHigh
if not na(lastSwingLow) and close < lastSwingLow and close[1] >= lastSwingLow and lastSwingLow != brokenSwingLow
lastBosBar := bar_index
lastBosBull := false
brokenSwingLow := lastSwingLow
//#endregion BOS Tracking
//#region LTF Volume Delta via request.security_lower_tf
[ltfBullVol, ltfBearVol] = request.security_lower_tf(syminfo.tickerid, ltfTf, [close > open ? volume : 0.0, close <= open ? volume : 0.0])
sumBullVol = 0.0
sumBearVol = 0.0
if not na(ltfBullVol) and ltfBullVol.size() > 0
for i = 0 to ltfBullVol.size() - 1
sumBullVol += ltfBullVol.get(i)
sumBearVol += ltfBearVol.get(i)
//#endregion LTF Volume Delta via request.security_lower_tf
//#region HTF FVG Nesting Data
[htfHigh0, htfLow0, htfHigh2, htfLow2] = request.security(syminfo.tickerid, htfTf, [high, low, high[2], low[2]], lookahead = barmerge.lookahead_off)
//#endregion HTF FVG Nesting Data
//#region Killzone Detection (New York Time)
nyHour = hour(time, "America/New_York")
nyMinute = minute(time, "America/New_York")
nyTime = nyHour * 100 + nyMinute
inKillzone() =>
inAsian = nyTime >= 2000
inLondon = nyTime >= 200 and nyTime < 500
inNYAM = nyTime >= 930 and nyTime < 1100
inNYPM = nyTime >= 1330 and nyTime < 1600
inAsian or inLondon or inNYAM or inNYPM
isKillzone = inKillzone()
//#endregion Killzone Detection (New York Time)
//#region Range for Premium / Discount
rangeHigh = ta.highest(high, rangeLb)
rangeLow = ta.lowest(low, rangeLb)
rangeMid = (rangeHigh + rangeLow) / 2.0
//#endregion Range for Premium / Discount
//#endregion GLOBAL CALCULATIONS
//#region FVG STORAGE
var fvgArr = array.new()
// Dashboard stats
var aTotal = 0
var aBarsSum = 0
var aBarsCount = 0
var bTotal = 0
var bBarsSum = 0
var bBarsCount = 0
var cTotal = 0
var cBarsSum = 0
var cBarsCount = 0
var dTotal = 0
var dBarsSum = 0
var dBarsCount = 0
//#endregion FVG STORAGE
//#endregion CALCULATIONS
//#region FUNCTIONS
//#region HELPER FUNCTIONS
gradeColor(string g) =>
switch g
"A" => colA
"B" => colB
"C" => colC
=> colD
gradeMinScore(string g) =>
switch g
"A" => 75
"B" => 50
"C" => 25
=> 0
passesFilter(string g) =>
minS = gradeMinScore(minGrade)
gScore = gradeMinScore(g)
gScore >= minS
dashPosition() =>
switch dashPos
"Top Right" => position.top_right
"Top Center" => position.top_center
"Top Left" => position.top_left
"Middle Right" => position.middle_right
"Middle Center" => position.middle_center
"Middle Left" => position.middle_left
"Bottom Right" => position.bottom_right
"Bottom Center" => position.bottom_center
=> position.bottom_left
dashTextSize() =>
switch dashSize
"Tiny" => size.tiny
"Small" => size.small
"Normal" => size.normal
"Large" => size.large
=> size.huge
avgBars(int sumBars, int count) =>
count > 0 ? str.tostring(math.round(sumBars / count)) + " Nến" : "—"
//#endregion HELPER FUNCTIONS
//#region Axis 1: Displacement Strength
scoreDisplacement(bool isBull) =>
// Tỷ lệ thân nến/biên độ (0-10)
bodyRange = high[1] - low[1]
ratio = bodyRange > 0 ? math.abs(close[1] - open[1]) / bodyRange : 0.0
ptsBody = ratio >= 0.85 ? 10 : ratio >= 0.70 ? 7 : ratio >= 0.55 ? 4 : 0
// Bội số ATR (0-10)
mult = atrVal > 0 ? bodyRange / atrVal : 0.0
ptsATR = mult >= 2.0 ? 10 : mult >= 1.5 ? 7 : mult >= 1.0 ? 4 : 0
// Liên tiếp cùng chiều (0-5)
bar3Aligned = isBull ? close[2] > open[2] : close[2] < open[2]
bar1Aligned = isBull ? close > open : close < open
alignCount = (bar3Aligned ? 1 : 0) + (bar1Aligned ? 1 : 0)
ptsConsec = alignCount == 2 ? 5 : alignCount == 1 ? 2 : 0
ptsBody + ptsATR + ptsConsec
//#endregion Axis 1: Displacement Strength
//#region Axis 2: Volume Delta
scoreVolumeDelta(bool isBull) =>
// Tỷ lệ tăng/giảm LTF (0-12) — nến dịch chuyển (bar N-1)
totalVol = sumBullVol[1] + sumBearVol[1]
dominance = totalVol > 0 ? (isBull ? sumBullVol[1] / totalVol : sumBearVol[1] / totalVol) : 0.0
ptsDom = dominance > 0.75 ? 12 : dominance >= 0.60 ? 8 : dominance >= 0.50 ? 4 : 0
// Khối lượng tương đối (0-8)
relVol = volSma20 > 0 ? volume[1] / volSma20 : 0.0
ptsRel = relVol > 2.0 ? 8 : relVol >= 1.5 ? 5 : relVol >= 1.0 ? 2 : 0
// Khối lượng tăng dần vào FVG (0-5)
rising = volume[1] > volume[2] and volume[2] > volume[3]
ptsRise = rising ? 5 : 0
ptsDom + ptsRel + ptsRise
//#endregion Axis 2: Volume Delta
//#region Axis 3: Contextual Location
scoreContextual(bool isBull, float fvgTop, float fvgBottom) =>
fvgMid = (fvgTop + fvgBottom) / 2.0
// Vùng Premium/Discount (0-8)
rangeSize = rangeHigh - rangeLow
ptsPD = 0
if rangeSize > 0
pos = (fvgMid - rangeLow) / rangeSize
if isBull
ptsPD := pos <= 0.5 ? 8 : math.round((1.0 - pos) * 8.0)
else
ptsPD := pos >= 0.5 ? 8 : math.round(pos * 8.0)
// Lồng ghép FVG đa khung (0-8)
ptsHTF = 0
htfBullFvg = not na(htfLow0) and not na(htfHigh2) and htfLow0 > htfHigh2
htfBearFvg = not na(htfHigh0) and not na(htfLow2) and htfHigh0 < htfLow2
if isBull and htfBullFvg
htfFvgTop = htfLow0
htfFvgBot = htfHigh2
if fvgTop <= htfFvgTop and fvgBottom >= htfFvgBot
ptsHTF := 8
else if not isBull and htfBearFvg
htfFvgTop = htfLow2
htfFvgBot = htfHigh0
if fvgTop <= htfFvgTop and fvgBottom >= htfFvgBot
ptsHTF := 8
// Gần sự kiện quét thanh khoản (0-5)
ptsSweep = 0
for i = 1 to sweepWin
if isBull
swingLowAtI = lastSwingLow[i]
if not na(swingLowAtI) and low[i] < swingLowAtI and close[i] > swingLowAtI
ptsSweep := 5
break
if not isBull
swingHighAtI = lastSwingHigh[i]
if not na(swingHighAtI) and high[i] > swingHighAtI and close[i] < swingHighAtI
ptsSweep := 5
break
// Thời điểm Killzone (0-4)
ptsKZ = isKillzone ? 4 : 0
ptsPD + ptsHTF + ptsSweep + ptsKZ
//#endregion Axis 3: Contextual Location
//#region Axis 4: Structural Alignment
scoreStructural(bool isBull) =>
// Xu hướng cấu trúc thị trường (0-10)
bullStruct = not na(prevSwingHigh) and not na(prevSwingLow) and lastSwingHigh > prevSwingHigh and lastSwingLow > prevSwingLow
bearStruct = not na(prevSwingHigh) and not na(prevSwingLow) and lastSwingHigh < prevSwingHigh and lastSwingLow < prevSwingLow
ptsMkt = 0
if isBull
ptsMkt := bullStruct ? 10 : bearStruct ? 0 : 2
else
ptsMkt := bearStruct ? 10 : bullStruct ? 0 : 2
// Căn chỉnh EMA (0-8)
ptsEma = 0
if isBull and close > ema50
ptsEma := 8
else if not isBull and close < ema50
ptsEma := 8
// BOS/CHoCH gần đây (0-7)
ptsBos = 2
if not na(lastBosBar) and (bar_index - lastBosBar) <= bosLb
if isBull and lastBosBull
ptsBos := 7
else if not isBull and not lastBosBull
ptsBos := 7
else
ptsBos := 0
ptsMkt + ptsEma + ptsBos
//#endregion Axis 4: Structural Alignment
//#region Weighted Total
calcWeightedTotal(int dsp, int vol, int ctx, int str_score) =>
wSum = dispW + volW + ctxW + strW
dW = wSum > 0 ? dispW : 25.0
vW = wSum > 0 ? volW : 25.0
cW = wSum > 0 ? ctxW : 25.0
sW = wSum > 0 ? strW : 25.0
ws = wSum > 0 ? wSum : 100.0
weighted = (dsp / 25.0) * dW + (vol / 25.0) * vW + (ctx / 25.0) * cW + (str_score / 25.0) * sW
result = math.round(weighted * (100.0 / ws))
math.min(result, 100)
getGrade(int dsp, int vol, int ctx, int str_score) =>
aPass = (not aReqDisp or dsp >= aMinDisp) and (not aReqVol or vol >= aMinVol) and (not aReqCtx or ctx >= aMinCtx) and (not aReqStr or str_score >= aMinStr)
bPass = (not bReqDisp or dsp >= bMinDisp) and (not bReqVol or vol >= bMinVol) and (not bReqCtx or ctx >= bMinCtx) and (not bReqStr or str_score >= bMinStr)
cPass = (not cReqDisp or dsp >= cMinDisp) and (not cReqVol or vol >= cMinVol) and (not cReqCtx or ctx >= cMinCtx) and (not cReqStr or str_score >= cMinStr)
aPass ? "A" : bPass ? "B" : cPass ? "C" : "D"
//#endregion Weighted Total
//#region FVG DETECTION
bullFVG = low > high[2]
bearFVG = high < low[2]
//#endregion FVG DETECTION
//#region TOOLTIP BUILDER
buildTooltip(int dsp, int vol, int ctx, int str_score, int total, string g, string st) =>
str.format("Điểm Chất Lượng FVG: {0}/100 ({1})\n──────────────────\nĐộ Dịch Chuyển: {2}/25\nDelta Khối Lượng: {3}/25\nBối Cảnh: {4}/25\nCấu Trúc: {5}/25\n──────────────────\nTrạng Thái: {6}", total, g, dsp, vol, ctx, str_score, st)
//#endregion TOOLTIP BUILDER
//#endregion FUNCTIONS
//#region EXECUTION
//#region CREATE NEW FVGs
isBullDetect = bullFVG
fvgTop = isBullDetect ? low : low[2]
fvgBottom = isBullDetect ? high[2] : high
// Gọi hàm chấm điểm mỗi nến để đảm bảo tính nhất quán series
dspScore = scoreDisplacement(isBullDetect)
volScore = scoreVolumeDelta(isBullDetect)
ctxScore = scoreContextual(isBullDetect, fvgTop, fvgBottom)
strScore = scoreStructural(isBullDetect)
totalScore = calcWeightedTotal(dspScore, volScore, ctxScore, strScore)
gradeVal = getGrade(dspScore, volScore, ctxScore, strScore)
if bullFVG or bearFVG
// Cập nhật thống kê dashboard
switch gradeVal
"A" => aTotal += 1
"B" => bTotal += 1
"C" => cTotal += 1
"D" => dTotal += 1
newFvg = FVG.new(bar_index, fvgTop, fvgBottom, isBullDetect, STATE_FRESH, dspScore, volScore, ctxScore, strScore, totalScore, gradeVal)
fvgArr.push(newFvg)
//#endregion CREATE NEW FVGs
//#region LIFECYCLE STATE MANAGEMENT
if fvgArr.size() > 0
for i = fvgArr.size() - 1 to 0
fvg = fvgArr.get(i)
if fvg.state == STATE_MIT
continue
isBull = fvg.isBullish
top = fvg.top
bot = fvg.bottom
// Kiểm tra vô hiệu hóa
mitigated = false
if mitMethod == "Close"
mitigated := isBull ? close < bot : close > top
else
mitigated := isBull ? low < bot : high > top
if mitigated
fvg.state := STATE_MIT
fvg.mitigatedAt := bar_index
barsAlive = bar_index - fvg.formedAt
switch fvg.grade
"A" =>
aBarsSum += barsAlive
aBarsCount += 1
"B" =>
bBarsSum += barsAlive
bBarsCount += 1
"C" =>
cBarsSum += barsAlive
cBarsCount += 1
"D" =>
dBarsSum += barsAlive
dBarsCount += 1
continue
// Kiểm tra lấp một phần
partial = false
if isBull
partial := close < top and close > bot
else
partial := close > bot and close < top
if partial and fvg.state != STATE_PARTIAL
fvg.state := STATE_PARTIAL
// Kiểm tra đã được test (giá chạm biên nhưng chưa vào)
else if not partial and fvg.state == STATE_FRESH
tested = false
if isBull
tested := low <= top and low[1] >= top
else
tested := high >= bot and high[1] <= bot
if tested
fvg.state := STATE_TESTED
// Hoàn nguyên từ partial về tested nếu giá thoát vùng
else if not partial and fvg.state == STATE_PARTIAL
fvg.state := STATE_TESTED
//#endregion LIFECYCLE STATE MANAGEMENT
//#region MEMORY MANAGEMENT
while fvgArr.size() > 200
fvgArr.shift()
//#endregion MEMORY MANAGEMENT
//#endregion EXECUTION
//#region VISUALS
//#region DEFERRED DRAWING (barstate.islast only)
var drawnBoxes = array.new()
var drawnLabels = array.new
Indicator Insider

FVG Quality Scorer – Bộ Chấm Điểm Chất Lượng FVG | Flux Charts
Isolated Scroll Container 📊 Chấm Điểm Chất Lượng FVG | Flux Charts Phân Tích Chỉ Báo · Phiên Bản Tiếng Việt · PineScript v6 1.1 Khái Niệm Cốt Lõi FVG Quality Scorer đánh giá mỗi vùng Fair Value Gap (FVG / vùng mất cân bằng) qua 4 trục chấm điểm

Adaptive Support and Resistance Zones – Vùng Hỗ Trợ và Kháng Cự Thích Ứng [BigBeluga]
Phân tích chuyên sâu về chỉ báo Adaptive S/R Zones [BigBeluga] — hệ thống hỗ trợ và kháng cự thích ứng theo biến động thị trường, tự động phát hiện các vùng giá quan trọng dựa trên pivot structure, lọc nhiễu bằng ATR, theo dõi breakout xác nhận và mở











