2013 May: Detecting Head & Shoulders Algorithmically, By Giorgos E. Siligardos

ICE Data Services -

 

HandS_Indicator.efs  

EFSLibrary - Discussion Board  

 

File Name: HandS_Indicator.efs

 

Description:
Detecting Head & Shoulders Algorithmically, By Giorgos E. Siligardos

 

Formula Parameters:

HandS_Indicator.efs

  • Price Source: Close
  • Max Pattern Length: 200
  • Line Color: red
  • Line Size: 2

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

 

Download File:
HandS_Indicator.efs

HandS_Indicator.efs



EFS Code:

HandS_Indicator.efs

/*********************************
Provided By:  
    Interactive Data Corporation (Copyright © 2013) 
    All rights reserved. This sample eSignal Formula Script (EFS)
    is for educational purposes only. Interactive Data Corporation
    reserves the right to modify and overwrite this EFS file with 
    each new release. 

Description:        
    HandS Indicator
    
Version:            1.00  18/03/2013

Formula Parameters:                     Default:
Price Source                            Close
Max Pattern Length                      200
Line Color                              red
Line Size                               2

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

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

var fpArray = new Array();

function preMain()
{
    setPriceStudy(true);
    setStudyTitle("HandS Indicator");

    setComputeOnClose();

    var x = 0;

    fpArray[x] = new FunctionParameter("fpPriceSource", FunctionParameter.STRING);
    with(fpArray[x++])
    {
        setName("Price Source");
        addOption("Open");
        addOption("Close");
        addOption("Low");
        addOption("High");
        addOption("HL/2");
        addOption("HLC/3");
        addOption("OHLC/4");
        setDefault("Close");
    }

    fpArray[x] = new FunctionParameter("fpMaxLength", FunctionParameter.NUMBER);
    with(fpArray[x++])
    {
        setName("Max Pattern Length");
        setLowerLimit(1);
        setDefault(200);
    } 

    fpArray[x] = new FunctionParameter("fpLineColor", FunctionParameter.COLOR);
    with(fpArray[x++])
    {
        setName("Line Color");
        setDefault(Color.red);
    } 

    fpArray[x] = new FunctionParameter("fpLineSize", FunctionParameter.NUMBER);
    with(fpArray[x++])
    {
        setName("Line Size");
        setDefault(2);
    } 
}


var bInit = false;
var bVersion = null;

var nPrev_HSDuration = 0;
var pUsedPoints = new Array();

var xSrc = null;

var nLinesCntr = 0;

function main(fpPriceSource, fpMaxLength, fpLineColor, fpLineSize)
{
    if (bVersion == null) bVersion = verify();
    if (bVersion == false) return;   

    if(!bInit)
    {
        switch (fpPriceSource)
        {
            case 'Open': 
               xSrc = open();
               break;
            case 'Close':
               xSrc = close();
               break;
            case 'High':
               xSrc = high();
               break;
            case 'Low':
               xSrc = low();
               break;
            case 'HL/2':
               xSrc = hl2();
               break;
            case 'HLC/3':
               xSrc = hlc3();
               break;
            case 'OHLC/4':
               xSrc = ohlc4();
               break;
            default : return;
        }

        bInit = true;
    }

    if(getBarState() == BARSTATE_ALLBARS)
    {
        pUsedPoints = new Array();
        nPrev_HSDuration = 0;
    }

    var nNumBars = getCurrentBarCount();

    var p1 = llv(5, xSrc).getValue(0); 
    if (p1 == null) return;

    var fac = 1;

    var nBarSince = -LastBarIndexWhen(function(i){return xSrc.getValue(i) < p1;});
    if (nBarSince == null) return;

    var He = hhv(Math.max(1, nBarSince), xSrc).getValue(0);
    if (He == null) return;

    var Heb = LastBarIndexWhen(function(i){return xSrc.getValue(i) == He;});
    
    var Rhalf = Math.max(1, -Heb);
    var Lhalf = fac * Rhalf;

    var b0b = Math.min(-Rhalf -Lhalf, -nBarSince);

    var tL1 = llv(Math.max(1, Int(2 * Lhalf / 3)), xSrc).getValue(-Rhalf);
    if (tL1 == null) return;

    var tL1b = LastBarIndexWhen(function(i){return xSrc.getValue(i - Rhalf) == tL1;}) - Rhalf;

    var H1 = hhv(Math.max(1, Math.abs(tL1b - b0b)), xSrc).getValue(tL1b);
    if (H1 == null) return;

    var H1b = LastBarIndexWhen(function(i){return xSrc.getValue(i + tL1b) == H1;}) + tL1b; 

    var eltimi = llv(Math.max(1, Math.abs(tL1b - H1b)), xSrc).getValue(tL1b);
    if (eltimi == null) return;
    
    var L1b = LastBarIndexWhen(function(i){return xSrc.getValue(i + tL1b) <= ((tL1 + 2 * eltimi) / 3);}) + tL1b; 
    if(L1b == null || L1b > 0) return;

    var L1 =  xSrc.getValue(L1b);

    var L2 = llv(Math.max(1, 2 * Int(Rhalf / 3)), xSrc).getValue(-Int(Rhalf / 3));
    if (L2 == null) return;

    var L2b = LastBarIndexWhen(function(i){return xSrc.getValue(i - Int(Rhalf / 3)) == L2;}) - Int(Rhalf / 3);

    var H2 = hhv(Math.max(1, -L2b), xSrc).getValue(0);
    if (H2 == null) return;

    var H2b = LastBarIndexWhen(function(i){return xSrc.getValue(i) == H2;});
    
    var L3b = LastBarIndexWhen(function(i){return i > H2b && xSrc.getValue(i) <= L2;});
    if(L3b == null || L3b > 0) return; 

    var L0b = LastBarIndexWhen(function(i){return xSrc.getValue(i + H1b) <= L1 + 0.2 * (H1 - L1);}) + H1b;
    if(L0b == null || L0b > 0) return;

    var L0 = xSrc.getValue(L0b);

    var L3 = xSrc.getValue(L3b);

// ================== CHECK FOR H&S PATTERN ======================

    var bExpr1 = H1 > (L1 + 0.20 * (He - L1));
    var bExpr2 = H1 < (He - 0.15 * (He - L1));
    var bExpr3 = H2 > (L2 + 0.25 * (He - L2));
    var bExpr4 = H2 < (He - 0.25 * (He - L2));
    var bExpr5 = L2 < (L1 + 0.40 * (He - L1));

    var bExpr6_1 = L2 > L1 + 0.20 * (He - L1);
    var bIfTrue_bExpr6_1 = hhv(Int(Math.max((Heb - L2b)/3, 1)), xSrc).getValue(L2b) < L2 + 0.7 * (He - L2);   
    var bExpr6 = bExpr6_1 ? bIfTrue_bExpr6_1 : true;

    var bExpr7 = L2 > (L1 - 0.15 * (He - L1));
    var bExpr8 = Math.abs((H1 - L1) - (H2 - L2)) < Math.min(H1 - L1, H2 - L2);

    var nVal9_1 = hhv(Math.max(1, Int((L1b - Heb)/2)), xSrc).getValue(-Int(Rhalf + (L1b - Heb)/2));
    var nVal9_2 = llv(Math.max(1, Int((Heb - L1b)/2)), xSrc).getValue(-Int(Rhalf));
    var bExpr9 = Math.abs(nVal9_1 - nVal9_2) < (He - L1)/2;

    var bExpr10 = hhv(Math.max(1, -L3b), close()).getValue(0) < L3 + (H2 - L3)/3;

    var bExpr11_1 = hhv(Math.max(1, Int((H2b - L2b)/3)), xSrc).getValue(H2b - Int(2*(H2b - L2b)/3)) > (L2 + 0.8 * (H2 - L2))
    var bExpr11_2 = llv(Math.max(1, Int((H2b - L2b)/3)), xSrc).getValue(H2b) < (L2 + 0.25 * (H2 - L2));
    var bExpr11 = (bExpr11_1 && bExpr11_2) == false;

    var bExpr12_1 = (H1 - L1) > 0.25 * (He - L1);
    var bExpr12_2 = (H2 - L2) > 0.25 * (He - L2);
    var bExpr12_3 = Math.min((L1b - H1b), (H2b - L2b)) > 0.25 * (L2b - L1b);
    var bExpr12 = (bExpr12_1 && bExpr12_2) || bExpr12_3;

    var bExpr13 = hhv(Math.max(1, H1b - L0b), xSrc).getValue(H1b) < (He - 0.15 * (He - L1));

    var bExpr14 = 2.5 * Math.min(Heb - H1b, H2b - Heb) > Math.max(Heb - H1b, H2b - Heb);
    var bExpr15 = 3 * Math.min(L1b - L0b, L3b - L2b) > Math.max(L1b - L0b, L3b - L2b);

    var bExpr16 = (L0b != null) && (L0b <= 0) && (-L0b <= nNumBars);

    var nHSDuration = 0;

    if 
    (
        bExpr1 &&
        bExpr2 &&
        bExpr3 &&
        bExpr4 &&
        bExpr5 &&
        bExpr6 &&
        bExpr7 &&
        bExpr8 &&
        bExpr9 &&
        bExpr10 &&
        bExpr11 &&
        bExpr12 &&
        bExpr13 &&
        bExpr14 &&
        bExpr15 &&
        bExpr16
    )
        nHSDuration = Math.abs(L3b - L0b);
// ===============================================================

    if (nHSDuration > fpMaxLength)
        return;

    if (nHSDuration > 0 && (nHSDuration - nPrev_HSDuration) > 1)
    {
        var pPatternLines = new Array();
        pPatternLines.push(new Line(L0b, H1b));
        pPatternLines.push(new Line(H1b, L1b));
        pPatternLines.push(new Line(L1b, Heb));
        pPatternLines.push(new Line(Heb, L2b));
        pPatternLines.push(new Line(L2b, H2b));
        pPatternLines.push(new Line(H2b, L3b));

        var pCurrentPoints = new Array();
        pCurrentPoints.push(nNumBars + L0b);
        pCurrentPoints.push(nNumBars + L1b);
        pCurrentPoints.push(nNumBars + L2b);
        pCurrentPoints.push(nNumBars + L3b);
        pCurrentPoints.push(nNumBars + H1b);
        pCurrentPoints.push(nNumBars + H2b);
        pCurrentPoints.push(nNumBars + Heb);

        var color = fpLineColor;

        var getColor = function(n)
        {
            var r = 2*n % 250; 
            var g = 250 - 2*n % 250; 
            var b = 5*n % 250;            
            return color = Color.RGB(r, g, b);
        }

        if (CheckForRepeatedPoints(pCurrentPoints))
            color = getColor(color % 250 * nLinesCntr % 250);

        DrawPatternLines(pPatternLines, xSrc, fpLineSize, color);

        for(var pointKey in pCurrentPoints)
            pUsedPoints.push(pCurrentPoints[pointKey]);
    }

    nPrev_HSDuration = nHSDuration;
}

function CheckForRepeatedPoints(pPoints)
{
    for(var pointKey in pPoints)
    {
        for (var usedPointKey in pUsedPoints)
        {
            if (pUsedPoints[usedPointKey] == pPoints[pointKey])
                return true;
        }
    }

    return false;
}

function Line(nStart, nEnd)
{
    this.Start = nStart;
    this.End = nEnd;
}

function DrawLine(oLine, xSeries, nThikness, nColor, sKey)
{
    drawLineRelative
    (
        oLine.Start,
        xSeries.getValue(oLine.Start),
        oLine.End,
        xSeries.getValue(oLine.End),
        PS_SOLID,
        nThikness, 
        nColor,
        sKey
    )
}

function DrawPatternLines(pLines, xSeries, nThikness, nColor)
{
    for (var key in pLines)
    {
        DrawLine(pLines[key], xSeries, nThikness, nColor, nLinesCntr++);
    }
}

function LastBarIndexWhen(fExpr)
{
    for (var i = 0; i < getCurrentBarCount(); i++)
    {
        if (fExpr(-i))
            return - i;
    }

    return null;
}

function Int(nNumber)
{
    return Math.floor(nNumber);
}

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

    return b;
}