//@LibraryID:1035,0 //@Name:Zig-Zag Values //@Description:Calculates and returns different values based on the Zig-Zag analytic. //@Returns:Number //@Width:90 //@Env:Production //@Update:Periodic,60 //Modified: Phil Tolhurst, ShareScope Support - 29-06-17 // 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. var zzValue = 10; var useHighLow = 0; var highLowList = ["Close","High/Low"]; var outputList = ["No. of peaks","No. of troughs","Avg. Peak Value","Avg. Trough Value"]; var outputChoice = 0; var dataSource = 0; var dataList = ["Daily","Weekly","Monthly"]; var useIntra = 0; var lookBack = 252; function init(status) { if (status == Loading || status == Editing) { zzValue = storage.getAt(0); useHighLow = storage.getAt(1); outputChoice = storage.getAt(2); dataSource = storage.getAt(3); useIntra = storage.getAt(4); lookBack = storage.getAt(5); } if (status == Adding || status == Editing) { dlg = new Dialog("Select Period Length", 220, 90); dlg.addOkButton(); dlg.addCancelButton(); dlg.addNumEdit("VAL1",55,5,-1,-1,"Zig-Zag","% of Value",zzValue,0.0001,100); dlg.addDropList("VAL2",55,22,80,-1,highLowList,"","",useHighLow); dlg.addDropList("VAL3",55,39,-1,-1,outputList,"Output the:","",outputChoice); dlg.addIntEdit("VAL6",55,56,-1,-1,"Calculate over","periods",lookBack,2,99999); dlg.addDropList("VAL4",55,73,-1,-1,dataList,"Data sourcel:","",dataSource); dlg.addTickBox("VAL5",130,75,-1,-1,"Include intraday data",useIntra); if (dlg.show()==Dialog.Cancel) return false; zzValue = dlg.getValue("VAL1"); useHighLow = dlg.getValue("VAL2"); outputChoice = dlg.getValue("VAL3"); dataSource = dlg.getValue("VAL4"); useIntra = dlg.getValue("VAL5"); lookBack = dlg.getValue("VAL6"); storage.setAt(0, zzValue); storage.setAt(1, useHighLow); storage.setAt(2, outputChoice); storage.setAt(3, dataSource); storage.setAt(4, useIntra ); storage.setAt(5, lookBack); } setTitle(outputList[outputChoice]+" "+(useIntra?"i":"")+dataList[dataSource]+" "+(useHighLow?"H/L":"C")); } function getVal(share) { var data = getData(share,dataSource,useIntra); if(data==undefined || data.length0 && zz[i].index==data.length-1 && (zz[i].value/zz[i-1].valuezz[i-1].value) { peakCount=peakCount+1; peakSum=peakSum+zz[i].value; } else { troughCount=troughCount+1; troughSum=troughSum+zz[i].value; } } if(outputChoice == 0) { return(peakCount); } if(outputChoice == 1) { return(troughCount); } if(outputChoice == 2) { if(peakCount==0) { return; } else { return(peakSum/peakCount).toFixed(2); } } if(outputChoice == 3) { if(troughCount==0) { return; } else { return(troughSum/troughCount).toFixed(2); } } return; } function zigZagCalcClose(data,zzPerc) { var zz = []; var hh,ll; //start loop to calculate zig-zag line values zz[0] = {index:0,value:data[0].close}; for (var i=0;idata[i].close) ll = data[i].close; if (hh/zz[0].value-1>=zzPerc || 1-ll/zz[0].value>=zzPerc) { zz[1] = {index:i,value:data[i].close}; continue; } } else { if (zz[zz.length-1].value>zz[zz.length-2].value) { if (data[i].close>zz[zz.length-1].value) { zz[zz.length-1].value = data[i].close; zz[zz.length-1].index = i; continue; } else if (1-data[i].close/zz[zz.length-1].value>=zzPerc) { zz[zz.length] = {index:i,value:data[i].close}; } } else { if (data[i].close=zzPerc) { zz[zz.length] = {index:i,value:data[i].close}; } } } } if (zz[zz.length-1].indexdata[i].low) ll = data[i].low; if (hh/zz[0].value-1>=zzPerc) { zz[1] = {index:i,value:data[i].high,avVol:0}; } else if (1-ll/zz[0].value>=zzPerc) { zz[1] = {index:i,value:data[i].low,avVol:0}; } } else { //if the line is moving up if (zz[zz.length-1].value>zz[zz.length-2].value) { if (data[i].high>zz[zz.length-1].value) //check for higher highs { zz[zz.length-1].value = data[i].high; zz[zz.length-1].index = i; } else if (1-data[i].low/zz[zz.length-1].value>=zzPerc) //otherwise check for reversals { for (var j=zz[zz.length-2].index+1;j<=zz[zz.length-1].index;j++) zz[zz.length-1].avVol += data[j].volume; zz[zz.length-1].avVol /= (zz[zz.length-1].index - zz[zz.length-2].index); zz[zz.length] = {index:i,value:data[i].low,avVol:0}; } } else //if the line is moving down { if (data[i].low=zzPerc) //otherwise check for reversals { for (var j=zz[zz.length-2].index+1;j<=zz[zz.length-1].index;j++) zz[zz.length-1].avVol += data[j].volume; zz[zz.length-1].avVol /= (zz[zz.length-1].index - zz[zz.length-2].index); zz[zz.length] = {index:i,value:data[i].high,avVol:0}; } } } } return zz; } function getData(share,dataSource,useIntra) { if (dataSource == 0)var data = share.getPriceArray(); if (dataSource == 1)var data = share.getWeeklyBarArray(); if (dataSource == 2)var data = share.getMonthlyBarArray(); if (data.length<2) return; if (dataSource==0 && useIntra==1) { //get a 24hour intraday bar var idata = share.getIBarArray(0,86400); //Check the bar is not undefined and has the correct length. //Check the date of the intraday data is today's date. //Check the date of the end-of-day data is not today's date. if (idata!=undefined && idata.length==1 && new Date().getDate()==idata[0].date.getDate() && new Date().getDate()!=data[data.length-1].date.getDate()) {//Add a new bar to the end of the current data array that adds the intraday Open,High,Low & Close data[data.length]={ open:share.getIOpen(), high:idata[0].high, low:idata[0].low, close:(share.getIClose()==null?share.getIMid():share.getIClose()), volume:idata[0].volume, dateNum:idata[0].dateNum}; } } if (dataSource==1 && useIntra==1) { //get a 24hour intraday bar var idata = share.getIBarArray(0,86400); //Check the bar is not undefined and has the correct length. //Check the date of the intraday data is today's date. //Check the date of the end-of-day data is not today's date. if (idata!=undefined && idata.length==1 && new Date().getDate()==idata[0].date.getDate() && new Date().getDate()!=data[data.length-1].date.getDate()) { if (idata[0].date.getDay()data[data.length-1].high?idata[0].high:data[data.length-1].high), low:(idata[0].lowdata[data.length-1].high?idata[0].high:data[data.length-1].high), low:(idata[0].low