Traders' Tip text
The IRSTS 1-2-3 rule, presented by author Sylvain Vervoort in June 2013 issue, originates from classic swing trading philosophy. With our simplified rendition of Vervoort’s quite complex system in our C# code, based on fixed percentage swings, we laid out a framework for detecting the 1-2-3 waves. The system marks each Wave on the chart, connecting them with colored lines, and enters on pullbacks in the direction of the primary trend (Wave 1). Its exits are simple profit target and stop loss based on optimizable percentages. Motivated trading system developers are welcome to experiment with adding other entry/exit rules and tweaking the swing detection routine.
For example, consider range bound markets. Wave 3, breaking through its father Wave 1’s extreme price, gets interruprted with a correction Wave 2 and then with Wave 1 in the opposite direction, signaling a potential trend change. In this case, the size of a correction wave may be taken as a factor when defining a new Wave 1: penetrations may only be considered significant if the preceding wave's extreme is broken by a multiple of the wave’s magnitude. In our opinion, this has a chance of improving overall performance of the technique in choppy markets, protecting from changing direction too soon.
On a closing note, the companion SVEHLZZperc indicator has been added to TASCIndicators library. Conceptually, it's a variation of the well-known trailing reverse method with a twist that combines fixed percentage swings and volatility reversals.
Figure 1. A Wealth-Lab 6 chart illustrating the application of the IRSTS 1-2-3 rule on a chart of HD (Home Depot Inc).
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
public class IRSTS123Strategy : WealthScript
{
private StrategyParameter paramPercent;
private StrategyParameter paramPT;
private StrategyParameter paramSL;
public IRSTS123Strategy()
{
paramPercent = CreateParameter("Reversal %",5.0, 1.0, 50.0, 1.0);
paramPT = CreateParameter("Profit Target %",20.0,1.0,50.0,1.0);
paramSL = CreateParameter("Stop Loss %",5.0,1.0,20.0,1.0);
}
protected override void Execute()
{
double percent = paramPercent.Value, target = paramPT.Value, stop = paramSL.Value;
const string t = "\t";
ZZBuilder zz = new ZZBuilder( Bars, percent );
List lst = zz.ZigZags;
for(int bar = 1; bar < Bars.Count; bar++)
{
if (IsLastPositionActive)
{
Position p = LastPosition;
double ProfitTgt = 0, StopPrice = 0;
if ( p.PositionType == PositionType.Long ) {
ProfitTgt = p.EntryPrice * (1 + target / 100.0d);
StopPrice = p.EntryPrice * (1 - stop / 100.0d);
}
else {
ProfitTgt = p.EntryPrice * (1 - target / 100.0d);
StopPrice = p.EntryPrice * (1 + stop / 100.0d);
}
if(!ExitAtStop(bar + 1, p, StopPrice, "Stop Loss"))
ExitAtLimit(bar + 1, p, ProfitTgt, "Profit Target");
}
else
{
if( lst != null ) {
if( lst.Count > 0 ) {
int idx = 0;
foreach( ZigZag z in lst ) {
if( z.barDetected == bar ) {
if( z.wave == Wave.W2 ) {
SetBackgroundColor( bar, !z.isMoveUp ? Color.FromArgb(30,Color.Green) : Color.FromArgb(30,Color.Red) );
Position p = !z.isMoveUp ? BuyAtMarket( bar+1 ) : ShortAtMarket( bar+1 );
if( p != null ) {
p.Tag = idx;
}
}
}
}
}
}
}
}
HideVolume(); SetBarColors(Color.Silver,Color.Silver);
}
}
public enum Wave {W1, W2, W3}
public class ZigZag
{
public int barDetected { get; set; }
public bool isMoveUp { get; set; }
public int zigBar { get; set; }
public int zagBar { get; set; }
public double inPrice { get; set; }
public double outPrice { get; set; }
public double magnitude { get; set; }
public Wave wave { get; set; }
public int leg3Count { get; set; }
public ZigZag(int barDetected, bool isMoveUp, int zigBar, int zagBar, double inPrice, double outPrice, double magnitude)
{
this.barDetected = barDetected;
this.isMoveUp = isMoveUp;
this.zigBar = zigBar;
this.zagBar = zagBar;
this.inPrice = inPrice;
this.outPrice = outPrice;
this.magnitude = magnitude;
this.leg3Count = 0;
}
}
public class ZZBuilder
{
private bool Initialized = false;
public Bars bars { get; private set; }
public double percent { get; private set; }
public List ZigZags { get; private set; }
public ZZBuilder( Bars b, double pct )
{
this.bars = b;
this.percent = pct;
BuildZZ();
IRSTS123WaveRule();
}
private void BuildZZ()
{
try
{
PeakTroughMode mode = WealthLab.Indicators.PeakTroughMode.Percent;
PeakBar pb = PeakBar.Series(bars.Close,percent,mode);
TroughBar tb = TroughBar.Series(bars.Close,percent,mode);
Trough tr = Trough.Series(bars.Close,percent,mode);
Peak pk = Peak.Series(bars.Close,percent,mode);
List lst = new List();
double t1 = 0, p1 = 0, inPrice, outPrice = 0, magnitude = 0;
int tb1 = 0, pb1, barDetected = 0, zigBar = 0, zagBar = 0;
bool detected = false;
for(int bar = 1; bar < bars.Count; bar++)
{
detected = false;
t1 = trbar;
tb1 = (int)tbbar;
if (tb1 == -1) continue;
p1 = pkbar;
pb1 = (int)pbbar;
if (pb1 == -1) continue;
if( tbbar > tbbar-1 && pbbar > -1){
detected = true;
}
if( pbbar > pbbar-1 && tbbar > -1 ){
detected = true;
}
if( detected ) {
bool isMoveUp = (tb1 < pb1);
magnitude = Math.Abs( bars.Highpb1 - bars.Lowtb1 );
if( isMoveUp ) {
zigBar = tb1; zagBar = pb1; inPrice = t1; outPrice = p1;
}
else {
zigBar = pb1; zagBar = tb1; inPrice = p1; outPrice = t1;
}
lst.Add( new ZigZag( bar, isMoveUp, zigBar, zagBar, inPrice, outPrice, magnitude ) );
}
}
ZigZags = lst;
Initialized = true;
}
catch
{
}
}
private void IRSTS123WaveRule()
{
if( ZigZags.Count > 0 )
{
ZigZags0.wave = Wave.W1;
for( int i = 1; i < ZigZags.Count; i++ )
{
ZigZag prev = ZigZagsi - 1;
ZigZag w = ZigZagsi;
switch (prev.wave)
{
case Wave.W1:
if( i > 0 ) {
if( prev.isMoveUp ) { // W2 breaks down through W1's base = new W1 down
w.wave = ( w.outPrice < prev.inPrice ) ? Wave.W1 : Wave.W2;
}
else { // W2 breaks out through W1's base = new W1 up
w.wave = ( w.outPrice > prev.inPrice ) ? Wave.W1 : Wave.W2;
}
}
break;
case Wave.W2:
if( w.isMoveUp ) { // prev Up, W1 = Down
if( w.outPrice >= prev.outPrice )
w.wave = Wave.W3;
}
else { // prev Down, W1 = Up
if( w.outPrice < prev.outPrice )
w.wave = Wave.W3;
}
break;
case Wave.W3:
if( w.isMoveUp ) { // prev Up, W1 = Down
if( w.outPrice > prev.outPrice )
w.wave = Wave.W2;
if( w.outPrice > prev.inPrice )
w.wave = Wave.W1;
}
else { // prev Down, W1 = Up
if( w.outPrice < prev.outPrice )
w.wave = Wave.W2;
if( w.outPrice < prev.inPrice )
w.wave = Wave.W1;
}
break;
default:
if( prev.magnitude > w.magnitude )
w.wave = Wave.W1;
break;
}
}
// Wave 3 Leg count
int c = 1;
for(int i = 0; i < ZigZags.Count; i++ ) {
ZigZag zz = ZigZagsi;
if( zz.wave == Wave.W3 ) {
zz.leg3Count = c;
c++;
}
else if( zz.wave == Wave.W1 )
c = 1;
}
}
}
}
}
Eugene
Wealth-Lab team
www.wealth-lab.com