2637 字
13 分鐘
反轉檢測 Pro v3.0 指標詳解:非重繪自適應 ZigZag 與三重 EMA 趨勢濾網
免責聲明
- 本站內容僅供教育與研究分享,不構成任何投資建議。
- 加密資產波動極高,請自行評估風險與倉位,損益自負。
- 文中提及的指標/案例僅作方法示範,不代表未來結果。
- 部分頁面可能包含返傭/贊助連結,若有將於頁內說明。
//@version=6indicator("反轉檢測 Pro v3.0 - 非重繪", shorttitle="反轉檢測 Pro v3.0", overlay=true, max_boxes_count=50, max_lines_count=200, max_labels_count=100)
// ============================================================================// 確認設置// ============================================================================confirmationMode = input.string("僅確認", "信號模式", options=["僅確認", "確認+預覽", "僅預覽"], group="信號控制")
confirmationBars = input.int(0, "額外確認K線數", minval=0, maxval=5, group="信號控制")
// ============================================================================// 靈敏度預設系統// ============================================================================input_sensitivity = input.string("中", "靈敏度預設", options=["極高", "高", "中", "低", "極低", "自訂"], group="主控制")
float atrMultiplier = switch input_sensitivity "極高" => 0.8 "高" => 1.2 "中" => 2.0 "低" => 2.8 "極低" => 3.5 => na
float percentThreshold = switch input_sensitivity "極高" => 0.005 "高" => 0.008 "中" => 0.01 "低" => 0.015 "極低" => 0.02 => na
// ============================================================================// 手動設置// ============================================================================bool isCustom = input_sensitivity == "自訂"
input_method = input.string("平均", "計算方法", options=["平均", "高低"], group="高級設置")
input_percentamount = isCustom ? input.float(0.01, "反轉百分比", group="高級設置") : 0.01input_revAmount = isCustom ? input.float(0.05, "絕對反轉值", group="高級設置") : 0.05input_atrreversal = isCustom ? input.float(2.0, "ATR乘數", group="高級設置") : 2.0input_atrlength = input.int(5, "ATR週期", minval=1, maxval=50, group="高級設置")input_averagelength = input.int(5, "平均週期", minval=1, maxval=50, group="高級設置")
// ============================================================================// 區域設置// ============================================================================input_showSupplyDemand = input.string("樞軸", "供需區顯示", options=["樞軸", "箭頭", "不顯示"], group="區域")
input_numbersuppdemandtoshow = input.int(3, "區域數量", minval=0, maxval=20, group="區域")
input_showsupplydemandcloud = input.bool(false, "顯示供需區域", group="區域")
input_zoneExtension = input.int(20, "區域延伸K線數", minval=5, maxval=100, group="區域")
zoneThickness = input.float(0.02, "區域厚度 (%)", minval=0.01, maxval=0.2, step=0.01, tooltip="矩形區域厚度占價格的百分比", group="區域")
// ============================================================================// 標籤設置// ============================================================================input_lineExtension = input.int(5, "停止線延伸", minval=1, maxval=50, group="標籤")
input_maxLines = input.int(10, "最大顯示線數", minval=3, maxval=50, group="標籤")
labelSizeOption = input.string("中", "標籤大小", options=["小", "中", "大"], group="標籤")
labelSize = switch labelSizeOption "小" => size.small "中" => size.normal "大" => size.large => size.normal
// ============================================================================// 資訊表設置// ============================================================================showInfoTable = input.bool(true, "顯示資訊表", group="資訊表")
tablePosition = input.string("右上", "表格位置", options=["右上", "左上", "上中", "右下", "左下", "下中"], group="資訊表")
tableSizeOption = input.string("中", "表格大小", options=["極小", "小", "中", "大", "特大"], group="資訊表")
// ============================================================================// CALCULATE FINAL REVERSAL THRESHOLD// ============================================================================float finalATRMult = isCustom ? input_atrreversal : atrMultiplierfloat finalPctThreshold = isCustom ? input_percentamount : percentThreshold
atrValue = ta.atr(input_atrlength)reversalAmount = math.max(close * finalPctThreshold / 100, math.max(input_revAmount, finalATRMult * atrValue))
// ============================================================================// MOVING AVERAGE SIGNALS// ============================================================================superfast_length = 9fast_length = 14slow_length = 21
mov_avg9 = ta.ema(close, superfast_length)mov_avg14 = ta.ema(close, fast_length)mov_avg21 = ta.ema(close, slow_length)
buy = mov_avg9 > mov_avg14 and mov_avg14 > mov_avg21 and low > mov_avg9stopbuy = mov_avg9 <= mov_avg14buynow = not buy[1] and buy
var int buysignal = 0buysignal := buynow and not stopbuy ? 1 : buysignal == 1 and stopbuy ? 0 : buysignal
sell = mov_avg9 < mov_avg14 and mov_avg14 < mov_avg21 and high < mov_avg9stopsell = mov_avg9 >= mov_avg14sellnow = not sell[1] and sell
var int sellsignal = 0sellsignal := sellnow and not stopsell ? 1 : sellsignal == 1 and stopsell ? 0 : sellsignal
// ============================================================================// COLORS// ============================================================================GREEN = #00FF00RED = #FF0000PURPLE = #ab47bc
// ============================================================================// ZIGZAG CALCULATION// ============================================================================priceh = input_method == "高低" ? high : ta.ema(high, input_averagelength)pricel = input_method == "高低" ? low : ta.ema(low, input_averagelength)
pricehConfirmed = priceh[confirmationBars]pricelConfirmed = pricel[confirmationBars]actualHighConfirmed = high[confirmationBars]actualLowConfirmed = low[confirmationBars]
var float zhigh = navar float zlow = navar float zhighActual = navar float zlowActual = navar int zhighbar = 0var int zlowbar = 0var int direction = 0
if na(zhigh) or na(zlow) zhigh := pricehConfirmed zlow := pricelConfirmed zhighActual := actualHighConfirmed zlowActual := actualLowConfirmed zhighbar := bar_index - confirmationBars zlowbar := bar_index - confirmationBars direction := 1
var float lastConfirmedPivotPrice = navar float lastConfirmedPivotActual = navar int lastConfirmedPivotBar = 0var bool lastConfirmedPivotIsHigh = falsevar bool confirmedPivotDetected = false
confirmedPivotDetected := false
if direction == 1 if pricehConfirmed > zhigh zhigh := pricehConfirmed zhighActual := actualHighConfirmed zhighbar := bar_index - confirmationBars
if zhigh - pricelConfirmed >= reversalAmount lastConfirmedPivotPrice := zhigh lastConfirmedPivotActual := zhighActual lastConfirmedPivotBar := zhighbar lastConfirmedPivotIsHigh := true confirmedPivotDetected := true direction := -1 zlow := pricelConfirmed zlowActual := actualLowConfirmed zlowbar := bar_index - confirmationBars
else if direction == -1 if pricelConfirmed < zlow zlow := pricelConfirmed zlowActual := actualLowConfirmed zlowbar := bar_index - confirmationBars
if pricehConfirmed - zlow >= reversalAmount lastConfirmedPivotPrice := zlow lastConfirmedPivotActual := zlowActual lastConfirmedPivotBar := zlowbar lastConfirmedPivotIsHigh := false confirmedPivotDetected := true direction := 1 zhigh := pricehConfirmed zhighActual := actualHighConfirmed zhighbar := bar_index - confirmationBars
// ============================================================================// PREVIEW DETECTION// ============================================================================var float previewPivotPrice = navar int previewPivotBar = 0var bool previewIsHigh = falsevar bool showPreview = false
showPreview := false
if confirmationMode != "僅確認" var float zhigh_preview = na var float zlow_preview = na var int direction_preview = 0
if na(zhigh_preview) zhigh_preview := priceh zlow_preview := pricel direction_preview := 1
if direction_preview == 1 if priceh > zhigh_preview zhigh_preview := priceh
if zhigh_preview - pricel >= reversalAmount previewPivotPrice := zhigh_preview previewPivotBar := bar_index previewIsHigh := true showPreview := true direction_preview := -1 zlow_preview := pricel
else if direction_preview == -1 if pricel < zlow_preview zlow_preview := pricel
if priceh - zlow_preview >= reversalAmount previewPivotPrice := zlow_preview previewPivotBar := bar_index previewIsHigh := false showPreview := true direction_preview := 1 zhigh_preview := priceh
// ============================================================================// SIGNAL DETECTION// ============================================================================var float EIL = navar float EIH = navar float EILActual = navar float EIHActual = navar int EILBar = 0var int EIHBar = 0var int dir = 0var int signal = 0
if confirmedPivotDetected if lastConfirmedPivotIsHigh EIH := lastConfirmedPivotPrice EIHActual := lastConfirmedPivotActual EIHBar := lastConfirmedPivotBar dir := -1 else EIL := lastConfirmedPivotPrice EILActual := lastConfirmedPivotActual EILBar := lastConfirmedPivotBar dir := 1
if dir > 0 and pricelConfirmed > EIL signal := signal <= 0 ? 1 : signalelse if dir < 0 and pricehConfirmed < EIH signal := signal >= 0 ? -1 : signal
U1 = signal > 0 and signal[1] <= 0D1 = signal < 0 and signal[1] >= 0
// ============================================================================// HELPER FUNCTION// ============================================================================formatPrice(float price) => priceStr = str.tostring(price, format.mintick) parts = str.split(priceStr, ".") intPart = array.get(parts, 0) decPart = array.size(parts) > 1 ? "." + array.get(parts, 1) : ""
intLen = str.length(intPart) result = "" for i = 0 to intLen - 1 if i > 0 and (intLen - i) % 3 == 0 result := result + "," result := result + str.substring(intPart, i, i + 1)
result + decPart
// ============================================================================// REVERSAL LABELS// ============================================================================var array<line> allLines = array.new<line>()var array<int> lineStartBars = array.new<int>()var array<int> lineEndBars = array.new<int>()
if U1 and confirmationMode != "僅預覽" pivotBar = EILBar exactLow = EILActual
lbl = label.new(pivotBar, exactLow, "反轉\n" + formatPrice(EIL), style=label.style_label_upper_right, color=color.new(GREEN, 0), textcolor=color.black, size=labelSize, textalign=text.align_left, xloc=xloc.bar_index)
endBar = pivotBar + input_lineExtension horizLine = line.new(pivotBar, exactLow, endBar, exactLow, color=color.new(GREEN, 0), width=2, style=line.style_solid, extend=extend.none) array.push(allLines, horizLine) array.push(lineStartBars, pivotBar) array.push(lineEndBars, endBar)
if D1 and confirmationMode != "僅預覽" pivotBar = EIHBar exactHigh = EIHActual
lbl = label.new(pivotBar, exactHigh, "反轉\n" + formatPrice(EIH), style=label.style_label_lower_right, color=color.new(RED, 0), textcolor=color.white, size=labelSize, textalign=text.align_left, xloc=xloc.bar_index)
endBar = pivotBar + input_lineExtension horizLine = line.new(pivotBar, exactHigh, endBar, exactHigh, color=color.new(RED, 0), width=2, style=line.style_solid, extend=extend.none) array.push(allLines, horizLine) array.push(lineStartBars, pivotBar) array.push(lineEndBars, endBar)
// ============================================================================// LINE MANAGEMENT// ============================================================================while array.size(allLines) > input_maxLines oldLine = array.shift(allLines) line.delete(oldLine) array.shift(lineStartBars) array.shift(lineEndBars)
// ============================================================================// PREVIEW LABELS// ============================================================================if showPreview and confirmationMode != "僅確認" and barstate.islast if previewIsHigh label.new(previewPivotBar, high, "反轉\n" + formatPrice(previewPivotPrice), style=label.style_label_lower_right, color=color.new(#FF6B6B, 70), textcolor=color.new(color.white, 40), size=labelSize, textalign=text.align_left) else label.new(previewPivotBar, low, "反轉\n" + formatPrice(previewPivotPrice), style=label.style_label_upper_right, color=color.new(#6BCF7F, 70), textcolor=color.new(color.black, 40), size=labelSize, textalign=text.align_left)
// ============================================================================// SUPPLY/DEMAND ZONES - THIN HORIZONTAL RECTANGLES// ============================================================================var array<box> allBoxes = array.new<box>()
if confirmedPivotDetected and input_showsupplydemandcloud // Pivot LOW = Demand Zone (GREEN), Pivot HIGH = Supply Zone (RED) bool isDemandZone = lastConfirmedPivotIsHigh == false
color zoneColor = isDemandZone ? color.new(GREEN, 85) : color.new(RED, 85) color zoneBorder = isDemandZone ? color.new(GREEN, 40) : color.new(RED, 40)
// Use actual pivot price for zone placement float pivotPrice = lastConfirmedPivotActual
// Create thin rectangle centered on pivot float halfThickness = (pivotPrice * zoneThickness / 100) / 2 float zoneTop = pivotPrice + halfThickness float zoneBottom = pivotPrice - halfThickness
int zoneStart = lastConfirmedPivotBar int zoneEnd = zoneStart + input_zoneExtension
zoneBox = box.new(zoneStart, zoneTop, zoneEnd, zoneBottom, border_color=zoneBorder, bgcolor=zoneColor, border_width=1, extend=extend.none, text = isDemandZone ? "需求" : "供給", text_size = size.tiny, text_color = isDemandZone ? color.new(GREEN, 20) : color.new(RED, 20), text_halign = text.align_center, text_valign = text.align_center)
array.push(allBoxes, zoneBox)
// Manage box countwhile array.size(allBoxes) > input_numbersuppdemandtoshow and input_numbersuppdemandtoshow > 0 oldBox = array.shift(allBoxes) box.delete(oldBox)
// ============================================================================// INFORMATION TABLE// ============================================================================tablePos = switch tablePosition "右上" => position.top_right "左上" => position.top_left "上中" => position.top_center "右下" => position.bottom_right "左下" => position.bottom_left "下中" => position.bottom_center => position.top_right
headerSize = switch tableSizeOption "極小" => size.tiny "小" => size.small "中" => size.normal "大" => size.large "特大" => size.huge => size.normal
cellSize = switch tableSizeOption "極小" => size.tiny "小" => size.small "中" => size.small "大" => size.normal "特大" => size.large => size.small
var table infoTable = table.new(tablePos, 2, 7, bgcolor=color.new(#1E1E1E, 5), frame_color=color.new(GREEN, 40), frame_width=2, border_width=1, border_color=color.new(#444444, 60))
if barstate.islast and showInfoTable table.cell(infoTable, 0, 0, "反轉檢測 PRO v3.0", text_color=color.new(GREEN, 0), text_size=headerSize, bgcolor=color.new(#0A0A0A, 0)) table.cell(infoTable, 1, 0, "非重繪", text_color=color.new(GREEN, 0), text_size=headerSize, bgcolor=color.new(#0A0A0A, 0))
table.cell(infoTable, 0, 1, "模式:", text_color=color.white, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0))
color modeColor = color.new(GREEN, 0) if confirmationMode == "確認+預覽" modeColor := color.new(#FFA500, 0) if confirmationMode == "僅預覽" modeColor := color.new(#FF6B6B, 0)
table.cell(infoTable, 1, 1, confirmationMode, text_color=modeColor, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0))
table.cell(infoTable, 0, 2, "靈敏度:", text_color=color.white, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0)) table.cell(infoTable, 1, 2, input_sensitivity, text_color=color.yellow, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0))
table.cell(infoTable, 0, 3, "ATR 乘數:", text_color=color.white, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0)) table.cell(infoTable, 1, 3, str.tostring(finalATRMult, "#.##"), text_color=color.aqua, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0))
table.cell(infoTable, 0, 4, "目前 ATR:", text_color=color.white, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0)) table.cell(infoTable, 1, 4, str.tostring(atrValue, format.mintick), text_color=color.aqua, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0))
table.cell(infoTable, 0, 5, "閾值:", text_color=color.white, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0)) table.cell(infoTable, 1, 5, str.tostring(reversalAmount, format.mintick), text_color=color.orange, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0))
table.cell(infoTable, 0, 6, "趨勢:", text_color=color.white, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0)) trendText = buysignal == 1 ? "多頭" : sellsignal == 1 ? "空頭" : "中性" trendTextColor = buysignal == 1 ? color.green : sellsignal == 1 ? color.red : color.purple table.cell(infoTable, 1, 6, trendText, text_color=trendTextColor, text_size=cellSize, bgcolor=color.new(#1E1E1E, 0))
// =================================================================// ALERTS// =================================================================
// Trend change detectiontrendToBullish = buysignal == 1 and buysignal[1] != 1trendToBearish = sellsignal == 1 and sellsignal[1] != 1
// Reversal alertsalertcondition(U1, "看漲反轉", "看漲反轉:{{close}}")alertcondition(D1, "看跌反轉", "看跌反轉:{{close}}")alertcondition(U1 or D1, "任一反轉", "反轉:{{close}}")
// EMA trend alertsalertcondition(buynow, "EMA 多頭訊號", "EMA 多頭訊號")alertcondition(sellnow, "EMA 空頭訊號", "EMA 空頭訊號")
// Trend change alertsalertcondition(trendToBullish, "趨勢轉為多頭", "趨勢轉為多頭")alertcondition(trendToBearish, "趨勢轉為空頭", "趨勢轉為空頭")
// Strong signal alertsstrongBullish = U1 and buysignal == 1strongBearish = D1 and sellsignal == 1
alertcondition(strongBullish, "強勢多頭訊號", "強勢多頭:{{close}}")alertcondition(strongBearish, "強勢空頭訊號", "強勢空頭:{{close}}")✨ 核心特點
- 僅確認非重繪:訊號鎖定不變,可加 0-5 根額外確認 K 線;預覽僅作早期提示
- 三重反轉閾值:百分比/絕對值/ATR 取最大值,兼顧幣價與波動
- 自適應靈敏度:極高→極低 + 自訂(ATR 乘數/反轉百分比/絕對值/計算方法)
- 三重 EMA 趨勢濾網:9/14/21 週期判斷多空與中性,支援趨勢警報
- 供需區薄矩形:綠=需求、紅=供給,可調厚度、延伸與數量
- 資訊表與警報:顯示模式、ATR、閾值與趨勢;支援反轉/趨勢/強弱訊號
🎯 適用場景
- 加密貨幣日內/剝頭皮(1-5 分鐘)
- 高波動時段的反轉捕捉
- 期貨/外匯/股票等資產亦適用
- 回測與警報策略(僅確認模式較穩定)
⚙️ 推薦設置
| 交易類型 | 靈敏度 | 信號模式 | 區域顯示 |
|---|---|---|---|
| 剝頭皮(1-2分鐘) | 極高/高 | 僅確認 | 開啟(3-5個區域) |
| 日內交易(5-15分鐘) | 中 | 確認+預覽 | 開啟(5-10個區域) |
| 波段交易(30分鐘+) | 低/極低 | 僅確認 | 可選 |
加密市場波動較大,建議先用「中」觀察;若噪音多就下調,想抓更早提示可上調但需接受更多假訊號與滑點風險。
🧠 使用建議
- ✅ 結合價格行為或成交量確認
- ✅ 以「僅確認」為主,預覽只作早期提醒
- ✅ 需要更保守時加入 1-2 根額外確認 K 線
- ✅ 使用水平線與供需區作為止損/目標參考
- ❌ 避免在震盪市場使用過高靈敏度
- ❌ 不要僅依賴預覽信號入場
📊 信號說明
- 綠色標籤:看漲反轉(支撐)
- 紅色標籤:看跌反轉(阻力)
- 預覽標籤(半透明):潛在反轉(可能消失)
- 供需區薄矩形:綠=需求、紅=供給
- K 線顏色:綠=多頭、紅=空頭、紫=中性
- 水平線:對應反轉價格,可作止損/目標參考
⚠️ 重要提示
- 本指標為教育工具,不構成投資建議
- 交易需自負風險,務必做好風險管理
- 預覽可能消失,回測/實盤以「僅確認」為準
- 建議先在模擬帳戶測試
支持與分享
如果這篇文章對你有幫助,歡迎分享給更多人或贊助支持!
反轉檢測 Pro v3.0 指標詳解:非重繪自適應 ZigZag 與三重 EMA 趨勢濾網
https://bkol.cc/posts/scripts/reversal-detection-non-repainting/
老莫筆記