2004 Mar: TriPatternRank.efs

ICE Data Services -

TriPatternRank.efs   

File Name: TriPatternRank.efs

Description:

Based on Mechanically Recognizing Triangular Formations by Giorgos Siligardos, Ph.D. This article appeared in the March 2004 issue of Stock & Commodities.

Formula Parameters:

  • Swing: # of Bars - 1
  • Swing: Wave Type - %Change in Price [%Change in Price, %Retracement]
  • Swing: Wave Percentage - 20%
  • Swing High Price Source - Close [Open, High, Low, Close]
  • Swing Low Price Source - Close [Open, High, Low, Close]
  • Line Thickness - 2
  • Confirmed Swing Line Color - Blue
  • Developing Swing Line Color - Yellow

Notes:
The related article is copyrighted material. If you are not a subscriber of Stocks & Commodities, please visit www.traders.com.
This formula has a minor modification made to it since the publication of the article. The logic for current developing leg of the zig zag has been modified to draw B-r line from point B to current bar close (r).

Download File:
TriPatternRank.efs


EFS Code:

/*****************************************************************
Provided By : eSignal. (c) Copyright 2004
Study:        Triangular Pattern Rank
Version:      1.1

1/21/2004

Formula Parameters:                 Default:
    * Swing: # of Bars              1
        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 3 represents 3.0%.  The number 0.03 
        represents 0.0003%.
    * Swing High Price Source       Close
    * Swing Low Price Source        Close
    * Line Thickness                2
    * Confirmed Swing Line Color    Blue
    * Developing Swing Line Color   Yellow

Notes:
1.1 *Modified logic for current developing leg of the zig zag
     to draw B-r line from B to current bar close (r).
*****************************************************************/

function preMain() {
    setPriceStudy(true);
    setStudyTitle("Triangular Pattern Rank ");
    setShowCursorLabel(false);
    setShowTitleParameters(false);
    setDefaultBarFgColor(Color.cyan);
    //setComputeOnClose();
    
    var fp1 = new FunctionParameter("nNum", FunctionParameter.NUMBER);
    fp1.setName("Swing: # of Bars");
    fp1.setLowerLimit(1);
    fp1.setDefault(1);
    
    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 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("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.yellow);
}


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 cColorDev = 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 nSpace = null;      // space between labels and bars
var A = null;           // Linear Regression y = Ax + B
var B = null;           // Linear Regression y = Ax + B
var r2 = 0;             // Linear Regression r-squared
var MA5 = null;         // 5 period MA Study
var SS = 0;             // Small Swings counter
var nTPR = 0;           // Triangular Pattern Rank

function main(nNum, sWaveType, nRet, sHighSource, sLowSource, nThickness, cColor1, cColor2) {
    var nState = getBarState();
    var nIndex = getCurrentBarIndex();
    var h = getValue(sHighSource);
    var l = getValue(sLowSource);
    var c = close();

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

    //Initialization
    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 (cColorDev == null) cColorDev = cColor2;
    if (sHSource == null) sHSource = sHighSource;
    if (sLSource == null) sLSource = sLowSource;
    if (x1a == null) x1a = 0;
    if (y1a == null) y1a = c;
    if (nSpace == null) {
        nSpace = ((high() - low())*.2);
        TFlabels(false, "NA");
    }
    if (MA5 == null) MA5 = new MAStudy(5, 0, "Close", MAStudy.SIMPLE);
    if (bInit == false) {
        bInit = Init(h,l,c);
    }

    // Swings
    if (nState == BARSTATE_NEWBAR) {
        nScntr += 1;
        // confirm Swings
        if (nScntr > nNumBars) {
            confirmSwings();
            if (bInit == true) {
                doLine("dev1");
                doLine("dev2");
            }
        }
    }
    checkSwings(h, l);

    if (bInit == true) doLine("dev2");
    
    //Linear Regression
    RegTriangle();

    return MA5.getValue(MAStudy.MA);
}


/***********************/
/******  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);
        x1a = x2a;
        y1a = y2a;
        x1b = x2b;
        y1b = y2b;
        x2a = x1b;
        y2a = y1b;
        if (vLastSwing == "H") y2b = getValue(sHSource);
        if (vLastSwing == "L") y2b = getValue(sLSource);
    }

    // dev1
    if (sType == "dev1") {
        drawLineRelative(x1a, y1a, x1b, y1b, PS_SOLID, 
            nThicknessG, cColorDev, sType);
    }
    
    // dev2
    if (sType == "dev2") {
        if (x2a != 0 && x2a != x2b) {
            drawLineRelative(x2a, y2a, 0, close(), PS_SOLID, 
                nThicknessG, cColorDev, sType);
        } else {
            removeLine(sType);
        }
    }
    
    return;
}


function TFlabels(bTF, nSS) {
    drawTextAbsolute(2, 100, "TF = "+bTF, Color.maroon, null, 
        Text.BOLD|Text.ONTOP|Text.LEFT|Text.BOTTOM|Text.RELATIVETOBOTTOM,
        null, 14, "TF");
    var nR = (Math.abs(y2a-y2b) / Math.abs(y1b-y1a));
    if (nR > 0) {
        nR = (100*nR).toFixed(2) + "%";
    } else {
        nR = "NA";
    }
    drawTextAbsolute(2, 80, "TPR = "+nTPR, Color.maroon, null, 
        Text.BOLD|Text.ONTOP|Text.LEFT|Text.BOTTOM|Text.RELATIVETOBOTTOM,
        null, 14, "TPR");
    drawTextAbsolute(2, 40, "%Ret = "+nR, Color.maroon, null, 
        Text.BOLD|Text.ONTOP|Text.LEFT|Text.BOTTOM|Text.RELATIVETOBOTTOM,
        null, 14, "Ret");
    drawTextAbsolute(2, 20, "SS = "+nSS, Color.maroon, null, 
        Text.BOLD|Text.ONTOP|Text.LEFT|Text.BOTTOM|Text.RELATIVETOBOTTOM,
        null, 14, "SS");
    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 RegTriangle() {
    var nState = getBarState();
    var nIndex = getCurrentBarIndex();
    var i = 0;
    
    // y = Ax + B;
    // A = SUM( (x-xAVG)*(y-yAVG) ) / SUM( (x-xAVG)^2 )
    // B = yAVG - (A*xAVG)
    // Slope = -1 * A
    
    if (nScntr < 10 || nScntr > 60) {
        //removeLine("reg");
        removeLine("Up");
        removeLine("Dn");
        nTPR = 0;
        TFlabels(false, 0);
    } else {
        var aReg = close(0, -nScntr);
        var vClose = aReg[0];
        if (aReg[nScntr-1] != null) {
            LinReg(aReg, aReg.length, true);
            // Reg
            //drawLineRelative(0, B, -(nScntr-1), (A*(nScntr-1)) + B, PS_SOLID, 1, Color.blue, "reg");
            //Up and Down indicators
            var aUp = new Array(nScntr);
            var aDn = new Array(nScntr);
            var RegValue = B;   // Reg indicator values
            SS = 0; // Small Swings
            i = 0;
            for (i = 0; i < nScntr; ++i) {
                if (aReg[i] > RegValue) {
                    aUp[i] = aReg[i];
                }
                if (aReg[i] < RegValue) {
                    aDn[i] = aReg[i];
                }
                if (i < nScntr-1) {
                    var vMA0 = MA5.getValue(MAStudy.MA, -i);
                    var vMA1 = MA5.getValue(MAStudy.MA, (-i-1));
                    if (vMA0 > RegValue && vMA1 <= RegValue + A) SS += 1;
                    if (vMA0 < RegValue && vMA1 >= RegValue + A) SS += 1;
                }
                RegValue += A;
            }
            i = 0;
            var j = 0;
            for (i = 0; i < nScntr; ++i) {
                if (aUp[i] == null) {
                    j = 0;
                    for (j = i+1; j < nScntr; ++j) {
                        if (aUp[j] != null) {
                            aUp[i] = aUp[j];
                            j = nScntr;
                        }
                    }
                }
                if (aDn[i] == null) {
                    j = 0;
                    for (j = i+1; j < nScntr; ++j) {
                        if (aDn[j] != null) {
                            aDn[i] = aDn[j];
                            j = nScntr;
                        }
                    }
                }
            }
            LinReg(aUp, aUp.length, false);
            var UpSlope = -A;
            drawLineRelative(0, B, -(nScntr-1), (A*(nScntr-1)) + B, PS_SOLID, 2, Color.red, "Up");
            LinReg(aDn, aDn.length, false);
            var DnSlope = -A;
            drawLineRelative(0, B, -(nScntr-1), (A*(nScntr-1)) + B, PS_SOLID, 2, Color.red, "Dn");
        }
        /*** Triangular Formation (TF) ***/
        var bTF = true;
        var pcntRetracement = (Math.abs(y2a-y2b) / Math.abs(y1b-y1a));
        if (SS < 3) bTF = false;
        if (UpSlope >= 0 || DnSlope <= 0) bTF = false;
        if (pcntRetracement > 0.50) bTF = false;
        if (bTF == false) nTPR = 0;
        
        /*** Triangular Pattern Rank (TPR) ***/
        if (bTF == true) {
            nTPR = 1;
            //rule 1
            if (nScntr >= 15 && nScntr <= 30) nTPR += 2;
            if (nScntr > 30 && nScntr <= 55) nTPR += 1;
            //rule 2
            if (r2 <= 0.2) nTPR += 2;
            if (r2 > 0.2 && r2 <= 0.5) nTPR += 1;
            //rule 3
            if (pcntRetracement <= 0.20) nTPR += 4;
            if (pcntRetracement > 0.20 && pcntRetracement <= 0.38) nTPR += 3;
            //rule 4
            var aVol = volume(0, -nScntr);
            LinReg(aVol, aVol.length, false);
            if (-A < 0) nTPR += 4;
        }
        TFlabels(bTF, SS);
    }
    
    return;
}


function LinReg(vArray, nLength, bR2) {
    var Len = nLength;
    var xSum = 0;
    var ySum = 0;
    var i = 0;
    for (i = 0; i < nLength; ++i) {
        if (vArray[i] == null) Len -= 1;
    }
    for (i = 0; i < Len; ++i) {
        xSum += i;
        ySum += vArray[i];
    }
    var xAvg = xSum/Len;
    var yAvg = ySum/Len;
    var aSum1 = 0;
    var aSum2 = 0;
    var aSum3 = 0;
    i = 0;
    for (i = 0; i < Len; ++i) {
        aSum1 += (i-xAvg) * (vArray[i]-yAvg); 
        aSum2 += (i-xAvg)*(i-xAvg);
        aSum3 += (vArray[i]-yAvg)*(vArray[i]-yAvg);
    }
    
    A = (aSum1 / aSum2);
    B = yAvg - (A*xAvg);
    
    // r-squared
    if (bR2 == true) r2 = (aSum1*aSum1) / (aSum2*aSum3);
    
    return;
}