2006 Nov: Building Automatic Trendlines

ICE Data Services -

AutomaticTrendlines.efs  

EFSLibrary - Discussion Board  

File Name: AutomaticTrendlines.efs


Description:
This study are based on the November 2006 article, Building Automatic Trendlines, by Giorgos E. Siligardos, PhD.

 

Formula Parameters:

  • Swing: # of Bars: 0
  • Swing: Wave Type: % Change in Price [% Retracement, % Change in Price]
  • Swing: Wave Percentage: 20
  • Line Thickness: 2
  • Confirmed Swing Line Color: Blue
  • Developing Swing Line Color: Red
  • Display Swing Labels: False
  • Display % Retracement Label: False
  • Number of Historical Labels: 100
  • Number of Historical Trend Lines: 10
  • Display Trend Lines: True
  • Trend Line Thickness: 2
  • Trend Line Color: Maroon

Notes:
The study contains several formula parameters that may be configured through the Edit Studies option in the Advanced Chart. There are several parameters that allow for the customization of the SI indicator that the trend lines are based on, which are the first three parameters. The defaults for these parameters set the SI indicator to the calculation described in the article with one exception. The percent change or percent retracement is based on the high or low of the bar for peaks and troughs, respectively. There are also parameters to control the formatting for the SI lines and the automatic trend lines as well as the colors for the most current peak or trough that is developing, but not yet confirmed. There are also two parameters that enable labels for the swings and a label for the current percent retracement value, which are both off by default. There are two parameters that can be used to limit the number of drawn swing lines and trend lines. The parameter for the trend lines sets the limit of 10 by default, which means 10 trend lines based on peaks and 10 additional trend lines based on the troughs. Lastly, there is a parameter for turning off the display of the trend lines. The related article is copyrighted material. If you are not a subscriber of Stocks & Commodities, please visit www.traders.com.

 

Download File:
AutomaticTrendlines.efs



EFS Code:

/***************************************
Provided By : eSignal (c) Copyright 2006
Description:  Building Automatic Trendlines 
              by Giorgos E. Siligardos, PhD

Version 1.0  08/31/2006

Notes:
* Nov 2006 Issue of Stocks and Commodities Magazine
* SI indicator is modified from EFS Library formula: 
  RealTimeSwings.efs (http://kb.esignalcentral.com/article.asp?article=1450&p=4)


Formula Parameters:                     Default:
    * Swing: # of Bars                  0
        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                  % Change in Price
        (% Retracement, % Change in Price)
    * Swing: Wave Percentage            20
        The number 5 will be treated as 5.0%.  The number 0.05 will 
        be treated as 0.0005%.
    * Line Thickness                    2
    * Confirmed Swing Line Color        Blue
    * Developing Swing Line Color       Red
    * Display Swing Labels              False
    * Display % Retracement Label       False
    * Number of Historical Labels       100
    * Number of Historical Trend Lines  10
    * Display Trend Lines               True
    * Trend Line Thickness              2
    * Trend Line Color                  Maroon

Notes:

1.1 3/24/2004
* Added labels to display point value, % retracement, number of bars
  and price level of swings.  The number of historical labels is set 
  to 100 for performance reasons.  Increase this number to view more
  historical labels.
* Added labels to display current swing's % retracement.

    Description of Swing Labels:
    At Swing Highs -  Points (% Retracement)
                      Price (Number of Bars)

    At Swing Lows -   Price (Number of Bars)
                      Points (% Retracement)


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

function preMain() {
    setPriceStudy(true);
    setStudyTitle("Automatic Trendlines ");
    setShowCursorLabel(false);
    setShowTitleParameters(false);
    
    var fp1 = new FunctionParameter("nNum", FunctionParameter.NUMBER);
    fp1.setName("Swing: # of Bars");
    fp1.setLowerLimit(0);
    fp1.setDefault(0);
    
    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(20);

    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 fp11 = new FunctionParameter("nNumATL", FunctionParameter.NUMBER);
    fp11.setName("Number of Historical Trend Lines");
    fp11.setLowerLimit(1);
    fp11.setDefault(10);    

    var fp12 = new FunctionParameter("bTrendLines", FunctionParameter.STRING);
    fp12.setName("Display Trend Lines");
    fp12.addOption("True");
    fp12.addOption("False");
    fp12.setDefault("True");    

    var fp13 = new FunctionParameter("nTLThickness", FunctionParameter.NUMBER);
    fp13.setName("Trend Line Thickness");
    fp13.setLowerLimit(1);
    fp13.setDefault(2);

    var fp14 = new FunctionParameter("cTLColor", FunctionParameter.COLOR);
    fp14.setName("Trend Line Color");
    fp14.setDefault(Color.maroon);
}

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
var nScntr = 0;         // bar counter for swing confirmation
var nLcntr = 0;         // label counter for swing labels
var aSwingsIndex = new Array(6); // tracks current swings indexes for last 5 swings
var aSwingsPrice = new Array(6); // tracks current swing prices for last 5 swings
var nNumLabelsG = null; // max number of swing labels
var bSwingLabelsG = null;  // controls swing labels display
var vSpace = null;      // spacer for Labels

// Automatic Trendline variables
var nTcntr = 0;         // counter for trendline TagIDs.
var bDrawn = false;     // flag for tracking the most recently drawn ATL
var aSwingsType = new Array(6);  // 1 =  peak, -1 = trough
var nNumATLG = null;       // global var for ATL limit.
var bTrendLinesG = null;   // global var for Trend Line display
var nTLThicknessG = null;  // global var for Trend Line thickness
var cTLColorG = null;      // global var for Trend Line color

function main(nNum, sWaveType, nRet, nThickness, cColor1, cColor2, 
                bSwingLabels, bRetLabel, nNumLabels, 
                nNumATL, bTrendLines, nTLThickness, cTLColor) {
    var nState = getBarState();
    var nIndex = getCurrentBarIndex();
    var h = high(0);
    var l = low(0);
    var c = close(0);
    var i = 0;

    // record keeping
    if (nState == BARSTATE_NEWBAR) {
        if (cntr > 500) cntr = 0;  // limits number of swing lines to 500.
        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 < 6; ++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 = "High";
        if (sLSource == null) sLSource = "Low";
        if (x1a == null) x1a = 0;
        if (y1a == null) y1a = c;
        if (nNumLabelsG == null) nNumLabelsG = nNumLabels;
        if (bSwingLabelsG == null) bSwingLabelsG = bSwingLabels;
        if (nNumATLG == null) nNumATLG = nNumATL;
        if (bTrendLinesG == null) bTrendLinesG = bTrendLines;
        if (nTLThicknessG == null) nTLThicknessG = nTLThickness;
        if (cTLColorG == null) cTLColorG = cTLColor;
        nLcntr = nNumLabels;
        // Initialize vSpace
        var OM = (close(0) * 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;
        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");
    }

    // Automatic Trendlines (ATL)
    if (nState == BARSTATE_NEWBAR && bDrawn == false) autoTL();
    
    return;
}



/******  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;
        if (j == 0) j = 1;
        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;
        nTcntr += 1;
        bDrawn = false;  // reset ATL drawing
        if (nTcntr > nNumATLG) nTcntr = 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);
            aSwingsType.pop();
            aSwingsType.unshift(1);  // record type of swing for ATL
        }
        if (vLastSwing == "L") {
            y2b = getValue(sLSource);
            aSwingsType.pop();
            aSwingsType.unshift(-1); // record type of swing for ATL
        }
    }

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


function autoTL() { // draw Automatic Trendlines
    if (bTrendLinesG == "False" || getBarState() != BARSTATE_NEWBAR) return;
    
    if (aSwingsIndex[4] == null) return;  // not enough confirmed swings yet

    var bCond = false;              // condition flag
    var nATLx1 = 0;                 // x-coordinate for start of ray
    var nATLx2 = 0;                 // x-coordinate for second point of ray
    var nATLy1 = null;              // y-coordinate for start of ray
    var nATLy2 = null;              // y-coordinate for second point of ray
    var sATL_ID = "ATL" + nTcntr;   // tagID for ATL
    var nIndex = getCurrentBarIndex();
    
    var P1 = aSwingsType[5];
    var P2 = aSwingsType[4];
    var P3 = aSwingsType[3];
    var P4 = aSwingsType[2];
    
    // Condition 1
    if (P4 == 1 && P2 == 1 && aSwingsPrice[4] < aSwingsPrice[2]) {
        if (atlHigh(aSwingsIndex[4], aSwingsIndex[2]) < high(0)) {
            nATLx1 = aSwingsIndex[4];
            nATLx2 = aSwingsIndex[2];
            nATLy1 = aSwingsPrice[4];
            nATLy2 = aSwingsPrice[2];
            sATL_ID += "_h";
            bCond = true;
        }
    }
    
    // Condition 2
    if (bCond == false) {
        if (P4 == -1 && P2 == -1 && aSwingsPrice[4] > aSwingsPrice[2]) {
            if (atlLow(aSwingsIndex[4], aSwingsIndex[2]) > low(0)) {
                nATLx1 = aSwingsIndex[4];
                nATLx2 = aSwingsIndex[2];
                nATLy1 = aSwingsPrice[4];
                nATLy2 = aSwingsPrice[2];
                sATL_ID += "_l";
                bCond = true;
            }
        }
    }
    
    // Condition 3
    if (bCond == false) {
        if (P3 == 1 && P1 == 1 && aSwingsPrice[5] < aSwingsPrice[3]) {
            if (atlHigh(aSwingsIndex[5], aSwingsIndex[3]) < atlHigh(aSwingsIndex[3], 0)) {
                nATLx1 = aSwingsIndex[5];
                nATLx2 = aSwingsIndex[3];
                nATLy1 = aSwingsPrice[5];
                nATLy2 = aSwingsPrice[3];
                sATL_ID += "_h";
                bCond = true;
            }
        }
    }
    
    // Condition 4
    if (bCond == false) {
        if (P3 == -1 && P1 == -1 && aSwingsPrice[5] > aSwingsPrice[3]) {
            if (atlLow(aSwingsIndex[5], aSwingsIndex[3]) > atlLow(aSwingsIndex[3], 0)) {
                nATLx1 = aSwingsIndex[5];
                nATLx2 = aSwingsIndex[3];
                nATLy1 = aSwingsPrice[5];
                nATLy2 = aSwingsPrice[3];
                sATL_ID += "_l";
                bCond = true;
            }
        }
    }
    
    
    // draw trend line
    if (nATLy1 != null && nATLy2 != null && bCond == true) {
        drawRay(nATLx1, nATLy1, nATLx2, nATLy2, sATL_ID);
        bDrawn = true;
    }
    
    return;
}


function atlHigh(x1, x2) {
    // returns highest high between bar indexes x1 and x2
    // where x2 is most recent index.    
    var nH = high(x1);
    
    for (var i = x1; i <= x2; i++) {
        nH = Math.max(nH, high(i));
    }
    
    return nH;
}

function atlLow(x1, x2) {
    // returns Lowest low between bar indexes x1 and x2
    // where x2 is most recent index.
    var nL = low(x1);
    
    for (var i = x1; i <= x2; i++) {
        nL = Math.min(nL, low(i));
    }

    return nL;
}

function getSlope(x1, y1, x2, y2) {
    return (y2 - y1) / (x2 - x1);
}

function drawRay(x1, y1, x2, y2, sID) {
    var x1_ray = x1;
    var y1_ray = y1;
    
    var nSlope = getSlope(x1, y1, x2, y2);
    
    // draw ray from bar with greatest slope factor between swings.
    var j = x1;
    var nEnd = x2;
    
    if (nSlope > 0) {         // up trend
        for(j; j <= nEnd; j++) {
            var nTempSlope = getSlope(j, low(j), x2, y2);
            if (nTempSlope > nSlope) {
                nSlope = nTempSlope;
                x1_ray = j;
                y1_ray = low(j);
            }
        }
    } else if (nSlope < 0) {  // down trend
        for(j; j <= nEnd; j++) {
            var nTempSlope = getSlope(j, high(j), x2, y2);
            if (nTempSlope < nSlope) {
                nSlope = nTempSlope;
                x1_ray = j;
                y1_ray = high(j);
            }
        }
    }
    
    var nSlopeAdj = nSlope * 500;
    y2 += nSlopeAdj;
    x2 += 500;
    
    drawLineRelative(x1_ray, y1_ray, x2, y2, PS_SOLID, 
        nTLThicknessG, cTLColorG, sID);

    return;
}