AutomaticTrendlines.efs
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; }