//+------------------------------------------------------------------+
//|                                            RiskLotIndicator.mq4  |
//| Donated by Lukeb, conversion of RiskLotCalculator EA to an       |
//| Indicator.  Created to show how to get various risk values for   |
//| risk management.  Demonstrates and displays a lot calculation    |
//| for a given risk to account equity.                              |
//|                                                                  |
//| I found similar discussion at these locations                    |
//|   http://articles.mql4.com/401  (Forex Trading ABC)              |
//|   http://codebase.mql4.com/1775 (Risk Advisor)                   |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Eric Babcock"
#property link      "http://www.forexcoachingpros.com/"
#property indicator_separate_window
   string IndicatorName = "RiskLotIndicator";

#define LOTMSGCOLOR Crimson
#define DEBUGMSGCOLOR DarkSlateGray

extern bool   UseFixedPipStopLoss = false; //  Set to true to use FixedPipsForRisk (Uses ATR in this example if false)
extern int    FixedPipsForRisk = 10;       //  Value for user chosen Pip Value example
extern int    TicksInAPip = 0;             //  it is usually 10 or 1; 10 if the broker supports fractional pips.  calculate if 0.  Need more direct formula to get this.
extern int    EquityRiskPercent = 2;       //  Percent of account equity to risk on a trade
extern int    MaxMarginPercentToUse = 30;  //  How much of the maximum Margin available should be used?
extern int    ATRBars = 10;                //  Number of bars to include in the ATR indication.
extern int    debugLevel = 2;              //  display debug info if not 0, program recognizes 0, 1 and 2 (greater than 1)


double riskToTake;   // convert risk Percent to a decimal value and store it here.
double dealerMaxLots; // Make it global just so it can be displayed.
double marginPctToUse;  // used to convert MaxMarginPercentToUse to a decimal value.
int    executionTime;
int    windowNum = 0;
string executionMsgText="initializing";

string tradeSizeObj = "LotTradeMsg";
string equityRiskObj = "EquityLotMsg";
string marginLotObj = "MarginLotMsg";
string executionMsgObj = "ExecutionSpeedMessage";

int init()
{
   IndicatorShortName(IndicatorName);
   int returnValue = 0;
   riskToTake = EquityRiskPercent;
   riskToTake = riskToTake/100;  // divide by 100 to put int percent into a decimal.
   marginPctToUse = MaxMarginPercentToUse;
   marginPctToUse = marginPctToUse/100;  // divide by 100 to put int percent into a decimal.
   
   returnValue = TestSystemInfo();
   
   if (returnValue <0)
   {
      Alert("The System is not providing information needed for the ",IndicatorName," indicator, exiting.  Please see the Experts Log.");
   }
   return(returnValue);
}

int deinit()
{
   ObjectDelete(tradeSizeObj);
   ObjectDelete(equityRiskObj);
   ObjectDelete(marginLotObj);
   ObjectDelete(executionMsgObj);
   return(0);
}

int start()
{
   executionTime = GetTickCount();
   windowNum=WindowFind(IndicatorName);
   double ticksInRisk;  //  Find the # of ticks between the entry and stop loss positions
   if (UseFixedPipStopLoss)
   {
      ticksInRisk = PipsToTicks(FixedPipsForRisk);
   } else
   {
       ticksInRisk = iATR(Symbol(), Period() , ATRBars, 0);
   }

   double lotsToOpen = LotsToOpen(riskToTake, ticksInRisk, marginPctToUse);  // Get the lots that can be opened for a Risk

   // The rest of "start()" is simply for display
    
   // Make ticksInRisk into an integer for display
   double minTickSize = MarketInfo(Symbol(), MODE_TICKSIZE);  // pre-calculate to use in the next line

   int ticksInRiskCount = MathFloor(ticksInRisk/minTickSize);          // Make an integer of ticksInRisk for display 

   string lotsToTradeText = MakeLotsToTradeText(lotsToOpen,ticksInRiskCount);
   DisplayObject(tradeSizeObj,lotsToTradeText,20,20,LOTMSGCOLOR);
   
   if ( debugLevel > 0 )
   {
      string equityRiskMsg = StringConcatenate("Your ",EquityRiskPercent," percent Equity Risk Limit is ",MaxtLotsForRisk(riskToTake, ticksInRisk)," lots for ",ticksInRiskCount," ticks.");
      DisplayObject(equityRiskObj,equityRiskMsg,140,20,DEBUGMSGCOLOR);
      
      string marginLimitMsg = StringConcatenate("Your ",MaxMarginPercentToUse," percent Margin Initiation Limit is ",MaxLotsThatCanBeOpened(marginPctToUse)," lots.");
      DisplayObject(marginLotObj,marginLimitMsg,500,20,DEBUGMSGCOLOR);
   }
   if ( debugLevel > 1 )
   {
      
      DisplayObject(executionMsgObj,executionMsgText,800,20,DEBUGMSGCOLOR);
   }
   executionTime = GetTickCount()-executionTime;
   executionMsgText = StringConcatenate("Execution Time = ",executionTime," milliseconds"); 
   return(0);
}

void DisplayObject(string objectLabel,string displayText, int xPos, int yPos, color textColor)
{
   int windowIndex = ObjectFind(objectLabel); // Find which window the object is in (does it exist?)
   if (windowIndex < 0) // No window found with the object in it - Create the object
   {//bool ObjectCreate( string name, int type, int window, datetime time1, double price1, datetime time2=0, double price2=0, datetime time3=0, double price3=0) 
      ObjectCreate(objectLabel, OBJ_LABEL, windowNum, 0, 0); // Create on the indicator window
      ObjectSet(objectLabel, OBJPROP_CORNER, 0);   // upper left corner
      ObjectSet(objectLabel, OBJPROP_XDISTANCE, xPos);  // justify at xPos from the right
      ObjectSet(objectLabel, OBJPROP_YDISTANCE, yPos);  // place at yPos from the top 
   }
   //bool ObjectSetText( string name, string text, int font_size, string font=NULL, color text_color=CLR_NONE) 
   ObjectSetText(objectLabel, displayText, 11, "Times New Roman", textColor);  // display latest calculation
}

string MakeLotsToTradeText(double lotsToOpen,int ticksInRiskCount)
{
   string tradeLotsMsg;
   if (!lotsToOpen)  // cannot open any lots with the risk parameters set
   {
      tradeLotsMsg = "Trade 0 Lots";    //  Message to display on the screen
   } else  // You can open lots
   {
     tradeLotsMsg = StringConcatenate("Trade ", DoubleToStr(lotsToOpen,3)," Lots."); //  Message to display on the screen
   }
   return(tradeLotsMsg);
}


double MaxLotsThatCanBeOpened(double maxMarginPct)
{
   double accountFreeMargin = AccountFreeMargin();       //  Total Margin Available
   
   accountFreeMargin = accountFreeMargin * maxMarginPct;  // reduce to the max desired to use in any trade.
   
   double marginToOpenALot = MarketInfo(Symbol(), MODE_MARGINREQUIRED);  //Margin required to open a lot
   
   double accountLeverage = AccountLeverage();              // Leverage provided for the account
      
   double minLotSize = MarketInfo(Symbol(), MODE_MINLOT);   // What is the size of the smallest part of a lot available
      
   double maxLotsThatCanBeOpened = (accountFreeMargin/marginToOpenALot); // I'm pretty sure this will be less than MarketInfo(Symbol(), MODE_MAXLOT), but check
   dealerMaxLots = MarketInfo(Symbol(), MODE_MAXLOT);      // Find the maximum lots the broker will allow in an order.
        
   if (maxLotsThatCanBeOpened > dealerMaxLots)             // Make sure the lots are less than the maximum the broker will allow.
      maxLotsThatCanBeOpened = dealerMaxLots;              // assign the smaller amount.
   
   //Get rid of the digits beyond the mininum Lot Step.
   maxLotsThatCanBeOpened = MathFloor(maxLotsThatCanBeOpened/minLotSize); // Made maximum significant digits be on Whole Number side of decimal point.
   maxLotsThatCanBeOpened = maxLotsThatCanBeOpened * minLotSize;  //  Put fractions of a lot back in decimal.  Now there are no digits beyond smallest allowed.
   return (maxLotsThatCanBeOpened); 
}

double LotsToOpen(double riskLevel, double ticksToRisk, double maxMarginPct)
{  
   double maxLotsThatCanBeOpened = MaxLotsThatCanBeOpened(maxMarginPct);    // Find the maximum # of lots that can be opened with margin parameters
   double lotsForRisk = MaxtLotsForRisk(double riskLevel, double ticksToRisk);
                      // Cannot Open any lots (or part of a lot) with the specified risk
   if ( lotsForRisk > maxLotsThatCanBeOpened )   // Largest Position that can be opened
      lotsForRisk = maxLotsThatCanBeOpened;      // Reduce to the Largest Position that can be opened.

   return(lotsForRisk);
}

double MaxtLotsForRisk(double riskLevel, double ticksToRisk)
{
   double minPartOfLotSize = MarketInfo(Symbol(), MODE_MINLOT); // Smallest part of a lot that can be opened.
         
   double oneTickValue = MarketInfo(Symbol(), MODE_TICKVALUE);  // Get the value of a single tick
     
   double totalRiskEquity = AccountEquity();                    // Get the total account Equity.  Some may choose Open Margin instead.
                                                                // double AccountFreeMargin() gives the total open margin 
   double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
        
   int riskTickCount = ticksToRisk/tickSize; // Make the ticks-to-risk into an Integer
   // the value of one tick - oneTickValue - must be the price movement of a single tick on the mininum part of a lot that can be opened.
   double minMovementCost = riskTickCount * oneTickValue;     // Minumum amount of money for the price movement in the risk.
   double baseCurrencyToRisk = riskLevel * totalRiskEquity;   // Total Money available for the price movememenmt.
   double lotsForRisk = (baseCurrencyToRisk)/minMovementCost; // lots that the risk money can cover.
   
   lotsForRisk = MathFloor(lotsForRisk/minPartOfLotSize) * minPartOfLotSize;   /* removes digits that are too insignificant.
      could use NormalizeDouble, but this allows us to do it without knowing the actual number of significant digits. */
   if ( lotsForRisk < minPartOfLotSize )     // smallest possible part of a lot to open
      lotsForRisk = 0;
   
   return(lotsForRisk); 
 }

double PipsToTicks(int FixedPipsForRisk)
{
   double ticksInAPip = TicksInOnePip();  /* Get the Number of ticks in One Pip.  Should be 1 (traditional) or 10 (fractional).
     Market Makers are offering Fractional Pip accounts, and calling those Pips.  I'd like to keep with a common
     definition (value) of a pip everywhere - generally the 5th significant digit in a price quote on the major
     currency pairs */
   double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);

   return((FixedPipsForRisk*ticksInAPip) * tickSize);  
}

int TicksInOnePip()
{  //  there should be a programatic way to get this for an account.
   // shouldn't have to guess based on Digits or $, or rely on user input.
   if (!TicksInAPip)  // no user supplied value, guess on one.
   {
      if ( (Digits==5) || (Digits==3) )
      {
         return(10);
      } else
      {
        return(1);
      }
   } else            // return the user supplied value.
   {
      if (TicksInAPip >0)
         return(TicksInAPip);
      else
         return(10);
   }
}

int TestSystemInfo()
{
   int returnValue = 0;
   string errMsgStr = "MarketInfo";
   if ( !MarketInfo(Symbol(), MODE_TICKVALUE) ) // no MarketInfo() value
   {
      errMsgStr = StringConcatenate(errMsgStr," MODE_TICVALUE");
      returnValue = -1;
   }
   if ( !MarketInfo(Symbol(), MODE_TICKSIZE) ) // no MarketInfo() value
   {
      errMsgStr = StringConcatenate(errMsgStr," MODE_TICKSIZE");
      returnValue = -1;
   }
   if ( !MarketInfo(Symbol(), MODE_MINLOT) ) // no MarketInfo() value
   {
      errMsgStr = StringConcatenate(errMsgStr," MODE_MINLOT");
      returnValue = -1;
   }
   if ( !MarketInfo(Symbol(), MODE_MAXLOT) ) // no MarketInfo() value
   {
      errMsgStr = StringConcatenate(errMsgStr," MODE_MAXLOT");
      returnValue = -1;
   }
   if ( !MarketInfo(Symbol(), MODE_MARGINREQUIRED) ) // no MarketInfo() value
   {
      errMsgStr = StringConcatenate(errMsgStr," MODE_MARGINREQUIRED");
      returnValue = -1;
   }
   if ( AccountEquity() <=0 ) // missing account information
   {
      errMsgStr = StringConcatenate(errMsgStr," AccountEquity()");
      returnValue = -1;
   }
   if ( AccountLeverage() <=0 ) // missing account information
   {
      errMsgStr = StringConcatenate(errMsgStr," AccountLeverage()");
      returnValue = -1;
   }
   if ( AccountFreeMargin() <=0 ) // missing account information
   {
      errMsgStr = StringConcatenate(errMsgStr," AccountFreeMargin()");
      returnValue = -1;
   }
   if ( iATR(Symbol(), Period() , ATRBars, 0) <= 0 ) // missing account information
   {
      errMsgStr = StringConcatenate(errMsgStr," ATR()");
      returnValue = -1;
   }
   if (returnValue <0)
   {
      errMsgStr = StringConcatenate(errMsgStr," have missing values, aborting");
      Print(errMsgStr);
   }
   return(returnValue);
}
//+------------------------------------------------------------------+

