Original text and WL4 code by
Giorgio Beltrame
WL5 translation by
Eugene.
Traders' Tip text
An exponential smoothing that
adds information to an indicator, rather than just
remove noise? Smoothing by adding? As strange as it may appear, it’s a simple yet effective idea.
In order to compare this "adjusted ROC" against the standard Rate of Change indicator, we wrote a simple ChartScript that simulates “trading the spread” of two ROCs between a stock and a broad market index. (See code for actual rules.) The performance of the strategy improves markedly when just changing from the simple ROC implementation to the new adjusted version of Luoma and Nikkinen, without other modifications!
Figure 1. Results of a 10-years historical simulation using the original script (with UseAdjustedROC = false), trading all 30 stocks from the Dow Jones Industrial Average (Dow 30), allocating 10% of equity to each new position, starting with $100,000.
Figure 2. Results of a 10-years historical simulation after setting UseAdjustedROC to "true", settings as above.
Strategy Code
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
public class MyStrategy : WealthScript
{
protected override void Execute()
{
int Period = 40;
bool UseAdjustedROC = true;
// get a broad market index (S&P500 is used here) and its ROC data
DataSeries sIndex = GetExternalSymbol( "^GSPC", true ).Close;
DataSeries sIndexROC = ROC.Series( sIndex, Period );
// get the ROC of selected stock
DataSeries sROC = ROC.Series( Close, Period );
if( UseAdjustedROC )
{
// change from standard ROCs to the "adjusted" version
sROC = EMA.Series( ROC.Series( Close, 1 ), Period, EMACalculation.Modern );
sIndexROC = EMA.Series( ROC.Series( sIndex, 1), Period, EMACalculation.Modern );
}
// compute the spread between the two (adjusted) ROCs
// that is, the difference: ROC(stock) - ROC(index)
DataSeries sSpread = sROC - sIndexROC;
sROC.Description = "sROC";
sIndexROC.Description = "sIndexROC";
sSpread.Description = "sSpread";
for(int bar = Period; bar < Bars.Count; bar++)
{
double R = sROC[bar];
if (IsLastPositionActive)
{
Position p = LastPosition;
// exit all (long) positions when a profit target is reached,
// or when the stock rises while the spread starts decreasing
if( ( p.NetProfitAsOfBarPercent( bar ) > 4 ) ||
( ( R > 0 ) & TurnDown( bar, sSpread ) ) )
SellAtMarket( bar + 1, Position.AllPositions );
}
// enter a new long position after the stock had a dip,
// as soon as the spread shows an improvement
if( ( R < 0 ) & TurnUp( bar, sSpread ) )
if( BuyAtMarket( bar + 1, (-R).ToString() ) != null )
// when trading multiple stocks, and not having enough cash to
// enter all alerts, instruct WL’s $imulator to trade those with
// the lowest (most negative) ROC value, hoping in a faster bounce
LastPosition.Priority = - R;
}
HideVolume();
ChartPane Pane = CreatePane( 50, false, true );
DrawHorzLine( Pane, 0, Color.Black, LineStyle.Solid, 2 );
PlotSeries( Pane, sROC, Color.Red, LineStyle.Solid, 1 );
PlotSeries( Pane, sIndexROC, Color.Blue, LineStyle.Dotted, 1 );
PlotSeries( Pane, sSpread, Color.Green, LineStyle.Solid, 2 );
}
}
}