/********************************************************** Description : Simple Risk/Reward Analysis Tool Garth Doverspike (c) Copyright 2003 ***********************************************************/ /**************************************************************************************** RiskReward - 12/08/03 RGD V0.0 Start Code 12/11/03 RGD V0.1 Added Point Value 12/30/03 RGD V0.2 Fixed floating button as newbars are painted Feel free to modify this code and use for your own private use. Please keep the corpyright headers intact. If you want to use this code as part of a commercial package please contact me first. * This code is provided as an example with no implied fitness for any specific application. USE AT YOUR OWN RISK. * There is no warranty expressed or implied on functionality - Use at your own risk * If you find a bug please email me This formula will allow a user to select an entry point, a target and a stop loss and will calculate risk reward statistics based on those selections. The formula will prompt the user to guide them through. *****************************************************************************************/ /****************************************************************************************** Use and Comments: Use: 1. After loading the formula, follow the prompts that should appear in the upper right portion of the chart. 2. Clicking on Risk/Reward will bring up or turn off the display of the risk/reward info 3. Reset will reset the risk/reward numbers AND start the process of allowing you to enter new numbers (via left mouse clicks) 4. You have three options on how clicks will be treated: a) The default is to use the price magnet and pick the bars OHLC value that was closest to the mouse click b) You can also use the magnet, but specify for each (Entry, Profit Target and Stop Loss) which OHLC value to use c) You can turn the price magnet off and use the exact values of the mouse position for price. d) While the Price Magnet is on or off for the entire chart, you can mix and match a&b. You can modify change these by using the edit studies dialog box. 5. **NOTE**NOTE**NOTE** In the current build (7.5 B627) the tip of the mouse curosr is used to determine mouse placement...and this can be different from where the crosshair cursor. This can get confusing as the red crosshair cursor determines what is shown in the cursor window, and that MIGHT be one bar later than where the tip of the mouse cursor is pointed! Bottom line: Use the tip of the mouse cursor for placement with the RiskReward tool...not the red crosshair. **NOTE**NOTE**NOTE** 6. **NOTE**NOTE**NOTE** Once you hit reset, ANY mouse click on the chart will be used as the input to the prompted for paramter. This means if you click on a button, the price value for that button will be used. If you click to scroll the chart, the price at that point will be used. Bottom line: Once you hit reset only click on the three points as you are prompted for them. If you forget, you can hit reset again and start over. Comments: Capturing mouse clicks is new to 7.5 as of B627. All the mouse click function available at this time are listed below in the code, most are commented out. I only use the sinlge left mouse button code in this EFS. I'm hoping in time we can get some features added to the EFS engine that will allow us to support things like collecting mouse clicks AND allow the user to scroll a chart without it confusing the heck out of the EFS. There are a few other things as well...but this is a start - and a nice one. Here come the user built line tools! *******************************************************************************************/ // Local Globals // These variables are not reset for each interations of main var bGo = false; var grID = 0; var nEntryP = 0, nStopP = 0, nTargetP = 0; var nRRRatio = 0, nRRLoss = 0, nRRProf = 0; var bRRon = false; var bPMag = false; var sEBT = null; var sSBT = null; var sTBT = null; var nPV = 1; var vState = 0; /* State Flags */ var nEntryState = 1; var nProfitTargetState = 2; var nStopState = 3; /* End State Flags */ // Support Functions // rnd function - round to two places function rnd(value) { // Round the price to two digits value *= 100; return Math.round(value, 2)/100; } // displayMsg function - Display Message to user function displayMsg( Msg ) { //clearText(); drawTextRelative(-30, 0, Msg, Color.maroon, Color.lightgrey, Text.FRAME | Text.ONTOP | Text.RELATIVETORIGHT | Text.RELATIVETOTOP, null, 20, "TheSame"); } // gID function - assigns unique identifier to graphic/text routines function gID() { grID++; return( grID ); } // ResetCallBack function - resets the globals and init's mouse grabs if the reset button is pressed function ResetCallBack(nButtonPressed) { if(nButtonPressed == BUTTON_LEFT){ // Cleanup // clearText(); // clearLines(); // Set proper state for new picks bGo = true; vState = nEntryState; nEntryP = 0; nStopP = 0; nTargetP = 0; nRRRatio = 0; nRRLoss = 0; nRRProf = 0; } } // drawRRResults function - Display Risk/Reward results function drawRRResults() { drawTextRelative(-20, 180, "Entry = " + rnd(nEntryP), Color.yellow, Color.navy, Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "Line1"); drawTextRelative(-20, 160, "Profit Target = " + rnd(nTargetP), Color.yellow, Color.navy, Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "Line2"); drawTextRelative(-20, 140, "Stop = " + rnd(nStopP), Color.yellow, Color.navy, Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "Line3"); drawTextRelative(-20, 120, "RR Ratio = " + rnd(nRRRatio), Color.yellow, Color.red, Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "Line4"); if (nPV == 1){ drawTextRelative(-20, 100, "RR Profit = " + rnd(nRRProf), Color.yellow, Color.red, Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "Line5"); drawTextRelative(-20, 80, "RR Loss = " + rnd(nRRLoss), Color.yellow, Color.red, Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "Line6"); } else { // Added v0.1 for Point Value drawTextRelative(-20, 100, "RR Profit = " + rnd(nRRProf) + " (" + rnd(nRRProf*nPV) + ")", Color.yellow, Color.red, Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "Line5"); drawTextRelative(-20, 80, "RR Loss = " + rnd(nRRLoss) + " (" + rnd(nRRLoss*nPV) + ")", Color.yellow, Color.red, Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "Line6"); } } // calcRR function - Calculate Risk/Reward function calcRR() { if (nEntryP < nTargetP){ // Must be a Long nRRProf = (nTargetP - nEntryP); nRRLoss = (nEntryP - nStopP); } else { // A Short nRRProf = (nEntryP - nTargetP); nRRLoss = (nStopP - nEntryP); } if (nRRProf != 0 || nRRLoss != 0) nRRRatio = nRRProf/nRRLoss; } // RWCallBack function - Displays or removes risk reward info function RWCallBack(nButtonPressed) { calcRR(); if (bRRon){ bRRon = false; }else{ bRRon = true; } if (bRRon){ drawRRResults(); } else { removeText("Line1"); removeText("Line2"); removeText("Line3"); removeText("Line4"); removeText("Line5"); removeText("Line6"); } } // CheckNear function - return the OHLC value closest to the mouse click function CheckNear(yValue, nOpen,nClose,nHigh,nLow) { if ( Math.abs(yValue-nOpen) <= Math.abs(yValue-nClose) ) { // Is click closer to Open or Close? // Open is closer if (Math.abs(yValue-nOpen) <= Math.abs(yValue-nHigh)) { // Is click closer to Open or High? // Open is closer if (Math.abs(yValue-nOpen) <= Math.abs(yValue-nLow)) { // Is click closer to Open or Low? // Open wins! return(nOpen); } else { // Low wins! return(nLow); } // High is closer than Open or Close } else if (Math.abs(yValue-nHigh) <= Math.abs(yValue-nLow)){ // Is click closer to High or Low? // High Wins! return(nHigh); } else { // Low Wins! return(nLow); } // Close is closer } else if (Math.abs(yValue-nClose) <= Math.abs(yValue-nHigh)) { // Is Click closer to Close or High? // Close is Closer if (Math.abs(yValue-nClose) < Math.abs(yValue-nLow)) { // Is Click closer to Close or Low? // Close Wins! return(nClose); } else { // Low Wins! return(nLow); } // High is closer } else if (Math.abs(yValue-nHigh) <= Math.abs(yValue-nLow)) { // Is Click closer to High or Low? // High Wins! return(nHigh); // Low Wins! } else { return(nLow); } } /* Mouse Button Code */ /* Mouse code not implemented in this EFS includes the following: function onLButtonUp( barIndex, yValue) { // Left Button Up - Doesn't seem to work debugPrint("LeftUp: " + barIndex + ", " + yValue); } function onRButtonDown( barIndex, yValue) { // Right Button Down - normal RT click menu also shows on chart debugPrint("RightDown: " + barIndex + ", " + yValue); } function onRButtonUp( barIndex, yValue) { // Right button up - Doesn't seem to work debugPrint("RightUp: " + barIndex + ", " + yValue); } function onLButtonDblClk( barIndex, yValue) { // Left button 2x click...you will get both LButton and LButtonDblClk debugPrint("LeftDblClk: " + barIndex + ", " + yValue); } function onRButtonDblClk( barIndex, yValue) { // Same as above but for Right button debugPrint("RightDblClk: " + barIndex + ", " + yValue); } */ // Implemnt Left Mouse button code: // onLButtonDown function - Prompt user for, and collect Entry, Profit and Stop info function onLButtonDown( barIndex, yValue) { // debugPrintln("RR LeftDown: " + barIndex + ", " + yValue); if (bGo){ if (bPMag == "true"){ // If Price Magnet Set // Determine Prices var nOpen = open(barIndex); var nClose = close(barIndex); var nHigh = high(barIndex); var nLow = low(barIndex); if (nOpen == null || nClose == null || nHigh == null || nLow == null){ // debugPrintln("Null return"); return; } } if (vState == nEntryState){ displayMsg("Click on entry price"); // debugPrintln("Entry"); vState++; } else if (vState == nProfitTargetState){ displayMsg("Click on target price"); // debugPrintln("Profit"); if (bPMag == "true"){ if (sEBT == "Nearest"){ // Get the nearest OHLC to click nEntryP = CheckNear(yValue, nOpen,nClose,nHigh,nLow); // debugPrintln("nEntryP = " + nEntryP); } else { // Just get the OHLC value specified in edit study nEntryP = getValue(sEBT, barIndex); if (nEntryP == null){ // debugPrintln(" Entry Null Return"); return; } } } else { // No Price Magnet - Just use exact mouse location // debugPrintln("Entry yValue"); nEntryP = yValue; } vState++; } else if (vState == nStopState){ displayMsg("Click on stop loss price"); // debugPrintln("Stop"); if (bPMag == "true"){ if (sSBT == "Nearest"){ nTargetP = CheckNear(yValue, nOpen,nClose,nHigh,nLow); } else { nTargetP = getValue(sTBT, barIndex); if (nTargetP == null){ // debugPrintln(" Profit Null Return"); return; } } } else { nTargetP = yValue; } vState++; } else if (vState == nStopState+1){ displayMsg("Click on reset to start"); // debugPrintln("Wrapup"); if (bPMag == "true"){ if (sTBT == "Nearest"){ nStopP = CheckNear(yValue, nOpen,nClose,nHigh,nLow); } else { nStopP = getValue(sSBT, barIndex); if (nStopP == null){ // debugPrintln(" Stop Null Return"); return; } } } else { nStopP = yValue; } vState = 0; bGo = false; } if (bRRon){ calcRR(); drawRRResults() } } else { displayMsg("Click on reset to start"); drawTextRelative(-10, 20, "Reset " + "@URL=EFS:ResetCallBack", Color.black, Color.red, Text.BUTTON | Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "RRRisk"); drawTextRelative(-25, 20, "Risk/Reward " + "@URL=EFS:RWCallBack", Color.black, Color.red, Text.BUTTON | Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "RRReset"); } } // PreMain functions defines the default look and feel for the study function preMain() { setPriceStudy(true); setStudyTitle("RiskReward"); /**************************************************************************** In the following section you determine the Label names for the returned plots and the specifications of the plots (Line style, thickness, color *****************************************************************************/ var fp1 = new FunctionParameter("bPriceMagnet", FunctionParameter.BOOLEAN); fp1.setName("Price Magnet"); fp1.setDefault(true); var fp2 = new FunctionParameter("sEntryBT", FunctionParameter.STRING); fp2.setName("Entry Bartype (must have Price Magnet = true)"); fp2.addOption("Open"); fp2.addOption("High"); fp2.addOption("Low"); fp2.addOption("Close"); fp2.addOption("Nearest"); fp2.setDefault("Nearest"); var fp3 = new FunctionParameter("sStopBT", FunctionParameter.STRING); fp3.setName("Stop Bartype (must have Price Magnet = true)"); fp3.addOption("Open"); fp3.addOption("High"); fp3.addOption("Low"); fp3.addOption("Close"); fp3.addOption("Nearest"); fp3.setDefault("Nearest"); var fp4 = new FunctionParameter("sTargetBT", FunctionParameter.STRING); fp4.setName("Target Bartype (must have Price Magnet = true)"); fp4.addOption("Open"); fp4.addOption("High"); fp4.addOption("Low"); fp4.addOption("Close"); fp4.addOption("Nearest"); fp4.setDefault("Nearest"); // Added V0.1 - Support for point multiple to determine actual $ amount var fp5 = new FunctionParameter("nPointValue", FunctionParameter.NUMBER); fp5.setName("Point Value"); fp5.setLowerLimit(1); fp5.setUpperLimit(1000); fp5.setDefault(1); } // Main - EFS workhorse (usually the work horse - not in this case however) function main(bPriceMagnet, sEntryBT, sStopBT, sTargetBT, nPointValue) { if (bPriceMagnet){ bPMag = bPriceMagnet; sEBT = sEntryBT; sSBT = sStopBT; sTBT = sTargetBT; } nPV = nPointValue; var nIndex = getCurrentBarIndex(); // var nBarState = getBarState(); // Get current bar state // if (nBarState == BARSTATE_ALLBARS){ // ALLBARS happens when all bars are being drawn (change of symbol or time) // } // Check for nIndex = 0 (all historic bars have been drawn) if (nIndex != 0){ return; } drawTextRelative(-10, 20, "Reset " + "@URL=EFS:ResetCallBack", Color.black, Color.red, Text.BUTTON | Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "RRRisk"); drawTextRelative(-25, 20, "Risk/Reward " + "@URL=EFS:RWCallBack", Color.black, Color.red, Text.BUTTON | Text.RELATIVETORIGHT | Text.RELATIVETOBOTTOM | Text.BOLD, null, null, "RRReset"); displayMsg("Click on reset to start"); // if (bGo){ // debugPrintln("bGo = " + bGo + " vState = " + vState); // } }