Traders' Tip text
This month, surprised readers may take a breath from huge code listings by author Sylvain Vervoort: the Strategy code we're presenting today is just slightly more than a page long. The simple SAR (stop-and-reverse) system buys when the close price breaks through the upper volatility band, and sells short when the close slips under the lower band.
To make money with this approach, traders might want to enhance the system with a trend/range filter (like ADX or Dreiss Choppiness index). When a market becomes less directional, there's not enough room for price reactions. For example, low probability trades could be avoided when the ADX readings are low.
Motivated readers are welcome to make their own comparisons to similar volatility-based bands available in Wealth-Lab, like ATR bands or Keltner ATR bands, by plugging in the DataSeries into the code.
To execute successfully in Wealth-Lab, the system requires the latest version of our
TASCIndicators library. Please install (or update if you haven't done so already) the library from the
wealth-lab.com site to its latest version 2013.07 or higher.
Figure 1. A Wealth-Lab 6 chart illustrating a market mood shift from preferable by the trend-following system to a suboptimal.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
using TASCIndicators;
namespace WealthLab.Strategies
{
public class SVEVolatilityBands : WealthScript
{
private StrategyParameter paramBandAvg;
private StrategyParameter paramVolaSum;
private StrategyParameter paramDevFact;
private StrategyParameter paramLowBAdj;
public SVEVolatilityBands()
{
paramBandAvg = CreateParameter("Band average", 8, 2, 100, 1);
paramVolaSum = CreateParameter("Vola Sum period", 13, 2, 100, 1);
paramDevFact = CreateParameter("Deviation factor", 3.55, 2, 10, 0.1);
paramLowBAdj = CreateParameter("Low Band Adj.", 0.9, 0.1, 3.0, 0.1);
}
protected override void Execute()
{
int average = paramBandAvg.ValueInt;
int volperiod = paramVolaSum.ValueInt;
double devfact = paramDevFact.Value;
double lowbandadjust = paramLowBAdj.Value;
EMACalculation m = EMACalculation.Modern;
AveragePriceC TypicalPrice = AveragePriceC.Series( Bars );
EMA emaMedianAvg = EMA.Series(EMA.Series(TypicalPrice,average,m),average,m);
SVEVolatilityBandUpper UpperBand = SVEVolatilityBandUpper.Series( Bars, average, volperiod, devfact, lowbandadjust );
SVEVolatilityBandLower LowerBand = SVEVolatilityBandLower.Series( Bars, average, volperiod, devfact, lowbandadjust );
for(int bar = GetTradingLoopStartBar(average * 3); bar < Bars.Count; bar++)
{
// Detect crossover/crossunder and store state in a variable
bool xo = CrossOver(bar, Close, UpperBand);
bool xu = CrossUnder(bar, Close, LowerBand);
// The first trade
if (Positions.Count == 0){
if ( xo )
BuyAtClose( bar );
else if( xu )
ShortAtClose( bar );
}
// Subsequent trades
else
{
Position p = LastPosition;
if ( p.PositionType == PositionType.Long )
{
if ( xu )
{
SellAtClose( bar, p );
ShortAtClose( bar );
}
}
else if ( xo )
{
CoverAtClose( bar, p );
BuyAtClose( bar );
}
}
}
PlotSeries(PricePane, emaMedianAvg, Color.Silver, LineStyle.Solid, 2);
PlotSeriesFillBand(PricePane, UpperBand, LowerBand, Color.Silver, Color.Transparent, LineStyle.Solid, 2);
HideVolume();
}
}
}
Eugene
Wealth-Lab team
www.wealth-lab.com