TRADERS’ TIPS

October 2016

Tips Article Thumbnail

For this month’s Traders’ Tips, the focus is Markos Katsanos’ article in this issue, “Which Trend Indicator Wins?” Here, we present the October 2016 Traders’ Tips code with possible implementations in various software.

The Traders’ Tips section is provided to help the reader implement a selected technique from an article in this issue or another recent issue. The entries here are contributed by software developers or programmers for software that is capable of customization.


logo

TRADESTATION: OCTOBER 2016

In “Which Trend Indicator Wins?” in this issue, author Markos Katsanos compares the effectiveness of four popular trend-detection indicators. He tests the ADX, R-squared, vertical horizontal filter (VHF), and efficiency ratio (ER). He includes some TradeStation EasyLanguage code for the strategies in his article.

TradeStation offers a number of advanced tools and capabilities to assist traders with strategy backtesting and optimization. The TradeStation Walk-Forward Optimizer (WFO) is an advanced strategy optimization tool that automates the complex, multistep task of carrying out the statistical walk-forward testing of a trading strategy’s optimized inputs. Trade­Station Portfolio Maestro is a backtesting tool that lets you evaluate the performance of a group of strategies applied to one or more symbols that make up your portfolio. As shown in Figure 1, Portfolio Maestro allows the user to analyze the performance of all of the author’s strategies simultaneously.

Sample Chart

FIGURE 1: TRADESTATION PORTFOLIO MAESTRO. Here are sample TradeStation Portfolio Maestro results for each of the author’s four strategies applied to USO using a daily interval.

To download the EasyLanguage code for the author’s strategies as well as an example Portfolio Maestro configuration export, please visit our TradeStation and EasyLanguage support forum. It can be found here: https://community.tradestation.com/Discussions/Topic.aspx?Topic_ID=142776. The ELD filename is “TASC_OCT2016.ELD.”

For more information about EasyLanguage in general, please see https://www.tradestation.com/EL-FAQ.

This article is for informational purposes. No type of trading or investment recommendation, advice, or strategy is being made, given, or in any manner provided by TradeStation Securities or its affiliates.

—Doug McCrary
TradeStation Securities, Inc.
www.TradeStation.com

BACK TO LIST

logo

METASTOCK: OCTOBER 2016

Markos Katsanos’ article in this issue, “Which Trend Indicator Wins,” presents formulas for four different indicators designed to identify trending markets. The MetaStock formulas for the four indicators are listed below.

ADX

This indicator is built into MetaStock. It can be used with the formula function:

ADX( periods )

For example, to use a 14-period ADX, you would write:

ADX( 14 )

The formulas for the ADX trend system are:

Buy:
( (Cross(ADX(14), 30) AND ADX(14) < 42) OR
(Cross(ADX(14), 22) AND ADX(14) > 1.8*LLV(ADX(14), 12) ) ) 
AND C > Mov(C, 50, S)
Sell:
Cross( Mov(C, 50, S), C )
Sell short:
( (Cross(ADX(14), 30) AND ADX(14) < 42) OR
(Cross(ADX(14), 22) AND ADX(14) > 1.8*LLV(ADX(14), 12) ) )
AND C < Mov(C, 50, S)
Buy to cover:
Cross( C, Mov(C, 50, S) )

Vertical horizontal filter (VHF)

This indicator is built into MetaStock. It can be used with the formula function:

VHF( data array, periods )

For example, to use a 14-period VHF on the close, you would write:

VHF( C, 14 )

The formulas for the VHF trend system are:

Buy:
( (Cross(VHF(C, 36), .38) AND VHF(C, 36) < .45 AND
   VHF(C, 36) > Ref(VHF(C, 36),-10)) OR
(Cross(VHF(C, 36), .24) AND VHF(C, 36) > 1.5*LLV(VHF(C, 36), 10) ) ) 
AND C > Mov(C, 50, S)
Sell:
Cross( Mov(C, 50, S), C )
Sell short:
( (Cross(VHF(C, 36), .38) AND VHF(C, 36) < .45 AND
   VHF(C, 36) > Ref(VHF(C, 36),-10)) OR
(Cross(VHF(C, 36), .24) AND VHF(C, 36) > 1.5*LLV(VHF(C, 36), 10) ) )
AND C < Mov(C, 50, S)
Buy to cover:
Cross( C, Mov(C, 50, S) )

Efficiency ratio (ER)

Perry Kaufman’s efficiency ratio can be added to MetaStock with the following formula:

tp:= 10; {time periods}
Abs( ROC( C, tp, $) ) / Sum( Abs( ROC( C, 1, $) ), tp)

As written, it is for a 10-period efficiency ratio. If you wish to use a different number of periods, just change the number on the first line.

The formulas for the efficiency ratio trend system are:

Buy:
er := Mov( Abs( ROC( C, 24, $) ) / Sum( Abs( ROC( C, 1, $) ), 24), 3, S);
( (Cross( er, .36) AND er < .42 AND er > Ref(er, -3)) OR
(Cross(er, .26) AND er > 2.5*LLV(er, 3) ) ) 
AND C > Mov(C, 50, S)
Sell:
Cross( Mov(C, 50, S), C )
Sell short:
er := Mov( Abs( ROC( C, 24, $) ) / Sum( Abs( ROC( C, 1, $) ), 24), 3, S);
( (Cross( er, .36) AND er < .42 AND er > Ref(er, -3)) OR
(Cross(er, .26) AND er > 2.5*LLV(er, 3) ) )
AND C < Mov(C, 50, S)
Buy to cover:
Cross( C, Mov(C, 50, S) )

R-squared

This indicator is built into MetaStock. It can be used with the formula function:

RSquared( data array, periods )

For example, to use a 21-period R-squared calculated on the close, you would write:

RSquared( C, 21 )

The formulas for the R-squared trend system are:

Buy:
r2 := RSquared(C, 18);
lr := LinRegSlope( C, 18 ) * 100;
Cross( r2, .42) AND r2 < .85 AND r2 > Ref(r2, -10) AND
C > Mov(C, 50, S) AND lr > 10
Sell:
Cross( Mov(C, 50, S), C )
Sell short:
r2 := RSquared(C, 18);
lr := LinRegSlope( C, 18 ) * 100;
Cross( r2, .42) AND r2 < .85 AND r2 > Ref(r2, -10) AND
AND C < Mov(C, 50, S) AND lr < -10
Buy to cover:
Cross( C, Mov(C, 50, S) )

—William Golson
MetaStock Technical Support
www.metastock.com

BACK TO LIST

logo

eSIGNAL: OCTOBER 2016

For this month’s Traders’ Tip, we’ve provided the ADX.efs (average directional index), ER.efs (efficiency ratio), R2.efs (R-squared) and VHF.efs (vertical horizontal filter) studies based on the formulas described Markos Katsanos’ article in this issue, “Which Trend Indicator Wins?” In his article, the author compares four indicators to see which one performs the best at identifying trends.

The studies contain formula parameters that may be configured through the edit chart window (right-click on the chart and select “edit chart”). A sample chart is shown in Figure 2.

Sample Chart

FIGURE 2: eSIGNAL. The four studies are plotted on a daily chart of USO.

To discuss this study or download a complete copy of the formula code, please visit the EFS library discussion board forum under the forums link from the support menu at www.esignal.com or visit our EFS KnowledgeBase at www.esignal.com/support/kb/efs/. The code is also available as a zipped archive here:

ADX.efs

/*********************************
Provided By:  
eSignal (Copyright c eSignal), a division of Interactive Data 
Corporation. 2016. All rights reserved. This sample eSignal 
Formula Script (EFS) is for educational purposes only and may be 
modified and saved under a new file name.  eSignal is not responsible
for the functionality once modified.  eSignal reserves the right 
to modify and overwrite this EFS file with each new release.

Description:        
    Which Trend Indicator Wins? by Markos Katsanos

Version:            1.00  08/10/2016

Formula Parameters:                     Default:
Length                                  14
Trend                                   30
Max ADX                                 42
Lag                                     12
Crit                                    22
Mult                                    1.8
MA Length                               50



Notes:
The related article is copyrighted material. If you are not a subscriber
of Stocks & Commodities, please visit www.traders.com.

**********************************/

var fpArray = new Array();

function preMain(){
    setPriceStudy(false);
    setDefaultBarFgColor(Color.RGB(0,148,255),0);
    
    var x=0;
    fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(14);
        setName("Length");
    }

    fpArray[x] = new FunctionParameter("Trend", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(100);  
        setDefault(30);
        setName("Trend");
    }

    fpArray[x] = new FunctionParameter("AdxMax", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(100);  
        setDefault(42);
        setName("Max ADX");
    }

    fpArray[x] = new FunctionParameter("Lag", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(12);
        setName("Lag");
    }

    fpArray[x] = new FunctionParameter("Crit", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(100);  
        setDefault(22);
        setName("Crit");
    }

    fpArray[x] = new FunctionParameter("Mult", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0.01);
        setUpperLimit(100);  
        setDefault(1.8);
        setName("Mult");
    }

    fpArray[x] = new FunctionParameter("MALength", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(50);
        setName("MA Length");
    }
}

var bInit = false;
var bVersion = null;
var xClose = null;
var xADX = null;
var xSMA = null;
var xLADX = null;

function main(Length, Trend, AdxMax, Lag, Crit, Mult, MALength){
    if (bVersion == null) bVersion = verify();
    if (bVersion == false) return;
    
    if (getCurrentBarCount() <= Length) return;
    
    if (getBarState() == BARSTATE_ALLBARS){
        xClose = null;
        xADX = null;
        xSMA = null;
        xLADX = null;
        bInit = false;
    }
    
    if (!bInit){
        xClose = close();
        xADX = adx(Length,Length);
        xLADX = lowerDonchian(Lag, xADX);
        xSMA = sma(MALength, xClose);
        addBand(Trend, PS_DASH, 1, Color.RGB(195,195,195),1);
        bInit = true;
    }

    var nADX = xADX.getValue(0);

    if (getCurrentBarIndex() != 0){
        var nSMA = xSMA.getValue(0);
        var nSMA_1 = xSMA.getValue(-1);
        var nADX_1 = xADX.getValue(-1);
        var nClose = xClose.getValue(0);
        var nClose_1 = xClose.getValue(-1);
        var nLADX = xLADX.getValue(0);
        
        if (Strategy.isLong() && nClose_1 > nSMA_1 && nClose < nSMA)
            Strategy.doSell("MA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
        else if (Strategy.isShort() && nClose_1 < nSMA_1 && nClose > nSMA)
            Strategy.doCover("XMA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
        
        if (!Strategy.isInTrade()){
            if (nADX_1 < Trend && nADX > Trend && nADX < AdxMax){
                if (nClose > nSMA)
                    Strategy.doLong("STRONGTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
                else if (nClose < nSMA)
                    Strategy.doShort("STRONGDOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
            }
            else if (nADX > (Mult * nLADX) && nADX_1 < Crit && nADX > Crit){
                if (nClose > nSMA)
                    Strategy.doLong("TREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
                else if (nClose < nSMA)
                    Strategy.doShort("DOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
            }
        }
    
        if (Strategy.isLong()) setBarBgColor(Color.RGB(0,60,0));
        else if (Strategy.isShort()) setBarBgColor(Color.RGB(170,46,46))
    }

    return xADX.getValue(0);
}
        
function verify(){
    var b = false;
    if (getBuildNumber() < 779){
        
        drawTextAbsolute(5, 35, "This study requires version 10.6 or later.", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "error");
        drawTextAbsolute(5, 20, "Click HERE to upgrade.@URL=https://www.esignal.com/download/default.asp", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "upgrade");
        return b;
    } 
    else
        b = true;
    
    return b;
}

ER.efs

/*********************************
Provided By:  
eSignal (Copyright c eSignal), a division of Interactive Data 
Corporation. 2016. All rights reserved. This sample eSignal 
Formula Script (EFS) is for educational purposes only and may be 
modified and saved under a new file name.  eSignal is not responsible
for the functionality once modified.  eSignal reserves the right 
to modify and overwrite this EFS file with each new release.

Description:        
    Which Trend Indicator Wins? by Markos Katsanos

Version:            1.00  08/10/2016

Formula Parameters:                     Default:
Length                                  24
ER Smooth                               3
Trend                                   0.36
Max ER                                  0.42
Lag                                     3
Mult                                    2.5
Crit                                    0.26
MA Length                               50



Notes:
The related article is copyrighted material. If you are not a subscriber
of Stocks & Commodities, please visit www.traders.com.

**********************************/

var fpArray = new Array();

function preMain(){
    setPriceStudy(false);
    setDefaultBarFgColor(Color.RGB(0,148,255),0);
    
    var x=0;
    fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(24);
        setName("Length");
    }

    fpArray[x] = new FunctionParameter("Smooth", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(3);
        setName("ER Smooth");
    }

    fpArray[x] = new FunctionParameter("Trend", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(1);  
        setDefault(0.36);
        setName("Trend");
    }

    fpArray[x] = new FunctionParameter("ERMax", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(1);  
        setDefault(0.42);
        setName("Max ER");
    }

    fpArray[x] = new FunctionParameter("Lag", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(3);
        setName("Lag");
    }

    fpArray[x] = new FunctionParameter("Mult", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0.01);
        setUpperLimit(100);  
        setDefault(2.5);
        setName("Mult");
    }

    fpArray[x] = new FunctionParameter("Crit", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(1);  
        setDefault(0.26);
        setName("Crit");
    }

    fpArray[x] = new FunctionParameter("MALength", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(50);
        setName("MA Length");
    }
}

var bInit = false;
var bVersion = null;
var xClose = null;
var xER = null;
var xSMA = null;
var xLER = null;

function main(Length, Smooth, Trend, ERMax, Lag, Mult, Crit, MALength){
    if (bVersion == null) bVersion = verify();
    if (bVersion == false) return;
    
    if (getCurrentBarCount() <= (Length + Smooth + Lag)) return;
    
    if (getBarState() == BARSTATE_ALLBARS){
        xClose = null;
        xER = null;
        xSMA = null;
        xLER = null;
        bInit = false;
    }
    
    if (!bInit){
        xClose = close();
        xER = efsInternal("calc_ER",xClose,Length);
        xER = sma(Smooth, xER);
        xLER = lowerDonchian(Lag, xER);
        xSMA = sma(MALength, xClose);
        addBand(Trend, PS_DASH, 1, Color.RGB(195,195,195),1);
        bInit = true;
    }

    var nER = xER.getValue(0);

    if (getCurrentBarIndex() != 0){
        var nER_1 = xER.getValue(-1);
        var nER_lag = xER.getValue(-Lag);
        var nLER = xLER.getValue(0);
        var nSMA = xSMA.getValue(0);
        var nSMA_1 = xSMA.getValue(-1);
        var nClose = xClose.getValue(0);
        var nClose_1 = xClose.getValue(-1);
        
        if (Strategy.isLong() && nClose_1 > nSMA_1 && nClose < nSMA)
            Strategy.doSell("MA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
        else if (Strategy.isShort() && nClose_1 < nSMA_1 && nClose > nSMA)
            Strategy.doCover("XMA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
        
        if (!Strategy.isInTrade()){
            if (nER_1 < Trend && nER > Trend && nER < ERMax && nER > nER_lag){
                if (nClose > nSMA)
                    Strategy.doLong("STRONGTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
                else if (nClose < nSMA)
                    Strategy.doShort("STRONGDOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
            }
            else if (nER > (Mult * nLER) && nER_1 < Crit && nER > Crit){
                if (nClose > nSMA)
                    Strategy.doLong("TREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
                else if (nClose > nSMA)
                    Strategy.doShort("DOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
            }
        }
    
        if (Strategy.isLong()) setBarBgColor(Color.RGB(0,60,0));
        else if (Strategy.isShort()) setBarBgColor(Color.RGB(170,46,46));  
    }

    return nER;
}

function calc_ER(Close,Length){
    var nNetCh = Math.abs(Close.getValue(0) - Close.getValue(-Length));
    var nSum = 0;
    for (var i = 0; i < Length; i++){
        nSum += Math.abs(Close.getValue(-i) - Close.getValue(-i-1));
    }

    if (nSum == 0) return;
    ER = nNetCh / nSum;
    
    return ER;
}

function verify(){
    var b = false;
    if (getBuildNumber() < 779){
        
        drawTextAbsolute(5, 35, "This study requires version 10.6 or later.", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "error");
        drawTextAbsolute(5, 20, "Click HERE to upgrade.@URL=https://www.esignal.com/download/default.asp", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "upgrade");
        return b;
    } 
    else
        b = true;
    
    return b;
}

R2.efs

/*********************************
Provided By:  
eSignal (Copyright c eSignal), a division of Interactive Data 
Corporation. 2016. All rights reserved. This sample eSignal 
Formula Script (EFS) is for educational purposes only and may be 
modified and saved under a new file name.  eSignal is not responsible
for the functionality once modified.  eSignal reserves the right 
to modify and overwrite this EFS file with each new release.

Description:        
    Which Trend Indicator Wins? by Markos Katsanos

Version:            1.00  08/10/2016

Formula Parameters:                     Default:
Length                                  18
R2 Smooth                               3
Trend                                   0.42
Max R2                                  0.85
Lag                                     10
LR Crit                                 10
MA Length                               50



Notes:
The related article is copyrighted material. If you are not a subscriber
of Stocks &amp; Commodities, please visit www.traders.com.

**********************************/

var fpArray = new Array();

function preMain(){
    setPriceStudy(false);
    setDefaultBarFgColor(Color.RGB(0,148,255),0);
    
    var x=0;
    fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(18);
        setName("Length");
    }

    fpArray[x] = new FunctionParameter("Smooth", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(3);
        setName("R2 Smooth");
    }

    fpArray[x] = new FunctionParameter("Trend", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(1);  
        setDefault(0.42);
        setName("Trend");
    }

    fpArray[x] = new FunctionParameter("R2Max", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(1);  
        setDefault(0.85);
        setName("Max R2");
    }

    fpArray[x] = new FunctionParameter("Lag", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(10);
        setName("Lag");
    }

    fpArray[x] = new FunctionParameter("LRcrit", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(10);
        setName("LR Crit");
    }

    fpArray[x] = new FunctionParameter("MALength", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(50);
        setName("MA Length");
    }
}

var bInit = false;
var bVersion = null;
var xClose = null;
var xR2Lin = null;
var xSlope = null;
var xR2 = null;
var xSMA = null;

function main(Length, Smooth, Trend, R2Max, Lag, LRcrit, MALength){
    if (bVersion == null) bVersion = verify();
    if (bVersion == false) return;
    
    if (getCurrentBarCount() <= (Length + Smooth + Lag)) return;
    
    if (getBarState() == BARSTATE_ALLBARS){
        xClose = null;
        xR2Lin = null;
        xSlope = null;
        xR2 = null;
        xSMA = null;
        bInit = false;
    }
    
    if (!bInit){
        xClose = close();
        xR2Lin = efsInternal("calc_R2", xClose, Length);
        if (xR2Lin.getValue(0) != null){
            xSlope = getSeries(xR2Lin, 0);
            xR2 = getSeries(xR2Lin,1);
        }
        xR2 = sma(Smooth, xR2);
        xSMA = sma(MALength, xClose);
        addBand(Trend, PS_DASH, 1, Color.RGB(195,195,195),0);
        bInit = true;
    }
 
    var nSlope = xSlope.getValue(0) * 100;
    var nR2 = xR2.getValue(0);

    if (getCurrentBarIndex() != 0){
        nR2_1 = xR2.getValue(-1);
        nR2_lag = xR2.getValue(-Lag);
        nClose = xClose.getValue(0);
        nClose_1 = xClose.getValue(-1);
        nSMA = xSMA.getValue(0);
        nSMA_1 = xSMA.getValue(-1);


        if (Strategy.isLong() &amp;&amp; nClose_1 > nSMA_1 &amp;&amp; nClose < nSMA)
            Strategy.doSell("MA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
        else if (Strategy.isShort() &amp;&amp; nClose_1 < nSMA_1 &amp;&amp; nClose > nSMA)
            Strategy.doCover("XMA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);

        if ((!Strategy.isInTrade()) &amp;&amp; nR2_1 < Trend &amp;&amp; nR2 >= Trend &amp;&amp; nR2 < R2Max &amp;&amp; nR2 > nR2_lag){
            if (nClose > nSMA &amp;&amp; nSlope > LRcrit)
                Strategy.doLong("STRONGTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
            else if (nClose < nSMA &amp;&amp; nSlope <= LRcrit)
                Strategy.doShort("STRONGDOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
        }

        if (Strategy.isLong()) setBarBgColor(Color.RGB(0,60,0));
        else if (Strategy.isShort()) setBarBgColor(Color.RGB(170,46,46));
    }
    return nR2;
}

function calc_R2(xClose, Length){
    var X = 0, Y = 0, XY = 0, X2 = 0, Y2 = 0;
    
    for (var i = 0; i < Length; i++){
        var xVal = xClose.getValue(-i);
        X += (i + 1);
        Y += xVal;
        XY += ((i + 1) * xVal);
        X2 += ((i + 1) * (i + 1));
        Y2 += (xVal * xVal);
    }

    var xAvg = X / Length;
    var yAvg = Y / Length;
    var aSum1 = 0, aSum2 = 0;

    for (var i = 0; i < Length; i++){
        aSum1 += (i-xAvg) * (xClose.getValue(-i)-yAvg); 
        aSum2 += (i-xAvg) * (i-xAvg);
    }

    var nSlope = aSum1 / aSum2;
    var R2 = Math.pow((Length * XY - X * Y) / Math.sqrt((Length * X2 - X * X) * (Length * Y2 - Y*Y)),2);
    return new Array(nSlope, R2);
}

function verify(){
    var b = false;
    if (getBuildNumber() < 779){
        
        drawTextAbsolute(5, 35, "This study requires version 10.6 or later.", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "error");
        drawTextAbsolute(5, 20, "Click HERE to upgrade.@URL=https://www.esignal.com/download/default.asp", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "upgrade");
        return b;
    } 
    else
        b = true;
    
    return b;
}

VHF.efs

/*********************************
Provided By:  
eSignal (Copyright c eSignal), a division of Interactive Data 
Corporation. 2016. All rights reserved. This sample eSignal 
Formula Script (EFS) is for educational purposes only and may be 
modified and saved under a new file name.  eSignal is not responsible
for the functionality once modified.  eSignal reserves the right 
to modify and overwrite this EFS file with each new release.

Description:        
    Which Trend Indicator Wins? by Markos Katsanos

Version:            1.00  08/10/2016

Formula Parameters:                     Default:
Length                                  14
Trend                                   30
Max VHF                                 42
Lag                                     12
Crit                                    22
Mult                                    1.8
MA Length                               50



Notes:
The related article is copyrighted material. If you are not a subscriber
of Stocks & Commodities, please visit www.traders.com.

**********************************/

var fpArray = new Array();

function preMain(){
    setPriceStudy(false);
    setDefaultBarFgColor(Color.RGB(0,148,255),0);
    
    var x=0;
    fpArray[x] = new FunctionParameter("Length", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(36);
        setName("Length");
    }

    fpArray[x] = new FunctionParameter("Trend", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(1);  
        setDefault(0.38);
        setName("Trend");
    }

    fpArray[x] = new FunctionParameter("VHFMax", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0);
        setUpperLimit(1);  
        setDefault(0.45);
        setName("Max VHF");
    }

    fpArray[x] = new FunctionParameter("Lag", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(10);
        setName("Lag");
    }

    fpArray[x] = new FunctionParameter("Crit", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1);  
        setDefault(0.24);
        setName("Crit");
    }

    fpArray[x] = new FunctionParameter("Mult", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(0.01);
        setUpperLimit(100);  
        setDefault(1.5);
        setName("Mult");
    }

    fpArray[x] = new FunctionParameter("MALength", FunctionParameter.NUMBER);
	with(fpArray[x++]){
        setLowerLimit(1);
        setUpperLimit(1000);  
        setDefault(50);
        setName("MA Length");
    }
}

var bInit = false;
var bVersion = null;
var xClose = null;
var xVHF = null;
var xSMA = null;
var xLVHF = null;

function main(Length, Trend, VHFMax, Lag, Crit, Mult, MALength){
    if (bVersion == null) bVersion = verify();
    if (bVersion == false) return;
    
    if (getCurrentBarCount() <= Length) return;
    
    if (getBarState() == BARSTATE_ALLBARS){
        xClose = null;
        xVHF = null;
        xSMA = null;
        xLVHF = null;
        xHighest = null;
        xLowest = null;
        bInit = false;
    }
    
    if (!bInit){
        xClose = close();
        xVHF = efsInternal("calc_VHF",xClose,Length);
        xLVHF = lowerDonchian(Lag, xVHF);
        xSMA = sma(MALength, xClose);
        addBand(0.35, PS_DASH, 1, Color.RGB(195,195,195),1);
        bInit = true;
    }

    nVHF = xVHF.getValue(0);

    if (getCurrentBarIndex() != 0){
        var nSMA = xSMA.getValue(0);
        var nSMA_1 = xSMA.getValue(-1);
        var nVHF_1 = xVHF.getValue(-1);
        var nVHF_lag = xVHF.getValue(-Lag);
        var nClose = xClose.getValue(0);
        var nClose_1 = xClose.getValue(-1);
        var nLVHF = xLVHF.getValue(0);
        
        if (Strategy.isLong() && nClose_1 > nSMA_1 && nClose < nSMA)
            Strategy.doSell("MA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
        else if (Strategy.isShort() && nClose_1 < nSMA_1 && nClose > nSMA)
            Strategy.doCover("XMA", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
        
        if (!Strategy.isInTrade()){
            if (nVHF_1 < Trend && nVHF > Trend && nVHF < VHFMax && nVHF > nVHF_lag){
                if (nClose > nSMA)
                    Strategy.doLong("STRONGTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
                else if (nClose < nSMA)
                    Strategy.doShort("STRONGDOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
            }
            else if (nVHF > (Mult * nLVHF) && nVHF_1 < Crit && nVHF > Crit){
                if (nClose > nSMA)
                    Strategy.doLong("TREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
                else if (nClose < nSMA)
                    Strategy.doShort("DOWNTREND", Strategy.MARKET, Strategy.NEXTBAR, Strategy.DEFAULT);
            }
        }
    
        if (Strategy.isLong()) setBarBgColor(Color.RGB(0,60,0));
        else if (Strategy.isShort()) setBarBgColor(Color.RGB(170,46,46))
    }

    return nVHF;
}

var xHighest = null;
var xLowest = null;

function calc_VHF(Close,Length){
    
    if (getBarState() == BARSTATE_ALLBARS){
        xHighest = upperDonchian(Length,Close);
        xLowest = lowerDonchian(Length,Close);
    }
    var nHighest = xHighest.getValue(0);
    var nLowest = xLowest.getValue(0);
    
    if (nHighest == null || nLowest == null) return;
    
    var nSum = 0;
    
    for (var i = 0; i < Length; i++){
        nSum += Math.abs(Close.getValue(-i) - Close.getValue(-i-1));
    }

    if (nSum == 0) return;
    VHF = (nHighest - nLowest) / nSum;
    
    return VHF;
}

function verify(){
    var b = false;
    if (getBuildNumber() < 779){
        
        drawTextAbsolute(5, 35, "This study requires version 10.6 or later.", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "error");
        drawTextAbsolute(5, 20, "Click HERE to upgrade.@URL=https://www.esignal.com/download/default.asp", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "upgrade");
        return b;
    } 
    else
        b = true;
    
    return b;
}

—Eric Lippert
eSignal, an Interactive Data company
800 779-6555, www.eSignal.com

BACK TO LIST

logo

WEALTHLAB: OCTOBER 2016

In “Which Trend Indicator Wins?” in this issue, author Markos Katsanos discusses four indicators for detecting trends. Here, we’ll present a framework that lets a Wealth-Lab user run any of the four example systems for testing the efficiency of these indicators. By dragging the respective parameter slider on the bottom left of the screen, one of the included subsystems (ADX, R2, VHF or ER) can be executed, or they can be executed all together (Figure 3).

Sample Chart

FIGURE 3: WEALTH-LAB. This illustrates example trades taken by all four trading systems at the same time on a daily chart of USO (US Oil ETF).

While most of the indicators discussed by Katsanos in the article are prepackaged, one of the participating indicators, the efficiency ratio, has been included in our Community Indicators library. This library can be installed additionally from our website’s extensions section.

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
using Community.Indicators;

namespace WealthLab.Strategies
{
	public class TASC201610Katsanos : WealthScript
	{
		private StrategyParameter paramStrategy2Run;		
      
		public TASC201610Katsanos()
		{
			paramStrategy2Run = CreateParameter("ADX/R2/VHF/ER/All",4,0,4,1);
		}
		
		protected override void Execute()
		{
			var whichStrategyToRun = paramStrategy2Run.ValueInt;
			HideVolume();
			
			switch (whichStrategyToRun)
			{
				case 0: SystemADX.Run(this); break;
				case 1: SystemR2.Run(this); break;
				case 2: SystemVHF.Run(this); break;
				case 3: SystemER.Run(this); break;
				case 4: SystemADX.Run(this); SystemR2.Run(this); SystemVHF.Run(this); SystemER.Run(this); break;
					
				default:
					break;
			}
		}
	}
	
	class SystemADX
	{
		public static void Run(WealthScript obj)
		{
			int periodADX = 14, adxTrend = 30, adxMax = 42, mab = 50, crit = 22, adxLag = 12;
			double adxMult = 1.8;
			var c = obj.Close;
			var adx = ADX.Series(obj.Bars,periodADX);
			var avgMAB = SMA.Series(c,mab);
			var adxLow = Lowest.Series(adx,adxLag);
			ChartPane p = obj.CreatePane(30,true,true);
			obj.PlotSeries(p, adx, Color.Red, LineStyle.Solid, 2 );
			
			for(int bar = obj.GetTradingLoopStartBar(1); bar < obj.Bars.Count; bar++)
			{
				if (obj.IsLastPositionActive)
				{
					if( obj.CrossUnder(bar, c, avgMAB) || obj.CrossOver(bar, c, avgMAB) )
						obj.ExitAtMarket( bar+1, obj.LastPosition, "MA" );
				}
				else
				{
					if( obj.CrossOver(bar, adx, adxTrend) && adx[bar] < adxMax & c[bar] > avgMAB[bar] )
						obj.BuyAtMarket(bar+1,"Strong Trend");

					if( adx[bar] > adxMult * adxLow[bar] && obj.CrossOver(bar, adx, crit) & c[bar] > avgMAB[bar] )
						obj.BuyAtMarket(bar+1,"Trend");

					if( obj.CrossOver(bar, adx, adxTrend) && adx[bar] < adxMax & c[bar] < avgMAB[bar] )
						obj.ShortAtMarket(bar+1,"Strong Dntrend");

					if( adx[bar] > adxMult * adxLow[bar] && obj.CrossOver(bar, adx, crit) & c[bar] < avgMAB[bar] )
						obj.ShortAtMarket(bar+1,"Downtrend");
				}
				
				// Workaround to prevent open positions from one system to be available for the next system to close
				if( bar == obj.Bars.Count-1 )
					obj.ExitAtClose( bar, Position.AllPositions, "Close All" );
			}
		}
	}
	
	class SystemR2
	{
		public static void Run(WealthScript obj)
		{
			int periodR2 = 18, smooth = 3, mab = 50, crit = 10, r2Lag = 10;
			double Trend = 0.42, r2Max = 0.85;
			var c = obj.Close;
			var r2 = RSquared.Series( c,periodR2 );
			var lr = 100 * LinearRegSlope.Series( c,periodR2 );
            var avgMAB = SMA.Series(c,mab);
			ChartPane p = obj.CreatePane(30,true,true);
			obj.PlotSeries(p, r2, Color.Blue, LineStyle.Solid, 2 );
	
			for(int bar = obj.GetTradingLoopStartBar(1 + smooth + r2Lag); bar < obj.Bars.Count; bar++)
			{
				if (obj.IsLastPositionActive)
				{
					if( obj.CrossUnder(bar, c, avgMAB) || obj.CrossOver(bar, c, avgMAB) )
						obj.ExitAtMarket( bar+1, obj.LastPosition, "MA" );
				}
				else
				{
					if( obj.CrossOver(bar,r2,Trend) && r2[bar] < r2Max && r2[bar] > r2[bar-r2Lag] )
					{
						if( c[bar] > avgMAB[bar] && lr[bar] > crit )
							obj.BuyAtMarket(bar+1,"Strong Trend");
						else
							if( c[bar] < avgMAB[bar] && lr[bar] < -crit )
							obj.ShortAtMarket(bar+1,"Strong Downtrend");					
					}
				}
				
				// Workaround to prevent open positions from one system to be available for the next system to close
				if( bar == obj.Bars.Count-1 )
					obj.ExitAtClose( bar, Position.AllPositions, "Close All" );
			}
		}
	}
	
	class SystemVHF
	{
		public static void Run(WealthScript obj)
		{
			int periodVHF = 36, mab = 50, vhfLag = 10;
			double vhfTrend = 0.38, vhfMax = 0.45, vhfMult = 1.5, crit = 0.24;
			var c = obj.Close;			
			var vhf = VHF.Series(c,periodVHF);
			var avgMAB = SMA.Series(c,mab);
			var vhfLow = Lowest.Series(vhf,vhfLag);
			ChartPane p = obj.CreatePane(30,true,true);
			obj.PlotSeries(p, vhf, Color.Magenta, LineStyle.Solid, 2 );

			for(int bar = obj.GetTradingLoopStartBar(1 + mab + vhfLag); bar < obj.Bars.Count; bar++)
			{				
				if (obj.IsLastPositionActive)
				{
					if( obj.CrossUnder(bar, c, avgMAB) || obj.CrossOver(bar, c, avgMAB) )
						obj.ExitAtMarket( bar+1, obj.LastPosition, "MA" );
				}
				else
				{					
					if( obj.CrossOver(bar, vhf, vhfTrend) && vhf[bar] < vhfMax & vhf[bar] > vhf[bar-vhfLag] & c[bar] > avgMAB[bar] )
						obj.BuyAtMarket(bar+1,"Strong Trend");
					
					if( vhf[bar] > vhfMult * vhfLow[bar] && obj.CrossOver(bar, vhf, crit) & c[bar] > avgMAB[bar] )
						obj.BuyAtMarket(bar+1,"Trend");
				
					if( obj.CrossOver(bar, vhf, vhfTrend) && vhf[bar] < vhfMax & vhf[bar] > vhf[bar-vhfLag] & c[bar] < avgMAB[bar] )
						obj.ShortAtMarket(bar+1,"Strong Dntrend");

					if( vhf[bar] > vhfMult * vhfLow[bar] && obj.CrossOver(bar, vhf, crit) & c[bar] < avgMAB[bar] )
						obj.ShortAtMarket(bar+1,"Downtrend");
				}
				
				// Workaround to prevent open positions from one system to be available for the next system to close
				if( bar == obj.Bars.Count-1 )
					obj.ExitAtClose( bar, Position.AllPositions, "Close All" );
			}
		}
	}
	
	class SystemER
	{
		public static void Run(WealthScript obj)
		{
			int periodER = 24, mab = 50, erLag = 3;
			double erTrend = 0.36, erMax = 0.42, erMult = 2.5, crit = 0.26;
			var c = obj.Close;			
			var er = ER.Series(c,periodER);
			var avgMAB = SMA.Series(c,mab);
			var erLow = Lowest.Series(er,erLag);
			ChartPane p = obj.CreatePane(30,true,true);
			obj.PlotSeries(p, er, Color.Black, LineStyle.Solid, 2 );

			for(int bar = obj.GetTradingLoopStartBar(1 + mab + erLag); bar < obj.Bars.Count; bar++)
			{				
				if (obj.IsLastPositionActive)
				{
					if( obj.CrossUnder(bar, c, avgMAB) || obj.CrossOver(bar, c, avgMAB) )
						obj.ExitAtMarket( bar+1, obj.LastPosition, "MA" );
				}
				else
				{					
					if( obj.CrossOver(bar, er, erTrend) && er[bar] < erMax & er[bar] > er[bar-erLag] & c[bar] > avgMAB[bar] )
						obj.BuyAtMarket(bar+1,"Strong Trend");
					
					if( er[bar] > erMult * erLow[bar] && obj.CrossOver(bar, er, crit) & c[bar] > avgMAB[bar] )
						obj.BuyAtMarket(bar+1,"Trend");
				
					if( obj.CrossOver(bar, er, erTrend) && er[bar] < erMax & er[bar] > er[bar-erLag] & c[bar] < avgMAB[bar] )
						obj.ShortAtMarket(bar+1,"Strong Dntrend");

					if( er[bar] > erMult * erLow[bar] && obj.CrossOver(bar, er, crit) & c[bar] < avgMAB[bar] )
						obj.ShortAtMarket(bar+1,"Downtrend");
				}
				
				// Workaround to prevent open positions from one system to be available for the next system to close
				if( bar == obj.Bars.Count-1 )
					obj.ExitAtClose( bar, Position.AllPositions, "Close All" );
			}
		}
	}
}

—Eugene, Wealth-Lab team
www.wealth-lab.com

BACK TO LIST

logo

AMIBROKER: OCTOBER 2016

In “Which Trend Indicator Wins?” author Markos Katsanos shows how various trend strength indicators (the ADX, R-squared, VHF, and ER) can be combined with a simple moving average crossover (Figure 4). A ready-to-use formula implementing the indicators and systems presented in Katsanos’ article is shown below. You can use the parameters window to select the system/indicator you would like to test and/or display.

Sample Chart

FIGURE 4: AMIBROKER. Here’s an example USO chart with a 50-day moving average. The R-squared, the efficiency ratio (ER), the vertical horizontal filter (VHF), and the ADX are shown in the lower panes.

function RSquared( array, period, smooth ) 
{ 
  bi = BarIndex(); 
  r = Correlation( bi, array, period); 
  rs = MA( r, smooth ); 
  return rs ^ 2; 
} 

function VHF( period ) 
{ 
  nom = HHV( High, period ) - LLV( Low, period ); 
  denom = Sum( abs( C - Ref( C, -1 ) ), period ); 

  return nom /  denom; 
} 


function EffRatio( period, smooth ) 
{ 
   nom = abs( C - Ref( C, -period ) ); 
   denom = Sum( abs( C - Ref( C, -1 ) ), period ); 

   return MA( nom/denom, smooth ); 
} 

SetOption("ReverseSignalForcesExit", False ); 

procedure SystemADX() 
{ 
   global Buy, Sell, Short, Cover; 
   
   period = 14; 
   trend = 30; 
   axmax = 42; 
   lag = 12; 
   crit = 22; 
   mult = 1.8; 
   maperiod = 50; 

   ax = ADX( period ); 
   mac = MA( Close, maperiod ); 
   
   strongbuy = Cross( ax, trend ) AND ax < axmax AND C > mac; 
   regbuy = ax > mult * LLV( ax, lag ) AND Cross( ax, crit ) AND C > mac; 
   
   Buy = strongbuy OR regbuy; 
   
   strongshort = Cross( ax, trend ) AND ax < axmax AND C < mac; 
   regshort = ax > mult * LLV( ax, lag ) AND Cross( ax, crit ) AND C < mac; 
     
   Short = strongshort OR regshort; 
   
   Sell = Cross( mac, C ); 
   Cover = Cross( C, mac ); 
   
   Plot( ax, "ADX-" + period, colorBlue ); 
   Plot( trend, "", colorDarkYellow); 
   
} 
   
procedure SystemR2() 
{ 
   global Buy, Sell, Short, Cover; 
   
   r2len = 18; 
   smooth = 3; 
   trend = 0.42; 
   r2max = 0.85; 
   lag = 10; 
   lrcrit = 10; 
   maperiod = 50; 
   
   R2 = RSquared( C, r2len, smooth ); 
   
   lr = 100 * LinRegSlope( C, r2len ); 
   
   flt = Cross( R2, trend ) AND R2 < R2max AND R2 > Ref( R2, -lag ); 
   
   mac = MA( C, maperiod ); 
   
   Buy = flt AND C > mac AND lr > lrcrit; 
   Short = flt AND C < mac AND lr < -lrcrit; 
   
   Sell = Cross( mac, C ); 
   Cover = Cross( C, mac ); 

   Plot( R2, "Rsquared-" + r2len, colorRed ); 
   Plot( trend, "", colorDarkYellow); 
} 
   
procedure SystemVHF() 
{ 
   global Buy, Sell, Short, Cover; 
   
   period = 36; 
   trend = 0.38; 
   lag = 10; 
   mult = 1.5; 
   maperiod = 50; 
   crit = 0.24; 
   vfmax = 0.45; 
   vf = VHF( period ); 

   mac = MA( C, maperiod ); 
     
   strongbuy = Cross( vf, trend ) AND vf < vfmax AND vf > Ref( vf, -lag ) 
               AND C > mac; 
   regbuy = vf > mult * LLV( vf, lag ) AND Cross( vf, crit ) AND C > mac; 
   
   Buy = strongbuy OR regbuy; 
   
   strongshort = Cross( vf, trend ) AND vf < vfmax AND vf > Ref( vf, -lag ) 
                 AND C < mac; 
                 
   regshort = vf > mult * LLV( vf, lag ) AND Cross( vf, crit ) AND C < mac; 
   
   Short = strongshort OR regshort; 
   
   Sell = Cross( mac, C ); 
   Cover = Cross( C, mac ); 
   
   Plot( vf, "VHF-" + period, colorDefault ); 
   Plot( trend, "", colorDarkYellow); 
} 
       
procedure SystemER() 
{ 
   global Buy, Sell, Short, Cover; 
   
   period = 24; 
   smooth = 3; 
   trend = 0.36; 
   ermax = 0.42; 
   erlag = 3; 
   mult = 2.5; 
   crit = 0.26; 
   maperiod = 50; 
   
   er = EffRatio( period, smooth ); 

   mac = MA( C, maperiod ); 

   strongbuy = Cross( er, trend ) AND er < ermax AND 
               er > Ref( er, -erlag ) AND C > mac; 
   regbuy = er > mult * LLV( er, erlag ) AND Cross( er, crit ) AND 
            C > mac; 
   
   Buy = strongbuy OR regbuy; 
   
   strongshort = Cross( er, trend ) AND er < ermax AND 
                 er > Ref( er, -erlag ) AND C < mac; 
   regshort = er > mult * LLV( er, erlag ) AND Cross( er, crit ) AND 
              C < mac; 

   Short = strongshort OR regshort; 
   
   Sell = Cross( mac, C ); 
   Cover = Cross( C, mac ); 
   
   Plot( er, "ER-" + period, colorGreen ); 
   Plot( trend, "", colorDarkYellow); 
}       

sys = ParamList("Select the system", "ADX|RSquared|VHF|ER" ); 

switch( sys ) 
{ 
case "ADX": 
    SystemADX(); 
    break; 
case "RSquared": 
    SystemR2(); 
    break; 
case "VHF": 
    SystemVHF(); 
    break; 
case "ER": 
    SystemER(); 
    break; 
default: 
    Error("Undefined system"); 
    break; 
} 

—Tomasz Janeczko, AmiBroker.com
www.amibroker.com

BACK TO LIST

logo

NEUROSHELL TRADER: OCTOBER 2016

The trend indicators described in Markos Katsanos’ article in this issue, “Which Trend Indicator Wins?” can be easily implemented in NeuroShell Trader with a few of NeuroShell Trader’s 800+ indicators. Simply select new indicator from the insert menu and use the indicator wizard to set up the following indicators:

ADX:	ADX(High,Low,Close,14,14)
VHF:	Divide(Range(Close,28),CumAbsMom(Close,28,1))
ER:	Divide(Abs(Momentum(Close,28)),CumAbsMom(Close,28,1))
R2:	LinTimeReg rsqrd(Close,21)

To implement the trend-following system for any of the indicators, simply select “new trading strategy” from the insert menu and enter the following in the appropriate locations of the Trading Strategy Wizard, substituting the appropriate test parameters for each indicator as outlined in the article’s test results:

BUY LONG CONDITIONS: [1 of which must be true]
And3(CrossAbove(Indicator,TREND),A<B(Indicator,INDMAX),A>B(Close,Avg(Close,MAB)))
And3(A>B(Indicator,Mul2(MULT,Min(Indicator,LAG))),CrossAbove(Indicator,CRIT),A>B(Close,Avg(Close,MAB)))

SELL LONG CONDITIONS: 
CrossBelow(Close,Avg(Close,MAB))

SELL SHORT CONDITIONS: [1 of which must be true]
And3(CrossAbove(Indicator,TREND),A<B(Indicator,INDMAX),A<B(Close,Avg(Close,MAB)))
And3(A>B(Indicator,Mul2(MULT,Min(Indicator,LAG))),CrossAbove(Indicator,CRIT),A<B(Close,Avg(Close,MAB)))

COVER SHORT CONDITIONS: 
CrossAbove(Close,Avg(Close,MAB))

Users of NeuroShell Trader can go to the Stocks & Commodities section of the NeuroShell Trader free technical support website to download a copy of this or any previous Traders’ Tips. A sample NeuroShell chart is shown in Figure 5.

Sample Chart

FIGURE 5: NEUROSHELL TRADER. This sample NeuroShell Trader chart shows the VHF trend-following system and the four trend-following indicators.

—Marge Sherald, Ward Systems Group, Inc.
301 662-7950, sales@wardsystems.com
www.neuroshell.com

BACK TO LIST

logo

NINJATRADER: OCTOBER 2016

Strategies based off of the ADX, R-squared, VHF, and ER indicators, as discussed in “Which Trend Indicator Wins?” by Markos Katsanos in this issue, are available for download at www.ninjatrader.com/SC/October2016SC.zip.

Once downloaded, from within the NinjaTrader Control Center window, select the menu File → Utilities → Import NinjaScript and select the downloaded file. This file is for NinjaTrader version 7.

You can review the source code of the custom VHF and ER indicators by selecting the menu Tools → Edit NinjaScript → Indicator from within the NinjaTrader Control Center window and selecting the VHF or ER file.

You can review the source code of the strategies by selecting the menu Tools → Edit NinjaScript → Strategy from within the NinjaTrader Control Center window and selecting the ADXStrategy, ERStrategy, R2Strategy, or VHFStrategy file.

A sample chart implementing the strategy is shown in Figure 6.

Sample Chart

FIGURE 6: NINJATRADER. The R-squared, ER, VHF, and ADX are shown detecting the downtrend in USO beginning with the ER indicator on November 13, 2015.

—Raymond Deux & Zachary Gauld
NinjaTrader, LLC
www.ninjatrader.com

BACK TO LIST

logo

UPDATA: OCTOBER 2016

Our Traders’ Tip for this month is based on the article by Mark Katsanos in this issue, “Which Trend Indicator Wins?” In it, the author compares four trend-following indicators to determine which is best for finding trends in real time, as opposed to in retrospect. The four indicators are: the ADX, Kaufman’s efficiency ratio, R-squared, and the vertical horizontal filter, each with standardized entry rules. A sample chart of the indicators is shown in Figure 7.

Sample Chart

FIGURE 7: UPDATA. This chart demonstrates the ADX, VHF, R-SQ, and ER applied to the ETF USO in daily resolution.

The Updata code for this article can be found in the Updata library and may be downloaded by clicking the custom menu and system library. Those who cannot access the library due to a firewall may ask our helpdesk for a copy, or copy and paste the code shown below.

'ADX
PARAMETER "ADX Period" #PERIOD=14
PARAMETER "Trend" @TREND=30 
PARAMETER "Lag" #LAG=12
PARAMETER "ADX Max" @ADXMAX=42
PARAMETER "Critical" @CRIT=22
PARAMETER "Mult" @MULT=1.8
PARAMETER "Avg" #AVGPER=50  
DISPLAYSTYLE 5LINES
INDICATORTYPE TOOL
INDICATORTYPE2 CHART 
COLOUR RGB(0,0,200) 
COLOUR2 RGB(0,0,0)
COLOUR3 RGB(200,0,0) 
COLOUR4 RGB(0,0,200)
COLOUR5 RGB(0,200,0) 
NAME "" ""
NAME2 "ADX" ""
@ADX=0
@AVG=0 
FOR #CURDATE=#AVGPER TO #LASTDATE
    @ADX=DIRMOV(#PERIOD,ADX) 
    @AVG=MAVE(#AVGPER) 
    'EXIT
    IF ORDERISOPEN=1 AND CLOSE<@AVG
       SELL CLOSE
    ELSEIF ORDERISOPEN=-1 AND CLOSE>@AVG
       COVER CLOSE
    ENDIF
    'LONG
    IF @ADX>@TREND AND @ADX<@ADXMAX AND CLOSE>@AVG
       BUY CLOSE
    ENDIF 
    IF @ADX>@MULT*PLOW(@ADX,#LAG) AND @ADX>@CRIT AND CLOSE>@AVG
       BUY CLOSE
    ENDIF
    'SHORT
    IF @ADX>@TREND AND @ADX<@ADXMAX AND CLOSE<@AVG
       SHORT CLOSE
    ENDIF 
    IF @ADX>@MULT*PLOW(@ADX,#LAG) AND @ADX>@CRIT AND CLOSE<@AVG
       SHORT CLOSE
    ENDIF  
    @PLOT=@AVG
    @PLOT2=@ADX
    @PLOT3=@ADXMAX
    @PLOT4=@CRIT
    @PLOT5=@MULT*PLOW(@ADX,#LAG)     
NEXT

'KAUFMAN EFFICIENCY RATIO SYSTEM
PARAMETER "Period" #PERIOD=20  
PARAMETER "Smooth" #SMOOTH=3
PARAMETER "Mult" @MULT=2.5  
PARAMETER "Lag" #LAG=3
PARAMETER "Max" @MAX=0.42  
PARAMETER "Critical" @CRIT=0.26
PARAMETER "Trend" @TREND=0.36 
DISPLAYSTYLE 5LINES
INDICATORTYPE TOOL
INDICATORTYPE2 CHART
COLOUR RGB(200,0,0)
COLOUR2 RGB(100,100,100) 
COLOUR3 RGB(200,0,0) 
COLOUR4 RGB(200,0,0) 
COLOUR5 RGB(200,0,0)
@ER=0 
@SIGNAL=0       
@DIFF=0  
@NOISE=0
NAME "" ""  
NAME2 "ER" ""
FOR #CURDATE=#PERIOD TO #LASTDATE   
   @DIFF=CLOSE-CLOSE(1) 
   @SIGNAL=ABS(CLOSE-CLOSE(#PERIOD))  
   @NOISE=SGNL(ABS(@DIFF),#PERIOD,M)*#PERIOD 
   @ER=SGNL(@SIGNAL/@NOISE,#SMOOTH,M)   
   'EXITS
   IF ORDERISOPEN>0 AND CLOSE<MAVE(#SMOOTH)
      SELL CLOSE
   ELSEIF ORDERISOPEN<0 AND CLOSE>MAVE(#SMOOTH)
      COVER CLOSE
   ENDIF   
   'LONG 
   IF @ER>@TREND AND @ER<@MAX AND @ER>HIST(@ER,#LAG) AND CLOSE>MAVE(#SMOOTH)
      BUY CLOSE
   ELSEIF @ER>@MULT*PLOW(@ER,#LAG) AND @ER>@CRIT AND CLOSE>MAVE(#SMOOTH)
      BUY CLOSE
   ENDIF   
   'SHORT 
   IF @ER>@TREND AND @ER<@MAX AND @ER>HIST(@ER,#LAG) AND CLOSE<MAVE(#SMOOTH)
      SHORT CLOSE
   ELSEIF @ER>@MULT*PLOW(@ER,#LAG) AND @ER>@CRIT AND CLOSE<MAVE(#SMOOTH)
      SHORT CLOSE
   ENDIF
   @PLOT=MAVE(#SMOOTH) 
   @PLOT2=@ER
   @PLOT3=@CRIT
   @PLOT4=@MAX 
   @PLOT5=@TREND 
NEXT

NAME R-Squared  
PARAMETER "Critical" @LCrit=10  
PARAMETER "Close Avg" #AVGPER=50 
PARAMETER "RSQ Period" #FPeriod=20
PARAMETER "RSQ MA LENGTH" #LENGTH=3   
PARAMETER "Smooth" #SMOOTH=3
PARAMETER "Trend" @TREND=0.42 
PARAMETER "R2 Max" @R2MAX=0.85 
PARAMETER "Lag" #LAG=10 
 
 
DISPLAYSTYLE  4LINES
PLOTSTYLE LINE RGB(255,0,0) 
PLOTSTYLE2 HISTOGRAM
PLOTSTYLE3 LINE RGB(0,0,255)
PLOTSTYLE4 LINE RGB(255,0,0)
INDICATORTYPE CHART NEWWINDOW 
INDICATORTYPE2 CHART NEWWINDOW   
INDICATORTYPE3 CHART NEWWINDOW
INDICATORTYPE4 CHART SUPERIMPOSERIGHT
 
@sumxy=0
@sumx=0
@sumy=0
@sumXsq=0 
#I=0
@b=0
@SLOPE=0
@sumYsq=0   
@RSQ=0
@TOP=0
@Bottom1=0
@Bottom2=0 
#RSQPeriod=#FPeriod
 
FOR #CURDATE=#FPeriod To #LASTDATE   
      @sumxy=0
      @sumx=0
      @sumy=0
      @sumXsq=0 
      @sumYsq=0          
      For #I=(#FPeriod) To 1 STEP -1
         @sumxy=@sumxy+(#I*CLOSE(#I))
         @sumy=@sumy+close(#I)
         @sumx=@sumx+(#I)
         @sumXsq=@sumXsq+EXPBASE(#I,2)
         @sumYsq=@sumYsq+EXPBASE(CLOSE(#I),2)
      Next   
      'Calculates SLOPE 
      @SLOPE=((@sumx*@sumy)-(@sumxy*#FPeriod) )/ ((@sumXsq*#FPeriod)-(EXPBASE(@sumx,2)) )  
      @PLOT2=@SLOPE 
      'Calculates RSQ-arranged so as to keep track of brackets 
      @TOP=( (#RSQPeriod*@sumxy)-(@sumx*@sumy) )
      @Bottom1=#RSQPeriod*@sumYsq-EXPBASE(@sumy,2)
      @Bottom2=#RSQPeriod*@sumXsq-EXPBASE(@sumx,2)
      @RSQ=EXPbase(@TOP/EXPBASE((@Bottom1*@Bottom2),0.5),2)
      @PLOT=@RSQ 
      'EXIT
      IF ORDERISOPEN=1 AND CLOSE>MAVE(#AVGPER)
         SELL CLOSE
      ENDIF 
      'EXIT
      IF ORDERISOPEN=-1 AND CLOSE<MAVE(#AVGPER)
         COVER CLOSE
      ENDIF 
      'ENTRIES
      IF @RSQ>@TREND AND @RSQ<@R2MAX AND @RSQ>HIST(@RSQ,#LAG)
         'LONG
         IF CLOSE>MAVE(#AVGPER) AND @SLOPE>@LCrit
            BUY CLOSE
         ENDIF 
         'SHORTS
         IF CLOSE<MAVE(#AVGPER) AND @SLOPE<@LCrit
            SHORT CLOSE
         ENDIF
      ENDIF     
NEXT

'VERTICAL HORIZONTAL FILTER
PARAMETER "Period" #PERIOD=28 
PARAMETER "Smooth" #SMOOTH=50
PARAMETER "Trend" @TREND=0.38
PARAMETER "Mult" @MULT=1.5
PARAMETER "Crit" @CRIT=0.24 
PARAMETER "Max" @MAX=0.42 
PARAMETER "Lag" #LAG=10
 
NAME "VHF " #PERIOD "" 
@VHF=0
FOR #CURDATE=#PERIOD TO #LASTDATE
   @VHF=(PHIGH(CLOSE,#PERIOD)-PLOW(CLOSE,#PERIOD))/(SGNL(ABS(CLOSE-CLOSE(1)),#PERIOD,M)*#PERIOD) 
   'EXITS
   IF CLOSE<MAVE(#SMOOTH) AND ORDERISOPEN=1
      SELL CLOSE
   ELSEIF CLOSE>MAVE(#SMOOTH) AND ORDERISOPEN=-1
      COVER CLOSE
   ENDIF
   'LONGS
   IF @VHF>@TREND AND @VHF<@MAX AND @VHF>HIST(@VHF,#LAG)
      IF CLOSE>MAVE(#SMOOTH)
         BUY CLOSE  
      ENDIF
   ENDIF
   IF @VHF>@MULT*PLOW(@VHF,#LAG) AND @VHF>@CRIT AND CLOSE>MAVE(#PERIOD)
      BUY CLOSE
   ENDIF 
   'SHORTS
   IF @VHF>@TREND AND @VHF<@MAX AND @VHF>HIST(@VHF,#LAG)
      IF CLOSE<MAVE(#SMOOTH)
         SHORT CLOSE  
      ENDIF
   ENDIF
   IF @VHF>@MULT*PLOW(@VHF,#LAG) AND @VHF>@CRIT AND CLOSE<MAVE(#PERIOD)
      SHORT CLOSE
   ENDIF    
   @PLOT=@VHF 
NEXT

—Updata support team
support@updata.co.uk
www.updata.co.uk

BACK TO LIST

MICROSOFT EXCEL: OCTOBER 2016

In “Which Trend Indicator Wins?” in this issue, author Markos Katsanos explores four indicators that were designed by their respective developers to assist the user in differentiating between trending markets and consolidating markets. Once the type of market has been determined, one can then apply trend-following indicators or oscillators, as appropriate, when making trading decisions.

As a way to compare these indicators, Katsanos designed a separate, simple trading system around each of the four indicators. In Figure 8 we have the basic controls for the simple moving average (on the price chart) and the four trend indicators, which are plotted as subcharts below the price chart.

Sample Chart

FIGURE 8: EXCEL, INDICATOR COMPARISON. This chart approximates Figure 3 from Katsanos’ article in this issue, “Which Trend Indicator Wins?”

The four individual trading systems each require a fair number of columns of formulas. All four indicators done on a single worksheet would make for a very busy worksheet. Thus, this spreadsheet has a separate worksheet for each of the four indicators.

Here is my observation about the trading simulations:

While developing this workbook, I encountered a couple of interesting artifacts:

  1. The code might generate a long or short entry signal while a trade of the other persuasion was already in progress. Thus, to simplify things a bit, I have set up the trading simulation as two separate accounts, one for long trades and one for short trades.
  2. The second artifact is that the simulation may generate additional long or short entry signals while a trade of that persuasion is already in progress. Here, my approach differs:
    1. Katsanos’ code will honor each of these as individual trade entries, risking an additional 50,000. The aggregate dollar figures involved can be impressive!

      “Sell all” and “BuyToCover all” statements in his code will close out these single to possibly multiple positions on the first available exit signal.

    2. Since my approach is “how would my account fare?” and I am going “all in” on the first signal, logically, there is nothing left in the account to take any advantage of any secondary entries.

      So the logic in this workbook will only recognize the first entry signal of a given persuasion for a transaction until the transaction exits. At most, my simulation will have one trade of each persuasion active at a time.

Figure 9 shows part of one of my trend indicator worksheets. Each of the four trend indicator worksheets has the same layout, as follows:

Across the top:

Across the bottom:

Sample Chart

FIGURE 9: EXCEL, INDICATOR CALCULATION. Here is a worksheet that can be used for indicator calculation and trading simulation.

The spreadsheet file for this Traders’ Tip can be downloaded here. To successfully download it, follow these steps:

—Ron McAllister
Excel and VBA programmer
rpmac_xltt@sprynet.com

BACK TO LIST

Originally published in the October 2016 issue of
Technical Analysis of STOCKS & COMMODITIES magazine.
All rights reserved. © Copyright 2016, Technical Analysis, Inc.