
Are You Ready to Climatize?
Climatize is an investment platform focused on renewable energy projects across America.
You can explore vetted clean energy offerings, with past projects including solar on farms in Tennessee, grid-scale battery storage in New York, and EV chargers in California.
Each project is reviewed for transparency and offers people access to fund development and construction loans for renewable energy in local communities.
As of November 2025, more than $13.2 million has been invested through the platform across 28 renewable energy projects. To date, over $3.6 million has already been returned to our growing investor base. Returns are not guaranteed, and past performance does not predict future results.
Check out Climatize to explore clean energy projects raising capital. Minimum investments start with as little as $10.
Climatize is an SEC-registered & FINRA member funding portal. Crowdfunding carries risk, including loss.
🚀 Your Algo Edge Just Leveled Up — Premium Plans Are Here!🚀
A year in, our Starter, Pro, and Elite Quant Plans are crushing it—members are live-trading bots and booking 1-on-1 wins. Now with annual + lifetime deals for max savings.
Every premium member gets: ✅ Full code from every article ✅ Private GitHub repos + templates ✅ 3–5 deep-dive paid articles/mo ✅ Early access + live strategy teardowns
Pick your edge:
Starter (€20/mo) → 1 paid article + public repos
Builder (€30/mo) → Full code + private repos (most popular)
Master (€50/mo) → Two 1-on-1 calls + custom bot built for you
Best deals: 📅 Annual: 2 months FREE 🔒 Lifetime: Own it forever + exclusive perks
First 50 annual/lifetime signups get a free 15-min audit. Don’t wait—the market won’t.
— AlgoEdge Insights Team

Most Traders draw Fibonacci levels by hand. That may be a problem.
Without a clear rule for where to start, it makes the implementation more difficult. This leads to subjective pivots and inconsistent angles.
This guide shows you how to automate the process using a volatility-based ZigZag approach meant for detecting 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.
Sticking with the same home insurance could cost you
Home insurance costs are rising fast, up nearly 40% nationwide in just the past few years. With premiums changing constantly, sticking with the same provider could mean overpaying by hundreds of dollars. Shopping around and comparing multiple insurers can help lock in better rates without losing the protection your home needs. Check out Money’s home insurance tool to shop around and see if you can save.

This article is structured as follows:
Why Fibonacci Fans Still Matter
Anchoring Fans with Price Structure
Python Implementation
What This Method Does Well and Where It Stops
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.
The full end-to-end workflow is available in a Google Colab notebook, exclusively for paid subscribers of my newsletter. Paid subscribers also gain access to the complete article, including the full code snippet in the Google Colab notebook, which is accessible below the paywall at the end of the article. Subscribe now to unlock these benefits!

Figure 1. Auto-generated Fibonacci fans for ETH-USD around the 2021 rally.
2. Anchoring Fans with Price Structure
The accuracy of any Fibonacci fan depends on where it’s anchored. If you get the pivot wrong, 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:

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

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.

If that value exceeds the threshold, the swing is valid. This logic gives us a clean list of swing points, filtered for significance.
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")
# PARAMETERS
TICKER = "ASML.AS"
START_DATE = "2023-06-01"
END_DATE = "2026-01-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:

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:

This tells us how large a move needs to be, relative to current price and recent volatility to count as a meaningful swing.
The full end-to-end workflow is available in a Google Colab notebook, exclusively for paid subscribers of my newsletter. Paid subscribers also gain access to the complete article, including the full code snippet in the Google Colab notebook, which is accessible below the paywall at the end of the article. Subscribe now to unlock these benefits!
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_MULT3.4. Spot Swings with ZigZag Logic
Next, 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 positionprice: price at the pivotisHigh:Truefor a high,Falsefor a low
This ZigZag-style logic filters out minor fluctuations and captures only meaningful structural swings.
zigzag_indices = [] # stores tuples: (bar_index, price, isHigh)
last_pivot_idx = 0
last_pivot_price = df["Close"].iloc[0]
# Determine if first pivot is a high or low
last_pivot_is_high = df["Close"].iloc[1] < last_pivot_price
# Initialize with the first pivot
zigzag_indices.append((last_pivot_idx, last_pivot_price, last_pivot_is_high))
# Basic significance check based on % move vs threshold
def is_significant(dev_thr, old_price, new_price):
return abs((new_price - old_price) / new_price)*100 > dev_thr
df_reset = df.reset_index(drop=True)
for i in range(1, len(df_reset)):
cp = df_reset["Close"].iloc[i]
dev_thr = df_reset["dev_threshold"].iloc[i]
if last_pivot_is_high:
if cp > last_pivot_price:
# New local high; update current pivot
last_pivot_idx = i
last_pivot_price = cp
zigzag_indices[-1] = (last_pivot_idx, last_pivot_price, last_pivot_is_high)
else:
if is_significant(dev_thr, last_pivot_price, cp):
# Price dropped enough → confirm high, start new low
zigzag_indices[-1] = (last_pivot_idx, last_pivot_price, True)
last_pivot_idx = i
last_pivot_price = cp
last_pivot_is_high = False
zigzag_indices.append((last_pivot_idx, last_pivot_price, last_pivot_is_high))
else:
if cp < last_pivot_price:
# New local low; update current pivot
last_pivot_idx = i
last_pivot_price = cp
zigzag_indices[-1] = (last_pivot_idx, last_pivot_price, last_pivot_is_high)
else:
if is_significant(dev_thr, last_pivot_price, cp):
# Price rose enough → confirm low, start new high
zigzag_indices[-1] = (last_pivot_idx, last_pivot_price, False)
last_pivot_idx = i
last_pivot_price = cp
last_pivot_is_high = True
zigzag_indices.append((last_pivot_idx, last_pivot_price, last_pivot_is_high))
# Ensure final pivot is updated
zigzag_indices[-1] = (last_pivot_idx, last_pivot_price, last_pivot_is_high)
# Abort if we didn’t find at least two pivots
if len(zigzag_indices) < 2:
raise ValueError("Not enough pivots found.")3.5. Anchor the Fans with Meaningful Pivots
Once pivots are detected, we split…
Subscribe to our premium content to read the rest.
Become a paying subscriber to get access to this post and other subscriber-only content.
Upgrade


