September 2007
TRADERS' TIPS

Here is this month's selection of Traders' Tips, contributed by various developers of technical analysis software to help readers more easily implement some of the strategies presented in this and other issues.

You can copy these formulas and programs for easy use in your spreadsheet or analysis software. Simply "select" the desired text by highlighting as you would in any word processing program, then use your standard key command for copy or choose "copy" from the browser menu. The copied text can then be "pasted" into any open spreadsheet or other software by selecting an insertion point and executing a paste command. By toggling back and forth between an application window and the open Web page, data can be transferred with ease.

This month's tips include formulas and programs for:

METASTOCK: TRADING TRENDLINE BREAKS
TRADESTATION: TRADING TRENDLINE BREAKS
eSIGNAL: TRADING TRENDLINE BREAKS
AMIBROKER: TRADING TRENDLINE BREAKS
WEALTH-LAB: TRADING TRENDLINE BREAKS
NEUROSHELL TRADER: TRADING TRENDLINE BREAKS
AIQ: TRADING TRENDLINE BREAKS
STRATASEARCH: TRADING TRENDLINE BREAKS
NINJATRADER: TRADING TRENDLINE BREAKS
TRADECISION: TRADING TRENDLINE BREAKS
TRADE NAVIGATOR: TRADING TRENDLINE BREAKS
VT TRADER: TRONGONE MOVING AVERAGES

or return to September 2007 Contents

 

METASTOCK: TRADING TRENDLINE BREAKS

For the MetaStock code to implement the techniques discussed in Sylvain Vervoort's article in this issue, "Trading Trendline Breaks, Part 2," see the sidebar in the article on page 24 for code provided by Vervoort. --Editor

GO BACK


TRADESTATION: TRADING TRENDLINE BREAKS

Sylvain Vervoort's articles in his two-part series, "Trading Trendline Breaks," describe the use of trendline breaks in trading. The articles include code that approximates the author's trading style. Vervoort's code identifies zigzag peaks and troughs in the present by looking at future price data. Obviously, this cannot be done in actual trading. The code given here identifies zigzag peaks and troughs in the present without looking at price data in the future.

FIGURE 1: TRADESTATION, TRADING TRENDLINE BREAKS. The strategy "ZigZag Percent," applied to a daily chart of IBM, is displayed in the upper panel. The yellow trendlines are drawn by one of TradeStation's built-in indicators, "ZigZag %". Both the strategy and the indicator use a 5% reversal to identify peaks and troughs. The lower pane displays the built-in "TrendLines Automatic" indicator, again applied to a daily chart of IBM.
TradeStation has a number of built-in studies for creating and trading trendlines. For example, the indicator "Trendlines Automatic" creates trendlines much like those described in the articles. The built-in strategies "TrendLine LE" and "TrendLine SE" provide long and short entries that are based on manually drawn trendlines. The EasyLanguage Library in TradeStation's online Support Forum contains other trendline-based strategies, including "TL_Entry," "TL_ProfitTarget," and "TL_StopLoss." Each of these strategies uses a manually drawn trendline to control trades. See, for example, the code at the following link:
https://www.tradestation.com/Discussions/Topic.aspx?Topic_ID=5863
Code for a strategy named "ZigZag Percent" is shown here. To download this code, go to the Support Center at TradeStation.com. Search for the file "ZigZag_Percent.Eld."

TradeStation does not endorse or recommend any particular strategy.
 

Strategy: ZigZag Percent
inputs:
    Price( Close ),
    RetracePct( 5 ) ;
variables:
    LowPrice( 0 ),
    HighPrice( 0 ),
    RetraceFctrUp( 0 ),
    RetraceFctrDn( 0 ) ;
if CurrentBar = 1 then
    begin
    LowPrice = Price ;
    HighPrice = Price ;
    RetraceFctrUp = 1 + 0.01 * RetracePct ;
    RetraceFctrDn = 1 - 0.01 * RetracePct ;
    end
else
    begin
    if Price < LowPrice then
        LowPrice = Price ;
    if Price > HighPrice then
        HighPrice = Price ;
    if MarketPosition < 1 and Price >= LowPrice *
     RetraceFctrUp then
        begin
        Buy this bar on Close ;
        HighPrice = Price ;
        end
    else if MarketPosition > -1 and Price <= HighPrice *
     RetraceFctrDn then
        begin
        Sell Short this bar on Close ;
        LowPrice = Price ;
        end ;
    end ;
--Mark Mills
TradeStation Securities, Inc.
A subsidiary of TradeStation Group, Inc.
www.TradeStation.com


GO BACK


eSIGNAL: TRADING TRENDLINE BREAKS

For this month's Traders' Tip, we've provided the eSignal formula code named "Trendline_Breaks.efs," based on the code given in Sylvain Vervoort's article in this issue, "Trading Trendline Breaks, Part 2."

The eSignal formula is a modified version of eSignal's RealTimeSwings.efs, which contains the code for reproducing the zigzag indicator.

There are several formula parameters that may be configured through the Edit Studies option in the Advanced Chart to change the definition of the swing points for the indicator, which include options for number of bars, wave type, wave percentage, swing high price source, swing low price source, colors, line thickness, the display of swing labels, and a percent retracement label.

The study is a price study configured for backtesting in the Strategy Analyzer with parameter defaults set to the zigzag definitions outlined in Vervoort's article. The study displays the zigzag indicator as well as the PU and PD price levels used for the trade signals, which are highlighted on the chart with "long" and "short" labels accompanied by up/down arrows. A sample chart is shown in Figure 2.
 


FIGURE 2: eSIGNAL, TRADING TRENDLINE BREAKS. The study displays the zigzag indicator as well as the PU and PD price levels used for the trade signals, which are highlighted on the chart with "long" and "short" labels accompanied by up/down arrows.
One difference between this EFS study and the logic used in Vervoort's article is that the forward-looking aspect of the signals has been removed. To create realistic signals that may be evaluated in real time, the trade conditions are evaluated on the bars that confirm the previous swing points by the appropriate price action as defined by the formula parameters.
/***************************************
Provided By :
    eSignal (Copyright © eSignal), a division of Interactive Data
    Corporation. 2007. 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:  Trading Trendline Breaks, Part 2
              by Sylvain Vervoort
Version 1.0  7/9/2007
Notes:
* Study is a modified version of eSignal's RealTimeSwings.efs,
    which incorporates Vervoort's strategy signals based on
    ZigZag indicator.
* Study provides historical analysis only, see author's notes
    in the article.
*   Description of Swing Labels:
        At Swing Highs -  Points (% Retracement)
                          Price (Number of Bars)
 
        At Swing Lows -   Price (Number of Bars)
                          Points (% Retracement)
Formula Parameters:                 Default:
    * Swing: # of Bars              5
        This is the minimum number of bars required to define a
        swing point. This number is for both sides of the swing
        point (i.e. 5 bars on the left and right of the swing bar).
    * Swing: Wave Type              % Retracement
        (% Retracement, % Change in Price)
    * Swing: Wave Percentage        30
        The number 5 will be treated as 5.0%.  The number 0.05 will
        be treated as 0.0005%.
    * Swing High Price Source       High
    * Swing Low Price Source        Low
    * Line Thickness                2
    * Confirmed Swing Line Color    Blue
    * Developing Swing Line Color   Red
    * Display Swing Labels          True
    * Display % Retracement Label   True
    * Number of Historical Labels   100
*****************************************************************/
function preMain() {
    setPriceStudy(true);
    setStudyTitle("Trend Line Breaks ");
    //setShowCursorLabel(false);
    setShowTitleParameters(false);
 
    //TASC mod
    setCursorLabelName("PD", 0);
    setCursorLabelName("PU", 1);
    setDefaultBarFgColor(Color.red, 0);     //PD
    setDefaultBarFgColor(Color.green, 1);   //PU
    setDefaultBarThickness(2, 0);
    setDefaultBarThickness(2, 1);
    setPlotType(PLOTTYPE_FLATLINES, 0);
    setPlotType(PLOTTYPE_FLATLINES, 1);
 
 
    var fp1 = new FunctionParameter("nNum", FunctionParameter.NUMBER);
    fp1.setName("Swing: # of Bars");
    fp1.setLowerLimit(1);
    fp1.setDefault(5);
 
    var fp2a = new FunctionParameter("sWaveType", FunctionParameter.STRING);
    fp2a.setName("Swing: Wave Type");
    fp2a.addOption("% Retracement");
    fp2a.addOption("% Change in Price");
    fp2a.setDefault("% Change in Price");
 
    var fp2 = new FunctionParameter("nRet", FunctionParameter.NUMBER);
    fp2.setName("Swing: Wave Percentage");
    fp2.setLowerLimit(0);
    fp2.setDefault(7);
    var fp3 = new FunctionParameter("sHighSource", FunctionParameter.STRING);
    fp3.setName("Swing High Price Source");
    fp3.addOption("Open");
    fp3.addOption("High");
    fp3.addOption("Low");
    fp3.addOption("Close");
    fp3.setDefault("Close");
    var fp4 = new FunctionParameter("sLowSource", FunctionParameter.STRING);
    fp4.setName("Swing Low Price Source");
    fp4.addOption("Open");
    fp4.addOption("High");
    fp4.addOption("Low");
    fp4.addOption("Close");
    fp4.setDefault("Close");
    var fp5 = new FunctionParameter("nThickness", FunctionParameter.NUMBER);
    fp5.setName("Line Thickness");
    fp5.setLowerLimit(1);
    fp5.setDefault(2);
    var fp6 = new FunctionParameter("cColor1", FunctionParameter.COLOR);
    fp6.setName("Confirmed Swing Line Color");
    fp6.setDefault(Color.blue);
    var fp7 = new FunctionParameter("cColor2", FunctionParameter.COLOR);
    fp7.setName("Developing Swing Line Color");
    fp7.setDefault(Color.red);
    var fp8 = new FunctionParameter("bSwingLabels", FunctionParameter.STRING);
    fp8.setName("Display Swing Labels");
    fp8.addOption("True");
    fp8.addOption("False");
    fp8.setDefault("False");
    var fp9 = new FunctionParameter("bRetLabel", FunctionParameter.STRING);
    fp9.setName("Display \% Retracement Label");
    fp9.addOption("True");
    fp9.addOption("False");
    fp9.setDefault("False");
    var fp10 = new FunctionParameter("nNumLabels", FunctionParameter.NUMBER);
    fp10.setName("Number of Historical Labels");
    fp10.setLowerLimit(1);
    fp10.setDefault(100);
}
var bEdit = true;       // tracks change of user inputs
var cntr = 0;           // image counter for swing lines
var bInit = false;      // initialization routine completion
var nNumBars = null;    // number of bars for defining swings
var sWaveTypeG = null;  // wave type for confirming swings
var nRetpcnt = null;    // percent retracement for defining swings
var nThicknessG = null; // line thickness
var cColorcon = null;   // confirmed swing color
var cColordev1 = null;  // developing swing color
var sHSource = null;    // price source for high swings
var sLSource = null;    // price source for low swings
var x1a = null;         // x-coordinate for point a of developing line 1
var x1b = null;         // x-coordinate for point b of developing line 1
var x2a = null;         // x-coordinate for point a of developing line 2
var x2b = null;         // x-coordinate for point b of developing line 2
var y1a = null;         // y-coordinate for point a of developing line 1
var y1b = null;         // y-coordinate for point b of developing line 1
var y2a = null;         // y-coordinate for point a of developing line 2
var y2b = null;         // y-coordinate for point b of developing line 2
var vLastSwing = null;  // tracking swing type of last confirmed swing (H or L)
var nScntr = 0;         // bar counter for swing confirmation
var nLcntr = 0;         // label counter for swing labels
var aSwingsIndex = new Array(4); // tracks current swings indexes for last 4 swings
var aSwingsPrice = new Array(4); // tracks current swing prices for last 4 swings
var nNumLabelsG = null; // max number of swing labels
var bSwingLabelsG = null;  // controls swing labels display
var vSpace = null;      // spacer for Labels
//TASC mod
var bBT = true;     // back test flag
var vPosition = 0;  // 0=flat, 1=long, -1=short
var nPD = null;
var nPU = null;
function main(nNum, sWaveType, nRet, sHighSource, sLowSource,
              nThickness, cColor1, cColor2, bSwingLabels, bRetLabel, nNumLabels) {
    var nState = getBarState();
    var nIndex = getCurrentBarIndex();
    var h = getValue(sHighSource);
    var l = getValue(sLowSource);
    var c = close();
    var i = 0;
    // record keeping
    if (nState == BARSTATE_NEWBAR) {
        if (cntr > 100) cntr = 0;
        if (x1a != null) x1a -= 1;
        if (x1b != null) x1b -= 1;
        if (x2a != null) x2a -= 1;
        if (x2b != null) x2b -= 1;
        i = 0;
        for (i = 0; i < 3; ++i) {
            if (aSwingsIndex[i] != null) aSwingsIndex[i] -= 1;
        }
    }
    //Initialization
    if (bEdit == true) {
        if (nNumBars == null) nNumBars = nNum;
        if (sWaveTypeG == null) sWaveTypeG = sWaveType;
        if (nRetpcnt == null) nRetpcnt = nRet/100;
        if (nThicknessG == null) nThicknessG = nThickness;
        if (cColorcon == null) cColorcon = cColor1;
        if (cColordev1 == null) cColordev1 = cColor2;
        if (sHSource == null) sHSource = sHighSource;
        if (sLSource == null) sLSource = sLowSource;
        if (x1a == null) x1a = 0;
        if (y1a == null) y1a = c;
        if (nNumLabelsG == null) nNumLabelsG = nNumLabels;
        if (bSwingLabelsG == null) bSwingLabelsG = bSwingLabels;
        nLcntr = nNumLabels;
        // Initialize vSpace
        var OM = (close() * 0.005); //offset multiplier
        var sInterval = getInterval();
        if (sInterval == "D") OM = OM*3;
        if (sInterval == "W") OM = OM*20;
        if (sInterval == "M") OM = OM*30;
        var TimeFrame = parseInt(sInterval);
        if (TimeFrame >= 1 && TimeFrame <= 5) {
            OM = OM*(TimeFrame/15);
        } else if (TimeFrame > 5 && TimeFrame <= 15) {
            OM = OM*(TimeFrame/10);
        }else if (TimeFrame > 15) {
            OM = OM*(TimeFrame/5);
        }
        if (!isNaN(TimeFrame)) OM = (OM/TimeFrame)*3;
        vSpace = OM;
 
        //TASC mod
        setDefaultBarThickness(nThickness, 0);
        setDefaultBarThickness(nThickness, 1);
 
        bEdit = false;
    }
    if (bInit == false) {
        bInit = Init(h,l,c);
    }
    // Swings
    if (nState == BARSTATE_NEWBAR) {
        nScntr += 1;
        // confirmed Swings
        if (nScntr > nNumBars) {
            confirmSwings();
            if (bInit == true) {
                doLine("dev1");
                doLine("dev2");
            }
        }
    }
 
    checkSwings(h, l);
    if (bInit == true) {
        doLine("dev1");
        doLine("dev2");
    }
    // % Retracement Label
    var nWaveRet = (Math.abs(y2a-y2b) / Math.abs(y1b-y1a))*100;
    if (x1b == x2b) nWaveRet = 0.0;
    if (bRetLabel == "True") {
        var sWaveRetText = " \%Retraced: " + nWaveRet.toFixed(2) + " ";
        drawTextRelative(2, y2b, sWaveRetText, cColordev1, null,
            Text.BOLD|Text.LEFT|Text.VCENTER|Text.FRAME, "Arial", 10, "Ret");
    }
    //TASC mod - back testing conditions
    if (nIndex == 0) bBT = false;
    if (nState == BARSTATE_NEWBAR) {
        if (vPosition != 1 && nPU != null && close(-1) >= nPU && vLastSwing == "L") { // long signal
            if (bBT == true) Strategy.doLong("long", Strategy.MARKET, Strategy.THISBAR);
            vPosition = 1;
            drawShape(Shape.UPARROW, BelowBar1, Color.green);
            drawText("Long", BelowBar2, Color.green, Text.FRAME|Text.BOLD, rawtime(0));
        } else if (vPosition != -1 && nPD != null &&
            close(-1) <= nPD && vLastSwing == "H") { // short signal
            if (bBT == true) Strategy.doShort("short", Strategy.MARKET, Strategy.THISBAR);
            vPosition = -1;
            drawShape(Shape.DOWNARROW, AboveBar1, Color.red);
            drawText("Short", AboveBar2, Color.red, Text.FRAME|Text.BOLD, rawtime(0));
        }
    }
 
    return new Array(nPD, nPU);
}
/******  Functions *****/
/***********************/
function Init(h,l,c) {
    if (close(-(nNumBars*2)) == null) {
        return false;
    } else {
        // Find initial line.
        // The initial line will be the first high or low swing,
        // which has the greater difference of the swing point to
        // the close of the first bar.
        var Index = getCurrentBarIndex()
        var hIndex = Index;
        var lIndex = Index;
        var j = nNumBars*2;
        var aHigh = getValue(sHSource, 0, -j);
        var aLow = getValue(sLSource, 0, -j);
        var vHH = aHigh[0];
        var vLL = aLow[0];
        var tempIndex = Index;
        var i = 0;
        for (i = 0; i < j; ++i) {
            if (aHigh[i] > vHH) {
                vHH = aHigh[i];
                hIndex = tempIndex;
            }
            if (aLow[i] < vLL) {
                vLL = aLow[i];
                lIndex = tempIndex;
            }
            tempIndex -= 1;
        }
        if (vHH - y1a > y1a - vLL) {
            vLastSwing = "L";
            x1b = hIndex - Index;
            y1b = vHH;
            doLine("dev1");
            x2a = x1b;
            y2a = vHH;
            x2b = 0;
            y2b = c;
            doLine("dev2");
        } else {
            vLastSwing = "H";
            x1b = lIndex - Index;
            y1b = vLL;
            doLine("dev1");
            x2a = x1b;
            y2a = vLL;
            x2b = 0;
            y2b = c;
            doLine("dev2");
        }
    }
 
    if (vLastSwing != null) {
        return true;
    } else {
        return false;
    }
}
function doLine(sType) {
    //confirmed
    if (sType == "con") {
        cntr += 1;
        drawLineRelative(x1a, y1a, x1b, y1b, PS_SOLID,
            nThicknessG, cColorcon, sType+cntr);
        //Swing Labels
        if (bSwingLabelsG == "True") doSwingLabels(sType);
        x1a = x2a;
        y1a = y2a;
        x1b = x2b;
        y1b = y2b;
        x2a = x1b;
        y2a = y1b;
        aSwingsIndex.pop();
        aSwingsIndex.unshift(x1b);
        aSwingsPrice.pop();
        aSwingsPrice.unshift(y1b);
        if (vLastSwing == "H") y2b = getValue(sHSource);
        if (vLastSwing == "L") y2b = getValue(sLSource);
        //TASC mod
        if (aSwingsPrice[0] != null && vLastSwing != null) {
            if (vLastSwing == "H") {
                nPD = aSwingsPrice[1] - (aSwingsPrice[1]*0.05);
            } else if (vLastSwing == "L") {
                nPU = aSwingsPrice[1] + (aSwingsPrice[1]*0.05);
            }
 
        }
    }
    // dev1
    if (sType == "dev1") {
        drawLineRelative(x1a, y1a, x1b, y1b, PS_SOLID,
            nThicknessG, cColordev1, sType);
        aSwingsIndex[0] = x1b;
        aSwingsPrice[0] = y1b;
        //Swing Labels
        if (bSwingLabelsG == "True") doSwingLabels(sType);
    }
 
    // dev2
    if (sType == "dev2") {
        if (x2a != 0 && x2a != x2b) {
            if ( (vLastSwing == "H" && sHSource == "Close") ||
                 (vLastSwing == "L" && sLSource == "Close") ) {
                x2b = 0;
                y2b = close();
            }
            drawLineRelative(x2a, y2a, x2b, y2b, PS_SOLID,
                nThicknessG, cColordev1, sType);
        } else {
            removeLine(sType);
        }
    }
 
    return;
}
function doSwingLabels(sType) {
    var sTagNamePts = "SwingPtsDev";
    var sTagNameRet = "SwingRetDev";
    var sTagNamePr = "SwingPrDev";
    var sTagNameBars = "SwingBarsDev";
    var nWaveRet = ((Math.abs(aSwingsPrice[1]-aSwingsPrice[0]) /
                     Math.abs(aSwingsPrice[1]-aSwingsPrice[2])) * 100);
    var nBars = (aSwingsIndex[0] - aSwingsIndex[1]);
 
    if (sType == "con") {
        //nWaveRet = (Math.abs(y2a-y2b) / Math.abs(y1b-y1a));
        nLcntr += 1;
        if (nLcntr > nNumLabelsG) nLcntr = 1;
        sTagNamePts = "SwingPts"+sType+nLcntr;
        sTagNameRet = "SwingRet"+sType+nLcntr;
        sTagNamePr = "SwingPr"+sType+nLcntr;
        sTagNameBars = "SwingBars"+sType+nLcntr;
    }
 
    var pts = (y1b-y1a).toFixed(2);
    if (y1a < y1b) { // swing high
        drawTextRelative(x1b, y1b+vSpace, pts + " ", eval("cColor"+sType), null,
            Text.BOTTOM|Text.RIGHT, "Arial", 10, sTagNamePts); // Points
        if (!isNaN(nWaveRet)) {
            drawTextRelative(x1b, y1b+vSpace, "| ("+nWaveRet.toFixed(2)+"\%)",
                eval("cColor"+sType), null, Text.BOTTOM|Text.LEFT, "Arial", 10,
                sTagNameRet); // % Retracement
        }
        drawTextRelative(x1b, y1b+vSpace, y1b.toFixed(2) + " ", eval("cColor"+sType), null,
            Text.TOP|Text.RIGHT, "Arial", 10, sTagNamePr); // Price
        if (!isNaN(nBars)) {
            drawTextRelative(x1b, y1b+vSpace, "| ("+nBars+" Bars)", eval("cColor"+sType), null,
                Text.TOP|Text.LEFT, "Arial", 10, sTagNameBars); // Number of Bars
        }
    } else { // swing low
        drawTextRelative(x1b, y1b-vSpace, pts + " ", eval("cColor"+sType), null,
            Text.TOP|Text.RIGHT, "Arial", 10, sTagNamePts); // Points
        if (!isNaN(nWaveRet)) {
            drawTextRelative(x1b, y1b-vSpace, "| ("+nWaveRet.toFixed(2)+"\%)",
                eval("cColor"+sType), null, Text.TOP|Text.LEFT, "Arial", 10,
                sTagNameRet); // % Retracement
        }
        drawTextRelative(x1b, y1b-vSpace, y1b.toFixed(2) + " ", eval("cColor"+sType), null,
            Text.BOTTOM|Text.RIGHT, "Arial", 10, sTagNamePr); // Price
        if (!isNaN(nBars)) {
            drawTextRelative(x1b, y1b-vSpace, "| ("+nBars+" Bars)", eval("cColor"+sType), null,
                Text.BOTTOM|Text.LEFT, "Arial", 10, sTagNameBars); // Number of Bars
        }
    }
    return;
}
function confirmSwings() {
    if (x1b != x2b) {   // underdeveloped dev1 line
        if (sWaveTypeG == "% Retracement") {
            var nWave = (Math.abs(y2a-y2b) / Math.abs(y1b-y1a));
        } else {
            var nWave = (Math.abs(y2a-y2b) / y1b);
        }
        if (vLastSwing == "L" && nWave >= nRetpcnt ) {
            // Swing High
            nScntr = 0;
            vLastSwing = "H";
            doLine("con");
        } else if (vLastSwing == "H" && nWave >= nRetpcnt ) {
            // Swing Low
            nScntr = 0;
            vLastSwing = "L";
            doLine("con");
        }
    }
 
    return;
}
function checkSwings(h, l) {
    // dev1
    if (vLastSwing == "L") {         // find Swing High
        if (h >= y1b) {  // higher high, no swing
            nScntr = 0;
            x1b = 0;
            y1b = h;
            doLine("dev1");
            x2a = 0;
            y2a = h;
        }
    } else if (vLastSwing == "H") {  // find Swing Low
        if (l <= y1b) {  // Lower low, no swing
            nScntr = 0;
            x1b = 0;
            y1b = l;
            doLine("dev1");
            x2a = 0;
            y2a = l;
        }
    }
    // dev2
    if (nScntr == 0) {
        x2b = 0;
        if (vLastSwing == "H") y2b = h;
        if (vLastSwing == "L") y2b = l;
    } else {
        if (vLastSwing == "H" && h >= y2b) {
            y2b = h; x2b = 0;
        } else if (vLastSwing == "L" && l <= y2b) {
            y2b = l; x2b = 0;
        }
    }
    return;
}
To discuss this study or download a complete copy of the formula, please visit the EFS Library Discussion Board forum under the Forums link at www.esignalcentral.com or visit our EFS KnowledgeBase at www.esignalcentral.com/support/kb/efs/. The eSignal formula scripts (EFS) are also available for copying and pasting from the STOCKS & COMMODITIES website at Traders.com.
--Jason Keck
eSignal, a division of Interactive Data Corp.
800 815-8256, www.esignalcentral.com
GO BACK

AMIBROKER: TRADING TRENDLINE BREAKS

Here is a direct translation of the MetaStock code provided in Sylvain Vervoort's article, "Trading Trendline Breaks, Part 2," to AmiBroker Formula Language.

The code has been expanded to include price and zigzag charts as well as buy/sell arrows. As Vervoort notes in his article, his formula is looking into the future and therefore is provided for illustration purposes only, and not for objective, computer-based trading. For trading, the author uses discretionary evaluation of trendlines drawn manually on the chart.
 

zz0 = Zig( Close, 7 );
zz1 = Ref( zz0, -1 );
zz2 = Ref( zz0, -2 );
tr = ValueWhen( zz0 > zz1 AND zz1 < zz2, zz1 );
pk = ValueWhen( zz0 < zz1 AND zz1 > zz2, zz1 );
PU = tr + abs( tr ) * 0.05;
PD = pk - abs( pk ) * 0.05;
res = IIf( Close >= PU AND zz0 > zz1, 1,
      IIf( Close <= PD AND zz0 < zz1, -1, 0 ) );
res = ValueWhen( res != 0, res );
Buy = res == 1 AND Ref( res, -1 ) == -1;
Sell = res == -1 AND Ref( res, -1 ) == 1;
Short = Sell;
Cover = Buy;
Plot( zz0, "ZigZag", colorBlue, styleThick );
Plot( Close, "Price", colorBlack, styleBar );
PlotShapes( Buy * shapeUpArrow, colorGreen, 0, L, -60 );
PlotShapes( Sell * shapeDownArrow, colorRed, 0, H, -60 );


A sample chart is shown in Figure 3.
 


FIGURE 3: AMIBROKER, TRENDLINES. This screenshot shows a daily chart of Boeing (BA), with a 7% zigzag line, and buy (green)/sell (red) arrows generated by the AmiBroker formula listing.
--Tomasz Janeczko, AmiBroker.com
www.amibroker.com


GO BACK


WEALTH-LAB: TRADING TRENDLINE BREAKS

I'm guilty of being a fan of trendlines, but while lines drawn on historical charts may look useful -- as they do in Sylvain Vervoort's article in this issue, "Trading Trendline Breaks, Part 2" -- it's unlikely that many of them would have been drawn precisely as shown without the benefit of looking at "future data." Repetitive data-processing tasks, such as drawing trendlines, can and are accomplished more easily and quantitatively by computer programs; in other words, without the subjectivity that accompanies drawing trendlines by hand.

Therefore, instead of translating the article's MetaStock code into WealthScript, we've done something much more interesting by creating a trendline study that approaches Vervoort's subjective technique, and you can actually use it for trading in any time frame.

Since programming requirements must be precise, the main problem to resolve is when to start drawing a trendline. Waiting for peaks and troughs may be too long after a trend is already apparent. Not waiting for peak and troughs can cause you to draw a trend too early before a pullback occurs. A timely pullback may not occur, again delaying drawing a trendline.

For sure, you can't draw a trendline unless some amount of data indicates that a trend exists. I used a weighted moving average (WMA) of a user-specified period to determine the direction of prices to narrow the search for a trend. Simply, if the WMA of the specified period was monotonically increasing for four bars, we search for an upward trendline (and vice versa). The origin of the line is chosen to be the previous lowest low (highest high) for the same period that matched a market structure low (high), which is a TurnUp (TurnDown) signal in Wealth-Lab. The trendline is then defined by the bar that generates a line with the least slope between the current bar and the origin. Once a trendline is established, it is extended until crossed by a user-specified series, but then extended a few more bars to show the crossing effect. In addition, accelerated lines are drawn after price diverges a user-specified percentage from the most-recent trendline.

The cherry-picked chart of AA in Figure 4 shows just how well the study can actually work. It's quite common, however, to see the phenomenon shown on the HPQ chart inset, in which a trendline is drawn, crossed, redrawn, crossed, and so on. Note that a trendline is not attempted to be redrawn in the same direction after a crossing until a specified number of bars have passed.


FIGURE 4: WEALTH-LAB, AUTOMATIC TRENDLINE DRAWING. The trendlines study draws lines automatically without the use of future information based on specifications that can be optimized in a trading strategy. Note that the time at which a trendline is defined is indicated by a small dot on the line.
WealthScript code:
{$I ‘Trendlines'}
HideVolume;
var obj: TChartLines = TChartLines.Create;
obj.FindTrendLines( 21, #Close, #Close, #Blue, 0.01, 5, 1.5, 20.0, IsLog );
obj.DrawLines( true, true );
-- Robert Sucher
www.wealth-lab.com
GO BACK

NEUROSHELL TRADER: TRADING TRENDLINE BREAKS

The strategy described by Sylvain Vervoort in "Trading Trendline Breaks, Part 2" can be easily implemented in NeuroShell Trader by combining a few of NeuroShell Trader's 800+ indicators into a trading strategy.

To recreate the strategy, select "New Trading Strategy ..." from the Insert menu and enter the following entry and exit conditions in the appropriate locations of the Trading Strategy Wizard:

Generate a buy long MARKET order if ALL of the following are true:
 

A>B( ZigZag%Plot(High,Low,7), Lag( ZigZag%Plot(High,Low,7), 1 ) )
A>B( Close, Multiply2 ( SelectiveMovAvg ( Lag(ZigZag%Plot(High,Low,7),1),
     And2( A<B( ZigZag%Plot(High,Low,7), Lag(ZigZag%Plot(High,Low,7),1) ),
     A>B( Lag(ZigZag%Plot(High,Low,7),1), Lag(ZigZag%Plot(High,Low,7),2) ) ), 1 ), 1.05 ) )
Generate a sell short MARKET order if ALL of the following are true:
A<B( ZigZag%Plot(High,Low,7), Lag( ZigZag%Plot(High,Low,7), 1 ) )
A<B( Close, Multiply2 ( SelectiveMovAvg ( Lag(ZigZag%Plot(High,Low,7),1),
     And2( A>B( ZigZag%Plot(High,Low,7), Lag(ZigZag%Plot(High,Low,7),1) ),
     A<B( Lag(ZigZag%Plot(High,Low,7),1), Lag(ZigZag%Plot(High,Low,7),2) ) ), 1 ), 0.95 ) )


If you have NeuroShell Trader Professional, you can also choose whether the system parameters should be optimized. After backtesting the trading strategy, use the "Detailed Analysis ..." button to view the backtest and trade-by-trade statistics for the trendline breaks strategy.

Note that the strategy described here is based on the zigzag indicator, which is a custom indicator that NeuroShell Trader users can download for free from www.ward.net. Since, as the author states in the article, the zigzag indicator looks into the future, this system is not tradable into the future. In addition, historical backtest results based on the zigzag indicator should be viewed as unrealistic.

A sample chart is shown in Figure 5. For more information on NeuroShell Trader, visit www.NeuroShell.com.
 




FIGURE 5: NEUROSHELL, TRENDLINE BREAK STRATEGY

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


GO BACK


AIQ: TRADING TRENDLINE BREAKS

AIQ recently introduced a chart pattern service with the ability to recognize classic chart patterns and alert the user to emerging as well as completed formations. The patterns are stored historically back to about February 2006, and the history bank will grow as time marches forward. In addition, the completed patterns can be accessed via the EDS code, thereby allowing a backtest to be performed for the patterns.

Since all of the completed pattern signals involve a breakout from a trendline, I decided to put most of the pattern signals together into one rule and then trade all the signals for the NASDAQ 100 list of stocks. I traded only in the direction of the breakout: long for upward breaks of a trendline and short for downward breaks of a trendline. Positions were entered the next day at the open after the trendline break and were exited after being held for three days without any other stops. Three positions maximum were held with one-third of capital allocated to each. The signals were chosen based on relative strength: strongest for long positions, weakest for short positions. I deducted three cents per share for commission and slippage for a round-trip trade. The types of breakouts included are: channel, flag, pennant, triangles, wedges, double bottoms and tops, and rectangles.

In addition, I devised some simple market timing filters using the NDX. In Figures 6 and 7, I show a comparison of trading with and without market timing filters. Because of the limited history currently available, I ran the test from February 10, 2006, to July 12, 2007. The best results come from the long signals with the market timing filter, which showed a 19% annualized return with an 8% maximum drawdown. The short side also showed improved results with the market timing filter, but overall, both short side tests were net losers for the test period.

FIGURE 6: AIQ, TRADING TRENDLINE BREAKS, LONG. Here is an equity curve comparison for trading trendline breaks long related to classic chart patterns with and without a market timing filter.



FIGURE 7: AIQ, TRADING TRENDLINE BREAKS, SHORT. Here is an equity curve comparison for trading trendline breaks short related to classic chart patterns with and without a market timing filter.

The AIQ code for completed chart pattern breakouts including the code for individual chart pattern signals is shown here. This code can be downloaded from the AIQ website at www.aiqsystems.com and also from www. tradersedgesystems.com/traderstips.htm. It can also be copied and pasted from the STOCKS & COMMODITIES website at www.Traders.com.
 
! TRADING TRENDLINE BREAKS
! Using AIQ's new chart pattern service
!        to test trendline breakouts
! Coded by: Paul Denning, 4/4/2007
!      Richard Denning, 7/12/2007
! breakUP of a down Channel (no shorts)
chanDwn is [Channel Down] .
chanBOlong if chanDwn > 0 .
! breakDOWN of an up Channel  (no longs)
lenChan is ChartPatternLength([Channel Up]).
chanUp is [Channel Up].
chanBOshort if chanUp > 2 or chanUp < 0.
! Flag up and down
FLAG is [Flag] .
upFLAG if FLAG > 0 .
dwnFLAG if FLAG < 0 .
! pennant up and down
pennant is [Pennant].
upPennant if pennant > 0 .
dwnPennant if pennant < 0 .
! ascending triangle (long)
AT is [Ascending Triangle] .
ascTri if AT > 0 .
! descending triangle breakdown (short)
DT is [Descending Triangle] .
descTri if DT < 0 .
! TRIANGLE long and short
tri is [Triangle] .
triLNG if tri > 0 .
triSRT if tri < 0 .
! LONG FALLING wedges
FW is [Falling Wedge] .
fallWdg if FW > 0 .
! SHORT FALLING wedges
fallWdgSRT if FW < 0 .
! SHORT RISING wedges
RW is [Rising Wedge] .
riseWdg if RW < 0 .
! LONG RISING wedges
riseWdgLNG if RW > 0 .
! long on double bottom
doubBot is [Double Bottom] .
doubleBt if doubBot > 0 .
! short on double top
doubTop is [Double Top] .
doubleTp if doubTop < 0 .
! rectangle up and down
rect is [Rectangle] .
upRect if rect > 0 .
dwnRect if rect < 0 .
! COMBINED PATTERNS FOR TESTING TRENDLINE BREAKS
ALL_Long if chanBOlong or upFLAG or upPennant or ascTri
      or triLNG or fallWdg or riseWdgLNG or doubleBt or upRect.
LX if {position days}=3.
NDXrule1 if TickerRULE("NDX",[close] > simpleavg([close],30)).
NDX_SPX is expavg(TickerUDF("NDX",[close])
      / TickerUDF("SPX",[close]),3).
rcNDXspx is (NDX_SPX / valresult(NDX_SPX,5) - 1) * 100.
ALL_LongM4 if (chanBOlong or upFLAG or upPennant or ascTri
      or triLNG or fallWdg or riseWdgLNG or doubleBt or upRect)
      and (NDXrule1 or (NDXrule1=0 and rcNDXspx > 1)).
ALL_Short if chanBOshort or dwnFLAG or dwnPennant
      or descTri or triSRT or fallWdgSRT or riseWdg
      or doubleTp or dwnRect.
SX if {position days}=3.
ALL_ShortM4 if (chanBOshort or dwnFLAG or dwnPennant
      or descTri or triSRT or fallWdgSRT or riseWdg
      or doubleTp or dwnRect)
      and (NDXrule1=0 or (NDXrule1 and rcNDXspx < -1)).
 !AIQ RELATIVE STRENGTH (for signal ranking)
STL is 32.
Q3s is STL / 4.
Q2s is (STL - Q3s) / 3.
Q1s is (STL - Q2s - Q3s) / 2.
Q0s is STL - Q1s - Q2s - Q3s.
ROCq3s is (val([close],Q2s,(Q1s+Q0s))
      - val([open],Q3s,(Q2s+Q1s+Q0s)))
      / val([open],Q3s,(Q2s+Q1s+Q0s)) * 100.
ROCq2s is (val([close],Q1s,Q0s)
     - val([open],Q2s,(Q1s+Q0s)))
      / val([open],Q2s,(Q1s+Q0s)) * 100.
ROCq1s is (val([close],Q0s,0)
      - val([open],Q1s,Q0s))
      / val([open],Q1s,Q0s) * 100.
ROCq0s is ([close] - val([open],Q0s,0))
      / val([open],Q0s,0) * 100.
RS_AIQst is ROCq0s * 0.40 + ROCq1s * 0.20
      + ROCq2s * 0.20 + ROCq3s * 0.20.


--Richard Denning
AIQ Systems
richard.denning@earthlink.net

GO BACK


STRATASEARCH: TRADING TRENDLINE BREAKS

Trendlines can be an excellent way to evaluate historical patterns, and they can also be quite helpful in identifying future support and resistance. But they have their limitations when it comes to actual trading. In the sample MetaStock code given by Sylvain Vervoort in his article in this issue, "Trading Trendline Breaks, Part 2," it's noted that the zigzag formula contains forward-looking values and therefore cannot be used in real trading. Indeed, trendlines cannot easily be drawn from their starting point without knowledge of the ending point.

With the above in mind, we still ran a backtest on the provided system and found it to be quite effective. Using a range of parameters and run against the NASDAQ 100, the annual returns were often more than 100% with percentage profitability over 90%. However, since the system contains forward-looking values and cannot be used for real trading, the results have limited practical value. We hope the author will offer tips for getting around this issue in the third part of his series.

In the meantime, StrataSearch contains a free plug-in that can create trendlines without forward-looking references. This is done by generating the trendline only after its placement and direction have been established. StrataSearch users can obtain this, as well as a plug-in to use the article's code, from the Shared Area of the StrataSearch User Forum.

A sample Stratasearch chart is in Figure 8.

FIGURE 8: STRATASEARCH, TRADING TRENDLINE BREAKS. While the Zig() formula provides the pivot points, the buy and sell signals themselves are triggered after a percentage movement away from the pivot.
//****************************************************************
// Trading Trendline Breaks - Buy or Buy To Cover
//****************************************************************
zz0 = zig(close, 7);
zz1 = ref(zz0, -1);
zz2 = ref(zz0, -2);
tr = ValueWhen(zz0>zz1 and zz1<zz2, zz1);
pk = ValueWhen(zz0<zz1 and zz1>zz2, zz1);
PU = tr+abs(tr)*0.05;
PD = pk-abs(pk)*0.05;
res = if(value>=PU and zz0>zz1, 1,
        if(value<=PD and zz0<zz1, -1, 0));
res = if(res<>0, res, ValueWhen(res<>0, res));
TTB_Buy = if(res=1 and ref(res, -1) = -1, 1, 0);
//****************************************************************
// Trading Trendline Breaks - Sell or Sell Short
//****************************************************************
zz0 = zig(close, 7);
zz1 = ref(zz0, -1);
zz2 = ref(zz0, -2);
tr = ValueWhen(zz0>zz1 and zz1<zz2, zz1);
pk = ValueWhen(zz0<zz1 and zz1>zz2, zz1);
PU = tr+abs(tr)*0.05;
PD = pk-abs(pk)*0.05;
res = if(value>=PU and zz0>zz1, 1,
        if(value<=PD and zz0<zz1, -1, 0));
res = if(res<>0, res, ValueWhen(res<>0, res));
TTB_Sell = if(res=-1 and ref(res, -1) = 1, 1, 0);
--Pete Rast
Avarin Systems, Inc.
www.StrataSearch.com


GO BACK


NINJATRADER: TRADING TRENDLINE BREAKS

We've developed an automatic trendline detection indicator based on Sylvain Vervoort's article in this issue, "Trading Trendline Breaks, Part 2." This indicator will detect and plot the prevailing trend, and will generate a visual and audio alert on a break of a trendline. (See Figure 9.) This indicator can be embedded in a NinjaScript strategy to create an automated trading system.

FIGURE 9: NINJATRADER, TRENDLINE BREAKS. This sample NinjaTrader chart shows the AutoTrendLine indicator applied to a one-minute S&P emini data series.
We created this indicator based on finding swing low or high points to generate a trendline. The sample code shown below demonstrates how the swing indicator can be used to scan for pivot points that will anchor a trendline.
 
NinjaTrader code:
int startBarsAgo     = 0;
int endBarsAgo     = 0;
int occurence     = 1;
// Scan from right to left for two swing low pivots where the more recent
// pivot is higher than the least recent pivot
while (Low[endBarsAgo] <= Low[startBarsAgo])
{
    startBarsAgo     = Swing(10).SwingLowBar(0, occurence + 1, CurrentBar);
    endBarsAgo     = Swing(10).SwingLowBar(0, occurence, CurrentBar);
    // If no trend was found exit from the loop
    if (startBarsAgo < 0 || endBarsAgo < 0)
        break;
    occurence++;
}
// Draw the up trend line if one was found
if (startBarsAgo > 0)
    DrawRay("T", startBarsAgo, Low[startBarsAgo], endBarsAgo, Low[endBarsAgo],
     Color.Blue);


This indicator can be downloaded from www.ninjatrader.com/SC/AutoTrendLine.zip. Once downloaded, from within the NinjaTrader Control Center window, select the menu File > Utilities > Import NinjaScript and select the downloaded file.

You can review the indicator's source code by selecting the menu Tools > Edit NinjaScript > Indicator from within the NinjaTrader Control Center window and selecting the indicator AutoTrendLine.

NinjaScript indicators are compiled DLLs that run native, not interpreted, which provides you with the highest performance possible.

--Raymond Deux, NinjaTrader
www.ninjatrader.com
GO BACK

TRADECISION: TRADING TRENDLINE BREAKS

In his article "Trading Trendline Breaks, Part 2," Sylvain Vervoort demonstrates techniques on how to apply breaks in trendlines to your market analysis and trading.

Tradecision's Strategy Builder enables you to recreate the trading trendline breaks system based on the concept explained by Vervoort in his article. A sample chart is in Figure 10.

FIGURE 10: TRADECISION, TRENDLINE BREAKS. Here, Sylvain Vervoort's trendline breaks strategy is applied to a Google daily chart along with the auto-trends study.
Entry Long
var
 
  zz0:=Zig(0.001, PERCENT);
  zz1:=zz0\1\;
  zz2:=zz0\2\;
  tr:=ValueWhen( zz0 >zz1 and zz1 < zz2, zz1);
  pk:=ValueWhen( zz0 < zz1 and zz1 >zz2, zz1);
  PU:=tr + Abs(tr) * 0.0005;
  PD:=pk - Abs(pk) * 0.0005;
  res:=iff(Close >= PU and zz0 >zz1, 1, iff(Close <= PD and zz0 < zz1, -1, 0));
  res_1:=iff(res <> 0, res, NthValueWhen(1, res <> 0, res));
end_var
println(0,res_1);
return  res_1 = 1 and res_1\1\ = -1;
Entry Short
var
  zz0:=Zig(0.001, PERCENT);
  zz1:=zz0\1\;
  zz2:=zz0\2\;
  tr:=ValueWhen( zz0 >zz1 and zz1 < zz2, zz1);
  pk:=ValueWhen( zz0 < zz1 and zz1 >zz2, zz1);
  PU:=tr + Abs(tr) * 0.0005;
  PD:=pk - Abs(pk) * 0.0005;
  res:=iff(Close >= PU and zz0 >zz1, 1, iff(Close <= PD and zz0 < zz1, -1, 0));
  res_1:=iff(res <> 0, res, ValueWhen( res <> 0, res));
end_var
return res_1 = -1 and res_1\1\ = 1;


To implement this strategy in Tradecision, visit the area "Traders' Tips from Tasc magazine" at https://tradecision.com/support/tasc_tips/tasc_traders_tips.htm or copy the code from the STOCKS & COMMODITIES website at www.Traders.com.

--Alex Grechanowski, Alyuda Research, Inc.
sales@tradecision.com, 510 931-7808
www.tradecision.com


GO BACK


TRADE NAVIGATOR: TRADING TRENDLINE BREAKS

Trendlines can be a very valuable trading tool. You can use the power of Trade Navigator to automatically draw trendlines based on supplied thresholds.

Sylvain Vervoort's article in this issue, "Trading Trendline Breaks, Part 2," discusses how to trade trendline breakouts. Here, we'll show how to add these functions to your Trade Navigator software. Please note that because the zigzag function relies on future data, it can't be tested or traded in real time.

You can either follow the step-by-step instructions we'll provide to recreate all the functions yourself, or you can simply download the file "SC0907," which will give you access to all of the functions described here.

We'll be creating 11 functions that build on one another. Our final result will be two highlighted bars that mark the buy and sell points.

The steps are the same for creating each function, so we'll walk you through the first and include the syntax for the rest. Just replace the syntax and the name to set up the other functions. Because they build on each other, it is important to enter them in the order given.

To create new functions, first click on the Edit menu and select Functions. You will be taken to the Trader's Toolbox feature of the Functions section. Click New. Type the following into the New Function window:

ZigZag (7).Bars Since (ZigZag (7) > -1 , 1 , 0)
The syntax for our first function is "zz0." Click Save. You will be prompted for a name. Type "zz0" and click OK.

To return to the function manager, just click the toolbox button.

Click New again to create a new function. Repeat the above steps for the following functions, making sure to give the exact name specified.

zz1:
ZigZag (7).Bars Since (ZigZag (7) > -1 , 2 , 0)
zz2:
ZigZag (7).Bars Since (ZigZag (7) > -1 , 3 , 0)
tr:
zz1.Bars Since (zz0 > zz1 And zz1 < zz2 , 1 , 0)
pk:
zz1.Bars Since (zz0 < zz1 And zz1 > zz2 , 1 , 0)
PU:
tr + Abs (tr) * 0.05
PD:
pk - Abs (pk) * 0.05
res:
IFF (Close >= PU And zz0 > zz1 , 1 , IFF (Close <= PD And zz0 < zz1 , -1 , 0))
res1:
IFF (res <> 0 , res , res.Bars Since (res <> 0 , 1 , 0))
Trading Trendline Breaks Buy:
res1 = -1 And res1.1 = 1
Trading Trendline Breaks Sell:
res1 = 1 And res1.1 = -1
The last two functions are really at the heart of Vervoort's article. They are actually highlighted bars that you can apply to your chart. They should indicate blue buys and red sells (Figure 11). To apply these highlight bars to your chart, follow these steps: Click on the Charts menu and then select Add to Chart. Click Highlight Bars.

FIGURE 11: TRADE NAVIGATOR, TRENDLINE BREAK STRATEGY. Highlighted bars indicate buys (blue) and sells (red). Please note that because the zigzag function relies on future data, it can't be tested or traded in real time.
The list will be sorted alphabetically, so scroll down toward the bottom and locate the two highlighted bars we created ("Trading Trendline Breaks Buy" and "Trading Trendline Breaks Sell"). You can select them both by holding down the Ctrl key on your keyboard and clicking on their names. After they are both selected, just click the Add button to add them to your charts.

For your convenience, we have created a file that you can download through Trade Navigator that will add the two highlight bars and their components to Trade Navigator for you. Download this free file named "SC0907" from within Trade Navigator and follow the upgrade prompts.

--Michael Herman
Genesis Financial Technologies
https://www.GenesisFT.com
GO BACK

VT TRADER: TRONGONE MOVING AVERAGES

For this month's Traders' Tip, we will revisit the May 2007 issue of STOCKS & COMMODITIES and the article by Anthony Trongone, "Moving Averages: Long On Talk, Short On Action." In the article, Trongone presents a simple 34-day simple moving average system with a few additional conditions (such as volume) used to filter trades.

Trongone advocates trading with a "long" market bias and exiting the market at the end of the day. Since the forex marketplace differs somewhat from the stock market, we've taken the opportunity to modify Trongone's original system rules slightly. In the stock market, volume typically represents the number of shares traded over a given period; however, there is no central exchange in the forex marketplace to provide such information. Therefore, forex volume is often measured as the number of price changes (that is, ticks) in a given period at a given broker. Although they are very different, both methods of calculating volume provide a measurement of market participation and interest in the given trading instrument.

We'll be using the tick-volume method to provide the volume element for this trading system. We've further modified the system's rules to allow the user to choose his or her own market bias: long trades, short trades, or long & short trades. See Figure 12 for a sample charts.

FIGURE 12: VT TRADER, TRONGONE MOVING AVERAGE FOR FOREX. Here is a GBP/JPY daily candlestick chart with the TMA trading system displayed. The Inspect Window displays some basic profit/loss statistics produced by the TMA system over the last 500 days.
We'll be offering our modified Trongone MA trading system for download in our user forums. The VT Trader code and instructions for creating it are as follows (the input variables are parameterized to allow customization):
 
1. Navigator Window>Tools>Trading Systems Builder>[New] button
2. In the Indicator Bookmark, type the following text for each field:
Name: TASC - 05/2007 - "Moving Averages: Long on Talk, Short on Action"
Short Name: vt_MALOTSOA
Label Mask: TASC - 05/2007 - "Moving Averages: Long on Talk, Short on Action" (MA:
      %pr1%,%tp1%,%mtp1% | Volume > %VolumeFilter% | Market Bias: %MarketBias:ls%)
3. In the Input Bookmark, create the following variables:
    [New] button... Name: pr1 , Display Name: MA Price , Type: price , Default: Close
    [New] button... Name: tp1 , Display Name: MA Periods , Type: integer , Default: 34
    [New] button... Name: mTp1 , Display Name: MA Type , Type: MA Type , Default: Simple
    [New] button... Name: VolumeFilter , Display Name: Volume Filter , Type: integer , Default: 500
[New] button... Name: MarketBias , Display Name: Choose Market Bias , Type: Enumeration ,
      Click [...] button, [New] button, create: Long_Trades_Only, Short_Trades_Only, and
      Long_and_Short_Trades selections; Click [OK] button; Default: Long_And_Short_Trades
4. In the Formula Bookmark, copy and paste the following formula:
{Provided By: Capital Market Services, LLC & Visual Trading Systems, LLC (c) Copyright 2007}
{Description: May 2007 Issue - Moving Averages: Long on Talk, Short on Action by Anthony Trongone}
{vt_MALOTSOA Version 1.0}
{Moving Average}
MA:= mov(pr1,tp1,mTp1);
{Volume}
_Volume:= Volume;
{Define Trade Entry Criteria}
LongEntryCond1:= O > MA;
LongEntryCond2:= ref(V,-1) > VolumeFilter;
LongEntryCond3:= (O - ref(C,-1)) < 0;
ShortEntryCond1:= O < MA;
ShortEntryCond2:= ref(V,-1) > VolumeFilter;
ShortEntryCond3:= (O - ref(C,-1)) > 0;
/***************************************
Provided By :
    eSignal (Copyright © eSignal), a division of Interactive Data
    Corporation. 2007. 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:  Trading Trendline Breaks, Part 2
              by Sylvain Vervoort
Version 1.0  7/9/2007
Notes:
* Study is a modified version of eSignal's RealTimeSwings.efs,
    which incorporates Vervoort's strategy signals based on
    ZigZag indicator.
* Study provides historical analysis only, see author's notes
    in the article.
*   Description of Swing Labels:
        At Swing Highs -  Points (% Retracement)
                          Price (Number of Bars)
 
        At Swing Lows -   Price (Number of Bars)
                          Points (% Retracement)
Formula Parameters:                 Default:
    * Swing: # of Bars              5
        This is the minimum number of bars required to define a
        swing point.  This number is for both sides of the swing
        point (i.e. 5 bars on the left and right of the swing bar).
    * Swing: Wave Type              % Retracement
        (% Retracement, % Change in Price)
    * Swing: Wave Percentage        30
        The number 5 will be treated as 5.0%.  The number 0.05 will
        be treated as 0.0005%.
    * Swing High Price Source       High
    * Swing Low Price Source        Low
    * Line Thickness                2
    * Confirmed Swing Line Color    Blue
    * Developing Swing Line Color   Red
    * Display Swing Labels          True
    * Display % Retracement Label   True
    * Number of Historical Labels   100
*****************************************************************/
function preMain() {
    setPriceStudy(true);
    setStudyTitle("Trend Line Breaks ");
    //setShowCursorLabel(false);
    setShowTitleParameters(false);
 
    //TASC mod
    setCursorLabelName("PD", 0);
    setCursorLabelName("PU", 1);
    setDefaultBarFgColor(Color.red, 0);     //PD
    setDefaultBarFgColor(Color.green, 1);   //PU
    setDefaultBarThickness(2, 0);
    setDefaultBarThickness(2, 1);
    setPlotType(PLOTTYPE_FLATLINES, 0);
    setPlotType(PLOTTYPE_FLATLINES, 1);
 
 
    var fp1 = new FunctionParameter("nNum", FunctionParameter.NUMBER);
    fp1.setName("Swing: # of Bars");
    fp1.setLowerLimit(1);
    fp1.setDefault(5);
 
    var fp2a = new FunctionParameter("sWaveType", FunctionParameter.STRING);
    fp2a.setName("Swing: Wave Type");
    fp2a.addOption("% Retracement");
    fp2a.addOption("% Change in Price");
    fp2a.setDefault("% Change in Price");
 
    var fp2 = new FunctionParameter("nRet", FunctionParameter.NUMBER);
    fp2.setName("Swing: Wave Percentage");
    fp2.setLowerLimit(0);
    fp2.setDefault(7);
    var fp3 = new FunctionParameter("sHighSource", FunctionParameter.STRING);
    fp3.setName("Swing High Price Source");
    fp3.addOption("Open");
    fp3.addOption("High");
    fp3.addOption("Low");
    fp3.addOption("Close");
    fp3.setDefault("Close");
    var fp4 = new FunctionParameter("sLowSource", FunctionParameter.STRING);
    fp4.setName("Swing Low Price Source");
    fp4.addOption("Open");
    fp4.addOption("High");
    fp4.addOption("Low");
    fp4.addOption("Close");
    fp4.setDefault("Close");
    var fp5 = new FunctionParameter("nThickness", FunctionParameter.NUMBER);
    fp5.setName("Line Thickness");
    fp5.setLowerLimit(1);
    fp5.setDefault(2);
    var fp6 = new FunctionParameter("cColor1", FunctionParameter.COLOR);
    fp6.setName("Confirmed Swing Line Color");
    fp6.setDefault(Color.blue);
    var fp7 = new FunctionParameter("cColor2", FunctionParameter.COLOR);
    fp7.setName("Developing Swing Line Color");
    fp7.setDefault(Color.red);
    var fp8 = new FunctionParameter("bSwingLabels", FunctionParameter.STRING);
    fp8.setName("Display Swing Labels");
    fp8.addOption("True");
    fp8.addOption("False");
    fp8.setDefault("False");
    var fp9 = new FunctionParameter("bRetLabel", FunctionParameter.STRING);
    fp9.setName("Display \% Retracement Label");
    fp9.addOption("True");
    fp9.addOption("False");
    fp9.setDefault("False");
    var fp10 = new FunctionParameter("nNumLabels", FunctionParameter.NUMBER);
    fp10.setName("Number of Historical Labels");
    fp10.setLowerLimit(1);
    fp10.setDefault(100);
}
var bEdit = true;       // tracks change of user inputs
var cntr = 0;           // image counter for swing lines
var bInit = false;      // initialization routine completion
var nNumBars = null;    // number of bars for defining swings
var sWaveTypeG = null;  // wave type for confirming swings
var nRetpcnt = null;    // percent retracement for defining swings
var nThicknessG = null; // line thickness
var cColorcon = null;   // confirmed swing color
var cColordev1 = null;  // developing swing color
var sHSource = null;    // price source for high swings
var sLSource = null;    // price source for low swings
var x1a = null;         // x-coordinate for point a of developing line 1
var x1b = null;         // x-coordinate for point b of developing line 1
var x2a = null;         // x-coordinate for point a of developing line 2
var x2b = null;         // x-coordinate for point b of developing line 2
var y1a = null;         // y-coordinate for point a of developing line 1
var y1b = null;         // y-coordinate for point b of developing line 1
var y2a = null;         // y-coordinate for point a of developing line 2
var y2b = null;         // y-coordinate for point b of developing line 2
var vLastSwing = null;  // tracking swing type of last confirmed swing (H or L)
var nScntr = 0;         // bar counter for swing confirmation
var nLcntr = 0;         // label counter for swing labels
var aSwingsIndex = new Array(4); // tracks current swings indexes for last 4 swings
var aSwingsPrice = new Array(4); // tracks current swing prices for last 4 swings
var nNumLabelsG = null; // max number of swing labels
var bSwingLabelsG = null;  // controls swing labels display
var vSpace = null;      // spacer for Labels
//TASC mod
var bBT = true;     // back test flag
var vPosition = 0;  // 0=flat, 1=long, -1=short
var nPD = null;
var nPU = null;
function main(nNum, sWaveType, nRet, sHighSource, sLowSource,
              nThickness, cColor1, cColor2, bSwingLabels, bRetLabel, nNumLabels) {
    var nState = getBarState();
    var nIndex = getCurrentBarIndex();
    var h = getValue(sHighSource);
    var l = getValue(sLowSource);
    var c = close();
    var i = 0;
    // record keeping
    if (nState == BARSTATE_NEWBAR) {
        if (cntr > 100) cntr = 0;
        if (x1a != null) x1a -= 1;
        if (x1b != null) x1b -= 1;
        if (x2a != null) x2a -= 1;
        if (x2b != null) x2b -= 1;
        i = 0;
        for (i = 0; i < 3; ++i) {
            if (aSwingsIndex[i] != null) aSwingsIndex[i] -= 1;
        }
    }
    //Initialization
    if (bEdit == true) {
        if (nNumBars == null) nNumBars = nNum;
        if (sWaveTypeG == null) sWaveTypeG = sWaveType;
        if (nRetpcnt == null) nRetpcnt = nRet/100;
        if (nThicknessG == null) nThicknessG = nThickness;
        if (cColorcon == null) cColorcon = cColor1;
        if (cColordev1 == null) cColordev1 = cColor2;
        if (sHSource == null) sHSource = sHighSource;
        if (sLSource == null) sLSource = sLowSource;
        if (x1a == null) x1a = 0;
        if (y1a == null) y1a = c;
        if (nNumLabelsG == null) nNumLabelsG = nNumLabels;
        if (bSwingLabelsG == null) bSwingLabelsG = bSwingLabels;
        nLcntr = nNumLabels;
        // Initialize vSpace
        var OM = (close() * 0.005); //offset multiplier
        var sInterval = getInterval();
        if (sInterval == "D") OM = OM*3;
        if (sInterval == "W") OM = OM*20;
        if (sInterval == "M") OM = OM*30;
        var TimeFrame = parseInt(sInterval);
        if (TimeFrame >= 1 && TimeFrame <= 5) {
            OM = OM*(TimeFrame/15);
        } else if (TimeFrame > 5 && TimeFrame <= 15) {
            OM = OM*(TimeFrame/10);
        }else if (TimeFrame > 15) {
            OM = OM*(TimeFrame/5);
        }
        if (!isNaN(TimeFrame)) OM = (OM/TimeFrame)*3;
        vSpace = OM;
 
        //TASC mod
        setDefaultBarThickness(nThickness, 0);
        setDefaultBarThickness(nThickness, 1);
 
        bEdit = false;
    }
    if (bInit == false) {
        bInit = Init(h,l,c);
    }
    // Swings
    if (nState == BARSTATE_NEWBAR) {
        nScntr += 1;
        // confirmed Swings
        if (nScntr > nNumBars) {
            confirmSwings();
            if (bInit == true) {
                doLine("dev1");
                doLine("dev2");
            }
        }
    }
 
    checkSwings(h, l);
    if (bInit == true) {
        doLine("dev1");
        doLine("dev2");
    }
    // % Retracement Label
    var nWaveRet = (Math.abs(y2a-y2b) / Math.abs(y1b-y1a))*100;
    if (x1b == x2b) nWaveRet = 0.0;
    if (bRetLabel == "True") {
        var sWaveRetText = " \%Retraced: " + nWaveRet.toFixed(2) + " ";
        drawTextRelative(2, y2b, sWaveRetText, cColordev1, null,
            Text.BOLD|Text.LEFT|Text.VCENTER|Text.FRAME, "Arial", 10, "Ret");
    }
    //TASC mod - back testing conditions
    if (nIndex == 0) bBT = false;
    if (nState == BARSTATE_NEWBAR) {
        if (vPosition != 1 && nPU != null && close(-1) >= nPU && vLastSwing == "L") { // long signal
            if (bBT == true) Strategy.doLong("long", Strategy.MARKET, Strategy.THISBAR);
            vPosition = 1;
            drawShape(Shape.UPARROW, BelowBar1, Color.green);
            drawText("Long", BelowBar2, Color.green, Text.FRAME|Text.BOLD, rawtime(0));
        } else if (vPosition != -1 && nPD != null &&
               close(-1) <= nPD && vLastSwing == "H") { // short signal
            if (bBT == true) Strategy.doShort("short", Strategy.MARKET, Strategy.THISBAR);
            vPosition = -1;
            drawShape(Shape.DOWNARROW, AboveBar1, Color.red);
            drawText("Short", AboveBar2, Color.red, Text.FRAME|Text.BOLD, rawtime(0));
        }
    }
 
    return new Array(nPD, nPU);
}
/******  Functions *****/
/***********************/
function Init(h,l,c) {
    if (close(-(nNumBars*2)) == null) {
        return false;
    } else {
        // Find initial line.
        // The initial line will be the first high or low swing,
        // which has the greater difference of the swing point to
        // the close of the first bar.
        var Index = getCurrentBarIndex()
        var hIndex = Index;
        var lIndex = Index;
        var j = nNumBars*2;
        var aHigh = getValue(sHSource, 0, -j);
        var aLow = getValue(sLSource, 0, -j);
        var vHH = aHigh[0];
        var vLL = aLow[0];
        var tempIndex = Index;
        var i = 0;
        for (i = 0; i < j; ++i) {
            if (aHigh[i] > vHH) {
                vHH = aHigh[i];
                hIndex = tempIndex;
            }
            if (aLow[i] < vLL) {
                vLL = aLow[i];
                lIndex = tempIndex;
            }
            tempIndex -= 1;
        }
        if (vHH - y1a > y1a - vLL) {
            vLastSwing = "L";
            x1b = hIndex - Index;
            y1b = vHH;
            doLine("dev1");
            x2a = x1b;
            y2a = vHH;
            x2b = 0;
            y2b = c;
            doLine("dev2");
        } else {
            vLastSwing = "H";
            x1b = lIndex - Index;
            y1b = vLL;
            doLine("dev1");
            x2a = x1b;
            y2a = vLL;
            x2b = 0;
            y2b = c;
            doLine("dev2");
        }
    }
 
    if (vLastSwing != null) {
        return true;
    } else {
        return false;
    }
}
function doLine(sType) {
    //confirmed
    if (sType == "con") {
        cntr += 1;
        drawLineRelative(x1a, y1a, x1b, y1b, PS_SOLID,
            nThicknessG, cColorcon, sType+cntr);
        //Swing Labels
        if (bSwingLabelsG == "True") doSwingLabels(sType);
        x1a = x2a;
        y1a = y2a;
        x1b = x2b;
        y1b = y2b;
        x2a = x1b;
        y2a = y1b;
        aSwingsIndex.pop();
        aSwingsIndex.unshift(x1b);
        aSwingsPrice.pop();
        aSwingsPrice.unshift(y1b);
        if (vLastSwing == "H") y2b = getValue(sHSource);
        if (vLastSwing == "L") y2b = getValue(sLSource);
        //TASC mod
        if (aSwingsPrice[0] != null && vLastSwing != null) {
            if (vLastSwing == "H") {
                nPD = aSwingsPrice[1] - (aSwingsPrice[1]*0.05);
            } else if (vLastSwing == "L") {
                nPU = aSwingsPrice[1] + (aSwingsPrice[1]*0.05);
            }
 
        }
    }
    // dev1
    if (sType == "dev1") {
        drawLineRelative(x1a, y1a, x1b, y1b, PS_SOLID,
            nThicknessG, cColordev1, sType);
        aSwingsIndex[0] = x1b;
        aSwingsPrice[0] = y1b;
        //Swing Labels
        if (bSwingLabelsG == "True") doSwingLabels(sType);
    }
 
    // dev2
    if (sType == "dev2") {
        if (x2a != 0 && x2a != x2b) {
            if ( (vLastSwing == "H" && sHSource == "Close") ||
               (vLastSwing == "L" && sLSource == "Close") ) {
                x2b = 0;
                y2b = close();
            }
            drawLineRelative(x2a, y2a, x2b, y2b, PS_SOLID,
                nThicknessG, cColordev1, sType);
        } else {
            removeLine(sType);
        }
    }
 
    return;
}
function doSwingLabels(sType) {
    var sTagNamePts = "SwingPtsDev";
    var sTagNameRet = "SwingRetDev";
    var sTagNamePr = "SwingPrDev";
    var sTagNameBars = "SwingBarsDev";
    var nWaveRet = ((Math.abs(aSwingsPrice[1]-aSwingsPrice[0]) /
                   Math.abs(aSwingsPrice[1]-aSwingsPrice[2])) * 100);
    var nBars = (aSwingsIndex[0] - aSwingsIndex[1]);
 
    if (sType == "con") {
        //nWaveRet = (Math.abs(y2a-y2b) / Math.abs(y1b-y1a));
        nLcntr += 1;
        if (nLcntr > nNumLabelsG) nLcntr = 1;
        sTagNamePts = "SwingPts"+sType+nLcntr;
        sTagNameRet = "SwingRet"+sType+nLcntr;
        sTagNamePr = "SwingPr"+sType+nLcntr;
        sTagNameBars = "SwingBars"+sType+nLcntr;
    }
 
    var pts = (y1b-y1a).toFixed(2);
    if (y1a < y1b) { // swing high
        drawTextRelative(x1b, y1b+vSpace, pts + " ", eval("cColor"+sType), null,
            Text.BOTTOM|Text.RIGHT, "Arial", 10, sTagNamePts); // Points
        if (!isNaN(nWaveRet)) {
            drawTextRelative(x1b, y1b+vSpace, "| ("+nWaveRet.toFixed(2)+"\%)",
                eval("cColor"+sType), null, Text.BOTTOM|Text.LEFT, "Arial", 10,
                sTagNameRet); // % Retracement
        }
        drawTextRelative(x1b, y1b+vSpace, y1b.toFixed(2) + " ", eval("cColor"+sType), null,
            Text.TOP|Text.RIGHT, "Arial", 10, sTagNamePr); // Price
        if (!isNaN(nBars)) {
            drawTextRelative(x1b, y1b+vSpace, "| ("+nBars+" Bars)", eval("cColor"+sType), null,
                Text.TOP|Text.LEFT, "Arial", 10, sTagNameBars); // Number of Bars
        }
    } else { // swing low
        drawTextRelative(x1b, y1b-vSpace, pts + " ", eval("cColor"+sType), null,
            Text.TOP|Text.RIGHT, "Arial", 10, sTagNamePts); // Points
        if (!isNaN(nWaveRet)) {
            drawTextRelative(x1b, y1b-vSpace, "| ("+nWaveRet.toFixed(2)+"\%)",
                eval("cColor"+sType), null, Text.TOP|Text.LEFT, "Arial", 10,
                sTagNameRet); // % Retracement
        }
        drawTextRelative(x1b, y1b-vSpace, y1b.toFixed(2) + " ", eval("cColor"+sType), null,
            Text.BOTTOM|Text.RIGHT, "Arial", 10, sTagNamePr); // Price
        if (!isNaN(nBars)) {
            drawTextRelative(x1b, y1b-vSpace, "| ("+nBars+" Bars)", eval("cColor"+sType), null,
                Text.BOTTOM|Text.LEFT, "Arial", 10, sTagNameBars); // Number of Bars
        }
    }
    return;
}
function confirmSwings() {
    if (x1b != x2b) {   // underdeveloped dev1 line
        if (sWaveTypeG == "% Retracement") {
            var nWave = (Math.abs(y2a-y2b) / Math.abs(y1b-y1a));
        } else {
            var nWave = (Math.abs(y2a-y2b) / y1b);
        }
        if (vLastSwing == "L" && nWave >= nRetpcnt ) {
            // Swing High
            nScntr = 0;
            vLastSwing = "H";
            doLine("con");
        } else if (vLastSwing == "H" && nWave >= nRetpcnt ) {
            // Swing Low
            nScntr = 0;
            vLastSwing = "L";
            doLine("con");
        }
    }
 
    return;
}
function checkSwings(h, l) {
    // dev1
    if (vLastSwing == "L") {         // find Swing High
        if (h >= y1b) {  // higher high, no swing
            nScntr = 0;
            x1b = 0;
            y1b = h;
            doLine("dev1");
            x2a = 0;
            y2a = h;
        }
    } else if (vLastSwing == "H") {  // find Swing Low
        if (l <= y1b) {  // Lower low, no swing
            nScntr = 0;
            x1b = 0;
            y1b = l;
            doLine("dev1");
            x2a = 0;
            y2a = l;
        }
    }
    // dev2
    if (nScntr == 0) {
        x2b = 0;
        if (vLastSwing == "H") y2b = h;
        if (vLastSwing == "L") y2b = l;
    } else {
        if (vLastSwing == "H" && h >= y2b) {
            y2b = h; x2b = 0;
        } else if (vLastSwing == "L" && l <= y2b) {
            y2b = l; x2b = 0;
        }
    }
    return;
}
6. Click the "Save" icon to finish building this trading system. To attach this trading system
   to a chart, right-click with the mouse within the chart window, and select Add Trading
   System -> TASC - 05/2007 - Moving Averages: Long on Talk, Short on Action from the list.
   Once they are attached to the chart, the parameters can be customized by right-clicking over
   the displayed trading system label and selecting "Edit trading systems properties."
To learn more about VT Trader, visit www.cmsfx.com.
--Chris Skidmore
Visual Trading Systems, LLC (courtesy of CMS Forex)
(866) 51-CMSFX, trading@cmsfx.com
www.cmsfx.com
GO BACK

Return to September 2007 Contents

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