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