//+------------------------------------------------------------------+ //| PositionSizeCalculator.mq5 | //| 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 description "2014-04-11, ver. 1.11 - added potential reward display and color/style input parameters." // 2013-11-11, ver. 1.10 - added optional Ask/Bid tracking for Entry line. // 2013-02-11, ver. 1.8 - completely revamped calculation process. // 2013-01-14, ver. 1.7 - fixed "division by zero" error. // 2012-12-10, ver. 1.6 - will use local values if both Entry and SL are missing. // 2012-11-02, ver. 1.5 - a more intelligent name prefix/postfix detection. // 2012-10-13, ver. 1.4 - fixed contract size in lot size calculation. // 2012-10-13, ver. 1.3 - proper lot size calculation for gold, silver and oil. // 2012-09-29, ver. 1.2 - improved account currency and reference pair detection. // 2012-05-10, ver. 1.1 - added support for setting risk in money. #property indicator_chart_window input double EntryLevel = 0; input double StopLossLevel = 0; input double TakeProfitLevel = 0; // TakeProfitLevel (Optional) input double Risk = 1; // Risk tolerance in percentage points input double MoneyRisk = 0; // Risk tolerance in account currency input bool UseMoneyInsteadOfPercentage = false; input bool UseEquityInsteadOfBalance = false; input bool DeleteLines = false; // DeleteLines - If true, will delete lines on deinitialization. Otherwise will leave lines, so levels can be restored. input bool UseAskBidForEntry = false; // UseAskBidForEntry - If true, Entry level will be updated to current Ask/Bid price automatically. input color entry_font_color = clrBlue; input color sl_font_color = clrLime; input color tp_font_color = clrYellow; input color ps_font_color = clrRed; input color rp_font_color = clrLightBlue; input color balance_font_color = clrLightBlue; input color rmm_font_color = clrLightBlue; input color pp_font_color = clrLightBlue; input color rr_font_color = clrYellow; input int font_size = 12; input string font_face = "Courier"; input ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER; input int distance_x = 10; input int distance_y = 15; input color entry_line_color = clrBlue; input color stoploss_line_color = clrLime; input color takeprofit_line_color = clrYellow; input ENUM_LINE_STYLE entry_line_style = STYLE_SOLID; input ENUM_LINE_STYLE stoploss_line_style = STYLE_SOLID; input ENUM_LINE_STYLE takeprofit_line_style = STYLE_SOLID; input int entry_line_width = 1; input int stoploss_line_width = 1; input int takeprofit_line_width = 1; string SizeText; double Size, RiskMoney; double PositionSize; double StopLoss; // Will be used instead of input parameters as they cannot be modified during runtime. double iEntryLevel; double iStopLossLevel; double iTakeProfitLevel; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ void OnInit() { if (ObjectFind(0, "EntryLine") > -1) { iEntryLevel = ObjectGetDouble(0, "EntryLine", OBJPROP_PRICE); ObjectSetInteger(0, "EntryLine", OBJPROP_STYLE, entry_line_style); ObjectSetInteger(0, "EntryLine", OBJPROP_COLOR, entry_line_color); ObjectSetInteger(0, "EntryLine", OBJPROP_WIDTH, entry_line_width); } else iEntryLevel = EntryLevel; if (ObjectFind(0, "StopLossLine") > -1) { iStopLossLevel = ObjectGetDouble(0, "StopLossLine", OBJPROP_PRICE); ObjectSetInteger(0, "StopLossLine", OBJPROP_STYLE, stoploss_line_style); ObjectSetInteger(0, "StopLossLine", OBJPROP_COLOR, stoploss_line_color); ObjectSetInteger(0, "StopLossLine", OBJPROP_WIDTH, stoploss_line_width); } else iStopLossLevel = StopLossLevel; if (ObjectFind(0, "TakeProfitLine") > -1) { iTakeProfitLevel = ObjectGetDouble(0, "TakeProfitLine", OBJPROP_PRICE); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_STYLE, takeprofit_line_style); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_COLOR, takeprofit_line_color); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_WIDTH, takeprofit_line_width); } else iTakeProfitLevel = TakeProfitLevel; if ((iEntryLevel == 0) && (iStopLossLevel == 0)) { Print(Symbol() + ": Entry and Stop-Loss levels not given. Using local values."); iEntryLevel = SymbolInfoDouble(Symbol(), SYMBOL_BIDHIGH); iStopLossLevel = SymbolInfoDouble(Symbol(), SYMBOL_BIDLOW); if (iEntryLevel == iStopLossLevel) iStopLossLevel -= _Point; } if (iEntryLevel - iStopLossLevel == 0) { Alert("Entry and Stop-Loss levels should be different and non-zero."); return; } if (UseAskBidForEntry) { if ((SymbolInfoDouble(_Symbol, SYMBOL_ASK) > 0) && (SymbolInfoDouble(_Symbol, SYMBOL_BID) > 0)) { // Long entry if (iStopLossLevel < SymbolInfoDouble(_Symbol, SYMBOL_BID)) iEntryLevel = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Short entry else if (iStopLossLevel > SymbolInfoDouble(_Symbol, SYMBOL_ASK)) iEntryLevel = SymbolInfoDouble(_Symbol, SYMBOL_BID); } } ObjectCreate(0, "Entry", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "Entry", OBJPROP_CORNER, corner); ObjectSetInteger(0, "Entry", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "Entry", OBJPROP_YDISTANCE, distance_y); ObjectSetString(0, "Entry", OBJPROP_TEXT, "Entry Lvl: " + DoubleToString(iEntryLevel, _Digits)); ObjectSetInteger(0, "Entry", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "Entry", OBJPROP_FONT, font_face); ObjectSetInteger(0, "Entry", OBJPROP_COLOR, entry_font_color); if (ObjectFind(0, "EntryLine") == -1) { ObjectCreate(0, "EntryLine", OBJ_HLINE, 0, TimeCurrent(), iEntryLevel); ObjectSetInteger(0, "EntryLine", OBJPROP_STYLE, entry_line_style); ObjectSetInteger(0, "EntryLine", OBJPROP_COLOR, entry_line_color); ObjectSetInteger(0, "EntryLine", OBJPROP_WIDTH, entry_line_width); ObjectSetInteger(0, "EntryLine", OBJPROP_WIDTH, 1); ObjectSetInteger(0, "EntryLine", OBJPROP_SELECTABLE, true); } ObjectCreate(0, "StopLoss", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "StopLoss", OBJPROP_CORNER, corner); ObjectSetInteger(0, "StopLoss", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "StopLoss", OBJPROP_YDISTANCE, distance_y + 15); ObjectSetString(0, "StopLoss", OBJPROP_TEXT, "Stop-Loss: " + DoubleToString(iStopLossLevel, _Digits)); ObjectSetInteger(0, "StopLoss", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "StopLoss", OBJPROP_FONT, font_face); ObjectSetInteger(0, "StopLoss", OBJPROP_COLOR, sl_font_color); if (ObjectFind(0, "StopLossLine") == -1) { ObjectCreate(0, "StopLossLine", OBJ_HLINE, 0, TimeCurrent(), iStopLossLevel); ObjectSetInteger(0, "StopLossLine", OBJPROP_STYLE, stoploss_line_style); ObjectSetInteger(0, "StopLossLine", OBJPROP_COLOR, stoploss_line_color); ObjectSetInteger(0, "StopLossLine", OBJPROP_WIDTH, stoploss_line_width); ObjectSetInteger(0, "StopLossLine", OBJPROP_WIDTH, 1); ObjectSetInteger(0, "StopLossLine", OBJPROP_SELECTABLE, true); } StopLoss = MathAbs(iEntryLevel - iStopLossLevel); int y_shift = 30; if (iTakeProfitLevel > 0) // Show TP line and RR ratio only if TakeProfitLevel input parameter is set by user or found via chart object. { ObjectCreate(0, "TakeProfit", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "TakeProfit", OBJPROP_CORNER, corner); ObjectSetInteger(0, "TakeProfit", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "TakeProfit", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetString(0, "TakeProfit", OBJPROP_TEXT, "Take-Profit: " + DoubleToString(iTakeProfitLevel, _Digits)); ObjectSetInteger(0, "TakeProfit", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "TakeProfit", OBJPROP_FONT, font_face); ObjectSetInteger(0, "TakeProfit", OBJPROP_COLOR, tp_font_color); y_shift += 15; if (ObjectFind(0, "TakeProfitLine") == -1) { ObjectCreate(0, "TakeProfitLine", OBJ_HLINE, 0, TimeCurrent(), iTakeProfitLevel); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_STYLE, takeprofit_line_style); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_COLOR, takeprofit_line_color); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_WIDTH, takeprofit_line_width); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_WIDTH, 1); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_SELECTABLE, true); } } if (UseEquityInsteadOfBalance) { SizeText = "Equity"; Size = AccountInfoDouble(ACCOUNT_EQUITY); } else { SizeText = "Balance"; Size = AccountInfoDouble(ACCOUNT_BALANCE); } ObjectCreate(0, "AccountSize", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "AccountSize", OBJPROP_CORNER, corner); ObjectSetInteger(0, "AccountSize", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "AccountSize", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetString(0, "AccountSize", OBJPROP_TEXT, "Acc. " + SizeText + ": " + DoubleToString(Size, 2)); ObjectSetInteger(0, "AccountSize", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "AccountSize", OBJPROP_FONT, font_face); ObjectSetInteger(0, "AccountSize", OBJPROP_COLOR, balance_font_color); y_shift += 15; if (!UseMoneyInsteadOfPercentage) { ObjectCreate(0, "Risk", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "Risk", OBJPROP_CORNER, corner); ObjectSetInteger(0, "Risk", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "Risk", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetString(0, "Risk", OBJPROP_TEXT, "Risk: " + DoubleToString(Risk, 2) + "%"); ObjectSetInteger(0, "Risk", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "Risk", OBJPROP_FONT, font_face); ObjectSetInteger(0, "Risk", OBJPROP_COLOR, rp_font_color); y_shift += 15; } ObjectCreate(0, "RiskMoney", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "RiskMoney", OBJPROP_CORNER, corner); ObjectSetInteger(0, "RiskMoney", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "RiskMoney", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "RiskMoney", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "RiskMoney", OBJPROP_FONT, font_face); ObjectSetInteger(0, "RiskMoney", OBJPROP_COLOR, rmm_font_color); y_shift += 15; if (iTakeProfitLevel > 0) { ObjectCreate(0, "PotentialProfit", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "PotentialProfit", OBJPROP_CORNER, corner); ObjectSetInteger(0, "PotentialProfit", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "PotentialProfit", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "PotentialProfit", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "PotentialProfit", OBJPROP_FONT, font_face); ObjectSetInteger(0, "PotentialProfit", OBJPROP_COLOR, pp_font_color); y_shift += 15; ObjectCreate(0, "RR", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "RR", OBJPROP_CORNER, corner); ObjectSetInteger(0, "RR", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "RR", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetString(0, "RR", OBJPROP_TEXT, "Reward/Risk: " + DoubleToString(MathAbs((iTakeProfitLevel - iEntryLevel) / (iEntryLevel - iTakeProfitLevel)), 1)); ObjectSetInteger(0, "RR", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "RR", OBJPROP_FONT, font_face); ObjectSetInteger(0, "RR", OBJPROP_COLOR, rr_font_color); y_shift += 15; } ObjectCreate(0, "PositionSize", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "PositionSize", OBJPROP_CORNER, corner); ObjectSetInteger(0, "PositionSize", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "PositionSize", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "PositionSize", OBJPROP_FONTSIZE, font_size + 1); ObjectSetString(0, "PositionSize", OBJPROP_FONT, font_face); ObjectSetInteger(0, "PositionSize", OBJPROP_COLOR, ps_font_color); y_shift += 15; if (AccountInfoString(ACCOUNT_CURRENCY) == "") return; CalculateRiskAndPositionSize(); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectDelete(0, "Entry"); if (DeleteLines) ObjectDelete(0, "EntryLine"); ObjectDelete(0, "StopLoss"); if (DeleteLines) ObjectDelete(0, "StopLossLine"); if (!UseMoneyInsteadOfPercentage) ObjectDelete(0, "Risk"); // Otherwise wasn't created. ObjectDelete(0, "AccountSize"); ObjectDelete(0, "RiskMoney"); ObjectDelete(0, "PositionSize"); if (iTakeProfitLevel > 0) { ObjectDelete(0, "TakeProfit"); if (DeleteLines) ObjectDelete(0, "TakeProfitLine"); ObjectDelete(0, "RR"); ObjectDelete(0, "PotentialProfit"); } ChartRedraw(); } //+------------------------------------------------------------------+ //| Main recalculation function used on every tick and on entry/SL | //| line drag | //+------------------------------------------------------------------+ void RecalculatePositionSize() { double tEntryLevel, tStopLossLevel, tTakeProfitLevel; // Update Entry to Ask/Bid if needed. if (UseAskBidForEntry) { tStopLossLevel = ObjectGetDouble(0, "StopLossLine", OBJPROP_PRICE); if ((SymbolInfoDouble(_Symbol, SYMBOL_ASK) > 0) && (SymbolInfoDouble(_Symbol, SYMBOL_BID) > 0)) { // Long entry if (tStopLossLevel < SymbolInfoDouble(_Symbol, SYMBOL_BID)) tEntryLevel = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Short entry else if (tStopLossLevel > SymbolInfoDouble(_Symbol, SYMBOL_ASK)) tEntryLevel = SymbolInfoDouble(_Symbol, SYMBOL_BID); ObjectSetDouble(0, "EntryLine", OBJPROP_PRICE, tEntryLevel); } } if (iEntryLevel - iStopLossLevel == 0) return; // If could not find account currency, probably not connected. if (AccountInfoString(ACCOUNT_CURRENCY) == "") return; tEntryLevel = ObjectGetDouble(0, "EntryLine", OBJPROP_PRICE); tStopLossLevel = ObjectGetDouble(0, "StopLossLine", OBJPROP_PRICE); tTakeProfitLevel = ObjectGetDouble(0, "TakeProfitLine", OBJPROP_PRICE); ObjectSetString(0, "Entry", OBJPROP_TEXT, "Entry Lvl: " + DoubleToString(tEntryLevel, _Digits)); ObjectSetString(0, "StopLoss", OBJPROP_TEXT, "Stop-Loss: " + DoubleToString(tStopLossLevel, _Digits)); if (tTakeProfitLevel > 0) ObjectSetString(0, "TakeProfit", OBJPROP_TEXT, "Take-Profit: " + DoubleToString(tTakeProfitLevel, _Digits)); 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 = DoubleToString(MathAbs((tTakeProfitLevel - tEntryLevel) / StopLoss), 1); else RR = "Invalid TP."; ObjectSetString(0, "RR", OBJPROP_TEXT, "Reward/Risk: " + RR); ObjectSetString(0, "PotentialProfit", OBJPROP_TEXT, "Reward: " + DoubleToString(RiskMoney * MathAbs((tTakeProfitLevel - tEntryLevel) / StopLoss), 2)); } if (UseEquityInsteadOfBalance) Size = AccountInfoDouble(ACCOUNT_EQUITY); else Size = AccountInfoDouble(ACCOUNT_BALANCE); ObjectSetString(0, "AccountSize", OBJPROP_TEXT, "Acc. " + SizeText + ": " + DoubleToString(Size, 2)); CalculateRiskAndPositionSize(); } //+------------------------------------------------------------------+ //| Object dragging handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Event ID const long& lparam, // Parameter of type long event const double& dparam, // Parameter of type double event const string& sparam // Parameter of type string events ) { if (id != CHARTEVENT_OBJECT_DRAG) return; if ((sparam != "EntryLine") && (sparam != "StopLossLine") && (sparam != "TakeProfitLine")) return; RecalculatePositionSize(); ChartRedraw(); } int OnCalculate(const int rates_total, // size of the price[] array const int prev_calculated, // bars handled on a previous call const int begin, // where the significant data start from const double& price[] // array to calculate ) { RecalculatePositionSize(); return(rates_total); } //+------------------------------------------------------------------+ //| Calculates risk size and position size. Sets object values. | //+------------------------------------------------------------------+ void CalculateRiskAndPositionSize() { if (!UseMoneyInsteadOfPercentage) RiskMoney = Size * Risk / 100; else RiskMoney = MoneyRisk; ObjectSetString(0, "RiskMoney", OBJPROP_TEXT, "Risk, money: " + DoubleToString(RiskMoney, 2)); double UnitCost = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double TickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); if ((StopLoss != 0) && (UnitCost != 0) && (TickSize != 0)) PositionSize = RiskMoney / (StopLoss * UnitCost / TickSize); ObjectSetString(0, "PositionSize", OBJPROP_TEXT, "Pos. Size: " + DoubleToString(PositionSize, 2)); } //+------------------------------------------------------------------+