Fibonacci Retracements in Pine Script: Anchors, Alerts and Test Limits
Fibonacci retracement levels divide a selected price move into proportional distances. Their usefulness depends on how the endpoints are chosen and when those endpoints become known. A mathematical ratio alone does not establish support, resistance or a profitable entry.

Define the anchors first
For an upward move from a low to a high, a retracement can be calculated as the high minus the range multiplied by a ratio. The familiar 50% level is a midpoint convention, not a Fibonacci sequence ratio. Ratios approaching 0.618 and 0.382 emerge from larger sequence terms; they are not exact ratios between every pair of terms.
The indicator below uses the highest high and lowest low in a rolling window. These are window extremes, not confirmed swing pivots. It does not establish whether the low occurred before the high, so it should not be described as automatically detecting a valid upward swing.
Complete rolling-range indicator
//@version=5
indicator("Dynamic Fibonacci Retracements", overlay=true, max_lines_count=10)
// Input parameters
lookback = input.int(20, title="Lookback Period", minval=5, maxval=100)
show_labels = input.bool(true, title="Show Level Labels")
fib_color = input.color(color.blue, title="Fibonacci Line Color")
// Find swing high and low
swing_high = ta.highest(high, lookback)
swing_low = ta.lowest(low, lookback)
range_size = swing_high - swing_low
// Calculate Fibonacci levels
fib_0 = swing_high
fib_236 = swing_high - range_size * 0.236
fib_382 = swing_high - range_size * 0.382
fib_50 = swing_high - range_size * 0.5
fib_618 = swing_high - range_size * 0.618
fib_786 = swing_high - range_size * 0.786
fib_100 = swing_low
// Plot the levels
plot(fib_0, "0.0%", color=fib_color, linewidth=1)
plot(fib_236, "23.6%", color=fib_color, linewidth=1)
plot(fib_382, "38.2%", color=fib_color, linewidth=2)
plot(fib_50, "50.0%", color=color.yellow, linewidth=2)
plot(fib_618, "61.8%", color=color.red, linewidth=3)
plot(fib_786, "78.6%", color=fib_color, linewidth=2)
plot(fib_100, "100.0%", color=fib_color, linewidth=1)
// Optional labels
if show_labels and barstate.islast
label.new(bar_index, fib_618, "61.8%", style=label.style_label_left, color=color.red, textcolor=color.white)
label.new(bar_index, fib_382, "38.2%", style=label.style_label_left, color=color.blue, textcolor=color.white)
label.new(bar_index, fib_50, "50.0%", style=label.style_label_left, color=color.yellow, textcolor=color.black)
Both anchors can change on a new extreme or when an old extreme leaves the window. Levels also move with an open bar. A crossover of a moving level can therefore arise from movement of price, the level, or both.
The example creates new labels on last-bar executions rather than maintaining three permanent labels; labels can accumulate across closed bars up to the platform’s limits. max_lines_count does not control these labels or the plot() calls. The red, thick 61.8% line is a display choice, not evidence that it is the most reliable level.
Optional indicator fragments
Append this RSI fragment to the indicator if you want to inspect the conjunction. Its tolerance is 1% of the current bar’s high-low range, not 1% of price. It tests the close’s proximity, not whether a wick touched the level. On a zero-range bar its strict comparison cannot pass:
// RSI confirmation at Fibonacci levels
rsi = ta.rsi(close, 14)
// Buy signal: Price at 61.8% Fib + RSI oversold
buy_signal = math.abs(close - fib_618) < (high - low) * 0.01 and rsi < 30
// Sell signal: Price at 38.2% Fib + RSI overbought
sell_signal = math.abs(close - fib_382) < (high - low) * 0.01 and rsi > 70
plotshape(buy_signal, "Buy", shape.triangleup, location.belowbar, color.green, size=size.small)
plotshape(sell_signal, "Sell", shape.triangledown, location.abovebar, color.red, size=size.small)
This alert fragment also depends on the earlier level variables. Its tolerance is narrower, at 0.5% of bar range:
// Alert conditions for Fibonacci levels
fib_618_touch = math.abs(close - fib_618) < (high - low) * 0.005
fib_382_touch = math.abs(close - fib_382) < (high - low) * 0.005
alertcondition(fib_618_touch, "Price at 61.8% Fib", "Price is touching the 61.8% Fibonacci retracement level")
alertcondition(fib_382_touch, "Price at 38.2% Fib", "Price is touching the 38.2% Fibonacci retracement level")
Adding alertcondition() does not create an active alert. Select the condition in TradingView’s alert dialog and choose the intended frequency. Recreate an alert after changing the source or settings if the running alert must use those changes.
A strategy fragment needs a separate script
The following existing fragment is a sketch, not a standalone backtest. It needs a version annotation and the level calculations. It replaces the indicator declaration; appending it alongside indicator() creates an invalid script with two declarations.
strategy("Fibonacci Backtest", overlay=true)
// Entry condition: Buy at 61.8% retracement
if close <= fib_618 * 1.01 and close >= fib_618 * 0.99
strategy.entry("Long", strategy.long)
// Exit condition: Take profit at 38.2% level
if close >= fib_382 * 0.99 and strategy.position_size > 0
strategy.close("Long")
// Stop loss: Below 78.6% level
if close <= fib_786 and strategy.position_size > 0
strategy.close("Long")
The entry checks a 1% band around the level. Exits use bar-close conditions and market orders, not resting stop or limit orders at the plotted levels. The range also keeps updating after entry unless you deliberately freeze the anchors. These choices can materially change results and must be specified before making comparisons.
No reproducible evidence supports the former AMD price story, BTC 11-of-16 comparison or NVDA 46%-to-62% win-rate claim. Evaluate one precisely defined anchor and execution policy with costs and a separate test period rather than selecting favorable screenshots.
Sources checked October 20, 2026: TradingView Fibonacci drawing tool, Pine alerts and strategies.

