Traders' Tip text
The modified ATR indicator presented in the article is available in the TASCIndicator library 1.0.7.0 and up. Here we provide the WealthScript code to enter a discretionary position, long or short, on the open of a specified date given by the Strategy Parameters. Included is the
ATRTrail class, which utilizes the modified ATR to calculate a trailing stop based on the most-recent close. You can use it with any of your Strategies. Once you create an ATRTrail object, just pass the bar number to the
ATRTrail.Price method. As with last month’s article, if you prefer to use touch stops, then WealthScript’s built-in ExitAtTrailingStop method is convenient.
Figure 1. INTC’s three black crows following a doji-island reversal made a nice setup to enter short with a relatively tight stop at the center of the pattern.
WealthScript Code (C#)
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
/* Class that encapsulates the ATR Trailing Stop, closing basis */
public class ATRTrail
{
private WealthScript ws;
private Bars bars;
private double stopPrice;
private bool longPosition;
private DataSeries atrMod;
public ATRTrail(WealthScript wL, Bars b, bool positionLong, double initialStop, int period, double factor )
{
ws = wL;
bars = b;
longPosition = positionLong;
stopPrice = initialStop;
atrMod = factor * TASCIndicators.ATRModified.Series(bars, period);
}
// Call this method to update and return the stop price on each bar after entry
public double Price(int bar)
{
double prevPrice = stopPrice;
double newPrice;
if (longPosition)
{
newPrice = bars.Close[bar] - atrMod[bar];
stopPrice = newPrice > stopPrice ? newPrice : stopPrice;
}
else
{
newPrice = bars.Close[bar] + atrMod[bar];
stopPrice = newPrice < stopPrice ? newPrice : stopPrice;
}
ws.DrawLine(ws.PricePane, bar-1, prevPrice, bar, stopPrice, Color.Blue, LineStyle.Solid, 1);
return stopPrice;
}
}
public class SAC_ATRTrailingStops : WealthScript
{
private StrategyParameter _isLong = null;
private StrategyParameter _initStop = null;
private StrategyParameter _period = null;
private StrategyParameter _atrMult = null;
private StrategyParameter _y = null;
private StrategyParameter _m = null;
private StrategyParameter _d = null;
public SAC_ATRTrailingStops()
{
_isLong = CreateParameter("Long = 1", 1, 0, 1, 1);
_initStop = CreateParameter("Initial Stop", 1.0, 0.25, 50.0, 0.25);
_period = CreateParameter("ATR Period", 5, 2, 100, 1);
_atrMult = CreateParameter("ATR Multiplier", 3.5, 1.0, 5.0, 0.1);
_m = CreateParameter("Month", 4, 1, 12, 1);
_d = CreateParameter("Day", 13, 1, 31, 1);
_y = CreateParameter("Year", 2009, 1990, 2012, 1);
}
/* Execute a strategy - trade on a specified date */
protected override void Execute()
{
DateTime dt;
try {
dt = new DateTime(_y.ValueInt, _m.ValueInt, _d.ValueInt);
}
catch {
DrawLabel(PricePane, "Invalid Date", Color.Red);
return;
}
int b = Bars.ConvertDateToBar(dt, false);
if (b < 1) {
DrawLabel(PricePane, "Date does not exist on chart", Color.Red);
return;
}
if( _isLong.ValueInt == 1 )
BuyAtMarket(b, "Discretionary");
else
ShortAtMarket(b, "Discretionary");
Position p = LastPosition;
// After creating a position, initialize a stop object
ATRTrail atrStop = new ATRTrail(this, Bars, p.PositionType == PositionType.Long, _initStop.Value, _period.ValueInt, _atrMult.Value);
for(int bar = b + 1; bar < Bars.Count; bar++)
{
if (p.Active)
{
if (p.PositionType == PositionType.Long)
{
if( Close[bar] < atrStop.Price(bar) )
ExitAtMarket(bar + 1, p);
}
else if( Close[bar] > atrStop.Price(bar) )
ExitAtMarket(bar + 1, p);
}
}
}
}
}