2637 字
13 分鐘

反轉檢測 Pro v3.0 指標詳解:非重繪自適應 ZigZag 與三重 EMA 趨勢濾網

免責聲明
  1. 本站內容僅供教育與研究分享,不構成任何投資建議。
  2. 加密資產波動極高,請自行評估風險與倉位,損益自負。
  3. 文中提及的指標/案例僅作方法示範,不代表未來結果。
  4. 部分頁面可能包含返傭/贊助連結,若有將於頁內說明。

//@version=6
indicator("反轉檢測 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.01
input_revAmount = isCustom ? input.float(0.05, "絕對反轉值", group="高級設置") : 0.05
input_atrreversal = isCustom ? input.float(2.0, "ATR乘數", group="高級設置") : 2.0
input_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 : atrMultiplier
float 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 = 9
fast_length = 14
slow_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_avg9
stopbuy = mov_avg9 <= mov_avg14
buynow = not buy[1] and buy
var int buysignal = 0
buysignal := buynow and not stopbuy ? 1 : buysignal == 1 and stopbuy ? 0 : buysignal
sell = mov_avg9 < mov_avg14 and mov_avg14 < mov_avg21 and high < mov_avg9
stopsell = mov_avg9 >= mov_avg14
sellnow = not sell[1] and sell
var int sellsignal = 0
sellsignal := sellnow and not stopsell ? 1 : sellsignal == 1 and stopsell ? 0 : sellsignal
// ============================================================================
// COLORS
// ============================================================================
GREEN = #00FF00
RED = #FF0000
PURPLE = #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 = na
var float zlow = na
var float zhighActual = na
var float zlowActual = na
var int zhighbar = 0
var int zlowbar = 0
var 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 = na
var float lastConfirmedPivotActual = na
var int lastConfirmedPivotBar = 0
var bool lastConfirmedPivotIsHigh = false
var 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 = na
var int previewPivotBar = 0
var bool previewIsHigh = false
var 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 = na
var float EIH = na
var float EILActual = na
var float EIHActual = na
var int EILBar = 0
var int EIHBar = 0
var int dir = 0
var 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 : signal
else if dir < 0 and pricehConfirmed < EIH
signal := signal >= 0 ? -1 : signal
U1 = signal > 0 and signal[1] <= 0
D1 = 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 count
while 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 detection
trendToBullish = buysignal == 1 and buysignal[1] != 1
trendToBearish = sellsignal == 1 and sellsignal[1] != 1
// Reversal alerts
alertcondition(U1, "看漲反轉",
"看漲反轉:{{close}}")
alertcondition(D1, "看跌反轉",
"看跌反轉:{{close}}")
alertcondition(U1 or D1, "任一反轉",
"反轉:{{close}}")
// EMA trend alerts
alertcondition(buynow, "EMA 多頭訊號", "EMA 多頭訊號")
alertcondition(sellnow, "EMA 空頭訊號", "EMA 空頭訊號")
// Trend change alerts
alertcondition(trendToBullish, "趨勢轉為多頭",
"趨勢轉為多頭")
alertcondition(trendToBearish, "趨勢轉為空頭",
"趨勢轉為空頭")
// Strong signal alerts
strongBullish = U1 and buysignal == 1
strongBearish = 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/
作者
老莫
發布於
2026-01-20
許可協議
CC BY-NC-SA 4.0
Profile Image of the Author
老莫
總是耕耘,總有收穫。
公告
歡迎光臨我的博客,這裏會分享我的日常和實戰中的收集、整理及總結,希望能對你有所幫助。
分類
標籤
站點統計
文章
172
分類
4
標籤
149
總字數
127,457
運行時長
0
最後活動
0 天前

目錄