//@Name:Auto Trends //@Description:Draws support and resistance levels on the graph based on the highs/lows of a Zig-Zag line. Based on Building Automatic Trendlines by Giorgos E. Siligardos, Stocks & Commodities Magazine. //@Env:Production // Care has been taken in preparing this code but it is provided without guarantee. // You are welcome to modify and extend it. Please add your name as a modifier if you distribute it. //Coded by: Richard Chiesa, ShareScript Support var zigzagPerc = 15; //zig-zag percentage var zigzagCol = Colour.LightBlue; var zigzagStyle = 0; var zigzagWidth = 1; var textCol = Colour.Black; var col1 = Colour.LightGreen; var col2 = Colour.LightRed; var linearLog = 0; var rounding = 2; var zz; var zzRatios; var showZZ = false; var showValues = false; var up1; var down1; var up01; var down01; var linLogToggle; function init(status) { if (status == Loading || status == Editing) { zigzagPerc = storage.getAt(0); zigzagCol = storage.getAt(1); zigzagStyle = storage.getAt(2); zigzagWidth = storage.getAt(3); showZZ = storage.getAt(4); col1 = storage.getAt(5); col2 = storage.getAt(6); showValues = storage.getAt(7); } if (status == Adding || status == Editing) { dlg = new Dialog("Monoparametric Trend", 180, 90); dlg.addOkButton(); dlg.addCancelButton(); dlg.addNumEdit("NUM1",8,-1,-1,-1,"","% Zig-Zag",zigzagPerc,0.001,100); dlg.addColLinePicker("CL1",8,-1,-1,-1,"","Zig-Zag Line",zigzagCol,zigzagStyle,zigzagWidth); dlg.addTickBox("TB1",85,23,35,-1, "show", showZZ); dlg.addColPicker("COL1",8,-1,-1,-1,"","Support",col1); dlg.addColPicker("COL2",8,-1,-1,-1,"","Resistance",col2); dlg.addTickBox("TB2",8,-1,70,-1, "Show slope values", showValues); if (dlg.show()==Dialog.Cancel) return false; zigzagPerc = dlg.getValue("NUM1"); zigzagCol = dlg.getValue("CL1").colour; zigzagStyle = dlg.getValue("CL1").pen; zigzagWidth = dlg.getValue("CL1").width; showZZ = dlg.getValue("TB1"); col1 = dlg.getValue("COL1"); col2 = dlg.getValue("COL2"); showValues = dlg.getValue("TB2"); storage.setAt(0, zigzagPerc); storage.setAt(1, zigzagCol); storage.setAt(2, zigzagStyle); storage.setAt(3, zigzagWidth); storage.setAt(4, showZZ); storage.setAt(5, col1); storage.setAt(6, col2); storage.setAt(7, showValues); } setTitle(zigzagPerc.toFixed(1)+"% Zig-Zag, "+(linearLog?"Logarithmic":"Linear")); up1 = createButton("+1%", upPerc1); up01 = createButton("+0.1%", upPerc01); down01 = createButton("-0.1%", downPerc01); down1 = createButton("-1%", downPerc1); linLogToggle = createButton("Linear", linLog); } function upPerc1() { if (zigzagPerc<=99) zigzagPerc++; storage.setAt(0, zigzagPerc); setTitle(zigzagPerc.toFixed(1)+"% Zig-Zag, "+(linearLog?"Logarithmic":"Linear")); draw(); } function upPerc01() { if (zigzagPerc<=99.9) zigzagPerc+=0.1; storage.setAt(0, zigzagPerc); setTitle(zigzagPerc.toFixed(1)+"% Zig-Zag, "+(linearLog?"Logarithmic":"Linear")); draw(); } function downPerc01() { if (zigzagPerc>0.1) zigzagPerc-=0.1; storage.setAt(0, zigzagPerc); setTitle(zigzagPerc.toFixed(1)+"% Zig-Zag, "+(linearLog?"Logarithmic":"Linear")); draw(); } function downPerc1() { if (zigzagPerc>1) zigzagPerc--; storage.setAt(0, zigzagPerc); setTitle(zigzagPerc.toFixed(1)+"% Zig-Zag, "+(linearLog?"Logarithmic":"Linear")); draw(); } function linLog() { linearLog = linearLog?0:1; setButtonText(linLogToggle, linearLog?"Logarithmic" : "Linear"); setTitle(zigzagPerc.toFixed(1)+"% Zig-Zag, "+(linearLog?"Logarithmic":"Linear")); draw(); } function onNewChart() { draw(); } function onZoom() { draw(); } function draw() { clearDisplay(); //clear(); setLayer(Layer.Top); zz = zigZagCalcClose(zigzagPerc/100); //print(f); //start drawing the zig-zag line if showZZ is true setPenStyle(zigzagStyle,zigzagWidth,zigzagCol); if (showZZ) { beginPath(); moveTo(zz[0].index,zz[0].value); for (var i=0;izz[i-1].value && zz[i].value=zz.length) var endj = bars.length; else var endj = zz[i+4].index; //this resets the second point of the trend the same as above, the only difference //is that if there are no higher closes between the new p2value and bars[endj-1], //the trend line is not drawn. Same thing if the slope turns negative for (var j=zz[i+2].index;jbars[j-1].close) { testBit = false; break; } } var linSlope = (p2value-p1value)/(p2index-p1index); var logSlope = Math.pow(p2value/p1value,1/(p2index-p1index)); if (testBit || (!linearLog && linSlope<=0) || (linearLog && logSlope<=1)) continue; var text = linearLog?((logSlope-1)*100).toFixed(2)+"%":linSlope.toFixed(2); setPenColour(col1); setBrushColour(col1); setFontStyle("Arial",12,col1); drawSymbol(p1index,p1value,Symbol.Circle,"",BoxAlign.Centre|BoxAlign.VCentre,6,true); drawSymbol(p2index,p2value,Symbol.Circle,"",BoxAlign.Centre|BoxAlign.VCentre,6,true); if (showValues) drawText((p1index+p2index)/2,(p1value+p2value)/2,text,BoxAlign.Right|BoxAlign.Below,0,0) moveTo(p1index,p1value); //draw the support line until a close price breaks through it var p3value = null; var p3index = null; for (var j=p1index+1;jp2index && bars[j].closezz[i-1].value && zz[i+1].valuezz[i+2].value) { var p1value = zz[i].value; var p1index = zz[i].index; var p2value = zz[i+2].value; var p2index = zz[i+2].index; //setPenColour(col2); //moveTo(p1index,p1value); //lineTo(p2index,p2value); //drawPath(); //if a close price between the two zigzag highs rises above the trend line between those two points //reset the start of the trend line to that close for (var j=zz[i].index;jp1value+(j-p1index)*linSlope) || (linearLog && bars[j].close>p1value*Math.pow(logSlope,(j-p1index)))) { p1value = bars[j].close; p1index = j; } } if (i+4>=zz.length) var endj = bars.length; else var endj = zz[i+4].index; //this resets the second point of the trend the same as above, the only difference //is that if there are no lower closes between the new p2value and bars[endj-1], //the trend line is not drawn. Same thing if the slope turns positive for (var j=zz[i+2].index;jp1value+(j-p1index)*linSlope) || (linearLog && bars[j].close>p1value*Math.pow(logSlope,(j-p1index)))) { p2value = bars[j].close; p2index = j; } } var testBit = true; for (var j=p2index+1;j=0) || (linearLog && logSlope>=1)) continue; var text = linearLog?((logSlope-1)*100).toFixed(2)+"%":linSlope.toFixed(2); setPenColour(col2); setBrushColour(col2); setFontStyle("Arial",12,col2); drawSymbol(p1index,p1value,Symbol.Circle,"",BoxAlign.Centre|BoxAlign.VCentre,6,true); drawSymbol(p2index,p2value,Symbol.Circle,"",BoxAlign.Centre|BoxAlign.VCentre,6,true); if (showValues) drawText((p1index+p2index)/2,(p1value+p2value)/2,text,BoxAlign.Right|BoxAlign.Above,0,0) moveTo(p1index,p1value); //draw the resistance line until a close price breaks through it var p3value = null; var p3index = null; for (var j=p1index+1;jp2index && bars[j].close>jval) { drawSymbol(j,jval,Symbol.Cross,"",BoxAlign.Centre|BoxAlign.VCentre,10,true); break; } } drawPath(); } } } //modified to work with logarithmic swings function zigZagCalcClose(zzPerc) { var zzLog = Math.log(1+zzPerc); var zz = []; var hh,ll; //start loop to calculate zig-zag line values zz[0] = {index:0,value:bars[0].close}; for (var i=0;ibars[i].close) ll = bars[i].close; if (Math.log(hh/zz[0].value)>=zzLog || -Math.log(ll/zz[0].value)>=zzLog) { zz[1] = {index:i,value:bars[i].close}; continue; } } else { if (zz[zz.length-1].value>zz[zz.length-2].value) { if (bars[i].close>zz[zz.length-1].value) { zz[zz.length-1].value = bars[i].close; zz[zz.length-1].index = i; continue; } else if (-Math.log(bars[i].close/zz[zz.length-1].value)>=zzLog) { zz[zz.length] = {index:i,value:bars[i].close}; } } else { if (bars[i].close=zzLog) { zz[zz.length] = {index:i,value:bars[i].close}; } } } } if (zz[zz.length-1].index