2004 Jul: btVolumeFlowIndicator.efs

ICE Data Services -


btVolumeFlowIndicator.efs 
  

File Name: btVolumeFlowIndicator.efs


Description:
Based on Volume Flow Indicator Performance by Markos Katsanos. This article appeared in the July 2004 issue of Stock & Commodities.


Formula Parameters:
VFI Length: 26
MA of VFI Length: 50
MA of VFI Type: EMA [SMA, EMA]
Back Test #: 1 [1,2,3,4]
- 1 Indicator Level
- 2 Divergence
- 3 Break from base
- 4 Indicator Direction

BT 2 Linear Regression Length: 120
BT 3 Linear Regression Length: 30
BT 4 SMA Length: 11
BT 4 LMA Length: 25
Initial Account Size: 10000

Notes:
The related article is copyrighted material. If you are not a subscriber of Stocks & Commodities, please visit www.traders.com.
The btVolumeFlowIndicator.efs is written for back testing with the Strategy Analyzer. The formula includes all four tests for the VFI as described in the article. When running the Strategy Analyzer, the user needs to configure the Formula Parameters in the Back Testing window (Tools-->Back Testing) before running the test. The formula also includes logic for taking short positions. However, if the user wants to exclude short trades they can uncheck the option in the Back Testing window, Allow Shorts, to exclude short trades for the test. On the chart, the bars where long positions were triggered are designated with a dark green background and the short positions are red. For those tests that have exit conditions, the bar where the exit signal is triggered will be have a gray background.

Download File:
btVolumeFlowIndicator.efs




EFS Code:






/*****************************************************************Provided By : eSignal. (c) Copyright 2004Study:        Back Testing Volume Flow Indicator by Markos KatsanosVersion:      1.05/4/2004Formula Parameters:                 Default:    VFI Length                      26    MA of VFI Length                50    MA of VFI Type                  EMA       SMA       EMA    Back Test #                     1       1 Indicator Level       2 Divergence       3 Break from base       4 Indicator Direction    BT 2 Linear Regression Length   120    BT 3 Linear Regression Length   30    BT 4 SMA Length                 11    BT 4 LMA Length                 25    Initial Account Size            10000*****************************************************************/function preMain() {    setStudyTitle("Back Test Volume Flow Indicator ");    setShowTitleParameters(false);    setCursorLabelName("VFI", 0);    setCursorLabelName("VFI MA", 1);    setDefaultBarFgColor(Color.green, 0);    setDefaultBarFgColor(Color.blue, 1);    setDefaultBarThickness(2, 0);    setDefaultBarThickness(2, 1);    addBand(0, PS_SOLID, 2, Color.black, "zero");        var fp0 = new FunctionParameter("nVFIlength", FunctionParameter.NUMBER);    fp0.setName("VFI Length");    fp0.setLowerLimit(1);    fp0.setDefault(26);        var fp1 = new FunctionParameter("nVFImaLen", FunctionParameter.NUMBER);    fp1.setName("MA of VFI Length");    fp1.setLowerLimit(1);    fp1.setDefault(50);        var fp1a = new FunctionParameter("sVFImaType", FunctionParameter.STRING);    fp1a.setName("MA of VFI Type");    fp1a.addOption("SMA");    fp1a.addOption("EMA");    fp1a.setDefault("EMA");        var fp2 = new FunctionParameter("nTest", FunctionParameter.NUMBER);    fp2.setName("Back Test #: ");    fp2.setLowerLimit(1);    fp2.setUpperLimit(4);    fp2.addOption(1);   // Indicator Level    fp2.addOption(2);   // Divergence    fp2.addOption(3);   // Break from base    fp2.addOption(4);   // Indicator Direction    fp2.setDefault(1);        var fp3 = new FunctionParameter("nLRlength", FunctionParameter.NUMBER);    fp3.setName("BT 2 Linear Regression Length");    fp3.setLowerLimit(1);    fp3.setDefault(120);    var fp4 = new FunctionParameter("nLRlength3", FunctionParameter.NUMBER);    fp4.setName("BT 3 Linear Regression Length");    fp4.setLowerLimit(1);    fp4.setDefault(30);    var fp5 = new FunctionParameter("nSMAlength", FunctionParameter.NUMBER);    fp5.setName("BT 4 SMA Length");    fp5.setLowerLimit(1);    fp5.setDefault(11);    var fp6 = new FunctionParameter("nLMAlength", FunctionParameter.NUMBER);    fp6.setName("BT 4 LMA Length");    fp6.setLowerLimit(1);    fp6.setDefault(25);    var fp7 = new FunctionParameter("nAcctStart", FunctionParameter.NUMBER);    fp7.setName("Initial Account Size");    fp7.setLowerLimit(1);    fp7.setDefault(10000);}var nTyp = null;                // Current typical pricevar nTyp1 = null;               // Previous typical pricevar nTypChg = 0;                // Current typical price changevar vVol = null;                // Current volumevar nVolSum = 0;                // Cumulative volume sumvar nVolAdj = 0;                // Current adjusted volumevar nVolMA = null;              // Current Vol MAvar nVolMA1 = null;             // Previous Vol MAvar aTypPrice = null;           // Array of changes in typical pricevar aVolume = null;             // Volume arrayvar VFI = null;                 // Current VFIvar aVFI = null;                // Array of VFI values for EMA calcvar aEMA = null;                // Array of VFI 3EMA valuesvar nTestNum = 1;               // Back Test Numbervar bEdit = false;// globals for EMAvar vEMA = new Array(5);var vEMA1 = new Array(5);var dPercent = new Array(0, 0, 0, 0, 0);var bPrimed = new Array(false, false, false, false, false);//[0] = VFI, [1] = (LRSI-LRS), [2] = ShortMA VFI, [3] = LongMA VFI, [4] = EMA of VFI// globals for back testingvar nAccount = 10000;var nEntry = null;var bLong = false;var bShort = false;var bExit = false;var aDivPrice = null;var aDivVFI = null;var aLRS_LRS1 = new Array(3);var vDivergence = null;var vDivergence1 = null;var aVFItest3 = null;var aLMA = null;var aSMA = null;var aClose = null;function main(nVFIlength, nVFImaLen, sVFImaType, nTest, nLRlength, nLRlength3,             nSMAlength, nLMAlength, nAcctStart) {    var nState = getBarState();    var vInter = 0;    var nCutoff = 0;    var vMAofEMA = null;    var dSum = 0;    var i = 0;        if (bEdit == false) {        if (aTypPrice == null) aTypPrice = new Array(nVFIlength);        if (aVolume == null) aVolume = new Array(nVFIlength);        if (aEMA == null) aEMA = new Array(nVFImaLen);        nTestNum = nTest;        var sTestText = "";        if (aVFI == null) {            if (nTestNum == 1) {                aVFI = new Array(7);                 sTestText = "Test 1: Indicator Level";            }            if (nTestNum > 1) {                aVFI = new Array(3);                if (nTestNum == 2) {                    sTestText = "Test 2: Divergence";                                    }            }            if (nTestNum == 3) {                addBand(-2, PS_SOLID, 2, Color.black, "zero");                aVFItest3 = new Array(20);                sTestText = "Test 3: Break From Base";                                }            if (nTestNum == 4) {                aSMA = new Array(nSMAlength);                aLMA = new Array(nLMAlength);                sTestText = "Test 4: Indicator Direction";            }        }        if (nTestNum == 4) {            setCursorLabelName("SMA VFI", 0);            setCursorLabelName("LMA VFI", 1);            setDefaultBarFgColor(Color.navy, 0);        }        drawTextAbsolute(1, 15, sTestText, Color.navy, null,             Text.RELATIVETOTOP|Text.RELATIVETOLEFT|Text.LEFT|Text.BOLD,            null, 12, "TestNum");        nAccount = nAcctStart;        bEdit = true;    }        if (nState == BARSTATE_NEWBAR) {        if (nTyp != null) {            aTypPrice.pop();            aTypPrice.unshift(nTypChg);            nTyp1 = nTyp;        }        if (nVol != null) {            aVolume.pop();            aVolume.unshift(nVol);        }        nVolMA1 = nVolMA;        nVolSum += nVolAdj;        if (VFI != null) {            aVFI.pop();            aVFI.unshift(VFI);        }        if (vEMA[0] != null) {            aEMA.pop();            aEMA.unshift(vEMA[0]);        }    }        nVol = volume();    if (nVol == null) return;    aVolume[0] = nVol;    if (aVolume[nVFIlength-1] != null) {        for (i = 0; i < nVFIlength; ++i) {            dSum += aVolume[i];        }        nVolMA = dSum/nVFIlength;    }        nTyp = (high() + low() + close()) / 3;    if (nTyp1 != null) {        nTypChg = (Math.log(nTyp) - Math.log(nTyp1));        aTypPrice[0] = nTypChg;    }        if (nVolMA == null || nVolMA1 == null) return;        if (aTypPrice[nVFIlength-1] != null) {        vInter = StDev(nVFIlength);        nCutoff = (.2 * vInter * close());    } else {        return;    }    nVolAdj = nVol;    //Minimal Change Cutoff    if ((nTyp - nTyp1) >= 0 && (nTyp - nTyp1) < nCutoff) nVolAdj = 0;    if ((nTyp - nTyp1) < 0 && (nTyp - nTyp1) > -nCutoff) nVolAdj = 0;    // Volume curtailment    if (nVolAdj > (2.5*nVolMA1)) nVolAdj = (2.5*nVolMA1);        if (nTyp - nTyp1 < 0) nVolAdj *= -1;    VFI = ((nVolSum + nVolAdj) / nVolMA1);    aVFI[0] = VFI;        if (aVFI[(aVFI.length)-1] != null) {        vEMA[0] = EMA(0, VFI, aVFI.length, aVFI);        aEMA[0] = vEMA[0];    }    if (aEMA[nVFImaLen-1] != null) {        if (sVFImaType == "SMA") {            dSum = 0;            i = 0;            for(i = 0; i < nVFImaLen; ++i) {                dSum += aEMA[i];            }            vMAofEMA = dSum/nVFImaLen;        } else if (sVFImaType == "EMA") {            vEMA[4] = EMA(4, aEMA[0], aEMA.length, aEMA);            vMAofEMA = vEMA[4];        }    }    if (vEMA[0] != null && nTestNum != 4) {        var nLine = 0;        if (nTestNum == 3) nLine = -2        if (vEMA[0] > nLine) {            setBarFgColor(Color.green, 0);        } else {            setBarFgColor(Color.red, 0);        }    }         // Back Testing Section    if (getCurrentBarIndex() < 0 && vEMA != null && vEMA1 != null) { // processing historical data for back testing        // draw entry signals        if (bLong == true) {            setBarBgColor(Color.darkgreen);            //drawShapeRelative(0, vEMA1[0], Shape.UPARROW, null,             //    Color.khaki, Shape.TOP|Shape.ONTOP);        }        if (bShort == true) {            setBarBgColor(Color.maroon);            //drawShapeRelative(0, vEMA1[0], Shape.DOWNARROW, null,             //    Color.khaki, Shape.BOTTOM|Shape.ONTOP);        }        if (bExit == true) {            setBarBgColor(Color.lightgrey);            //drawShapeRelative(0, vEMA1[0], Shape.DIAMOND, null,             //    Color.khaki, Shape.BOTTOM|Shape.ONTOP);        }                bLong = false;        bShort = false;        bExit = false;                var nLotSize = Math.floor(nAccount/open(1));                // Test 1   Indicator Level        if (nTestNum == 1) {            if (vEMA[0] > 0 && vEMA1[0] <= 0 && Strategy.isLong() == false) { // long                if (Strategy.isShort() == true) {                    nAccount += ( (vEntry-open(1)) * (-Strategy.getPositionSize()) );                    Strategy.doCover("Close Short", Strategy.MARKET, Strategy.NEXTBAR, Strategy.getPositionSize());                    nLotSize = Math.floor(nAccount/open(1))                }                bLong = true;                vEntry = open(1);                Strategy.doLong("Crossing Up", Strategy.MARKET, Strategy.NEXTBAR, nLotSize);            } else if (vEMA[0] < 0 && vEMA1[0] >= 0 && Strategy.isShort() == false) { // short                if (Strategy.isLong() == true) {                    nAccount += ( (open(1)-vEntry) * (Strategy.getPositionSize()) );                    Strategy.doSell("Close Long", Strategy.MARKET, Strategy.NEXTBAR, Strategy.getPositionSize());                    nLotSize = Math.floor(nAccount/open(1))                }                bShort = true;                vEntry = open(1);                Strategy.doShort("Crossing Down", Strategy.MARKET, Strategy.NEXTBAR, nLotSize);            }        }        // Test 2   Divergence        if (nTestNum == 2) {            if (aDivPrice == null) aDivPrice = new Array(nLRlength);            if (aDivVFI == null) aDivVFI = new Array(nLRlength);                        if (vDivergence != null) vDivergence1 = vDivergence;            // VFI1 conversion = VFI + Math.abs(Lowest(VFI)) + 10;            var VFI1 = (VFI + Math.abs(Lowest()) + 10).toFixed(2)*1;            var LRS, LRS1;                        aDivPrice.pop();            aDivPrice.unshift(close());            aDivVFI.pop();            aDivVFI.unshift(VFI1);            if (aDivPrice[nLRlength-1] != null && aDivVFI[nLRlength-1] != null) {                LRS = (LinearRegressionSlope(aDivPrice) / aDivPrice[nLRlength-1]);                LRS1 = (LinearRegressionSlope(aDivVFI) / aDivVFI[nLRlength-1]);                aLRS_LRS1.pop();                aLRS_LRS1.unshift((LRS1 - LRS));                if (aLRS_LRS1[2] != null) {                    vEMA[1] = EMA(1, aLRS_LRS1[0], 3, aLRS_LRS1);                    vDivergence = vEMA[1] * 100;                    if (vDivergence != null) {                        if (Strategy.isInTrade() == false) {                            if (vDivergence1 > 100 && vDivergence < vDivergence1 && LRS1 > 0) {  // Long entry                                bLong = true;                                vEntry = open(1);                                Strategy.doLong("Long Divergence", Strategy.MARKET, Strategy.NEXTBAR, nLotSize);                            } else if (vDivergence1 < -100 && vDivergence > vDivergence1 && LRS1 < 0) {  // Short entry                                bShort = true;                                vEntry = open(1);                                Strategy.doShort("Short Divergence", Strategy.MARKET, Strategy.NEXTBAR, nLotSize);                            }                        } else if (Strategy.isLong()) { // exit long                            if (vDivergence < 0 && LRS1 < 0) {                                nAccount += ( (open(1)-vEntry) * (Strategy.getPositionSize()) );                                bExit = true;                                Strategy.doSell("Exit Long", Strategy.MARKET, Strategy.NEXTBAR, Strategy.getPositionSize());                            }                        } else if (Strategy.isShort()) {  // exit short                            if (vDivergence > 0 && LRS1 > 0) {                                nAccount += ( (vEntry-open(1)) * (-Strategy.getPositionSize()) );                                bExit = true;                                Strategy.doCover("Exit Short", Strategy.MARKET, Strategy.NEXTBAR, Strategy.getPositionSize());                            }                        }                    }                }            }        }        // Test 3        if (nTestNum == 3) {            if (aClose == null) aClose = new Array(nLRlength3);            var bC1Up = false;  // Condition 1            var bC1Dn = false;            var bC2Up = false;  // Condition 2            var bC2Dn = false;            var bC3Up = false;  // Condition 3            var bC3Dn = false;            var bC4Up = false;  // Condition 4            var bC4Dn = false;            // Condition 1            if (vEMA[0] > -2) {                bC1Up = true;            } else if (vEMA[0] < -2) {                bC1Dn = true;            }            // Condition 2            aVFItest3.pop();            aVFItest3.unshift(vEMA[0]);            var nLRangle = null;            if (aVFItest3[19] != null) {                nLRangle = LinearRegressionSlope(aVFItest3)/100;                nLRangle = (Math.atan(nLRangle) / (Math.PI/180));            }            if (nLRangle != null) {                if (nLRangle > 0) bC2Up = true;                if (nLRangle < 0) bC2Dn = true;            }            // Condition 3            if (vEMA[0] > vMAofEMA) bC3Up = true;            if (vEMA[0] < vMAofEMA) bC3Dn = true;            // Condition 4            var LRS = null;            aClose.pop();            aClose.unshift(close());            if (aClose[nLRlength3-1] != null) {                LRS = LinearRegressionSlope(aClose);                if (LRS < (0.006*aClose[nLRlength3-1]) && LRS > 0) bC4Up = true;                if (LRS > (-0.006*aClose[nLRlength3-1]) && LRS < 0) bC4Dn = true;            }                        if (Strategy.isInTrade() == false) {                if (bC1Up == true && bC2Up == true && bC3Up == true && bC4Up == true) {  // Long entry                    bLong = true;                    vEntry = open(1);                    Strategy.doLong("Long Break", Strategy.MARKET, Strategy.NEXTBAR, nLotSize);                } else if (bC1Dn == true && bC2Dn == true && bC3Dn == true && bC4Up == true) {  // Short entry                    bShort = true;                    vEntry = open(1);                    Strategy.doShort("Short Break", Strategy.MARKET, Strategy.NEXTBAR, nLotSize);                }            } else if (Strategy.isLong()) { // exit long                if (nLRangle <= -40 || vEMA[0] < -2) {                    nAccount += ( (open(1)-vEntry) * (Strategy.getPositionSize()) );                    bExit = true;                    Strategy.doSell("Exit Long", Strategy.MARKET, Strategy.NEXTBAR, Strategy.getPositionSize());                }            } else if (Strategy.isShort()) {  // exit short                if (nLRangle >= 40 || vEMA[0] > -2) {                    nAccount += ( (vEntry-open(1)) * (-Strategy.getPositionSize()) );                    bExit = true;                    Strategy.doCover("Exit Short", Strategy.MARKET, Strategy.NEXTBAR, Strategy.getPositionSize());                }            }        }        // Test 4        if (nTestNum == 4) {            aSMA.pop();            aSMA.unshift(vEMA[0]);            aLMA.pop();            aLMA.unshift(vEMA[0]);            if (aSMA[nSMAlength-1] != null) {                vEMA[2] = EMA(2, vEMA[0], nSMAlength, aSMA);            }            if (aLMA[nLMAlength-1] != null) {                vEMA[3] = EMA(3, vEMA[0], nLMAlength, aLMA);            }            if (vEMA[2] > vEMA[3] && vEMA1[2] < vEMA1[3] && Strategy.isLong() == false) { // long                if (Strategy.isShort() == true) {                    nAccount += ( (vEntry-open(1)) * (-Strategy.getPositionSize()) );                    Strategy.doCover("Close Short", Strategy.MARKET, Strategy.NEXTBAR, Strategy.getPositionSize());                    nLotSize = Math.floor(nAccount/open(1))                }                bLong = true;                vEntry = open(1);                Strategy.doLong("Crossing Up", Strategy.MARKET, Strategy.NEXTBAR, nLotSize);            } else if (vEMA[2] < vEMA[3] && vEMA1[2] > vEMA1[3] && Strategy.isShort() == false) { // short                if (Strategy.isLong() == true) {                    nAccount += ( (open(1)-vEntry) * (Strategy.getPositionSize()) );                    Strategy.doSell("Close Long", Strategy.MARKET, Strategy.NEXTBAR, Strategy.getPositionSize());                    nLotSize = Math.floor(nAccount/open(1))                }                bShort = true;                vEntry = open(1);                Strategy.doShort("Crossing Down", Strategy.MARKET, Strategy.NEXTBAR, nLotSize);            }        }    }    // End of Back Testing    if (nTestNum != 4) {        return new Array(vEMA[0], vMAofEMA);    } else {        return new Array(vEMA[2], vEMA[3]);    }}/************************************************* Functions *************************************************/function StDev(nLength) {    var sumX = 0;    var sumX2 = 0;    for (i = 0; i < nLength; ++i) {        sumX += aTypPrice[i];        sumX2 += (aTypPrice[i] * aTypPrice[i])    }    var meanX = (sumX/nLength);    var stdev = Math.sqrt((sumX2/nLength) - (meanX*meanX));    return stdev;}function EMA(nNum, nItem, nLength, aArray) {    var nBarState = getBarState();    var dSum = 0.0;    if(nBarState == BARSTATE_ALLBARS || bPrimed[nNum] == false) {        dPercent[nNum] = (2.0 / (nLength + 1.0));        bPrimed[nNum] = false;    }    if (nBarState == BARSTATE_NEWBAR) {        vEMA1[nNum] = vEMA[nNum];    }    if(bPrimed[nNum] == false) {        for(i = 0; i < nLength; i++) {            dSum += aArray[i];        }        bPrimed[nNum] = true;        return (dSum / nLength);    } else {        return (((nItem - vEMA1[nNum]) * dPercent[nNum]) + vEMA1[nNum]);    }}function Lowest() {    var vLowVFI = VFI;    for (var i = 0; i < aVFI.length; ++i) {        if (aVFI[i] != null) {            vLowVFI = Math.min(vLowVFI, aVFI[i]);        }    }        return vLowVFI;}function LinearRegressionSlope(aArray, nLength) {    var nIndex = getCurrentBarIndex();    var i = 0;    if (nLength == null) nLength = aArray.length;        // y = Ax + B;    // A = SUM( (x-xAVG)*(y-yAVG) ) / SUM( (x-xAVG)^2 )    // A = slope    // B = yAVG - (A*xAVG);        if (aArray[nLength-1] != null) {        var xSum = 0;        var ySum = 0;        i = 0;        for (i = 0; i < nLength; ++i) {            xSum += i;            ySum += aArray[i];        }        var xAvg = xSum/nLength;        var yAvg = ySum/nLength;        var aSum1 = 0;        var aSum2 = 0;        i = 0;        for (i = 0; i < nLength; ++i) {            aSum1 += (i-xAvg) * (aArray[i]-yAvg);             aSum2 += (i-xAvg)*(i-xAvg);        }        var A = (aSum1 / aSum2);        var B = yAvg - (A*xAvg);    }        var vSlope = (-A.toFixed(8)*100);        return vSlope;}