Williams Fractal
I. What is Williams Fractal
Williams Fractal is a technical analysis tool introduced by renowned trading master Bill Williams in his classic book Trading Chaos (published 1995). It identifies local extremes on price charts (fractal highs and fractal lows) to mark potential support levels, resistance levels, and trend reversal points.
Historical Background
Bill Williams was a technical analysis pioneer who integrated chaos theory, fractal geometry, and nonlinear dynamics into trading. He believed that markets are fundamentally chaotic systems, but within the chaos lies an inherent order — fractal structures are the manifestation of this order. In mathematics, a “fractal” refers to a geometric structure that exhibits self-similarity at different scales. Williams applied this concept to financial markets, arguing that price movements display similar reversal patterns across different timeframes.
Williams Fractal is an important component of the Bill Williams trading system (which includes the Alligator indicator, AO Oscillator, AC Accelerator, etc.). In his system, fractals are used to confirm trend breakout entry signals.
Indicator Classification
- Type: Overlay indicator, drawn as arrows or markers on the price chart
- Category: Other Overlay / Pattern Recognition
- Default Parameters: period = 2 (2 bars on each side of the center bar, forming a 5-bar fractal pattern)
- Data Requirements: Requires High and Low data
Williams Fractal is essentially a local extremum detection algorithm. It identifies the location of the highest and lowest points within a group of 5 (or more) bars — these locations are often nodes where short-term bull-bear forces shift.
II. Mathematical Principles and Calculation
Core Definition
For the default parameter (2 bars on each side), fractals are defined as follows:
Fractal Up (Bearish Fractal):
Bar forms a fractal up when:
The center bar’s high is strictly greater than the highs of the two bars on each side.
Fractal Down (Bullish Fractal):
Bar forms a fractal down when:
The center bar’s low is strictly less than the lows of the two bars on each side.
Generalized Formula
When period = , the center bar requires bars on each side, for a total of bars:
Step-by-Step Calculation
- Select window size: Default , total window length = .
- Iterate through each bar: From bar to bar (where is the total number of bars).
- Check fractal up condition: Is the current bar’s High the maximum of all High values within the window?
- Check fractal down condition: Is the current bar’s Low the minimum of all Low values within the window?
- Mark fractals: Annotate the bar position where conditions are met as a fractal up or fractal down.
Signal Delay Characteristics
Fractals have an inherent delay issue: because bars on the right side of the center bar must appear before a fractal can be confirmed:
- With default parameters, fractal signals are delayed by at least 2 bars
- In live trading, when you see a fractal being marked, it is actually an event from 2 bars ago
A single bar can be both a fractal up and a fractal down — this occurs when the bar has both the highest High and the lowest Low (i.e., a long doji or extreme volatility bar).
III. Python Implementation
import numpy as np
import pandas as pd
def williams_fractal(high: pd.Series, low: pd.Series,
period: int = 2) -> pd.DataFrame:
"""
Calculate Williams Fractal
Parameters
----------
high : pd.Series
High price series
low : pd.Series
Low price series
period : int
Number of bars on each side of the center bar, default is 2 (5-bar pattern)
Returns
-------
pd.DataFrame
DataFrame with fractal_up and fractal_down columns.
True indicates a fractal exists at that position.
"""
n = len(high)
fractal_up = pd.Series(False, index=high.index, name="fractal_up")
fractal_down = pd.Series(False, index=low.index, name="fractal_down")
high_arr = high.values
low_arr = low.values
for i in range(period, n - period):
# Check fractal up: is the center bar's High the highest in the window?
is_up = True
for j in range(1, period + 1):
if high_arr[i] <= high_arr[i - j] or high_arr[i] <= high_arr[i + j]:
is_up = False
break
fractal_up.iloc[i] = is_up
# Check fractal down: is the center bar's Low the lowest in the window?
is_down = True
for j in range(1, period + 1):
if low_arr[i] >= low_arr[i - j] or low_arr[i] >= low_arr[i + j]:
is_down = False
break
fractal_down.iloc[i] = is_down
return pd.DataFrame({"fractal_up": fractal_up, "fractal_down": fractal_down})
def williams_fractal_vectorized(high: pd.Series, low: pd.Series,
period: int = 2) -> pd.DataFrame:
"""
Vectorized version of Williams Fractal (better performance)
"""
fractal_up = pd.Series(True, index=high.index)
fractal_down = pd.Series(True, index=low.index)
for i in range(1, period + 1):
fractal_up &= (high > high.shift(i)) & (high > high.shift(-i))
fractal_down &= (low < low.shift(i)) & (low < low.shift(-i))
# Set boundaries to False
fractal_up.iloc[:period] = False
fractal_up.iloc[-period:] = False
fractal_down.iloc[:period] = False
fractal_down.iloc[-period:] = False
return pd.DataFrame({
"fractal_up": fractal_up,
"fractal_down": fractal_down,
})
# ========== Usage Example ==========
if __name__ == "__main__":
np.random.seed(42)
dates = pd.date_range("2024-01-01", periods=100, freq="D")
price = 100 + np.cumsum(np.random.randn(100) * 0.8)
df = pd.DataFrame({
"date": dates,
"open": price + np.random.randn(100) * 0.2,
"high": price + np.abs(np.random.randn(100) * 1.0),
"low": price - np.abs(np.random.randn(100) * 1.0),
"close": price + np.random.randn(100) * 0.15,
"volume": np.random.randint(1000, 10000, size=100),
})
df.set_index("date", inplace=True)
# Calculate fractals
fractals = williams_fractal_vectorized(df["high"], df["low"], period=2)
df = pd.concat([df, fractals], axis=1)
# Print fractal up points
up_fractals = df[df["fractal_up"]]
print("=== Fractal Up (Potential Resistance Levels) ===")
print(up_fractals[["high", "low", "close"]].head(10))
# Print fractal down points
down_fractals = df[df["fractal_down"]]
print("\n=== Fractal Down (Potential Support Levels) ===")
print(down_fractals[["high", "low", "close"]].head(10))
# Statistics
print(f"\nTotal bars: {len(df)}")
print(f"Fractal up count: {df['fractal_up'].sum()}")
print(f"Fractal down count: {df['fractal_down'].sum()}")
# Fractal breakout signals
# Price breaks above the most recent fractal up High -> bullish
# Price breaks below the most recent fractal down Low -> bearish
df["last_up_fractal"] = np.where(df["fractal_up"], df["high"], np.nan)
df["last_up_fractal"] = df["last_up_fractal"].ffill()
df["last_down_fractal"] = np.where(df["fractal_down"], df["low"], np.nan)
df["last_down_fractal"] = df["last_down_fractal"].ffill()
df["breakout_up"] = df["close"] > df["last_up_fractal"].shift(1)
df["breakout_down"] = df["close"] < df["last_down_fractal"].shift(1)
print("\n=== Fractal Breakout Signals ===")
breakouts = df[df["breakout_up"] | df["breakout_down"]]
print(breakouts[["close", "last_up_fractal", "last_down_fractal",
"breakout_up", "breakout_down"]].tail(10))
IV. Problems the Indicator Solves
1. Identifying Local Support/Resistance Levels
Fractal points mark local extremes in price, which naturally form support and resistance levels:
| Fractal Type | Corresponding Price | Interpretation |
|---|---|---|
| Fractal Up | High value | Potential resistance — bulls reached their limit here and then retreated |
| Fractal Down | Low value | Potential support — bears reached their limit here and then bounced |
2. Confirming Trend Breakouts
The most important signal in the Bill Williams system is the fractal breakout:
- Buy signal: Price breaks above the most recent fractal up’s High — bulls breach the prior high, indicating a potential trend initiation or continuation
- Sell signal: Price breaks below the most recent fractal down’s Low — bears breach the prior low, indicating a potential downtrend initiation
3. Combined Use with the Alligator Indicator
In Bill Williams’ complete trading system, fractal signals are filtered through the Alligator indicator:
- Only when a fractal up is above the Alligator’s Teeth line is the long signal valid
- Only when a fractal down is below the Alligator’s Teeth line is the short signal valid
- If a fractal is inside the Alligator’s “mouth,” the signal is ignored
4. Wave Counting Assistance
Fractal points can assist with Elliott Wave analysis:
- Consecutive fractal ups and downs form the highs and lows of wave structures
- Connecting fractal points outlines the market’s swing structure
Fractal signals are inherently delayed by 2 bars (with default parameters). In fast-moving markets, by the time a fractal is confirmed, the price may have already moved a significant distance. Do not use fractals as your sole entry signal.
V. Advantages, Disadvantages, and Use Cases
Advantages
| Advantage | Description |
|---|---|
| Intuitive concept | Identifying local highs and lows aligns with intuition and is easy to understand |
| Minimal parameters | Only one parameter (period), simple to adjust |
| Strong objectivity | Fractal determination is entirely based on mathematical conditions, with no subjective element |
| Cross-market applicability | Works with any market and timeframe that has OHLC data |
| Systematic support/resistance | Automatically generates support/resistance levels, avoiding subjective bias from manual trendlines |
Disadvantages
| Disadvantage | Description |
|---|---|
| Signal lag | Requires right-side bars for confirmation; delayed by at least bars |
| Excessive fractals | In ranging markets, dense clusters of fractal points make filtering difficult |
| No strength information | All fractals carry equal weight; cannot distinguish important from minor reversal points |
| No volume component | Does not consider volume for validation, potentially marking low-quality reversal points |
| False breakout risk | Fractal breakout strategies are prone to consecutive false breakouts in ranging markets |
Use Cases
- Trending markets: Fractal breakout strategies perform best in clearly trending markets
- Daily and higher timeframes: Short-period fractals contain too much noise; daily fractals are more meaningful
- Combined with Alligator: Best results within the complete Bill Williams system
- Support/resistance annotation: Automated marking of key price levels to assist discretionary analysis
Comparison with Related Indicators
| Comparison | Williams Fractal | Pivot Points | Zig Zag |
|---|---|---|---|
| Detection method | Local extremes (fixed window) | Formula-based | Percentage deviation |
| Parameters | period | None | deviation% |
| Lag | bars | None (prior day data) | Uncertain (repaints) |
| Signal density | Relatively high | Fixed (one set per day) | Adjustable |
- Increasing the period parameter (e.g., to 3 or 5) reduces the number of fractals and filters out more significant extreme points.
- Combining Alligator indicator filtering with fractal signals is the standard practice in the Bill Williams system and is strongly recommended.
- You can rank fractals by the absolute height of their High/Low values and prioritize those at the most extreme price levels.
- In multi-timeframe analysis, fractals on higher timeframes carry more significance than lower timeframes — daily fractals outweigh hourly fractals.
- Connecting fractal points can quickly outline the swing structure of price, assisting Elliott Wave analysis.