اندیکاتور نادارایا-واتسون (Nadaraya-Watson Envelope)

نادارایا-واتسون با یک روش آماری به نام رگرسیون کرنل، خطی نرم از قیمت می‌سازد و دو باند در اطرافش رسم می‌کند؛ در Vlevel حالت پیش‌فرض آن بدون «بازنقاشی» است.

چیست

رگرسیون نادارایا-واتسون روشی آماری برای تخمین روند پنهان در داده‌های پرنویز است. برای هر نقطه، قیمت‌های نزدیک با وزن بیشتر و قیمت‌های دورتر با وزن کمتر (با کرنل گاوسی) میانگین گرفته می‌شوند. نتیجه خطی بسیار نرم است و باندهای بالا و پایین بر اساس میانگین خطای قیمت از این خط رسم می‌شوند.

چطور محاسبه می‌شود

  1. وزن هر کندل گذشته = exp(−فاصله² ÷ (۲ × پهنای باند²))
  2. خط میانی = میانگین وزنی قیمت‌ها با این وزن‌ها
  3. باندها = خط میانی ± ضریب × میانگین فاصله‌ی مطلق قیمت از خط

تریدرها چطور آن را می‌خوانند

  • خروج قیمت از باند بالا یا پایین و برگشت به داخل، سیگنال رایج این اندیکاتور است.
  • شیب خط میانی جهت کلی حرکت را نشان می‌دهد.
  • در بازارهای رنج و نوسانی بهتر از روندهای پرقدرت کار می‌کند.

تنظیمات

پهنای باند (Bandwidth) میزان نرمی خط را تعیین می‌کند و ضریب، فاصله‌ی باندها را. در Vlevel حالت پیش‌فرض بدون بازنقاشی است و فقط از کندل‌های گذشته استفاده می‌کند؛ نمای کلاسیک با گزینه‌ی Repainting Smoothing روشن می‌شود.

کجا گول می‌زند

  • نسخه‌ی کلاسیک این اندیکاتور «بازنقاشی» می‌کند: مقدارهای گذشته با آمدن کندل جدید عوض می‌شوند و روی چارت عالی به نظر می‌رسد، ولی در زمان واقعی آن سیگنال‌ها وجود نداشتند.
  • پیش از اعتماد به سیگنال‌ها، با بازنقاشی خاموش (حالت پیش‌فرض) بک‌تست بگیر.

کد Pine Script آن در Vlevel

این همان اسکریپتی است که چارت اجرا می‌کند. در پنجره‌ی اندیکاتورهای چارت می‌توانی آن را باز کنی، تغییر دهی و نسخه‌ی خودت را ذخیره کنی.

Nadaraya-Watson Envelope · Pine Script v6
//@version=6
indicator("Nadaraya-Watson Envelope", shorttitle = "NWE", overlay = true, max_lines_count = 500, max_labels_count = 500, max_bars_back = 500)
h       = input.float(8.0, "Bandwidth", minval = 1, step = 0.5, tooltip = "How many bars the smoothing reaches: higher is smoother and slower.")
mult    = input.float(3.0, "Envelope Multiplier", minval = 0, step = 0.1, tooltip = "The bands' distance, in multiples of the mean absolute distance between price and the middle line.")
src     = input.source(close, "Source")
envLen  = input.int(200, "Envelope Length", minval = 10, maxval = 1000, tooltip = "Bars the mean absolute distance is averaged over (without repainting).")
repaint = input.bool(false, "Repainting Smoothing", tooltip = "On: the classic look, smoothing with bars on both sides, redrawn on every bar over the recent window (the past changes; don't backtest on it). Off: each bar only uses itself and the bars before it, so nothing drawn changes later.")
win     = input.int(250, "Repainting Window", minval = 20, maxval = 250, tooltip = "Bars redrawn when repainting is on.")
signals = input.bool(true, "Show Signals", tooltip = "A mark where price crosses back inside a band.")
showMid = input.bool(false, "Show Middle Line")
upCol   = input.color(#ff4d6a, "Upper Band")
dnCol   = input.color(#2ee6a6, "Lower Band")
midCol  = input.color(color.new(#8a959c, 30), "Middle Line")

// ---- Without repainting: each bar's estimate uses only it and the bars before it, up to 4
// bandwidths back (the weight beyond is under 0.04% of the nearest one), so nothing drawn changes
// later. ta.kernel is Vlevel's built-in for the Gaussian-weighted average (a Pine loop doing the
// same sums is far slower).
nw = ta.kernel(src, h, math.min(500, int(math.ceil(h * 4))))
mae = ta.sma(math.abs(src - nw), envLen) * mult
upper = nw + mae
lower = nw - mae
plot(repaint ? na : upper, "Upper", color = upCol, width = 2)
plot(repaint ? na : lower, "Lower", color = dnCol, width = 2)
plot(repaint or not showMid ? na : nw, "Middle", color = midCol)
plotshape(signals and not repaint and ta.crossunder(src, upper), "Sell", style = shape.triangledown, location = location.abovebar, color = upCol, size = size.tiny)
plotshape(signals and not repaint and ta.crossover(src, lower), "Buy", style = shape.triangleup, location = location.belowbar, color = dnCol, size = size.tiny)

// ---- Repainting: on the last bar, every bar of the window smoothed with the bars on both sides of
// it (ta.kernel_window, index 0 = the last bar), and the envelope at the mean absolute distance.
ys = ta.kernel_window(src, h, win)
if repaint and barstate.islast and array.size(ys) > 1
    n = array.size(ys) - 1
    float sae = 0.0
    for i = 0 to n
        sae += math.abs(src[i] - array.get(ys, i))
    m = sae / (n + 1) * mult
    for i = 1 to n
        y0 = array.get(ys, i)
        y1 = array.get(ys, i - 1)
        line.new(bar_index - i, y0 + m, bar_index - i + 1, y1 + m, color = upCol, width = 2)
        line.new(bar_index - i, y0 - m, bar_index - i + 1, y1 - m, color = dnCol, width = 2)
        if showMid
            line.new(bar_index - i, y0, bar_index - i + 1, y1, color = midCol)
        if signals and src[i] > y0 + m and src[i - 1] < y1 + m
            label.new(bar_index - i + 1, src[i - 1], "▼", color = color.new(color.black, 100), textcolor = upCol, style = label.style_label_down, size = size.small)
        if signals and src[i] < y0 - m and src[i - 1] > y1 - m
            label.new(bar_index - i + 1, src[i - 1], "▲", color = color.new(color.black, 100), textcolor = dnCol, style = label.style_label_up, size = size.small)

اندیکاتورها قیمت‌های گذشته را توصیف می‌کنند و هیچ‌کدام آینده را پیش‌بینی نمی‌کنند. برای آموزش است، نه توصیه‌ی سرمایه‌گذاری.