TASC 2007-04 | Adjusted ROC (Luoma & Nikkinen)

Modified on 2015/07/03 11:39 by Eugene — Categorized as: TASC Traders Tips

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 ); } } }