A line-break band system that encodes directional strength using count-based alpha and ATR-driven volatility offsets
TrendWave Bands don’t try to predict market direction. They react to it cleanly, visually, and in real time.Â
We combine smoothed price levels with adaptive volatility buffers to show trend transitions as they emerge, not after they’re obvious.
Each time price crosses above or below the dynamic bands, a new regime begins. As the trend extends, the bands grow more visible to reflect strength and duration.
Let’s implement the analysis, from calculating the bands, defining trend direction and building the visuals to highlight transitions and persistance.
This article is structure as follows:
- The TrendWave System
- Compute Bands & Trend Flip Markers
- Plotting Bands and Transition Perdiods
- Discussion — Limitations, and Improvements
End-to-end Google Notebook provided below.
1. TrendWave Bands and How They Work
TrendWave Bands combine smoothed price levels with dynamic volatility buffers to identify trend transitions.Â
Unlike fixed-width envelopes, these bands adapt in real time to changing market conditions.
The process starts with the typical price, which averages the daily high, low, and close:
This price acts as the trigger for detecting trend shifts.
To define the adaptive band structure:
- Volatility is computed as a simple moving average of the daily range:

2. A smoothed price is added or subtracted from volatility to create raw bands:
- Upper Raw Band:
- Lower Raw Band:

Crossover logic detects the moment the typical price breaks through these levels:
- If
hlc3
crosses above the upper raw band → trend shifts up - If
hlc3
crosses below the lower raw band → trend shifts down
Once a trend is active, a counter tracks how long it persists (up to a fixed cap).Â
This trend duration modulates the opacity of the plotted line, the longer the trend, the more visible the band.
To extend the bands and reflect price pressure, an additional layer is added using the Average True Range:
- True Range:
- Then smoothed over 100 periods:
These values are used to define extended “wave lines”:
- Upper Band:
- Lower Band:

Finally, trend flip markers are added when the trend direction changes and the regime shifts.Â
2. Python Implementation
2.1 User Parameters
You set the ticker and date range to pull historical data. Any Yahoo Finance symbol works — stocks, ETFs, crypto, etc.
The LENGTH
parameter controls smoothing. Lower values make the bands react faster. Higher values filter noise and slow down trend shifts.
The FACTOR
scales band width using volatility. Increase it for wider, more stable bands. Decrease it to make the system more responsive.
Color settings define how trends appear visually — green for up, red for down, cyan for extensions by default.
Toggles like SHOW_BANDS
, GRADIENT
, and SHOW_DASHBOARD
control visuals and whether to print a quick summary to the console.
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("dark_background")
# USER PARAMETERS
TICKER = "ASML.AS" # Ticker symbol used to pull data from Yahoo Finance
START_DATE = "2023-01-01" # Start date for historical data
END_DATE = "2026-06-01" # End date for historical data
LENGTH = 50 # Lookback window used to smooth signals and compute trend structure
FACTOR = 1.0 # Multiplier for volatility band width; higher => wider bands
COLOR_UP = "#00FF00" # Line color when trend is up
COLOR_DN = "#DD1A1A" # Line color when trend is down
COLOR_UL = "#00FFFF" # Color used for wave extensions (ATR-based bands)
SHOW_BANDS = True # Toggle to draw the trend-following bands
GRADIENT = True # If True, use opacity gradient in the fill for visual emphasis
SHOW_DASHBOARD = True # If True, print config and trend summary in the console
2.2 Data Download & Preprocessing
Daily price data is downloaded for the selected ticker.Â
We ensure that key columns such as High, Low, Close, and Volume are correctly identified and standardized.Â
The date index is also converted into a NumPy array for plotting.
# Data Download & Preprocessing
# Download daily data
df = yf.download(TICKER, start=START_DATE, end=END_DATE, interval="1d", auto_adjust=False)
if df.empty:
print("No data returned for the given ticker/date range.")
exit(1)
# Flatten multi-index columns if present
if isinstance(df.columns, pd.MultiIndex):
df.columns = df.columns.map("_".join)
# Robust column renaming:
# Expected keys for price data.
keys = ["Open", "High", "Low", "Close", "Volume"]
rename_dict = {}
for key in keys:
if key not in df.columns:
# Search for any column that starts with the key
for col in df.columns:
if col.startswith(key):
rename_dict[col] = key
print(f"Column '{col}' found. Renaming it to '{key}'.")
break
if rename_dict:
df.rename(columns=rename_dict, inplace=True)
# Verify required columns exist now.
for key in ["High", "Low", "Close"]:
if key not in df.columns:
print(f"Required column '{key}' not found after renaming. Available columns: {df.columns}")
exit(1)
df.dropna(inplace=True)
if df.empty:
print("Data is empty after cleaning. Exiting.")
exit()
# Standardize column names for later use.
df["Price"] = df["Close"]
df["Time"] = df.index
# Convert the date index to a NumPy array (for positional indexing).
dates_arr = df.index.to_numpy()
2.3 Compute TrendWave Bands & Trend Flip Markers
Now we calculate the core structure that drives the indicator.
First, we create the typical price by averaging each day’s high, low, and close. This gives a more stable signal than using close price alone.
Next, we build two adaptive bands:
- The upper raw band uses a short-term moving average plus a volatility buffer.
- The lower raw band uses a longer average minus the same buffer.
Volatility is measured as the average high–low range over 70 bars, scaled by the FACTOR
. These bands expand or contract in real time.
We then track when price crosses these bands:
- If the typical price crosses above the upper band → trend is up
- If it crosses below the lower band → trend is down
- Otherwise, the previous trend continues
Each time a trend is active, we count how long it lasts, capped at 70. This count is used later to control line transparency: longer trends = stronger visual signal.
To give the structure more context, we also calculate ATR(100). This extends the bands to form outer “waves” that reflect price pressure beyond the trend core.
Finally, we detect when the trend direction changes and store those points. These flip markers are plotted later to highlight trend shifts visually.
Newsletter