Trading the Channel

One of the simplest form of trend trading opens positions when the price crosses its moving average, and closes or reverses them when the price crosses back. In the latest TASC issue, Perry Kaufman suggested an alternative. He is using a linear regression line with an upper and lower band for trend trading. Such a band indicator can be used to trigger long or short positions when the price crosses the upper or lower band, or when it gets close.

Let’s first code the bands. They are simply a regression line moved up or down so that it crosses through the highest and lowest price peaks and valleys. Here’s the piece of code in C:

var Slope = LinearRegSlope(seriesC(),N);
var Intercept = LinearRegIntercept(seriesC(),N);
var LinVal, HighDev = 0, LowDev = 0;
for(i=N; i>0; i--) {
  LinVal = Intercept + Slope*(N-i);
  HighDev = max(HighDev,priceC(i)-LinVal);
  LowDev = min(LowDev,priceC(i)-LinVal);
}

N is the number of bars for which the regression line is calculated. The line has the formula y = b + m*x, where b is the intercept and m the slope. The code generates both for the previous N bars, then calculates in the loop the maximum and minimum deviations (HighDev, LowDev). The regression value (LinVal) is calculated from the intercept and slope with the above formula. Since the bar offset i runs backwards from the current bar, the bar number that’s multiplied with the slope runs from N down to 0. Here’s the code applied to a SPY chart from 2025:

The candles can exceed the upper and lower bands because only the close price is used for the bands. It would probably improve the system, at least in theory, when we used the high and low prices instead.

Kaufman suggests several methods of trading with these bands; we’re here using the ‘Inside Channel’ method since it is, according to Kaufman, the most profitable. We open a long position when the price comes within a zone around the lower band, and close the position (or open a short position) when the price comes within a zone around the upper band. Here’s the complete trading system in C for Zorro, using the above code to calculate the bands.

void run()
{
  BarPeriod = 1440;
  StartDate = 20100101;
  LookBack = 150;
  assetList("AssetsIB");
  asset("SPY");
  if(is(LOOKBACK)) return;

  int i, N = 40;
  var Factor = 0.2;
  var Slope = LinearRegSlope(seriesC(),N);
  var Intercept = LinearRegIntercept(seriesC(),N);
  var LinVal, HighDev = 0, LowDev = 0;
  for(i=N; i>0; i--) {
    LinVal = Intercept + Slope*(N-i);
    HighDev = max(HighDev,priceC(i)-LinVal);
    LowDev = min(LowDev,priceC(i)-LinVal);
  }
  var Zone = Factor*(HighDev+LowDev);
  if(!NumOpenLong && priceC(0) < LinVal+LowDev+Zone)
    enterLong();
  if(!NumOpenShort && priceC(0) > LinVal+HighDev-Zone)
    exitLong();
}

We’ve selected the AssetsIB asset list, which contains the margins, commissions and other parameters from an US broker (IBKR). So the backtest simulates trading with IBKR. The resulting equity curve with the default parameters, N = 40 and Zone Factor = 20%, already shows promise with a 2.8 profit factor:

However, Kaufman mentioned that he tested N values from 20 to 150, and zone factors from 5% to 50%. We’ll do the same by optimizing these parameters. Of course a backtest with the best optimization result would be meaningless due to bias (see https://zorro-project.com/backtest.php). Therefore we’re using walk forward optimization for an out-of-sample backtest. Since anything with Zorro is done in code, we’ll insert 4 lines of C code for the optimization:

set(PARAMETERS); // optimize parameters
NumWFOCycles = 5;
N = optimize(40,20,150,10);
Factor = optimize(0.2,0.05,0.5,0.05);

We also changed the trading from only long positions to long and short by replacing exitLong with enterShort By default, entering a short position automatically exits the long one, and vice versa. So the system is always in the market with 1 share, either long or short. The walk forward optimization takes about 3 seconds. This is the resulting equity curve:

The profit factor rose to 7, with a 76% win rate – not bad for such a simple system. The code can be downloaded from the 2025 script repository on https://financial-hacker.com

8 thoughts on “Trading the Channel”

  1. Thanks for the article! Are you sure this is correct? var Zone = Factor*(HighDev+LowDev);
    LowDev is negative, so this calculation makes Zone vary below and above zero, which, when Zone is negative, makes the ‘inner channel’ go outside of the main channel.

  2. You’re absolutely correct. Zone is in fact a ratio of the difference of the band distances to the center. It can be positive or negative, and can result in trigger points outside the bands. For trading only inside the bands, you had to subtract the deviations:

    var Zone = Factor*(HighDev-LowDev);

    However I found that this produces worse results. You can try it. From the various possible ways of calculating the zone, this one is the best I found.

  3. Can you please share the resource you referred to from Perry Kaufman, where we can find more information?
    Thank you for your article.

  4. In what relation was the training and trading for the WFO cycles? For example 75% training and 25% trading on unseen data?

    And why 5 cycles?

    Thanks!!

  5. Thank you Petra for the very interesting article.
    (The script in the 2025 repository is missing: thank you for share it)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.