
SUPERTREND, ATR, ADX, EMA, AI
Vấn đề lớn nhất của chiến lược SuperTrend truyền thống? Các tham số cố định có hiệu suất rất khác nhau trong các môi trường thị trường khác nhau. Phiên bản tăng cường AI này bằng cách điều chỉnh động số nhân ATR, nâng số nhân lên gấp 2 lần giá trị cơ bản trong thời gian biến động cao và giảm xuống 0,85 lần trong thời gian biến động thấp. Dữ liệu đánh giá cho thấy cơ chế thích ứng này có thể làm giảm đáng kể tín hiệu giả trong thị trường lắc lư.
Sự đổi mới cốt lõi nằm trong hệ thống lọc ba lớp: nhận dạng trạng thái thị trường, đánh giá tín hiệu AI và cơ chế xác nhận nhiều lần. Không còn đơn giản là giá phá vỡ đường SuperTrend để tham gia, nhưng yêu cầu điểm AI đạt hơn 65 điểm để kích hoạt tín hiệu giao dịch. Hệ thống đánh giá này tổng hợp xem xét 5 chiều như tăng giao dịch, mức độ lệch giá và sự nhất quán của xu hướng.
Cơ chế xếp hạng được thiết kế tinh tế: khối lượng giao dịch tăng trọng lượng 20 điểm, khoảng cách giá từ đường SuperTrend là 25 điểm, sự nhất quán của EMA xu hướng là 20 điểm, chất lượng của tình trạng thị trường là 15 điểm, khoảng cách giữa giá trước và đường xu hướng là 20 điểm. Tổng cộng 100 điểm, ngưỡng 65 điểm mặc định có nghĩa là chỉ có tín hiệu chất lượng cao có thể được lọc.
Cụ thể, khi khối lượng giao dịch vượt quá 2, 5 lần trung bình 20 chu kỳ, bạn sẽ nhận được 20 điểm, và khi giá lệch khỏi đường SuperTrend hơn 1,5 lần ATR, bạn sẽ nhận được 25 điểm. Đánh giá định lượng này tránh phán đoán chủ quan, mỗi tín hiệu có dữ liệu rõ ràng để hỗ trợ. Trong thực tế, khuyến nghị điều chỉnh yêu cầu điểm tối thiểu theo đặc điểm của các giống khác nhau.
Chiến lược sử dụng tỷ lệ ATR và chỉ số ADX để xác định ba trạng thái thị trường: giai đoạn xu hướng ((regime = 1), giai đoạn biến động cao ((regime = 2), giai đoạn chấn động ((regime = 0). Khi tỷ lệ ATR vượt quá 1,4, thì được coi là giai đoạn biến động cao, khi ADX thấp hơn 20 và tỷ lệ ATR thấp hơn 0,9 thì là giai đoạn chấn động.
Logic điều chỉnh nhân tự thích ứng: nhân trong thời kỳ biến động cao tăng 40% x tỷ lệ ATR -1,0 và nhân trong thời kỳ dao động giảm xuống 85% giá trị cơ sở. Điều này có nghĩa là nhân cơ sở 3.0 có thể điều chỉnh xuống còn 4,2 trong thời gian biến động cực đoan và giảm xuống còn 2,55 trong thời gian dao động. Cơ chế điều chỉnh động này làm tăng đáng kể khả năng thích ứng của chiến lược trong các môi trường thị trường khác nhau.
Hạn chế động ATR là lựa chọn ưu tiên, mặc định 2.5 lần ATR có thể chịu được sự biến động bình thường và dừng lại kịp thời. Hạn chế tỷ lệ phần trăm phù hợp với tỷ lệ biến động tương đối ổn định, chế độ SuperTrend ngay lập tức thanh toán khi xu hướng đảo ngược.
Cài đặt dừng hỗ trợ mô hình chiết khấu lợi nhuận rủi ro, tỷ lệ chiết khấu lợi nhuận rủi ro mặc định là 2.5:1 có lợi thế về mặt thống kê. Sau khi kích hoạt theo dõi dừng lỗ, đường dừng lỗ của vị trí kiếm tiền sẽ được điều chỉnh động theo khoảng cách ATR 2,5 lần, tối đa hóa lợi nhuận trong tình huống xu hướng.
Bộ lọc xu hướng EMA đảm bảo chỉ tham gia khi 50 chu kỳ EMA hướng phù hợp, tránh giao dịch ngược. Bộ lọc thời kỳ dao động trực tiếp bỏ qua tín hiệu chế độ = 0, mặc dù có thể bỏ lỡ một số cơ hội nhưng giảm đáng kể tỷ lệ tín hiệu giả.
Bộ lọc khối lượng giao dịch yêu cầu khối lượng giao dịch cao hơn trung bình 20 chu kỳ khi nhập, đảm bảo có đủ sự tham gia của thị trường để hỗ trợ giá phá vỡ. Thời gian nguội 10 chu kỳ ngăn chặn giao dịch thường xuyên, giảm chi phí giao dịch.
Đối với tiền điện tử, khuyến nghị nâng điểm AI tối thiểu lên 70 điểm, cổ phiếu truyền thống có thể giảm xuống 60 điểm. Các nhà giao dịch tần số cao có thể rút ngắn thời gian nguội xuống còn 5 chu kỳ, các nhà đầu tư dài hạn khuyến nghị kéo dài đến 15 chu kỳ.
Các tham số độ dài ATR 10 là điểm cân bằng được tối ưu hóa, quá ngắn sẽ trở nên quá nhạy cảm, quá dài sẽ bị chậm trễ. Các nhân cơ bản 3.0 phù hợp với hầu hết các giống, các giống có dao động cao có thể được điều chỉnh xuống còn 3.5, các giống có dao động thấp giảm xuống còn 2.5.
Dấu hiệu nguy cơ quan trọng: Kết quả đánh giá lịch sử không đại diện cho hiệu suất lợi nhuận trong tương lai. Chiến lược có thể bị thua lỗ liên tục trong điều kiện thị trường cực đoan, nên kiểm soát chặt chẽ vị trí duy nhất không vượt quá 30% tổng số vốn.
/*backtest
start: 2026-01-01 00:00:00
end: 2026-02-24 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"PAXG_USDT","balance":500000}]
*/
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © DefinedEdge
//@version=6
strategy("SuperTrend AI Strategy [Adaptive]", "ST AI Strategy ◈",
overlay = true,
initial_capital = 10000,
default_qty_type = strategy.percent_of_equity,
default_qty_value= 30,
commission_type = strategy.commission.percent,
commission_value = 0.06,
slippage = 2,
pyramiding = 0,
calc_on_every_tick = false,
max_labels_count = 500)
// ============================================================================
// INPUTS
// ============================================================================
// --- SuperTrend Core ---
GRP_ST = "◈ SuperTrend"
i_atLen = input.int(10, "ATR Length", minval=1, maxval=50, group=GRP_ST)
i_bMult = input.float(3.0, "Base Multiplier", minval=0.5, maxval=10.0, step=0.1, group=GRP_ST)
i_src = input.source(hl2, "Source", group=GRP_ST)
// --- Regime Detection ---
GRP_REG = "◈ Regime Detection"
i_regLen = input.int(40, "Regime Lookback", minval=10, maxval=100, group=GRP_REG)
i_adxLen = input.int(14, "ADX Length", minval=5, maxval=50, group=GRP_REG)
i_adxThr = input.float(20, "ADX Trend Threshold", minval=10, maxval=40, step=1, group=GRP_REG)
i_adapt = input.bool(true, "Adaptive Multiplier", group=GRP_REG)
// --- AI Scoring ---
GRP_AI = "◈ AI Engine"
i_trendLen = input.int(50, "Trend EMA Length", minval=10, maxval=200, group=GRP_AI)
i_volLen = input.int(20, "Volume MA Length", minval=5, maxval=50, group=GRP_AI)
i_minSc = input.int(65, "Min Signal Score", minval=0, maxval=90, group=GRP_AI, tooltip="Only enter trades when the AI score meets this threshold.")
// --- Risk Management ---
GRP_RISK = "◈ Risk Management"
i_slMode = input.string("ATR", "Stop Loss Mode", options=["ATR", "Percent", "SuperTrend"], group=GRP_RISK, tooltip="ATR: dynamic SL based on volatility. Percent: fixed %. SuperTrend: exit on trend flip.")
i_slAtr = input.float(2.5, "SL ATR Multiplier", minval=0.5, maxval=8.0, step=0.1, group=GRP_RISK)
i_slPct = input.float(3.0, "SL Percent", minval=0.5, maxval=10.0, step=0.1, group=GRP_RISK)
i_tpMode = input.string("RR", "Take Profit Mode", options=["RR", "Percent", "None"], group=GRP_RISK, tooltip="RR: risk/reward ratio based on SL distance. Percent: fixed %. None: hold until SL or flip.")
i_tpRR = input.float(2.5, "TP Risk:Reward", minval=0.5, maxval=10.0, step=0.1, group=GRP_RISK)
i_tpPct = input.float(6.0, "TP Percent", minval=0.5, maxval=20.0, step=0.1, group=GRP_RISK)
i_trail = input.bool(true, "Trailing Stop", group=GRP_RISK, tooltip="When enabled, SL trails in the direction of the trade after entry.")
i_trailAtr = input.float(2.5, "Trail ATR Mult", minval=0.5, maxval=8.0, step=0.1, group=GRP_RISK)
// --- Filters ---
GRP_FLT = "◈ Trade Filters"
i_trendF = input.bool(true, "EMA Trend Filter", group=GRP_FLT, tooltip="Only take longs above EMA, shorts below EMA.")
i_regF = input.bool(true, "Skip Ranging", group=GRP_FLT, tooltip="Skip entries during ranging regime. Reduces whipsaws.")
i_volF = input.bool(true, "Volume Filter", group=GRP_FLT, tooltip="Only enter if volume is above average.")
i_sigCD = input.int(10, "Cooldown (bars)", minval=0, maxval=50, group=GRP_FLT)
// --- Backtest Window ---
GRP_BT = "◈ Backtest"
bool inWindow = true
// --- Visuals ---
GRP_VIS = "◈ Visuals"
i_sGlow = input.bool(true, "Band Glow Effect", group=GRP_VIS)
i_sRegBg = input.bool(true, "Regime Background", group=GRP_VIS)
// --- Colors ---
GRP_COL = "◈ Colors"
i_cBull = input.color(color.new(#089981, 0), "Bull", inline="c1", group=GRP_COL)
i_cBear = input.color(color.new(#f23645, 0), "Bear", inline="c1", group=GRP_COL)
// ============================================================================
// CORE CALCULATIONS
// ============================================================================
int n = bar_index
float atr = ta.atr(i_atLen)
float safeAtr = nz(atr, 0.001)
// ── Regime Detection ──
float atrMa = ta.sma(atr, i_regLen)
float atrRatio = atrMa > 0 ? atr / atrMa : 1.0
// ADX
float upMove = high - high[1]
float dnMove = low[1] - low
float plusDM = upMove > dnMove and upMove > 0 ? upMove : 0
float minusDM = dnMove > upMove and dnMove > 0 ? dnMove : 0
float trueR = ta.tr
float smoothTR = ta.rma(trueR, i_adxLen)
float smoothPD = ta.rma(plusDM, i_adxLen)
float smoothND = ta.rma(minusDM, i_adxLen)
float plusDI = smoothTR > 0 ? 100 * smoothPD / smoothTR : 0
float minusDI = smoothTR > 0 ? 100 * smoothND / smoothTR : 0
float diSum = plusDI + minusDI
float dx = diSum > 0 ? 100 * math.abs(plusDI - minusDI) / diSum : 0
float adx = ta.rma(dx, i_adxLen)
var int regime = 1
if atrRatio > 1.4
regime := 2
else if adx < i_adxThr and atrRatio < 0.9
regime := 0
else
regime := 1
// ── Adaptive Multiplier ──
float adaptMult = i_bMult
if i_adapt
if regime == 2
adaptMult := i_bMult * (1.0 + (atrRatio - 1.0) * 0.4)
else if regime == 0
adaptMult := i_bMult * 0.85
adaptMult := math.max(math.min(adaptMult, i_bMult * 2.0), i_bMult * 0.5)
// ── SuperTrend ──
var float stBand = na
var int stDir = 1
float upperBase = i_src + adaptMult * atr
float lowerBase = i_src - adaptMult * atr
float prevBand = nz(stBand[1], stDir == 1 ? lowerBase : upperBase)
if stDir == 1
stBand := math.max(lowerBase, prevBand)
if close < stBand
stDir := -1
stBand := upperBase
else
stBand := math.min(upperBase, prevBand)
if close > stBand
stDir := 1
stBand := lowerBase
bool trendFlip = stDir != stDir[1]
// ── Trend EMA ──
float trendMa = ta.ema(close, i_trendLen)
bool trendUp = close > trendMa
bool trendDn = close < trendMa
// ── Volume ──
float volMa = ta.sma(volume, i_volLen)
// ============================================================================
// AI SIGNAL SCORING
// ============================================================================
scoreSignal(bool isBull) =>
float score = 0
// Factor 1: Volume surge (0-20)
float vRat = volMa > 0 ? volume / volMa : 1.0
score += vRat >= 2.5 ? 20 : vRat >= 1.5 ? 14 : vRat >= 1.0 ? 8 : 3
// Factor 2: Displacement beyond band (0-25)
float disp = isBull ? (close - stBand) : (stBand - close)
float dispAtr = safeAtr > 0 ? disp / safeAtr : 0
score += dispAtr >= 1.5 ? 25 : dispAtr >= 0.8 ? 18 : dispAtr >= 0.3 ? 12 : dispAtr > 0 ? 5 : 0
// Factor 3: EMA trend alignment (0-20)
bool aligned = (isBull and trendUp) or (not isBull and trendDn)
float emaDist = math.abs(close - trendMa) / safeAtr
score += aligned and emaDist > 0.5 ? 20 : aligned ? 14 : emaDist < 0.3 ? 8 : 2
// Factor 4: Regime quality (0-15)
score += regime == 1 ? 15 : regime == 2 ? 8 : 3
// Factor 5: Band distance before flip (0-20)
float prevDist = not na(stBand[1]) ? math.abs(close[1] - stBand[1]) / safeAtr : 0
score += prevDist >= 2.0 ? 20 : prevDist >= 1.0 ? 14 : prevDist >= 0.5 ? 8 : 3
int(math.min(math.round(score), 100))
// ============================================================================
// TRADE LOGIC
// ============================================================================
var int lastEntryBar = 0
var float entryPrice = 0.0
var float slPrice = 0.0
var float tpPrice = 0.0
var int lastSigScore = 0
var int lastSigDir = 0
var bool lastSigBright = false
bool longEntry = false
bool shortEntry = false
int sigScore = 0
// Cooldown check
bool cdOk = (n - lastEntryBar) > i_sigCD
if trendFlip and inWindow and cdOk and barstate.isconfirmed
if stDir == 1
sigScore := scoreSignal(true)
// Apply filters
bool passScore = sigScore >= i_minSc
bool passTrend = not i_trendF or trendUp
bool passReg = not i_regF or regime != 0
bool passVol = not i_volF or (volume > volMa)
if passScore and passTrend and passReg and passVol
longEntry := true
else
sigScore := scoreSignal(false)
bool passScore = sigScore >= i_minSc
bool passTrend = not i_trendF or trendDn
bool passReg = not i_regF or regime != 0
bool passVol = not i_volF or (volume > volMa)
if passScore and passTrend and passReg and passVol
shortEntry := true
// ── Calculate SL/TP Levels ──
float slDist = 0.0
if i_slMode == "ATR"
slDist := atr * i_slAtr
else if i_slMode == "Percent"
slDist := close * i_slPct / 100
else // SuperTrend mode
slDist := math.abs(close - stBand)
float tpDist = 0.0
if i_tpMode == "RR"
tpDist := slDist * i_tpRR
else if i_tpMode == "Percent"
tpDist := close * i_tpPct / 100
// ── Execute Entries ──
if longEntry
// Close any existing short
if strategy.position_size < 0
strategy.close("Short")
entryPrice := close
slPrice := close - slDist
tpPrice := i_tpMode != "None" ? close + tpDist : na
strategy.entry("Long", strategy.long)
if not na(slPrice) and i_tpMode != "None" and not na(tpPrice)
strategy.exit("Long Exit", "Long", stop=slPrice, limit=tpPrice,
trail_points = i_trail ? slDist / syminfo.mintick : na,
trail_offset = i_trail ? (atr * i_trailAtr) / syminfo.mintick : na)
else if not na(slPrice)
strategy.exit("Long SL", "Long", stop=slPrice,
trail_points = i_trail ? slDist / syminfo.mintick : na,
trail_offset = i_trail ? (atr * i_trailAtr) / syminfo.mintick : na)
lastEntryBar := n
lastSigScore := sigScore
lastSigDir := 1
lastSigBright := sigScore >= 70
if shortEntry
// Close any existing long
if strategy.position_size > 0
strategy.close("Long")
entryPrice := close
slPrice := close + slDist
tpPrice := i_tpMode != "None" ? close - tpDist : na
strategy.entry("Short", strategy.short)
if not na(slPrice) and i_tpMode != "None" and not na(tpPrice)
strategy.exit("Short Exit", "Short", stop=slPrice, limit=tpPrice,
trail_points = i_trail ? slDist / syminfo.mintick : na,
trail_offset = i_trail ? (atr * i_trailAtr) / syminfo.mintick : na)
else if not na(slPrice)
strategy.exit("Short SL", "Short", stop=slPrice,
trail_points = i_trail ? slDist / syminfo.mintick : na,
trail_offset = i_trail ? (atr * i_trailAtr) / syminfo.mintick : na)
lastEntryBar := n
lastSigScore := sigScore
lastSigDir := -1
lastSigBright := sigScore >= 70
// ── SuperTrend Flip Exit (if SL mode is SuperTrend) ──
if i_slMode == "SuperTrend" and trendFlip
if strategy.position_size > 0 and stDir == -1
strategy.close("Long", comment="ST Flip")
if strategy.position_size < 0 and stDir == 1
strategy.close("Short", comment="ST Flip")
// ============================================================================
// VISUALS
// ============================================================================
// ── Band Color ──
color bandCore = stDir == 1 ? i_cBull : i_cBear
color bandColor = regime == 2 ? color.new(#ffab00, 0) : regime == 0 ? color.new(#78909c, 20) : bandCore
// ── Glow ──
plot(i_sGlow ? stBand : na, "Glow Outer", color=color.new(bandColor, 85), linewidth=6, style=plot.style_linebr)
plot(i_sGlow ? stBand : na, "Glow Mid", color=color.new(bandColor, 70), linewidth=4, style=plot.style_linebr)
// ── Band ──
plot(regime != 0 ? stBand : na, "Band (Solid)", color=bandColor, linewidth=2, style=plot.style_linebr)
plot(regime == 0 ? stBand : na, "Band (Ranging)", color=bandColor, linewidth=2, style=plot.style_linebr)
// ── Flip Dot ──
plot(trendFlip ? stBand : na, "Flip Dot", color=bandCore, style=plot.style_circles, linewidth=5, join=false)
// ── Regime Background ──
bgcolor(i_sRegBg and regime == 2 ? color.new(#ffab00, 95) : na, title="Volatile BG")
bgcolor(i_sRegBg and regime == 0 ? color.new(#78909c, 96) : na, title="Ranging BG")
// ── Trend EMA ──
plot(trendMa, "Trend EMA", color=color.new(trendUp ? i_cBull : i_cBear, 65), linewidth=1)
// ============================================================================
// HIDDEN PLOTS
// ============================================================================
plot(sigScore > 0 ? sigScore : na, "Signal Score", display=display.none)
plot(adaptMult, "Adaptive Mult", display=display.none)
plot(adx, "ADX", display=display.none)
plot(float(regime), "Regime", display=display.none)
plot(float(stDir), "Trend Direction", display=display.none)