//+------------------------------------------------------------------+
//|                                       PositionSizeCalculator.mq4 |
//|                             Copyright © 2012-2014, Andriy Moraru |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2012-2014, Andriy Moraru"
#property link      "http://www.earnforex.com"
#property version   "1.11"

#property description "Calculates position size based on account balance/equity,"
#property description "currency, currency pair, given entry level, stop-loss level"
#property description "and risk tolerance (set either in percentage points or in base currency)."

#property indicator_chart_window

extern double EntryLevel = 0;
extern double StopLossLevel = 0;
extern double TakeProfitLevel = 0; // Optional
extern double Risk = 1; // Risk tolerance in percentage points
extern double MoneyRisk = 0; // Risk tolerance in account currency
extern bool UseMoneyInsteadOfPercentage = false;
extern bool UseEquityInsteadOfBalance = false;
extern bool DeleteLines = false; // If true, will delete lines on deinitialization. Otherwise will leave lines, so levels can be restored.
extern bool UseAskBidForEntry = false; // If true, Entry level will be updated to current Ask/Bid price automatically.

extern color entry_font_color = clrBlue;
extern color sl_font_color = clrLime;
extern color tp_font_color = clrYellow;
extern color ps_font_color = clrRed;
extern color rp_font_color = clrLightBlue;
extern color balance_font_color = clrLightBlue;
extern color rmm_font_color = clrLightBlue;
extern color pp_font_color = clrLightBlue;
extern color rr_font_color = clrYellow;
extern int font_size = 12;
extern string font_face = "Courier";
extern int corner = 0; //0 - for top-left corner, 1 - top-right, 2 - bottom-left, 3 - bottom-right
extern int distance_x = 10;
extern int distance_y = 15;
extern color entry_line_color = clrBlue;
extern color stoploss_line_color = clrLime;
extern color takeprofit_line_color = clrYellow;
extern ENUM_LINE_STYLE entry_line_style = STYLE_SOLID;
extern ENUM_LINE_STYLE stoploss_line_style = STYLE_SOLID;
extern ENUM_LINE_STYLE takeprofit_line_style = STYLE_SOLID;
extern int entry_line_width = 1;
extern int stoploss_line_width = 1;
extern int takeprofit_line_width = 1;

string SizeText;
double Size, RiskMoney;
double PositionSize;
double StopLoss;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   if (ObjectFind("EntryLine") > -1)
   {
      EntryLevel = ObjectGet("EntryLine", OBJPROP_PRICE1);
      ObjectSet("EntryLine", OBJPROP_STYLE, entry_line_style);
      ObjectSet("EntryLine", OBJPROP_COLOR, entry_line_color);
      ObjectSet("EntryLine", OBJPROP_WIDTH, entry_line_width);
   }
   if (ObjectFind("StopLossLine") > -1)
   {
      StopLossLevel = ObjectGet("StopLossLine", OBJPROP_PRICE1);
      ObjectSet("StopLossLine", OBJPROP_STYLE, stoploss_line_style);
      ObjectSet("StopLossLine", OBJPROP_COLOR, stoploss_line_color);
      ObjectSet("StopLossLine", OBJPROP_WIDTH, stoploss_line_width);
   }
   if (ObjectFind("TakeProfitLine") > -1)
   {
      TakeProfitLevel = ObjectGet("TakeProfitLine", OBJPROP_PRICE1);
      ObjectSet("TakeProfitLine", OBJPROP_STYLE, takeprofit_line_style);
      ObjectSet("TakeProfitLine", OBJPROP_COLOR, takeprofit_line_color);
      ObjectSet("TakeProfitLine", OBJPROP_WIDTH, takeprofit_line_width);
   }
   
   if ((EntryLevel == 0) && (StopLossLevel == 0))
   {
      Print(Symbol() + ": Entry and Stop-Loss levels not given. Using local values.");
      EntryLevel = High[0];
      StopLossLevel = Low[0];
      if (EntryLevel == StopLossLevel) StopLossLevel -= Point;
   }
   if (EntryLevel - StopLossLevel == 0)
   {
      Alert("Entry and Stop-Loss levels should be different and non-zero.");
      return(-1);
   }

   if (UseAskBidForEntry)
   {
      RefreshRates();
      if ((Ask > 0) && (Bid > 0))
      {
         // Long entry
         if (StopLossLevel < Bid) EntryLevel = Ask;
         // Short entry
         else if (StopLossLevel > Ask) EntryLevel = Bid;
      }
   }

   ObjectCreate("EntryLevel", OBJ_LABEL, 0, 0, 0);
   ObjectSet("EntryLevel", OBJPROP_CORNER, corner);
   ObjectSet("EntryLevel", OBJPROP_XDISTANCE, distance_x);
   ObjectSet("EntryLevel", OBJPROP_YDISTANCE, distance_y);
   ObjectSetText("EntryLevel", "Entry Lvl:    " + DoubleToStr(EntryLevel, Digits), font_size, font_face, entry_font_color);

   if (ObjectFind("EntryLine") == -1) 
   {
      ObjectCreate("EntryLine", OBJ_HLINE, 0, Time[0], EntryLevel);
      ObjectSet("EntryLine", OBJPROP_STYLE, entry_line_style);
      ObjectSet("EntryLine", OBJPROP_COLOR, entry_line_color);
      ObjectSet("EntryLine", OBJPROP_WIDTH, entry_line_width);
      ObjectSet("EntryLine", OBJPROP_WIDTH, 1);
   }

   ObjectCreate("StopLoss", OBJ_LABEL, 0, 0, 0);
   ObjectSet("StopLoss", OBJPROP_CORNER, corner);
   ObjectSet("StopLoss", OBJPROP_XDISTANCE, distance_x);
   ObjectSet("StopLoss", OBJPROP_YDISTANCE, distance_y + 15);
   ObjectSetText("StopLoss", "Stop-Loss:    " + DoubleToStr(StopLossLevel, Digits), font_size, font_face, sl_font_color);
      
   if (ObjectFind("StopLossLine") == -1)
   {
      ObjectCreate("StopLossLine", OBJ_HLINE, 0, Time[0], StopLossLevel);
      ObjectSet("StopLossLine", OBJPROP_STYLE, stoploss_line_style);
      ObjectSet("StopLossLine", OBJPROP_COLOR, stoploss_line_color);
      ObjectSet("StopLossLine", OBJPROP_WIDTH, stoploss_line_width);
      ObjectSet("StopLossLine", OBJPROP_WIDTH, 1);
   }
   StopLoss = MathAbs(EntryLevel - StopLossLevel);
   
   int y_shift = 30;
   
   if (TakeProfitLevel > 0) // Show TP line and RR ratio only if TakeProfitLevel input parameter is set by user or found via chart object.
   {
      ObjectCreate("TakeProfit", OBJ_LABEL, 0, 0, 0);
      ObjectSet("TakeProfit", OBJPROP_CORNER, corner);
      ObjectSet("TakeProfit", OBJPROP_XDISTANCE, distance_x);
      ObjectSet("TakeProfit", OBJPROP_YDISTANCE, distance_y + y_shift);
      ObjectSetText("TakeProfit", "Take-Profit:  " + DoubleToStr(TakeProfitLevel, Digits), font_size, font_face, tp_font_color);
      y_shift += 15;

      if (ObjectFind("TakeProfitLine") == -1) 
      {
         ObjectCreate("TakeProfitLine", OBJ_HLINE, 0, Time[0], TakeProfitLevel);
         ObjectSet("TakeProfitLine", OBJPROP_STYLE, takeprofit_line_style);
         ObjectSet("TakeProfitLine", OBJPROP_COLOR, takeprofit_line_color);
         ObjectSet("TakeProfitLine", OBJPROP_WIDTH, takeprofit_line_width);
         ObjectSet("TakeProfitLine", OBJPROP_WIDTH, 1);
      }
   }
   
   if (UseEquityInsteadOfBalance)
   {
      SizeText = "Equity";
      Size = AccountEquity();
   }
   else
   {
      SizeText = "Balance";
      Size = AccountBalance();
   }
   ObjectCreate("AccountSize", OBJ_LABEL, 0, 0, 0);
   ObjectSet("AccountSize", OBJPROP_CORNER, corner);
   ObjectSet("AccountSize", OBJPROP_XDISTANCE, distance_x);
   ObjectSet("AccountSize", OBJPROP_YDISTANCE, distance_y + y_shift);
   ObjectSetText("AccountSize", "Acc. " + SizeText + ": " + DoubleToStr(Size, 2), font_size, font_face, balance_font_color);
   y_shift += 15;
   
   if (!UseMoneyInsteadOfPercentage)
   {
      ObjectCreate("Risk", OBJ_LABEL, 0, 0, 0);
      ObjectSet("Risk", OBJPROP_CORNER, corner);
      ObjectSet("Risk", OBJPROP_XDISTANCE, distance_x);
      ObjectSet("Risk", OBJPROP_YDISTANCE, distance_y + y_shift);
      ObjectSetText("Risk", "Risk:         " + DoubleToStr(Risk, 2) + "%", font_size, font_face, rp_font_color);
      y_shift += 15;
   }
   
   ObjectCreate("RiskMoney", OBJ_LABEL, 0, 0, 0);
   ObjectSet("RiskMoney", OBJPROP_CORNER, corner);
   ObjectSet("RiskMoney", OBJPROP_XDISTANCE, distance_x);
   ObjectSet("RiskMoney", OBJPROP_YDISTANCE, distance_y + y_shift);
   y_shift += 15;

   if (TakeProfitLevel > 0)
   {
      ObjectCreate("PotentialProfit", OBJ_LABEL, 0, 0, 0);
      ObjectSet("PotentialProfit", OBJPROP_CORNER, corner);
      ObjectSet("PotentialProfit", OBJPROP_XDISTANCE, distance_x);
      ObjectSet("PotentialProfit", OBJPROP_YDISTANCE, distance_y + y_shift);
      y_shift += 15;

      ObjectCreate("RR", OBJ_LABEL, 0, 0, 0);
      ObjectSet("RR", OBJPROP_CORNER, corner);
      ObjectSet("RR", OBJPROP_XDISTANCE, distance_x);
      ObjectSet("RR", OBJPROP_YDISTANCE, distance_y + y_shift);
      ObjectSetText("RR", "Reward/Risk:  " + DoubleToStr(MathAbs((TakeProfitLevel - EntryLevel) / (EntryLevel - TakeProfitLevel)), 1), font_size, font_face, rr_font_color);
      y_shift += 15;
   }

   ObjectCreate("PositionSize", OBJ_LABEL, 0, 0, 0);
   ObjectSet("PositionSize", OBJPROP_CORNER, corner);
   ObjectSet("PositionSize", OBJPROP_XDISTANCE, distance_x);
   ObjectSet("PositionSize", OBJPROP_YDISTANCE, distance_y + y_shift);

   CalculateRiskAndPositionSize();

   return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
   ObjectDelete("EntryLevel");
   if (DeleteLines) ObjectDelete("EntryLine");
   ObjectDelete("StopLoss");
   if (DeleteLines) ObjectDelete("StopLossLine");
   if (!UseMoneyInsteadOfPercentage) ObjectDelete("Risk"); // Otherwise wasn't created.
   ObjectDelete("AccountSize");
   ObjectDelete("RiskMoney");
   ObjectDelete("PositionSize");
   if (TakeProfitLevel > 0)
   {
      ObjectDelete("TakeProfit");
      if (DeleteLines) ObjectDelete("TakeProfitLine");
      ObjectDelete("RR");
      ObjectDelete("PotentialProfit");
   }
   return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   double tEntryLevel, tStopLossLevel, tTakeProfitLevel;
   // Update Entry to Ask/Bid if needed.
   if (UseAskBidForEntry)
   {
      RefreshRates();
      if ((Ask > 0) && (Bid > 0))
      {
         tStopLossLevel = ObjectGet("StopLossLine", OBJPROP_PRICE1);
         // Long entry
         if (tStopLossLevel < Bid) tEntryLevel = Ask;
         // Short entry
         else if (tStopLossLevel > Ask) tEntryLevel = Bid;
         ObjectSet("EntryLine", OBJPROP_PRICE1, tEntryLevel);
      }
   }
   
   if (EntryLevel - StopLossLevel == 0) return(0);

   // If could not find account currency, probably not connected.
   if (AccountCurrency() == "") return(0);

   tEntryLevel = ObjectGet("EntryLine", OBJPROP_PRICE1);
   tStopLossLevel = ObjectGet("StopLossLine", OBJPROP_PRICE1);
   tTakeProfitLevel = ObjectGet("TakeProfitLine", OBJPROP_PRICE1);
   ObjectSetText("EntryLevel", "Entry Lvl:    " + DoubleToStr(tEntryLevel, Digits), font_size, font_face, entry_font_color);
   ObjectSetText("StopLoss", "Stop-Loss:    " + DoubleToStr(tStopLossLevel, Digits), font_size, font_face, sl_font_color);
   if (tTakeProfitLevel > 0) ObjectSetText("TakeProfit", "Take-Profit:  " + DoubleToStr(tTakeProfitLevel, Digits), font_size, font_face, tp_font_color);

   StopLoss = MathAbs(tEntryLevel - tStopLossLevel);

   if (tTakeProfitLevel > 0)
   {
      string RR;
      // Have valid take-profit level that is above entry for SL below entry, or below entry for SL above entry.
      if (((tTakeProfitLevel > tEntryLevel) && (tEntryLevel > tStopLossLevel)) || ((tTakeProfitLevel < tEntryLevel) && (tEntryLevel < tStopLossLevel)))
         RR = DoubleToStr(MathAbs((tTakeProfitLevel - tEntryLevel) / StopLoss), 1);
      else RR = "Invalid TP.";
      ObjectSetText("RR", "Reward/Risk:  " + RR, font_size, font_face, takeprofit_line_color);
      ObjectSetText("PotentialProfit", "Reward:       " + DoubleToStr(RiskMoney * MathAbs((tTakeProfitLevel - tEntryLevel) / StopLoss), 2), font_size, font_face, pp_font_color);
   }
   
   if (UseEquityInsteadOfBalance) Size = AccountEquity();
   else Size = AccountBalance();
   ObjectSetText("AccountSize", "Acc. " + SizeText + ": " + DoubleToStr(Size, 2), font_size, font_face, balance_font_color);

   CalculateRiskAndPositionSize();

   return(0);
}

//+------------------------------------------------------------------+
//| Calculates risk size and position size. Sets object values.      |
//+------------------------------------------------------------------+
void CalculateRiskAndPositionSize()
{
   if (!UseMoneyInsteadOfPercentage) RiskMoney = Size * Risk / 100;
   else RiskMoney = MoneyRisk;
   ObjectSetText("RiskMoney", "Risk, money:  " + DoubleToStr(RiskMoney, 2), font_size, font_face, rmm_font_color);

   double UnitCost = MarketInfo(Symbol(), MODE_TICKVALUE);
   double TickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
   if ((StopLoss != 0) && (UnitCost != 0) && (TickSize != 0)) PositionSize = RiskMoney / (StopLoss * UnitCost / TickSize);
   
   ObjectSetText("PositionSize", "Pos. Size:    " + DoubleToStr(PositionSize, 2), font_size + 1, font_face, ps_font_color);

}
//+------------------------------------------------------------------+