Back To Top

April 2, 2025

Auto-Detect Price Pivot Zones with Fibonacci Fans

Map support and resistance zones using ATR and Zigzag-based Fibonacci fans anchored to key price pivots.

Most traders draw Fibonacci levels by hand. That may be a problem. 

Without a clear rule for where to start, it’s difficult to correctly implement Fibonacci fans. This leads to subjective pivots and inconsistent angles.

This guide shows you how to automate the process using volatility-based ZigZag approach to detect significant swing highs and lows.

Once the key swing points are identified, Fibonacci fan lines are plotted to reflect the underlying geometry of the trend.

Automatic Fibanacci Lines (OPTIMIZED)

1. Why Fibonacci Fans Still Matter

Fibonacci fans are rooted in number theory. The ratios they use — 0.382, 0.5, 0.618 — come from the Fibonacci sequence, a pattern studied since the 13th century. 

These proportions appear in natural systems, architecture, and financial markets. Traders began applying them seriously in the 1970s, building on the popularity of Fibonacci retracements and extensions.

Fans take those same ratios but project them diagonally across time. You anchor one end to a major pivot and extend lines outward. Each line represents a structural zone where price may react 

These levels often align with how trends expand and contract. That’s why they still matter: they reflect the natural ‘rhythm’ of price movement over time.

Take the Ethereum’s 2021 rally as an example. A fan anchored from the July low to the September high mapped key reaction zones during the pullback. Price stalled and bounced along the 38.2% and 61.8% lines.

2. Anchoring Fans with Price Structure

The accuracy of any Fibonacci fan depends on where it’s anchored. Get the pivot wrong, and every line that follows loses meaning.

Our method uses recent volatility to filter out insignificant moves. Instead of hardcoding a minimum point difference or fixed time window, we define a relative threshold based on the asset’s price behavior.

We start by calculating a naive 10-bar range to estimate local volatility:

Formula 1 ATRA10

Then we convert this into a percentage deviation from the current close:

Formula 2 Threshold Formula

This threshold defines how large a move must be — relative to recent volatility — to qualify as a swing. A higher multiplier captures only major pivots. A lower one makes the method more sensitive.

Each new bar is compared to the last confirmed pivot. If the percentage change exceeds the threshold, it’s marked as a new high or low.

Formula 3. Percentage Change

If that value exceeds the threshold, the swing is valid. This logic gives us a clean list of swing points, filtered for significance and responsive to market conditions.

Once pivots are detected, we sort them into highs and lows. From there, we define two anchor types:

  • Top fans: drawn from swing highs
  • Bottom fans: drawn from swing lows

3. Python Implementation

3.1. User Parameters

Define the ticker, date range, fan settings, and pivot selection logic.

You can choose whether to draw top fans (from swing highs), bottom fans (from swing lows), or both. 

Each fan is anchored to a pivot you select — like the lowest low, second lowest, or third lowest point in the detected swings.

The same applies for highs. For example, setting top_origin = 2 anchors the fan from the second-highest high.

				
					import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
plt.style.use("dark_background")

# USER PARAMETERS
TICKER = "ASML.AS"
START_DATE = "2023-06-01"
END_DATE = "2026-03-01"
INTERVAL = "1d"

# How sensitive the pivot detection should be.
# Higher values = fewer pivots (only big moves count), lower values = more pivots.
DEVIATION_MULT = 3.0

# Fibonacci fan levels to plot (as ratios)
FIB_LEVELS = [1.0, 0.75, 0.618, 0.5, 0.382, 0.25, 0.0]
REVERSE_FANS = False # If True, the fan lines will be flipped (mirrored logic).

# Control whether to show top fans (from highs) and/or bottom fans (from lows).
draw_top_fans = True
draw_bottom_fans = True

# Choose which pivot the fan lines should start from.
# Example: if you set this to 1, the fan will start from the highest (or lowest) pivot found.
# Set to 2 if you want the second highest/lowest, and so on.
# This lets you control which swing point to use for drawing the fans.
top_origin_selection = 1
bottom_origin_selection = 1

# FIB COLORS
fib_colors = {
    1.0: "#787b86",
    0.75: "#2196f3",
    0.618: "#009688",
    0.5: "#4caf50",
    0.382: "#81c784",
    0.25: "#f57c00",
    0.0: "#787b86"
}
				
			

3.2. Download Market Data

Pull historical price data from Yahoo Finance. Clean up column names, drop NaNs, and convert the index to datetime.

				
					# DOWNLOAD DATA & SET DATETIME INDEX
df = yf.download(TICKER, start=START_DATE, end=END_DATE, interval=INTERVAL)
if df.empty:
    raise ValueError("No data returned from yfinance.")

if isinstance(df.columns, pd.MultiIndex):
    df.columns = [col[0] for col in df.columns]

df.dropna(subset=["Open","High","Low","Close"], inplace=True)
df.index = pd.to_datetime(df.index)
				
			

3.3. Volatility and Deviation Threshold

We calculate a simple 10-bar ATR to estimate recent price volatility. Instead of using the traditional ATR formula. We use the naive range outlined above:

Formula 1 ATRA10

This gives us the total price range over the last 10 bars. To make it relative to the current price, we convert it into a percentage-based deviation threshold:

Formula 5. Dev Threshold

This tells us how large a move needs to be — relative to current price and recent volatility — to count as a meaningful swing.

The higher the DEVIATION_MULT, the fewer pivots will be detected.

				
					# CALCULATE NAIVE ATR (10-bar) & DEVIATION THRESHOLD
df["ATR10"] = df["High"].rolling(10).max() - df["Low"].rolling(10).min()
df["dev_threshold"] = (df["ATR10"] / df["Close"]) * 100 * DEVIATION_MULT
				
			

3.4. Spot Swings with ZigZag Logic

We scan through closing prices to detect significant swing highs and lows using percentage change from the last pivot.

If the price moves beyond the deviation threshold, we confirm a new pivot and store it as a tuple:

(index, price, isHigh)

  • index: bar position
  • price: price at the pivot
  • isHigh: True for a high, False for a low

This ZigZag-style logic filters out minor fluctuations and captures only meaningful structural swings.

Prev Post

Draw Trend Lines Algorithmically with Multi-Anchored Regressions

Next Post

Price Transitions with Adaptive Trend Wave Bands

post-bars
Mail Icon

Newsletter

Get Every Weekly Update & Insights

[mc4wp_form id=]

Leave a Comment