TASC 2017-01 | Mean-Reversion Swing Trading (Calhoun)

Modified on 2016/11/30 11:40 by Eugene — Categorized as: TASC Traders Tips

Traders' Tip text

This month, author Ken Calhoun shows clean and concise rules for entering in a trend continuation following a pullback after a breakout. This pattern (widely known as "1-2-3 pattern") could be programmed to apply to intraday or daily charts alike. Our rendition targets daily charts.

Since the rules didn't put much emphasis on exits, leaving this to the trader, we added a simple profit target. Below you can find the complete trading system's rules:

Entry:
  1. Wait for a 5-day close-to-close uptrend.
  2. If the closing prices retrace 50% of the uptrend's distance (±1 percentage point) over the next 10 bars, a valid pullback has formed.
  3. Look for the prices to rebound and buy on a stop 50 cents above the retracement price. If this condition hasn’t been met within 10 days, the setup is invalidated.
  4. A second entry is made if first position has been established and the high price breaks through the 15-day highest high price.

Exit:
  1. Exit on a $1 stop loss from the retracement price.
  2. Exit with a profit target at 2 times the retracement distance.

Motivated readers could make various improvements to the program's logic. For instance, to help this technique work equally well on stocks outside the $20-70 price range, make the stop loss and profit target adaptive by replacing the fixed dollar distance with a unit of ATR. Another suggestion is to avoid trades when a stock is not really trending as the 1-2-3 pattern seems to perform best in established trends.

Image

Figure 1 shows a potential trade in HeidelbergCement AG (ticker HEI.DE in Wealth-Data).

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;

namespace WealthLab.Strategies { public class MeanReversionSwingTrading : WealthScript { private StrategyParameter paramUptrendDays; private StrategyParameter paramReversionLasts; private StrategyParameter paramReversionExpiresAfter; private StrategyParameter paramSecondEntry; public MeanReversionSwingTrading() { paramUptrendDays = CreateParameter("Days in uptrend", 5, 2, 10, 1); paramReversionLasts = CreateParameter("Reversion lasts", 4, 2, 12, 2); paramReversionExpiresAfter = CreateParameter("Expires after", 10, 5, 20, 5); paramSecondEntry = CreateParameter("Second entry?", 1, 0, 1, 1); } protected override void Execute() { bool runup = false, reversion = false, pullback = false, secondEntry = paramSecondEntry.ValueInt == 1; int runupBar = -1, pBar = -1, reversionBar = -1, uptrendDays = paramUptrendDays.ValueInt; double runupStart = 0, runupHigh = 0, runupRange = 0, pivotPrice = 0, reversionThreshold = 1.0; Highest hi = Highest.Series(High,15); if( secondEntry ) PlotSeries(PricePane, hi, Color.Blue, LineStyle.Dashed, 1); for(int bar = GetTradingLoopStartBar(15); bar < Bars.Count; bar++) { if( ActivePositions.Count > 0 ) { if(!SellAtStop(bar+1, Position.AllPositions, pivotPrice - 1.0, "$1 stop")) SellAtLimit(bar + 1, Position.AllPositions, pivotPrice + 2.0, "Profit Target"); } if( ActivePositions.Count == 0 ) // First entry { if( !runup ) { if(CumUp.Series(Close,1)[bar] >= uptrendDays) { runup = true; runupBar = bar; runupStart = Low[bar-5]; runupHigh = High[runupBar]; runupRange = runupHigh - runupStart; pivotPrice = runupStart + (runupRange / 2d); DrawLine(PricePane,runupBar,runupHigh,bar-5,runupStart,Color.Blue,LineStyle.Solid,2); } } if( !pullback ) { if( runup ) { if( bar >= runupBar + paramReversionExpiresAfter.ValueInt ) { runup = false; //AnnotateBar("Pullback timeout exceeded",bar,false,Color.Red); } else { double runupInPercent = runupRange / (double)runupStart * 100d; double closeToExact = (reversionThreshold / 100d); if( (Low[bar] <= (pivotPrice * 1.0+closeToExact)) && (Low[bar] >= (pivotPrice * 1.0-closeToExact)) ) { pullback = true; pBar = bar; DrawLine(PricePane,runupBar,runupHigh,pBar,pivotPrice,Color.Red,LineStyle.Solid,2); } } } } // Buy at stop if price moves $0.50 the pivot price or above if( pullback ){ double entryStop = pivotPrice + 0.50; if( BuyAtStop( bar + 1, entryStop, "1st" ) != null ) { DrawLine(PricePane,bar,entryStop,pBar,entryStop,Color.DarkGreen,LineStyle.Dotted,2); runup = false; pullback = false; LastPosition.Tag = pivotPrice; } else // Invalidate entry if too many bars have passed since pullback if( bar >= pBar + paramReversionExpiresAfter.ValueInt ) { pullback = false; AnnotateBar("Rebound timeout exceeded",bar,true,Color.Orange); } } } else //Second entry if( ActivePositions.Count == 1 && secondEntry ) BuyAtStop(bar+1, hi[bar], "2nd"); } } } }

Eugene
Wealth-Lab team
www.wealth-lab.com