Goertzel
Background
The Goertzel indicator scans a price series for the cycles hiding inside it, keeps the strongest few, and rebuilds them into a single smooth, noise-filtered curve. Because only the strongest cycles survive, the random jitter is stripped away and what is left is the rhythmic component the market is actually moving on. That curve can also be projected a short way into the future, so it doubles as a low-lag cycle forecast.
It is based on the published work of Dennis Meyers (his "Adaptive 10 Cycle Goertzel-DFT" system). The toolbox generalises his fixed ten-cycle method into a configurable indicator — you choose the cycle range, how many cycles to keep, and how far to project — so it is an extension of that work rather than a straight copy.
The Goertzel algorithm in plain English
A Fourier transform breaks a signal into the separate cycles that make it up. The catch with the ordinary transform (the DFT, and its fast cousin the FFT) is that it can only measure cycles at fixed, evenly-spaced lengths — for a window of N bars it can read the N-bar, N/2-bar, N/3-bar cycle and so on, but nothing in between. A real 14-bar cycle that falls between two of those slots gets smeared across them.
The Goertzel algorithm is a focused slice of the same maths that fixes this: it can measure any cycle length you ask for, including the ones that fall between the FFT's fixed slots. It is the very algorithm your phone uses to detect which touch-tone button you pressed — fast, exact detection of a specific tone. Here we point it at price instead of dial tones. It holds up well even when the noise is as large as the cycle you are looking for, which is where the older maximum-entropy approach behind some of the cycle engines starts to struggle.
It is not free of trade-offs. To find a cycle reliably in noisy data it needs enough history for the longest cycle to repeat about three times (hence the window rule below), and it cannot separate two cycles whose lengths sit very close together — it reports their blended average instead. (The classic knock on Goertzel was also that it is slow; the toolbox's implementation removes that, updating each bar in constant time rather than rescanning the whole window.)
How the indicator works
The market's cycles drift — they appear, fade and change length over time — so the indicator re-measures them on every bar rather than once. At each bar it:
- takes a trailing window of price and removes its average level and straight-line trend, so the transform is not swamped by the trend (a strong trend otherwise manufactures false low-frequency cycles that drown out the real ones);
- uses Goertzel to measure the strength of every cycle length across the range you asked for;
- keeps the strongest few — you choose how many, up to ten;
- adds those cycles back together to estimate the price a chosen number of bars ahead, and at the current bar; and
- accumulates the bar-to-bar difference between those two values into a running curve.
Building the output as a running sum of small changes — rather than plotting the raw reconstruction — keeps it smooth as the window slides forward and the underlying numbers jump around. The result is a momentum-style curve whose slope and turning points are what you watch, not its absolute level.
Included formula
The toolbox ships a ready-made Goertzel N-Cycle formula that wires all of this up for you, so you don't have to type the code below. In AmiBroker open the Charts window, expand the WiseTraderToolbox group and drag Goertzel N-Cycle onto a chart.
It plots the projected cycle curve and includes the band-breakout trading system described below — complete with buy, sell, short and cover signals you can backtest — as well as an Exploration for scanning a watchlist. The cycle range, the number of cycles, the projection and the band sizes are all set from its parameters.
The exported function
At the heart of the indicator is a single exported AFL function you can call directly. The rest of this page shows how to plot it and build the system yourself, in case you want to adapt it.
Goertzel(Data, NoBestCycles, WindowSize, Projection, CycleMax, CycleMin, BestCycleMethod)
| Parameter | Description |
|---|---|
| Data | The array the Goertzel scan is calculated on. It must not contain empty/null values at the left edge of the data. |
| NoBestCycles | How many of the strongest cycles to combine into the output curve. Must be between 1 and 10 (and no more than the number of cycle lengths being scanned). The signal is built from these N best cycles. |
| WindowSize | The number of trailing bars analysed at each bar. It must be at least
three times the largest cycle (CycleMax): a
reliable cycle reading needs to see at least three full repetitions. For example,
with CycleMax = 100 the window must be 300 or more. Your data must
contain at least this many bars. |
| Projection | How many bars into the future the extracted cycles are projected. Must be 1 or greater. |
| CycleMax | The longest cycle length (in bars) to scan for. Maximum allowed is 270. |
| CycleMin | The shortest cycle length (in bars) to scan for. Minimum allowed is
6, and it must not exceed CycleMax. |
| BestCycleMethod | How "best" is judged when ranking cycles: 0 ranks by
amplitude (raw strength of the cycle), 1 ranks by
strength (amplitude weighted toward longer cycles). Must be 0 or
1. |
Returns a per-bar array holding the noise-filtered, projected cycle curve.
The most common mistake is too small a WindowSize. It must be at least
3 × CycleMax or the indicator returns an empty result with an error.
Remember to give the chart enough history to cover the window.
Plotting the curve
// Scan cycles from 6 to 100 bars, keep the 3 strongest (by amplitude),
// project 1 bar ahead. Window = 3 x 100 = 300.
signal = Goertzel( Close, 3, 300, 1, 100, 6, 0 );
Plot( signal, "Goertzel cycle signal", colorBlue );
Start with a small NoBestCycles (1–3) for the cleanest curve; add
more cycles only if you want the reconstruction to follow finer detail. Watch the
curve's slope and turning points rather than its absolute level.
The trading system
The curve still has small wiggles, so simply buying every time it ticks up would whipsaw you. Meyers traded it with a stop-and-reverse band breakout that ignores the small moves and only acts on a decisive change of direction. You are always in the market, long or short:
- While short, remember the lowest point the curve has reached. Flip
long only once it has rallied a set amount (
pntUp) above that low. - While long, remember its highest point. Flip short
once it has fallen a set amount (
pntDn) below that high.
curve = Goertzel( Close, 3, 300, 1, 100, 6, 0 );
pntUp = 2.0; // how far the curve must rally off its low to flip long
pntDn = 2.0; // how far it must drop off its high to flip short
Buy = Sell = Short = Cover = 0;
pos = 0; // 1 = long, -1 = short, 0 = not yet positioned
ext = curve[ 0 ]; // running extreme: lowest while short, highest while long
for( i = 1; i < BarCount; i++ )
{
if( pos == 1 ) // long: trail the high, flip short on a pntDn drop
{
ext = Max( ext, curve[ i ] );
if( curve[ i ] <= ext - pntDn )
{
Sell[ i ] = Short[ i ] = 1;
pos = -1; ext = curve[ i ];
}
}
else // short / unpositioned: trail the low, flip long on a pntUp rally
{
ext = Min( ext, curve[ i ] );
if( curve[ i ] >= ext + pntUp )
{
Cover[ i ] = Buy[ i ] = 1;
pos = 1; ext = curve[ i ];
}
}
}
Plot( curve, "Goertzel curve", colorBlue );
For intraday futures Meyers added two refinements: close any position near the session's
end so nothing is carried overnight, and ignore signals in the first hour of the day to
sidestep opening-gap whipsaws. The two band sizes (pntUp and
pntDn) are the only things to tune, and he fitted them with walk-forward
optimisation — fit on one block of history, then trade the untouched block that
follows, never the same data you optimised on.
This is the easiest place to fool yourself. Almost any price series — even a random one
— can be made to look profitable in-sample by tuning pntUp,
pntDn, the cycle range or NoBestCycles. That in-sample result
tells you nothing about the future. Judge the system only on data it was not
optimised on, across several different windows. Like the cycle estimators, the most
recent curve values also update as new bars arrive, so a turn can look cleaner in
hindsight than it did live.
The original method always kept exactly the ten strongest cycles and ranked them by raw
amplitude. The toolbox lets you keep anywhere from one to ten, and adds a second ranking
mode (BestCycleMethod = 1, which weights toward longer cycles) — useful
extensions when you want a cleaner or a more trend-biased curve. See
Digital Signal Processing for how this fits with the
rest of the section, and EndPointFFT for a closely
related Fourier-based curve from the same author.