Technique explained as code
Channel breakout
Draw a channel from the highest high and the lowest low of recent days. The rule acts when price closes outside it. Code you can run, and an honest account of how it fails.
Risk warning: CFDs and spread bets are complex instruments and come with a high risk of losing money rapidly due to leverage. You can lose money on these products, and each provider publishes its own loss figure under FCA rules. You should consider whether you understand how they work and whether you can afford to take the high risk of losing your money. Automated and API trading can amplify losses through bugs, connectivity failures and runaway orders.
Teaching material only. This page explains how a technique works as code and how to test an idea honestly. It is not investment advice, not a recommendation and not an invitation to trade. Every price on it is invented, so no result here says anything about any real market, and nothing here suggests that a technique makes money.
What it is
Take the highest price of the last twenty days and the lowest price of the last ten. Those two lines make a channel that price has stayed inside.
A breakout rule gets in when price closes above the top of the channel and gets out when it closes below the bottom. It is sometimes called a Donchian channel, after the trader who described it.
The idea behind it
A new high means that everyone who bought in the last twenty days is sitting on a gain and no recent seller's price is left above. The thinking is that a market making new highs is more likely to be in a lasting move than one stuck in the middle of its range.
Like the moving average crossover it follows a move that has already started. The difference is that it uses the extremes of the range, not the average, so it reacts to a single strong day.
The code
The detail to watch is which days go into the channel. It is built from the days before yesterday and then compared with yesterday's close. If yesterday were inside its own channel, its close could never be above the top of it.
Data the script reads: invented-prices.csv. Invented values, never a real market. Save it in one folder and run the script from that folder.
The file that was run: channel_breakout.py
# Channel breakout: act when price leaves the range it has been in.
# Prices come from invented-prices.csv: a random walk made by a script, never a real market.
# The result therefore says nothing about any real market. Read it for the method.
import csv
highs, lows, closes = [], [], []
with open("invented-prices.csv", newline="") as f:
for row in csv.DictReader(f):
highs.append(float(row["high"]))
lows.append(float(row["low"]))
closes.append(float(row["close"]))
# Example values, picked only because they are easy to follow. They are not a suggestion.
ENTRY_DAYS = 20 # the channel that has to be broken to get in
EXIT_DAYS = 10 # a shorter channel on the other side, to get out
COST = 0.10 # points paid on every completed trade, a stand in for the spread
holding = False
entry = 0.0
results = []
START = ENTRY_DAYS + 1
for day in range(START, len(closes)):
yesterday = day - 1
# The channel is built from the days BEFORE yesterday, then compared with yesterday's close.
# Nothing from today is used to decide.
top = max(highs[yesterday - ENTRY_DAYS:yesterday])
bottom = min(lows[yesterday - EXIT_DAYS:yesterday])
price = closes[day] # act at today's close
if not holding and closes[yesterday] > top:
holding, entry = True, price
elif holding and closes[yesterday] < bottom:
results.append(price - entry - COST)
holding = False
if holding:
results.append(closes[-1] - entry - COST)
wins = [r for r in results if r > 0]
print("Trades:", len(results))
print("Trades that ended above zero:", len(wins))
if wins:
print("Largest single trade:", round(max(results), 2))
print("Total points after costs:", round(sum(results), 2))
print("Total without the largest trade:", round(sum(results) - max(results), 2))
print("Simply holding over the same days:", round(closes[-1] - closes[START] - COST, 2))What the run printed
Trades: 14 Trades that ended above zero: 5 Largest single trade: 4.43 Total points after costs: -9.94 Total without the largest trade: -14.37 Simply holding over the same days: -10.82
Output of the run on , copied as printed. Every input to this run was invented, so the result says nothing about any real market.
How to read it
Fourteen trades, five of them above zero. As with most rules of this kind, most breakouts fail: price pokes outside the channel and comes straight back.
The script prints the total with and without the largest single trade. If taking one trade away changes the picture, the result is one event, not a pattern. Here the total is below zero either way.
The total is close to what simply holding did over the same days. The prices are a random walk, so a new high in this file tells you nothing about the next day. The numbers say nothing about any real market.
How it fails or misleads
- False breakouts. Price closes just outside the channel, the rule gets in at what turns out to be the extreme, and price falls back inside. In a market that is going nowhere this is the usual outcome.
- It buys high on purpose. The entry is always at a recent extreme, so the distance to the exit line is large and each failed trade costs more than a failed crossover.
- Gaps. The test acts at the closing price the day after the signal. A market that jumps on the open can be a long way from the price the test assumed.
- Few trades and one large one. Totals from breakout tests are often carried by a single long move, which makes them fragile and easy to over read.
- The channel lengths are free choices, and a test will happily find a pair that suited the past.
The page on how a backtest lies shows the mistakes that make any rule look better in a test than it is.
What to try changing
These are programming exercises. Each one changes the code and asks you to explain what you see. None of them is a search for settings to use.
- Change the code so the channel includes yesterday. Count the trades. Explain to yourself why the number is what it is.
- Add the mirror image: a rule that acts on a close below the lowest low. Keep the two sets of results in separate lists.
- Print how many days each trade lasted, and the average for trades above and below zero.
- Use the highest close in place of the highest high. Does anything change, and why might it?
- Record the day number of every entry and work out how many came within five days of the previous exit.
How this page was made
Each script on this page was run with Python 3.12 on Windows, standard library only, from a folder holding the data files listed. No network and no account. Each exited with code 0, and what is shown is everything it printed.
The data files whose names begin with the word invented are written by make_invented_data.py from fixed seeds, so anyone can make the same files again and get the same output. This site is not published if the code or a data file has changed since the run.