/******************************************************************** Title: Murrey Math Lines Script (for eSignal 7.X) By: Chris D. Kryza (Divergence Software, Inc.) Email: ckryza@verizon.net; ckryza@sr-analyst.com Web: http://www.sr-analyst.com Incept: 12/02/2002 Version: 3.8.0 NOTE!!!!: This script was developed and tested using the features available in: ESignal Release Candidate 7.2 Build 541 Dated: 01/21/03 If you are using an earlier eSignal version, it is possible and even likely that errors will be generated when you attempt to use this script. ===================================================================== Fix History: 11/21/2005 - Added support for tick data. Note that there is no guarantee 3.8.0 that the vertical time lines will be correct when tick data is being used. 10/12/2004 - Changed default date. 3.7.0 03/18/2004 - Fixed price display/format issue. 3.6.0 12/13/2003 - Turn off study parameters if running 7.5 or better. 3.5.0 10/17/2003 - Added Alerts code. Can now set a Sound or Popup alert 3.4.0 on any MM level. 04/30/2003 - Added vertical line colors option. Added AutoFrame 3.3.0 status display. 03/24/2003 - Reworked the MM line-draw code and incorporated the 3.2.0 Baby Eighths feature. 03/14/2003 - Added support for new features available in Build 566. 3.1.0 Quick-Parameter editing and Price Format. Click on the aqua "Murrey Tool" text frame below the buttons and the Edit Parameters dialog will pop-up. 03/04/2003 - Modified error message for invalid start date. Note 3.0.0 that the Start Date (or Zero Date) must be an actual trading day (e.g., cannot be a Sat, Sun or holiday). 02/09/2003 - Fixed rounding of high and low prices for purposes 2.9.1 of frame calculation. 02/07/2003 - Modified code so that grid display is truncated at 2.9.0 +3/8 and -3/8. 02/05/2003 - Added new option to control the display of the 2.8.0 vertical time lines. 01/30/2003 - Modified button code to align buttons to left side of 2.7.0 chart. 01/28/2003 - Added code to support Weekly and Monthly bars. 2.6.0 01/23/2003 - Minor fix to button code so that original user preferences 2.5.0 are preserved through button clicks. 01/22/2003 - Added new option that will display buttons on the chart 2.4.0 to allow the user to quickly change frame size and display speed and momentum lines. 01/17/2003 - Modified AutoFrame code so that it selects the Frame 2.3.0 Size that results in a 1/8th increment size that is the closest to the preferred increment size, as opposed to simply greater than the preferred increment size. Also, shortened Zero Date display in Config Text. Added display of current frame size just to the right of the OverBought/OverSold text fields. 01/17/2003 - Modified code used to calculate vertical time bars 2.2.0 when script is used intraday. Now, 11:58am is used as the "center" of the day and all vertical time lines are calculated from this offset. 01/15/2003 - Changed MM line colors. Added support for a frame 2.1.0 size of 8. Added code for Momentum lines. Added code to auto-compute the best frame size based upon the size of the desired 1/8th increment. 12/18/2002 - Rounded 8th line price display to 2 digits (was 3). 2.0.0 12/15/2002 - Remove "4" from the CandlesPerSquare options since it cannot 1.9.0 be supported because of line draw limitations (smallest x-axis increment is 1 and a square size of 4 would require an x-axis increment of 0.5.) 12/14/2002 - Added text indicators to highlight the High and Low price 1.8.0 bars that are being used in the frame calculation. A small "H" will appear above the high bar and a small "L" will appear below the low bar. Minor cosmetic fixes to price display. 12/13/2002 - Removed background color calls (changed to null) so user's 1.7.0 preferred background color will be used. Fixed MML calculation. Changed frPadding to frRightMargin to make it's use a little clearer. 12/12/2002 - Encapsulated the drawing of Circles into a function 1.6.0 that also clips the lines when necessary. Added "Padding" parameter which allows user to define how many bars into the future to draw the current price/time square. Circles and Speed lines will clipped according to this setting. 12/11/2002 - Kludge to align labels more closely to center of line. 1.5.0 Added code to clip new boxes so less space is wasted on the right side of the screen when a new price/time box is drawn. 12/11/2002 - Added support for frame size of 128. Added StartDate as 1.4.0 an option that can be changed from within ESignal's "Edit Studies" feature. Added nDispInBorder option which will force the 8th line values to be displayed in the ESignal price pane area. Cosmetic fixes to be sure "old" lines are removed on a frame redraw. Renamed the option "frWicks" to "frIgnoreWicks" to make it's use a little clearer (i.e., 1=ignore, 0=don't ignore) 12/10/2002 - Fix (hopefully) to intraday time code. Added highlight 1.3.0 color to 4/8th line in current price/time square. Added display of each 1/8th line price. Cleaned up some cosmetic items. 12/09/2002 - Converted MML code to handle start of frame for intraday 1.2.0 operation. 12/06/2002 - Modified MML calculation code, added calculation and 1.1.0 display of levels (on right) for next larger frame. Added code to handle intraday operation. 12/02/2002 - Initial release. 1.0.5 ===================================================================== Project Description: Murrey Math is a trading methodology developed by Prof. TH Murrey and it is based on the theories of WD Gann. Prof. Murrey has written a book which describes his methodology in detail and he also offers seminars at various times during the year. More information on Prof. Murrey's methodology can be found at his web site, which is www.MurreyMathTrading.com. This script generates a template that can be used in conjuction with Mr. Murrey's methodology. This script will only be useful to you if you have a good understanding of the Murrey Math trading methodology. The code is fairly heavily commented and I tried to keep things as simplistic and sequential as possible so that it would be easy to follow. If you come across any fixes or have any ideas on how to spruce it up, I would appreciate it if you would let me know (c.kryza@gte.net). =============================================== Configuration Options: =============================================== Set Candles per Square Set OptIncrement (used with AutoFrame) Set FrameMultiplier (e.g., 1.0, 1.5, 2.0) Set IgnoreWicks (e.g., base price range on closes only or highs/lows) Set Circles (e.g., draw them or don't draw them) Set DrawSpeedLinesUp (e.g., draw bot-to-top speed lines in most recent square) Set DrawSpeedLinesDn (e.g., draw top-to-bot speed lines in most recent square) Set DrawMomLinesUp (e.g., draw 45-degree mom lines in most recent square) Set DrawMomLinesDn (e.g., draw 45-degree mom lines in most recent square) Set RightMargin (e.g., number of bars to pad on right when new square is created) Set ZeroDate (e.g., annual frame reset date. Need to modify this each year) Set nUseButtons (e.g., display option buttons on chart) Set DrawTimeLines (e.g., display vertical time lines on chart) so the actual variable options are: frSize = {8, 16, 32, 64, 128 and 999 (for auto)} frOptIncrement = {any real number} frMult = {1.0, 1.5, 2.0} frIgnoreWicks = {1, 0} frSpeedUp = {1, 0} frSpeedDn = {1, 0} frMomUp = {1, 0} frMomDn = {1, 0} frCircles = {1, 0} frIntraOffset = {1, 0} frRightMargin = {any integer} frStartDate = {type in a valid date} frUseButtons = {1, 0} frTimeLines = {1, 0} frBaby8 = {1, 0} frVertColor = {1, 0} ********************************************************************/ //If you ever need to change the start date, please just type in the //new date below. It will carry forward to the menu as the new default. var START_DATE = "October 5, 2005"; //This is an EFS script for ESignal Version 7.2 or higher. If in doubt, download the lastest version //from the eSignal Web site. var xWAVFILE = "bullet.wav"; //WAV file to play on alerts //Global Variables var grID=0; var aFPArray = new Array(); var aButtonArray = new Array(); //Array to hold values for button-selected config items var aLabels = new Array("", "Mom Dn", "Mom Up", "Spd Dn", "Spd Up", "Auto", "128", "64", "32", "16", "8", "Baby", "(-)"); var aColor = new Array(Color.blue,Color.yellow,Color.red,Color.teal,Color.blue,Color.teal,Color.red,Color.yellow); var aThick = new Array(4,2,2,2,2,2,2,2); var bCollapsed = false; //collapse button bar var bButtonPressed = false; //button just pressed var nProx = 0; var nAlertLevel = 0; var nBarCounter = 0; var nLastAlertBar = 0; var bAlertSound = false; var bAlertList = false; var AutoFrame; var FrameMultiplier; var CandlesPerSquare; var BestSize; var DrawSpeedLinesUp; var DrawSpeedLinesDn; var DrawMomLinesUp; var DrawMomLinesDn; var DrawTimeLines; var DrawCircles; var DrawBaby8; var IgnoreWicks; var VerticalColors; var IntraFrame; var DisplayConfig = 1; //Display config text in chart? 0=No 1=Yes var Padding; var ZeroDate; //These are a few user-config options that are //not provided for in the EFS Studies dialog since //they do not need to be changed frequently. Just //edit them here and save the script. var CompactFormat = true; //only display lines to +3/8 and -3/8 if true var ButtonOffset = 4; //use this to shift the button display to the right, 0 is the default //Various color settings that user can customize; var ConflictColor = Color.lightgrey; var UpSpeedLineColor = Color.navy; var DnSpeedLineColor = Color.maroon; var ShadowTextColor = Color.darkgrey; var RegTextColor = Color.black; var RegVLineColor = Color.lightgrey; var PriVLineColor = Color.blue; var HighPriceColor = Color.navy; var LowPriceColor = Color.navy; var PriceAttrib = Text.VCENTER | Text.LEFT; //change to Text.LEFT | Text.BOLD for bolded prices var PriceColor = Color.navy; //== PreMain function required by ESignal to set things up function preMain() { var x; setPriceStudy(true); setStudyTitle("MM"); setShowCursorLabel(false); setComputeOnClose(); grID = 0; //turn off title parameters if we are running 7.5 or better if ( getBuildNumber() >= 627 ) { setShowTitleParameters( false ); } //Initialize our button array for (x=1; x<15; x++) { aButtonArray[x] = 0; } //if user is running an earlier version, skip the following code if (getBuildNumber() < 564) return; x=0; //Set up our function parameter options aFPArray[x] = new FunctionParameter( "frSize", FunctionParameter.STRING); with (aFPArray[x]) { setName("Frame Size"); addOption("8"); addOption("16"); addOption("32"); addOption("64"); addOption("128"); addOption("999"); setDefault("64"); } x++; aFPArray[x] = new FunctionParameter( "frOptIncrement", FunctionParameter.NUMBER); with (aFPArray[x]) { setName( "Increment" ); setLowerLimit( 0 ); setUpperLimit( 100 ); setDefault( 2.0 ); } x++; aFPArray[x] = new FunctionParameter( "frMult", FunctionParameter.STRING); with (aFPArray[x]) { setName("Multiplier"); addOption("1.0"); addOption("1.5"); addOption("2.0"); setDefault("1.5"); } x++; aFPArray[x] = new FunctionParameter( "frIgnoreWicks", FunctionParameter.STRING); with (aFPArray[x]) { setName("Ignore Wicks?"); addOption("1"); addOption("0"); setDefault("1"); } x++; aFPArray[x] = new FunctionParameter( "frSpeedUp", FunctionParameter.STRING); with (aFPArray[x]) { setName("Draw Speed Lines Up?"); addOption("1"); addOption("0"); setDefault("0"); } x++; aFPArray[x] = new FunctionParameter( "frSpeedDn", FunctionParameter.STRING); with (aFPArray[x]) { setName("Draw Speed Lines Dn?"); addOption("1"); addOption("0"); setDefault("0"); } x++; aFPArray[x] = new FunctionParameter( "frMomUp", FunctionParameter.STRING); with (aFPArray[x]) { setName("Draw MOM Lines Up?"); addOption("1"); addOption("0"); setDefault("0"); } x++; aFPArray[x] = new FunctionParameter( "frMomDn", FunctionParameter.STRING); with (aFPArray[x]) { setName("Draw MOM Lines Dn?"); addOption("1"); addOption("0"); setDefault("0"); } x++; aFPArray[x] = new FunctionParameter( "frCircles", FunctionParameter.STRING); with (aFPArray[x]) { setName("Draw Circles?"); addOption("1"); addOption("0"); setDefault("1"); } x++; aFPArray[x] = new FunctionParameter( "frRightMargin", FunctionParameter.NUMBER); with (aFPArray[x]) { setName( "Right Margin" ); setLowerLimit( 0 ); setUpperLimit( 100 ); setDefault( 15 ); } x++; aFPArray[x] = new FunctionParameter( "frStartDate", FunctionParameter.STRING); with (aFPArray[x]) { setName("Start Date"); setDefault( START_DATE ); } x++; aFPArray[x] = new FunctionParameter( "frUseButtons", FunctionParameter.STRING); with (aFPArray[x]) { setName("Use Buttons?"); addOption("1"); addOption("0"); setDefault("1"); } x++; aFPArray[x] = new FunctionParameter( "frTimeLines", FunctionParameter.STRING); with (aFPArray[x]) { setName("Draw Time Lines?"); addOption("1"); addOption("0"); setDefault("1"); } x++; aFPArray[x] = new FunctionParameter( "frBaby8", FunctionParameter.STRING); with (aFPArray[x]) { setName("Draw Baby 8ths?"); addOption("1"); addOption("0"); setDefault("0"); } x++; aFPArray[x] = new FunctionParameter( "frVertColor", FunctionParameter.STRING); with (aFPArray[x]) { setName("Color Vertical Lines?"); addOption("1"); addOption("0"); setDefault("0"); } x++; // 8th level for alert aFPArray[x] = new FunctionParameter( "AlertLev", FunctionParameter.STRING); with( aFPArray[x] ) { setName( "MM Level for Alert" ); for (y=0; y<9; y++) addOption( ""+y+"" ); setDefault( "0" ); } x++; //proximity percent aFPArray[x] = new FunctionParameter( "Proximity", FunctionParameter.NUMBER); with( aFPArray[x] ) { setName( "Alert Proximity (%)" ); setLowerLimit( 0.001 ); setUpperLimit( 100 ); setDefault( 0.01 ); } x++; aFPArray[x] = new FunctionParameter( "alertUseSound", FunctionParameter.STRING); with( aFPArray[x] ) { setName( "Alert Use Sound" ); addOption( "T" ); addOption( "F" ); setDefault( "F" ); } x++; aFPArray[x] = new FunctionParameter( "alertUseList", FunctionParameter.STRING); with( aFPArray[x] ) { setName( "Alert Use Popup" ); addOption( "T" ); addOption( "F" ); setDefault( "F" ); } } //== main function. All the dirty work is done here function main(frSize, frOptIncrement, frMult, frIgnoreWicks, frSpeedUp, frSpeedDn, frMomUp, frMomDn, frCircles, frRightMargin, frStartDate, frUseButtons, frTimeLines, frBaby8, frVertColor, AlertLev, Proximity, alertUseSound, alertUseList ) { var aLows, aHighs; var aDates; var sbText; var ssText; var sTitle; var xx, w, x, y, z; var M,I,N,MM,NN; var nLookBack=0; var nDispInBorder; var nUseButtons; var nPriceHigh, nPriceLow; var nOffsetHigh, nOffsetLow; var nPriceRange; var nSR, nSRA, nSI; var nFirstFrame; var nNewFrame; var nLHBase, nLVBase; var nLDiff, nUDiff, nVDiff; var nPlot1, nPlot2; var nAdjust; var nBestFrameSize; var nBestDiff; var nTmp; var nlTop1, nlTop2; var nlBot1, nlBot2; var nSegment; var nFlags; var nER; var nOctaves = 0; var nOctLev; var nxTop, nxBot; var nExtreme; var nLevUp; var nFoundOffset; var nIncrement; var nUBound, nLBound; var nMultiplier; var nOffset; var nLeftSide; var nRightSide; var nBarsToPad; var nStart; var dtToday, dtTime; var Hrs, Min; var Yr, Yr1; var Mo, Mo1; var Da, Da1; //script is initializing if ( getBarState() == BARSTATE_ALLBARS ) { return null; } //main() gets called after every bar is drawn... we don't want/need to call our //Murrey stuff until all bars are drawn.. saves on processing time if (( getCurrentBarIndex() != -1 ) && ( bButtonPressed == false ) ) { return; } bButtonPressed = false; //Get the current time (HH:MM) //Parse out the Year, Month, Day, Hour and Minute dtToday = new Date(); Yr = dtToday.getYear(); Mo = dtToday.getMonth(); Da = dtToday.getDay(); Hrs = dtToday.getHours(); Min = dtToday.getMinutes(); //******************************************************* //******* BEGINNING OF USER CONFIGURATION OPTIONS ******* //Set the Frame nMultiplier. Options appear to be 1, 1.5 and 2.0 //Apparently, MurreyMath program uses 1.5 (or so some people say). //Frame nMultiplier only affects the size of lookback period from what I can see //Notes to self: //If 1 is used: 32 frame size will use 32 bars. //If 1.5 is used: 32 frame will use (32 * 1.5)+1 or 49 bars //If 2.0 is used: 32 frame will use (32 * 2) or 64 bars. //Set program defaults for all user options here AutoFrame = 0; //Assume no AutoFrame being used FrameMultiplier = 1.5; //1, 1.5 AND 2.0 are the options here CandlesPerSquare = 64; //8, 16, 32, 64 and 128 are the options (or Auto) BestSize = 2.0; //Default best increment size to 2 ES points DrawSpeedLinesUp = 0; //Draw speedlines in current square (upper) DrawSpeedLinesDn = 0; //Draw speedlines in current suqare (lower) DrawMomLinesUp = 0; //Draw Momentum lines Up DrawMomLinesDn = 0; //Draw Momentum lines Down DrawCircles = 1; //Draw the Circles DrawBaby8 = 0; //Draw the Baby Eighths DrawTimeLines = 1; //Draw the vertical time lines IgnoreWicks = 1; //calc high/low price using closes only VerticalColors = 0; //color vertical time lines? IntraFrame = 0; //start intraday frame on today's open Padding = 15; //Max bars to pad on right when creating new price/time square nDispInBorder = 0; //write 8th line price values to chart (1=write the to actual price pane) nUseButtons = 1; //display buttons on chart by default //"ZeroDate" seems to be ?????. We need to find the offset back to that date. ZeroDate = new Date( START_DATE ); //Reset program options if criteria passed directly to main function //== get the frame size if (frSize != null) { if (frSize == 999) { AutoFrame = 1; CandlesPerSquare = 8; } else { if ((frSize != 8) && (frSize != 16) && (frSize != 32) && (frSize != 64) && (frSize != 128)) { displayError("Error: Invalid Frame Size. (8, 16, 32, 64 or 128)"); return; } CandlesPerSquare = frSize; } } //== get the optimal increment (used with AutoFrame) if (frOptIncrement != null) { if (isNaN(frOptIncrement)) { displayError("Error: frIncrement must be a positive real number."); return; } BestSize = frOptIncrement; } //== get the frame multiplier if (frMult != null) { if ((frMult != 1) && (frMult != 1.5) && (frMult != 2)) { displayError("Error: Invalid Frame Multiple! (1, 1.5 or 2 )"); return; } FrameMultiplier = frMult; } //== draw speed lines up? if (frSpeedUp != null) { if ((frSpeedUp != 0) && (frSpeedUp != 1)) { displayError("Error: Invalid SpeedLine selection (0 or 1 only)"); return; } DrawSpeedLinesUp = frSpeedUp; } //== draw speed lines down? if (frSpeedDn != null) { if ((frSpeedDn != 0) && (frSpeedDn != 1)) { displayError("Error: Invalid SpeedLine selection (0 or 1 only)"); return; } DrawSpeedLinesDn = frSpeedDn; } //== draw momentum lines up? if (frMomUp != null) { if ((frMomUp != 0) && (frMomUp != 1)) { displayError("Error: Invalid Momentum Line selection (0 or 1 only)"); return; } DrawMomLinesUp = frMomUp; } //== draw momentum lines down? if (frMomDn != null) { if ((frMomDn != 0) && (frMomDn != 1)) { displayError("Error: Invalid Momenetum Line selection (0 or 1 only)"); return; } DrawMomLinesDn = frMomDn; } //== ignore wicks in price calculations? if (frIgnoreWicks != null) { if ((frIgnoreWicks != 0) && (frIgnoreWicks != 1)) { displayError("Error: Invalid Wicks selection (0 or 1 only)"); return; } IgnoreWicks = frIgnoreWicks; } //== draw circles? if (frCircles != null) { if ((frCircles != 0) && (frCircles != 1)) { displayError("Error: Invalid Conflict Circles selection (0 or 1 only)"); return; } DrawCircles = frCircles; } //== get right margin clipping if (frRightMargin != null) { if (isNaN(frRightMargin)) { displayError("Error: frRightMargin must be a positive integer value."); return; } Padding = Math.round( frRightMargin ); } //== get the start date for frame calculations if (frStartDate != null) { ZeroDate = new Date( frStartDate ); if (ZeroDate == null) { displayError("Error: Invalid Frame Start Date (Example: October 5, 2005)"); return; } } //== use on-screen buttons? if (frUseButtons != null) { if ((frUseButtons != 0) && (frUseButtons != 1)) { displayError("Error: Invalid nUseButtons selection (0 or 1 only)"); return; } nUseButtons = frUseButtons; } //== draw vertical timelines? if (frTimeLines != null) { if ((frTimeLines != 0) && (frTimeLines != 1)) { displayError("Error: Invalid Time Lines selection (0 or 1 only)"); return; } DrawTimeLines = frTimeLines; } //== draw baby eighths? if (frBaby8 != null) { if ((frBaby8 != 0) && (frBaby8 != 1)) { displayError("Error: Invalid Baby Eighths selection (0 or 1 only)"); return; } DrawBaby8 = frBaby8; } //== Color Vertical Lines? if (frVertColor != null) { if ((frVertColor != 0) && (frVertColor != 1)) { displayError("Error: Invalid Vert Color selection (0 or 1 only)"); return; } VerticalColors = frVertColor; } //get our proximity value for alerts as a percentage nProx = Proximity/100; nAlertLevel = eval( AlertLev ); bAlertSound = alertUseSound == "T"; bAlertList = alertUseList == "T"; //Here, we check to see if the user has changed any options via the onscreen //buttons. Each config item is represented as an array member. for (x=1; x<15; x++) { if (aButtonArray[x] != 0) { nTmp = aButtonArray[x]; //Frame Size if (x==1) { if (nTmp==999) { AutoFrame = 1; CandlesPerSquare = 8; } else { AutoFrame = 0; CandlesPerSquare = nTmp; } } //SpeedLines Up if (x==5) { if (nTmp==1) { DrawSpeedLinesUp = 1; } else { DrawSpeedLinesUp = 0; } } //SpeedLines Down if (x==6) { if (nTmp==1) { DrawSpeedLinesDn = 1; } else { DrawSpeedLinesDn = 0; } } //MonLines Up if (x==7) { if (nTmp==1) { DrawMomLinesUp = 1; } else { DrawMomLinesUp = 0; } } //MomLines Down if (x==8) { if (nTmp==1) { DrawMomLinesDn = 1; } else { DrawMomLinesDn = 0; } } //Baby Eighths if (x==14) { if (nTmp==1) { DrawBaby8 = 1; } else { DrawBaby8 = 0; } } } } //nMultiplier is "Price Multiplier" and should typically be left at 1. nMultiplier = 1.0; //*************************************** //***** END OF USER CONFIGURATION OPTIONS //*************************************** //****** CODE STARTS HERE *************** //*************************************** //called on each new bar if ( getBarState() == BARSTATE_NEWBAR ) { nBarCounter++; } //if we are intraday, get the offset back to the user-selected IntraFrame. //If IntraFrame is 0 then frame will start on today's first bar, if IntraFrame //is 1 then frame will start on yesterday's first bar. nOffset = 0; //get time stamp of last bar on record var dtTime = getValue("time", 0 ); Yr1 = dtTime.getYear(); Mo1 = dtTime.getMonth(); Da1 = dtTime.getDay(); if(dtTime != null) { //IntraFrame is 0 so just get offset to today's open if (IntraFrame == 0) { //if before mkt open then force our frame display if ((Da1==Da) && (Mo1==Mo) && (Yr1==Yr)) { //trading day has started so get index of first bar of today nOffset = ( getFirstBarIndexOfDay(dtTime) ); if (nOffset == null) { displayError("Error: Offset to First Bar of Day is NULL."); return; } } //Dates don't match up, probably because market not open yet else { //If it's not Sat or Sun, set offet ahead to beginning of "today" if ((Da != 0) && (Da != 6)) { //!!Noticed issue just when market opens but before first bar "completes". Offset //appears to be incorrect. Hopefully, this code will fix that. It is not a major //problem because everything lines up correctly as soon as the first bar of the day //completes but it is annoying. //This logic basically says that if the system date and the date value of the last COMPLETED bar //in ESignal don't match up BUT it is later than 9:30AM EST (i.e., market is open), set the offset //to 0 (i.e., the bar currently being drawn). After this first bar completes this will no longer //be an issue since this code no longer get called. if ((Hrs==9) && (Min>30)) { nOffset = 0; } else { nOffset = 1; } //nOffset = ( getFirstBarIndexOfDay(dtTime) ); myPrint("Offset to 1st bar of Day: ", nOffset); } //Otherwise, set offset back to beginning of last trading day //So, if we are looking at a chart over the weekend, the frame //will still be starting on the first bar of Friday. else { nOffset = ( getFirstBarIndexOfDay(dtTime) ); if (nOffset == null) { displayError("Error: Offset to First Bar of Day is NULL."); return; } myPrint("Offset to 1st bar of Day: ", nOffset); } } } //else IntraFrame must be 1 so get offset to yesterday's open else { if ((Da1==Da) && (Mo1==Mo) && (Yr1==Yr)) { //trading day has started so get index of first bar of yesterday nOffset = getPreviousTradingDay(dtTime); myPrint("Prev Day: ", nOffset ); if (nOffset != null) { //now, get the index of the first bar nOffset = ( getFirstBarIndexOfDay(nOffset) ); if (nOffset == null) { displayError("Error: Offset to First Bar of Day is NULL."); return; } myPrint("Offset to 1st bar of Day: ", nOffset); } else { displayError("Error: Offset to Previous Trading Day is NULL."); return; } } //trading day has not yet started so, technically, we are still displaying //yesterday's data. So, set offset to first bar of day. else { nOffset = ( getFirstBarIndexOfDay(dtTime) ); if (nOffset == null) { displayError("Error: Offset to First Bar of Day is NULL."); return; } } } } else { displayError("Error obtaining system TIME."); return; } //reset our graphic ID global variable so that a fresh redraw will take place on each new bar grID = 0; //nOffset = 0; //clear our workspace clearLines(); clearText(); //If user selected the "AutoFrame" option, we will attempt to find the best frame size to use in the //current situation. We do this by cycling through all of the available frame sizes (e.g., 8, 16, 32, //64 and 128) and seeing which one generates an increment that most closely matches our preferred //increment size "BestSize"). If user is not using the AutoFrame option then this code is skipped and //the user's specified CandlesPerSquare setting is used instead. if (AutoFrame == 1) { CandlesPerSquare = 8; } //Initialize our status variable nBestDiff = 99999999.0; for (xx=1; xx<=6; xx++) { if ( xx>1 ) { CandlesPerSquare = CandlesPerSquare * 2; } //On the 6th (and last) pass, set the CandlesPerSquare to the best //setting we have found so far and perform the calcs one last time. //This will give the frame size that has the 1/8th increment that is the //closest (in terms of absolute distance) from the preferred increment //size. if (xx==6) { CandlesPerSquare = nBestFrameSize; } //Get the offset back to the Frame Reset Date (will change each year) //We only use this with Daily bars... ignored with intraday bars since //we use a different approach to set the frame start. if (getInterval()=='D') { BarsToZero = getFirstBarIndexOfDay( ZeroDate ); if (BarsToZero == null) { displayError("Error: StartDate must be a valid trading day (no weekends or holidays)"); return; } myPrint("Offset to zero date: ", BarsToZero ); } //added code to support weekly and monthly charts else if ((getInterval()=='W') || (getInterval()=="M")) { aDates = getValue("time", 0, -70); nFoundOffset = 0; for (x=0; x<70;x++) { if (aDates[x] 0 it means we are intraday and we are looking at a chart //before the market open. if (BarsToZero>0) { nNewFrame = -1; } else { nNewFrame = Math.abs(BarsToZero%CandlesPerSquare); } //nFirstFrame is the offset back to the first price/time square that we will draw //We will ultimately draw 4 complete price/time squares with the last square containing //the recent data for analysis nFirstFrame = nNewFrame + CandlesPerSquare * 3; //Number of blanks to pad on right of chart to complete last square nBarsToPad = CandlesPerSquare - nNewFrame; //LookBack is the # of days we will scroll back in time to find the //highest and lowest prices traded. If the FrameMultiplier is being //used we will take care of that here nLookBack = ( CandlesPerSquare * FrameMultiplier ); if (FrameMultiplier == 1.5) { nLookBack = nLookBack + 1; } /* load the last x bars into our arrays for calculation purposes */ aHighs = getValue("High", 0, -nLookBack); aLows = getValue("Low", 0, -nLookBack); vCloses = getValue("Close", 0, -nLookBack); vOpens = getValue("Open", 0, -nLookBack); nPriceHigh = -99999999.0; nPriceLow = 99999999.0; //find highest high in during lookback //if IgnoreWicks is on, only look at closes/opens for (x=0; xnPriceHigh) { nPriceHigh = nExtreme; nOffsetHigh = x; } } else { if (aHighs[x]>nPriceHigh) { nPriceHigh = aHighs[x]; nOffsetHigh = x; } } } //find lowest low during lookback //if IgnoreWicks is on, only look at closes/opens for (x=0; x=nPriceHigh) { return; } //Record the price range based on our high and low nPriceRange = (nPriceHigh - nPriceLow); //Calculate Scale Frame if (nPriceHigh>25) { if (Math.log(0.4*nPriceHigh)/Math.log(10)-Math.floor(Math.log(0.4*nPriceHigh)/Math.log(10)) > 0) { nSR = Math.exp(Math.log(10)*(Math.floor(Math.log(0.4*nPriceHigh)/Math.log(10))+1)); } else { nSR = Math.exp(Math.log(10)*(Math.floor(Math.log(0.4*nPriceHigh)/Math.log(10)))); } nSRA = rnd(nSR,9); } else { nSR = 100.0 * Math.exp(Math.log(8) * (Math.floor(Math.log(0.005*nPriceHigh)/Math.log(8)))); nSRA = rnd(nSR,9) * 8; } //Round Scale Frame to 9 digits nSR = rnd(nSR,9); N = 0.0; if (Math.log(nSR/(nPriceHigh-nPriceLow))/Math.log(8) <= 0){ N = 0; } else { if (Frac( Math.log(nSR/(nPriceHigh-nPriceLow))/Math.log(8)) == 0) { N = Math.floor( Math.log(nSR/(nPriceHigh-nPriceLow))/Math.log(8)); } else { N = Math.floor( Math.log(nSR/(nPriceHigh-nPriceLow))/Math.log(8))+1; } } //Calculate Scale Interval and top and bottom of frame nSI = nSR*Math.exp( -(N)*Math.log(8) ); M = Math.floor(( (1.0/Math.log(2)) * Math.log((nPriceHigh-nPriceLow)/nSI)) + 0.0000001); I = Math.round(( (nPriceHigh+nPriceLow) * 0.5) / (nSI*Math.exp((M-1)*Math.log(2)))); nxBot = (I-1)*nSI*Math.exp((M-1)*Math.log(2)); nxTop = (I+1)*nSI*Math.exp((M-1)*Math.log(2)); //Determine if we need to shift frame if ((nPriceHigh-nxTop>0.25*(nxTop-nxBot)) || (nxBot-nPriceLow>0.25*(nxTop-nxBot))) { nER = 1; } else { nER = 0; } if (nER==0) { MM = M; NN = N; } else if ((nER==1) && (M<2)) { MM = M+1; NN = N; } else { MM = 0; NN = N-1; } //Recalc Scale Interval and top and bottom of frame nSI = rnd( nSR*Math.exp(-(NN)*Math.log(8)), 9); I = Math.round(((nPriceHigh+nPriceLow)*0.5)/(nSI*Math.exp((MM-1)*Math.log(2)))); nxBot = (I-1)*nSI*Math.exp((MM-1)*Math.log(2)); nxTop = (I+1)*nSI*Math.exp((MM-1)*Math.log(2)); //Calculate our line increment nIncrement = (nxTop-nxBot)/8.0; //If we are not using the AutoFrame feature, just break out //of this loop now... we are done. if ( AutoFrame != 1 ) { break; } //Keep track of the frame size that creates a 1/8th increment //that most closely matches our preferred increment size if (xx<6) { if ( Math.abs( BestSize-nIncrement ) < nBestDiff ) { nBestFrameSize = CandlesPerSquare; nBestDiff = Math.abs( BestSize-nIncrement ); } } } //Determine number of octaves if (nSR==nSI) { nOctaves = 1; } else { nOctaves = 1; nOctLev = nSR; while (1==1) { nOctLev = nOctLev/8; nOctaves++; if (nOctLev<=nSI) { break; } if (nOctaves>30) { break; } } } //debug print lines //myPrint("Scale Frame: ", nSRA); //myPrint("Scale Interval: ", nSI); //myPrint("Frame Interval: ", nIncrement); //myPrint("Frame Height: ", nxTop-nxBot); //myPrint("Frame Hgt (nSI): ", (nxTop-nxBot)/nSI); //myPrint("nOctaves: ", nOctaves); //myPrint("Frame Top: ", nxTop); //myPrint("Frame Bot: ", nxBot); //myPrint("Price High: ", nPriceHigh); //myPrint("Price Low: ", nPriceLow); //myPrint("Price Range: ", nPriceRange); //Resolve our offset (e.g., we don't need it anymore) if (getInterval()=='D') { nOffset = 1; } else { nOffset = 0; } nLeftSide = -(nFirstFrame); //Clip new price/time squares if we are in an intraday time frame //This conserves a lot of otherwise wasted space on right side of //screen. nRightSide = nBarsToPad; if (getInterval()!='XX') { //Enable for all for now nRightSide = Math.min( nBarsToPad, Padding ); } //02/06/03 - modified script so that only the first 3 levels in // the oversold/overbought zones will be displayed if (CompactFormat==false) { nUBound = nxBot+((16)*nIncrement); nLBound = nxBot-((8)*nIncrement); } else { nUBound = nxBot+((11)*nIncrement); nLBound = nxBot-((3)*nIncrement); } nSegment = Math.floor( ( nFirstFrame-nNewFrame )/24 ); //Should never be less than 2 but put this in just in case //so script won't lock up if (nSegment==0) { nSegment = 1; } myPrint("XSegment: ", nSegment); //Alerts Code nTmp = nxBot+(nAlertLevel*nIncrement); nUEnv = nTmp + (nTmp * (nProx)); nLEnv = nTmp - (nTmp * (nProx)); //if current close is within the prescribed % envelope around the desired MM level, //generate an alert. if ( close(0)>=nLEnv && close(0)<=nUEnv && nBarCounter != nLastAlertBar ) { generateAlert( 1, null, bAlertList, bAlertSound ); nLastAlertBar = nBarCounter; } //Note: Here I am setting the Octave count back to 1 so that the chart will display only the //MML values directly to the right of the MMI values. If you want the chart to display all //possible bMML, mMML and MML values then just REM out the following line. nOctaves = 1; nSR = nSRA; //Kludge to move labels up a bit so they center on the lines nAdjust = nIncrement/5; //display our buttons if so selected by user if (nUseButtons == 1) { nFlags = Text.ONTOP | Text.BUTTON | Text.RELATIVETOLEFT | Text.RELATIVETOBOTTOM; _Font = "Arial"; _Size = 10; cX = getTextWidth( " Mom Dn ", _Font, _Size ); cY = getTextHeight( "AAYY", _Font, _Size ); //collapse our buttons and just display the 'restore' button. if (bCollapsed==true) { drawTextPixel(ButtonOffset, 13, " (+) " + "@URL=EFS:setRestore", Color.red, Color.grey, nFlags, _Font, _Size, gID()); } //otherwise, display all of our buttons else { nStart = nxBot-(2*nIncrement); z = 30; for (x=1; x<13; x++) { nTmp = " "+aLabels[x]; drawTextPixel(ButtonOffset, z, nTmp + "@URL=EFS:set"+x, Color.black, Color.grey, nFlags, _Font, _Size, gID(), cX, cY); z+=cY+4; } drawTextPixel(ButtonOffset,13, "Murrey Tool@URL=EFS:callES", Color.grey, Color.aqua, Text.FRAME | Text.LEFT | Text.RELATIVETOLEFT | Text.RELATIVETOBOTTOM, "Comic Sans MS", 10, gID()); } } //display our ATR if ( AutoFrame == 1 ) { sStr = "AutoFrame is ON"; } else { sStr = "AutoFrame is OFF"; } drawTextPixel(114,2, sStr, Color.grey, null , Text.LEFT | Text.RELATIVETOLEFT | Text.RELATIVETOBOTTOM, null, 10, gID()); //draw out horizontal MM lines nAbsBot = nxBot-(8*nIncrement); for (x=0; x<25; x++) { if (CompactFormat==true) { if ((x<5) || (x>19)) continue; } drawLineAbsolute(-((nFirstFrame+nOffset)-1), nAbsBot+((x)*nIncrement), nRightSide, nAbsBot+((x)*nIncrement), PS_SOLID, ((x==24) || (x==0)) ? 2 : aThick[x%8], aColor[x%8], gID()); if (x<8) { drawTextAbsolute(nRightSide+2, nAbsBot+((x)*nIncrement), "-"+ ((x==0) ? 8 : (8-x)%8) +"/8", Color.blue, null, Text.LEFT | Text.VCENTER | Text.BOLD , null, 11, gID()); } else if (x>16) { drawTextAbsolute(nRightSide+2, nAbsBot+((x)*nIncrement), "+"+ ((x==24) ? 8 : (x)%8) +"/8", Color.blue, null, Text.LEFT | Text.VCENTER | Text.BOLD , null, 11, gID()); } else { drawTextAbsolute(nRightSide+2, nAbsBot+((x)*nIncrement), " "+ ((x==16) ? 8 : (x%8)) +"/8", Color.black, null, Text.LEFT | Text.VCENTER | Text.BOLD , null, 11, gID()); } drawTextAbsolute(nRightSide+(7), nAbsBot+((x)*nIncrement), " "+priceFormat( ( nAbsBot+((x)*nIncrement) ) ), PriceColor, null, PriceAttrib, null, 11, gID()); //draw baby eighths if (DrawBaby8==1 && (CompactFormat==true ? x<19 : x<24) ) { drawLineAbsolute(-((nFirstFrame+nOffset)-1), nAbsBot+((x+0.5)*nIncrement), nRightSide, nAbsBot+((x+0.5)*nIncrement), PS_SOLID, ((x==24) || (x==0)) ? 2 : 1, Color.grey, gID()); drawTextAbsolute(nRightSide+(7), nAbsBot+((x+0.5)*nIncrement), " "+priceFormat( ( nAbsBot+((x+0.5)*nIncrement)) ), Color.teal, null, PriceAttrib, null, 11, gID()); } //Display the MML, MMML, mMML and bMML lines based on number of Octaves for (z=(nOctaves-1); z>=0; z--){ nLevUp = (( (nAbsBot+(x*nIncrement))/(nSR/Math.pow(8,z))) - Math.floor( ((nAbsBot+(x*nIncrement))/(nSR/Math.pow(8,z))) )) * 8.0; if ( (nLevUp%1 == 0) && (nUseButtons != 55)) { drawTextAbsolute(nRightSide+(5*(z+2)+2), nAbsBot+(x*nIncrement)+nAdjust, " ["+nLevUp+"/8]", Color.maroon, null, Text.CENTER | Text.BOLD, null, 11, gID()); } } } //Display titles for our Label columns if (nUseButtons != 55) { drawTextAbsolute(nRightSide+2, nUBound+(2*nIncrement), " "+"[Frame Size: "+nSR+"]", Color.black, null, Text.Top | Text.BOLD, null, 11, gID()); drawTextAbsolute(nRightSide+2, nUBound+(1*nIncrement), " "+"[MMI]", Color.black, null, Text.Top | Text.BOLD, null, 11, gID()); for (z=(nOctaves-1); z>=0; z--) { if (z==0) { sTitle = "MML"; } else if (z==1) { sTitle = "MMML"; } else if (z==2) { sTitle = "mMML"; } else if (z==3) { sTitle = "bMML"; } drawTextAbsolute(nRightSide+(4*(z+2)+2), nUBound+(1*nIncrement), " ["+sTitle+"]", Color.black, null, Text.Top | Text.BOLD, null, 11, gID()); } } //Draw our vertical time lines (vertical 1/8ths) and also draw our Circles x = 0; for (z=nLeftSide; z<=nRightSide+10; z=(z+nSegment)) { if (DrawTimeLines==1) { //draw the vertical lines, every 8th bar draw the beginning of the next square in blue if (x%8 == 0) { if (z<=nRightSide) { drawLineAbsolute(z, nLBound, z, nUBound, PS_SOLID, 2, PriVLineColor, gID()); } } else { if (z<=nRightSide) { if ( VerticalColors == true ) { drawLineAbsolute(z, nLBound, z, nUBound, PS_SOLID, 1, aColor[x%8], gID()); } else { drawLineAbsolute(z, nLBound, z, nUBound, PS_SOLID, 2, RegVLineColor, gID()); } } } } //now draw our Circles if ( (x%2 == 0) && (DrawCircles == 1) ) { if (x%4 != 0) { gCircle(nLeftSide+(x-1)*nSegment, nxBot+(1*nIncrement), nSegment, nIncrement, nRightSide); gCircle(nLeftSide+(x-1)*nSegment, nxBot+(5*nIncrement), nSegment, nIncrement, nRightSide); } if ((x==4) || (x==12) || (x==20) || (x==28) ) { gCircle(nLeftSide+(x-1)*nSegment, nxBot+(3*nIncrement), nSegment, nIncrement, nRightSide); } } x++; } if (DrawTimeLines==1) { //Draw in our first frame line drawLineAbsolute( nLeftSide, nLBound, nLeftSide, nUBound, PS_SOLID, 2, PriVLineColor, gID()); } //Code to draw speed lines in the most recent price/time square //User can select to draw Upper and/or Lower speed lines nLVBase = nLeftSide + (24 * nSegment); nlTop2 = nxTop; nlTop1 = 0; if (DrawSpeedLinesUp == 1) { for (x=2; x<=14; x=x+2) { if (x>8) { nlTop2 = (nlTop2-(2*nIncrement)); nlTop1 = (nLVBase + (8*nSegment)); //nRightSide; } else { nlTop1 = nLVBase+(x*nSegment); } nPlot1 = ClipLine(0, nRightSide, nLVBase, nxBot, nlTop1, nlTop2); nPlot2 = ClipLine(1, nRightSide, nLVBase, nxBot, nlTop1, nlTop2); drawLineAbsolute(nLVBase, nxBot, nPlot1, nPlot2, PS_SOLID, 1, UpSpeedLineColor, gID()); } } nlBot2 = nxBot; nlBot1 = 0; if (DrawSpeedLinesDn == 1) { for (x=2; x<=14; x=x+2) { if (x>8) { nlBot2 = (nlBot2+(2*nIncrement)); nlBot1 = (nLVBase + (8*nSegment)); } else { nlBot1 = nLVBase+(x*nSegment); } nPlot1 = ClipLine(0, nRightSide, nLVBase, nxTop, nlBot1, nlBot2); nPlot2 = ClipLine(1, nRightSide, nLVBase, nxTop, nlBot1, nlBot2); drawLineAbsolute(nLVBase, nxTop, nPlot1, nPlot2, PS_SOLID, 1, DnSpeedLineColor, gID()); } } //Code to draw momentum lines in the most recent price/time square //User can select to draw Upper and/or Lower momentum lines nlTop1 = 0; nlTop2 = nxBot; nlBot1 = 0; nlBot2 = nxBot; if (DrawMomLinesDn == 1) { for (x=2; x<=14; x=x+2) { if (x>8) { nlTop1 = nlTop1 + (2 * nSegment); nlTop2 = nxTop; nlBot2 = nlBot2 + (2 * nIncrement); nlBot1 = nLVBase + (8 * nSegment); } else { nlTop1 = nLVBase; nlBot2 = nxBot; nlTop2 = nxBot + (x * nIncrement); nlBot1 = nLVBase + (x * nSegment); } if (nlTop18) { nlTop1 = nlTop1 + (2 * nSegment); nlTop2 = nxBot; nlBot2 = nlBot2 - (2 * nIncrement); nlBot1 = nLVBase + (8 * nSegment); } else { nlTop1 = nLVBase; nlBot2 = nxTop; nlTop2 = nxBot + ((8-x) * nIncrement); nlBot1 = nLVBase + (x * nSegment); } if (nlTop18) { sbText = "OverBought (Frame: "+CandlesPerSquare+")"; ssText = "OverSold (Frame: "+CandlesPerSquare+")"; } else { sbText = "OverBought"; ssText = "OverSold"; } if ((nLVBase+14)= 564) { return( formatPriceNumber( nValue ) ); } return( nValue ); } //== gCircle function draws our Circles (which //== are actually diamonds). It performs clipping //== when necessary so the lines do not extend past the end //== of the visible price/time square. function gCircle( x, y, xStep, yStep, Right ) { var z; var xx1, xx2; var yy1, yy2; var tmpx, tmpy; for (z=1; z<=4; z++) { if (z==1) { xx1 = x; yy1 = y+yStep; xx2 = x+xStep; yy2 = y+(2*yStep); } else if (z==2) { xx1 = x; yy1 = y+yStep; xx2 = x+xStep; yy2 = y; } else if (z==3) { xx1 = x+xStep; yy1 = y+(2*yStep); xx2 = x+(2*xStep); yy2 = y+yStep; } else if (z==4) { xx1 = x+xStep; yy1 = y; xx2 = x+(2*xStep); yy2 = y+yStep; } if (xx1Right) { tmpx = ClipLine(0, Right, xx1, yy1, xx2, yy2); tmpy = ClipLine(1, Right, xx1, yy1, xx2, yy2); xx2 = tmpx; yy2 = tmpy; } drawLineAbsolute(xx1, yy1, xx2, yy2, PS_SOLID, 2, ConflictColor, gID()); } } } //== ClipLine function returns new endpoints for a line drawn when //== we need to clip it for display purposes. //== Call it once for the X coordinate and again for the Y coordinate function ClipLine( XorY, Right, x1, y1, x2, y2 ){ var vSlope; var xVal; var yVal; if (x2<=Right) { if (XorY==0) { return x2; } else { return y2; } } else { vSlope = (y2-y1)/(x2-x1); if (x2>Right) { xVal = Right; yVal = y1 + (vSlope * (Right - x1)); } if (XorY==0) { return xVal; } else { return yVal; } } } //== getOffset function determines the first vertical offset for the day //== based upon 11:58am being the exact center of the trading day. Some //== of this is hard-coded for now (i.e., the setHours stuff). function getOffset( Interval, Frame ) { var dtToday = new Date(); var dtText = new Date(); var nDiff; var nTmp; var Result; //when subtracting dates, divide by 1000 and then by 60 to obtain the minutes dtToday.setHours(11, 58, 0); dtText.setHours(9, 30, 0); nDiff = (((dtToday-dtText)/60)/1000); nDiff = (nDiff/Interval); nTmp = nDiff % Frame; if (nTmp==nDiff) { Result = Frame-nDiff; } else { Result = Frame-nTmp; } return( Math.ceil(Result) ); } //== gID function assigns unique identifier to graphic/text routines function gID() { grID++; return( grID ); } //== round to N digits. function rnd(value, N) { var n; var mult=1; for(n=0;n= 564) { askForInput("MM Script Parameters"); } } return; }