//+------------------------------------------------------------------+ //| PositionSizeCalculator.mq5 | //| Copyright © 2012-2015, Andriy Moraru | //+------------------------------------------------------------------+ #property copyright "Copyright © 2012-2015, Andriy Moraru" #property link "http://www.earnforex.com " #property version "1.17" #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 "Can display reward/risk ratio based on take-profit." #property description "Can also show total portfolio risk based on open trades and pending orders." #property description "2015-02-16, ver. 1.17 - separate window, input/output values, more warnings." // 2015-02-13, ver. 1.16 - margin, warnings, number formatting, rounding down. // 2015-01-30, ver. 1.15 - DeleteLines also clears old lines when attaching. // 2014-12-19, ver. 1.14 - fixed minor bug when restarting MT5; also, lines are no longer hidden from object list. // 2014-10-03, ver. 1.13 - added portfolio risk calculation. // 2014-09-17, ver. 1.12 - position size is now rounded down. // 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 description "WARNING: There is no guarantee that the output of this indicator is correct. Use at your own risk." #property indicator_separate_window enum ENTRY_TYPE { Instant, Pending }; input bool ShowPortfolioRisk = false; // ShowPortfolioRisk - If true, current portfolio risk and potential portfolio risk will be shown. input bool ShowMargin = false; // ShowMargin - If true, margin calculations for planned position will be shown. input ENTRY_TYPE EntryType = Instant; // EntryType - If Instant, Entry level will be updated to current Ask/Bid price automatically; if Pending, Entry level will remain intact and StopLevel warning will be issued if needed. 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 CountPendingOrders = false; // CountPendingOrders - If true, portfolio risk calculation will also involve pending orders. input bool IgnoreOrdersWithoutStopLoss = false; // IgnoreOrdersWithoutStopLoss - If true, portfolio risk calculation will skip orders without stop-loss. input int MaxNumberLength = 14; // MaxNumberLength - How many digits will there be in numbers as maximum? 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 margin_font_color = clrSlateBlue; input color stopout_font_color = clrRed; input color pp_font_color = clrLightBlue; input color rr_font_color = clrYellow; input color div_font_color = clrSlateGray; input int font_size = 13; input string font_face = "Courier"; input ENUM_BASE_CORNER corner = CORNER_LEFT_UPPER; input int distance_x = 10; input int distance_y = 15; input int second_column_x = 400; 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, OutputRiskMoney; double OutputPositionSize; double StopLoss; // Will be used instead of input parameters as they cannot be modified during runtime. double iEntryLevel, iStopLossLevel, iTakeProfitLevel; // Will be used in two or more functions. double tEntryLevel, tStopLossLevel, tTakeProfitLevel; // -1 because it is checked in the initialization function. double TickSize = -1, ContractSize, AccStopoutLevel, TickValue, InitialMargin = 0, MaintenanceMargin = 0, MinLot, MaxLot, LotStep; string AccountCurrency, MarginCurrency, ProfitCurrency; long AccStopoutMode; int LotStep_digits; ENUM_SYMBOL_CALC_MODE MarginMode; int Window = -1; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ void OnInit() { IndicatorSetString(INDICATOR_SHORTNAME, "Position Size Calculator"); EventSetTimer(1); } //+------------------------------------------------------------------------------+ //| Will be called from OnCalculate() or Timer() after Window number is detected.| //+------------------------------------------------------------------------------+ void Initialization() { if ((ObjectFind(0, "EntryLine") > -1) && (!DeleteLines)) { 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); ObjectSetInteger(0, "EntryLine", OBJPROP_HIDDEN, false); } else iEntryLevel = EntryLevel; if ((ObjectFind(0, "StopLossLine") > -1) && (!DeleteLines)) { 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); ObjectSetInteger(0, "StopLossLine", OBJPROP_HIDDEN, false); } else iStopLossLevel = StopLossLevel; if ((ObjectFind(0, "TakeProfitLine") > -1) && (!DeleteLines)) { 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); ObjectSetInteger(0, "TakeProfitLine", OBJPROP_HIDDEN, false); } else iTakeProfitLevel = TakeProfitLevel; if (DeleteLines) { ObjectDelete(0, "EntryLine"); ObjectDelete(0, "StopLossLine"); ObjectDelete(0, "TakeProfitLine"); } 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 (EntryType == Instant) { 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, Window, 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: " + JustifyRight(DoubleToString(iEntryLevel, _Digits), MaxNumberLength)); 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); } if (EntryType == Instant) ObjectSetInteger(0, "EntryLine", OBJPROP_SELECTABLE, false); else ObjectSetInteger(0, "EntryLine", OBJPROP_SELECTABLE, true); ObjectCreate(0, "StopLoss", OBJ_LABEL, Window, 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: " + JustifyRight(DoubleToString(iStopLossLevel, _Digits), MaxNumberLength)); 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, Window, 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: " + JustifyRight(DoubleToString(iTakeProfitLevel, _Digits), MaxNumberLength)); 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, Window, 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 + ": " + JustifyRight(DoubleToString(Size, 2), MaxNumberLength)); 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; y_shift += 15; ObjectCreate(0, "Divider", OBJ_LABEL, Window, 0, 0); ObjectSetInteger(0, "Divider", OBJPROP_CORNER, corner); ObjectSetInteger(0, "Divider", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "Divider", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetString(0, "Divider", OBJPROP_TEXT, " " + JustifyRight("Input", MaxNumberLength) + JustifyRight("Result", MaxNumberLength)); ObjectSetInteger(0, "Divider", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "Divider", OBJPROP_FONT, font_face); ObjectSetInteger(0, "Divider", OBJPROP_COLOR, div_font_color); y_shift += 15; ObjectCreate(0, "Risk", OBJ_LABEL, Window, 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, ""); 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, Window, 0, 0); ObjectSetInteger(0, "RiskMoney", OBJPROP_CORNER, corner); ObjectSetInteger(0, "RiskMoney", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "RiskMoney", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetString(0, "RiskMoney", OBJPROP_TEXT, ""); 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, Window, 0, 0); ObjectSetInteger(0, "PotentialProfit", OBJPROP_CORNER, corner); ObjectSetInteger(0, "PotentialProfit", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "PotentialProfit", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetString(0, "PotentialProfit", OBJPROP_TEXT, ""); 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, Window, 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, ""); 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, Window, 0, 0); ObjectSetInteger(0, "PositionSize", OBJPROP_CORNER, corner); ObjectSetInteger(0, "PositionSize", OBJPROP_XDISTANCE, distance_x); ObjectSetInteger(0, "PositionSize", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetString(0, "PositionSize", OBJPROP_TEXT, ""); ObjectSetInteger(0, "PositionSize", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "PositionSize", OBJPROP_FONT, font_face); ObjectSetInteger(0, "PositionSize", OBJPROP_COLOR, ps_font_color); y_shift += 15; if (ShowPortfolioRisk) { y_shift = 45; ObjectCreate(0, "CurrentPortfolioMoneyRisk", OBJ_LABEL, Window, 0, 0); ObjectSetInteger(0, "CurrentPortfolioMoneyRisk", OBJPROP_CORNER, corner); ObjectSetInteger(0, "CurrentPortfolioMoneyRisk", OBJPROP_XDISTANCE, distance_x + second_column_x); ObjectSetInteger(0, "CurrentPortfolioMoneyRisk", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "CurrentPortfolioMoneyRisk", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "CurrentPortfolioMoneyRisk", OBJPROP_FONT, font_face); ObjectSetInteger(0, "CurrentPortfolioMoneyRisk", OBJPROP_COLOR, rmm_font_color); ObjectSetString(0, "CurrentPortfolioMoneyRisk", OBJPROP_TEXT, ""); y_shift += 15; ObjectCreate(0, "CurrentPortfolioRisk", OBJ_LABEL, Window, 0, 0); ObjectSetInteger(0, "CurrentPortfolioRisk", OBJPROP_CORNER, corner); ObjectSetInteger(0, "CurrentPortfolioRisk", OBJPROP_XDISTANCE, distance_x + second_column_x); ObjectSetInteger(0, "CurrentPortfolioRisk", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "CurrentPortfolioRisk", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "CurrentPortfolioRisk", OBJPROP_FONT, font_face); ObjectSetInteger(0, "CurrentPortfolioRisk", OBJPROP_COLOR, rp_font_color); ObjectSetString(0, "CurrentPortfolioRisk", OBJPROP_TEXT, ""); y_shift += 15; ObjectCreate(0, "PotentialPortfolioMoneyRisk", OBJ_LABEL, Window, 0, 0); ObjectSetInteger(0, "PotentialPortfolioMoneyRisk", OBJPROP_CORNER, corner); ObjectSetInteger(0, "PotentialPortfolioMoneyRisk", OBJPROP_XDISTANCE, distance_x + second_column_x); ObjectSetInteger(0, "PotentialPortfolioMoneyRisk", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "PotentialPortfolioMoneyRisk", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "PotentialPortfolioMoneyRisk", OBJPROP_FONT, font_face); ObjectSetInteger(0, "PotentialPortfolioMoneyRisk", OBJPROP_COLOR, rmm_font_color); ObjectSetString(0, "PotentialPortfolioMoneyRisk", OBJPROP_TEXT, ""); y_shift += 15; ObjectCreate(0, "PotentialPortfolioRisk", OBJ_LABEL, Window, 0, 0); ObjectSetInteger(0, "PotentialPortfolioRisk", OBJPROP_CORNER, corner); ObjectSetInteger(0, "PotentialPortfolioRisk", OBJPROP_XDISTANCE, distance_x + second_column_x); ObjectSetInteger(0, "PotentialPortfolioRisk", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "PotentialPortfolioRisk", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "PotentialPortfolioRisk", OBJPROP_FONT, font_face); ObjectSetInteger(0, "PotentialPortfolioRisk", OBJPROP_COLOR, rp_font_color); ObjectSetString(0, "PotentialPortfolioRisk", OBJPROP_TEXT, ""); } if (ShowMargin) { if (ShowPortfolioRisk) y_shift += 30; // One blank line. else y_shift = 45; ObjectCreate(0, "PositionMargin", OBJ_LABEL, Window, 0, 0); ObjectSetInteger(0, "PositionMargin", OBJPROP_CORNER, corner); ObjectSetInteger(0, "PositionMargin", OBJPROP_XDISTANCE, distance_x + second_column_x); ObjectSetInteger(0, "PositionMargin", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "PositionMargin", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "PositionMargin", OBJPROP_FONT, font_face); ObjectSetInteger(0, "PositionMargin", OBJPROP_COLOR, margin_font_color); ObjectSetString(0, "PositionMargin", OBJPROP_TEXT, ""); y_shift += 15; ObjectCreate(0, "FutureUsedMargin", OBJ_LABEL, Window, 0, 0); ObjectSetInteger(0, "FutureUsedMargin", OBJPROP_CORNER, corner); ObjectSetInteger(0, "FutureUsedMargin", OBJPROP_XDISTANCE, distance_x + second_column_x); ObjectSetInteger(0, "FutureUsedMargin", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "FutureUsedMargin", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "FutureUsedMargin", OBJPROP_FONT, font_face); ObjectSetInteger(0, "FutureUsedMargin", OBJPROP_COLOR, margin_font_color); ObjectSetString(0, "FutureUsedMargin", OBJPROP_TEXT, ""); y_shift += 15; ObjectCreate(0, "FutureFreeMargin", OBJ_LABEL, Window, 0, 0); ObjectSetInteger(0, "FutureFreeMargin", OBJPROP_CORNER, corner); ObjectSetInteger(0, "FutureFreeMargin", OBJPROP_XDISTANCE, distance_x + second_column_x); ObjectSetInteger(0, "FutureFreeMargin", OBJPROP_YDISTANCE, distance_y + y_shift); ObjectSetInteger(0, "FutureFreeMargin", OBJPROP_FONTSIZE, font_size); ObjectSetString(0, "FutureFreeMargin", OBJPROP_FONT, font_face); ObjectSetInteger(0, "FutureFreeMargin", OBJPROP_COLOR, margin_font_color); ObjectSetString(0, "FutureFreeMargin", OBJPROP_TEXT, ""); } } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(Window, OBJ_LABEL); if (DeleteLines) { ObjectDelete(0, "EntryLine"); ObjectDelete(0, "StopLossLine"); if (iTakeProfitLevel > 0) ObjectDelete(0, "TakeProfit"); } ChartRedraw(); } //+--------------------------------------------------------------------------+ //| Called every second to initialize the indicator if start fails to do so. | //+--------------------------------------------------------------------------+ void OnTimer() { if (Window == -1) { Window = ChartWindowFind(); Initialization(); } EventKillTimer(); RecalculatePositionSize(); } //+------------------------------------------------------------------+ //| Main recalculation function used on every tick and on entry/SL | //| line drag. | //+------------------------------------------------------------------+ void RecalculatePositionSize() { double Ask, Bid; // Update Entry to Ask/Bid if needed. if (EntryType == Instant) { 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) == "") || (!TerminalInfoInteger(TERMINAL_CONNECTED))) return; else if (TickSize == -1) { GetSymbolAndAccountData(); } Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); tEntryLevel = ObjectGetDouble(0, "EntryLine", OBJPROP_PRICE); tStopLossLevel = ObjectGetDouble(0, "StopLossLine", OBJPROP_PRICE); tTakeProfitLevel = ObjectGetDouble(0, "TakeProfitLine", OBJPROP_PRICE); double StopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) * _Point; string WarningEntry = "", WarningSL = "", WarningTP = ""; double AskBid = 0; if (EntryType == Instant) { if (tStopLossLevel < Ask) AskBid = Ask; else if (tStopLossLevel > Bid) AskBid = Bid; if (!AskBid) WarningSL = " (Wrong value!)"; } else if (EntryType == Pending) { if (tStopLossLevel < tEntryLevel) AskBid = Ask; else if (tStopLossLevel > tEntryLevel) AskBid = Bid; if (AskBid) { if (MathAbs(AskBid - tEntryLevel) < StopLevel) WarningEntry = " (Too close!)"; } else WarningSL = " (Wrong value!)"; } ObjectSetString(0, "Entry", OBJPROP_TEXT, "Entry Lvl: " + JustifyRight(DoubleToString(tEntryLevel, _Digits), MaxNumberLength) + WarningEntry); if (MathAbs(tStopLossLevel - tEntryLevel) < StopLevel) WarningSL = " (Too close!)"; ObjectSetString(0, "StopLoss", OBJPROP_TEXT, "Stop-Loss: " + JustifyRight(DoubleToString(tStopLossLevel, _Digits), MaxNumberLength) + WarningSL); if (tTakeProfitLevel > 0) { if (MathAbs(tTakeProfitLevel - tEntryLevel) < StopLevel) WarningTP = " (Too close!)"; ObjectSetString(0, "TakeProfit", OBJPROP_TEXT, "Take-Profit: " + JustifyRight(DoubleToString(tTakeProfitLevel, _Digits), MaxNumberLength) + WarningTP); } StopLoss = MathAbs(tEntryLevel - tStopLossLevel); if (StopLoss == 0) { Print("Stop-loss should be different from Entry."); return; } if (UseEquityInsteadOfBalance) Size = AccountInfoDouble(ACCOUNT_EQUITY); else Size = AccountInfoDouble(ACCOUNT_BALANCE); ObjectSetString(0, "AccountSize", OBJPROP_TEXT, "Acc. " + SizeText + ": " + JustifyRight(FormatDouble(DoubleToString(Size, 2)), MaxNumberLength)); CalculateRiskAndPositionSize(); } //+------------------------------------------------------------------+ //| Gets basic info on Symbol and Account. It remains unchanged. | //+------------------------------------------------------------------+ void GetSymbolAndAccountData() { TickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); MinLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); MaxLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX); LotStep = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP); LotStep_digits = CountDecimalPlaces(LotStep); if (ShowMargin) { ContractSize = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_CONTRACT_SIZE); MarginMode = (ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_CALC_MODE); AccountCurrency = AccountInfoString(ACCOUNT_CURRENCY); MarginCurrency = SymbolInfoString(Symbol(), SYMBOL_CURRENCY_MARGIN); ProfitCurrency = SymbolInfoString(Symbol(), SYMBOL_CURRENCY_PROFIT); AccStopoutMode = AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE); AccStopoutLevel = AccountInfoDouble(ACCOUNT_MARGIN_SO_SO); TickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); InitialMargin = SymbolInfoDouble(_Symbol, SYMBOL_MARGIN_INITIAL); MaintenanceMargin = SymbolInfoDouble(_Symbol, SYMBOL_MARGIN_MAINTENANCE); if (MaintenanceMargin == 0) MaintenanceMargin = InitialMargin; } } //+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+ //| Transaction event handler | //+------------------------------------------------------------------+ void OnTradeTransaction( const MqlTradeTransaction& trans, // trade transaction structure const MqlTradeRequest& request, // request structure const MqlTradeResult& result // result structure ) { RecalculatePositionSize(); ChartRedraw(); } //+------------------------------------------------------------------+ //| Trade event handler | //+------------------------------------------------------------------+ void OnTrade() { RecalculatePositionSize(); } 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() { double RiskMoney, PositionSize; double DisplayRisk = Risk; if (!UseMoneyInsteadOfPercentage) { RiskMoney = RoundDown(Size * Risk / 100, 2); } else { RiskMoney = MoneyRisk; DisplayRisk = Round(MoneyRisk / Size * 100, 2); } double UnitCost = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); if ((StopLoss != 0) && (UnitCost != 0) && (TickSize != 0)) PositionSize = RoundDown(RiskMoney / (StopLoss * UnitCost / TickSize), 2); OutputPositionSize = PositionSize; if (PositionSize < MinLot) OutputPositionSize = MinLot; else if (PositionSize > MaxLot) OutputPositionSize = MaxLot; double steps = OutputPositionSize / LotStep; if (MathFloor(steps) < steps) OutputPositionSize = MathFloor(steps) * LotStep; OutputRiskMoney = Round((StopLoss * UnitCost / TickSize) * OutputPositionSize, 2); ObjectSetString(0, "RiskMoney", OBJPROP_TEXT, "Risk, money: " + JustifyRight(FormatDouble(DoubleToString(RiskMoney, 2)), MaxNumberLength) + JustifyRight(FormatDouble(DoubleToString(OutputRiskMoney, 2)), MaxNumberLength)); ObjectSetString(0, "Risk", OBJPROP_TEXT, "Risk: " + JustifyRight(DoubleToString(DisplayRisk, 2), MaxNumberLength) + "%" + JustifyRight(DoubleToString(Round(OutputRiskMoney / Size * 100, 2), 2), MaxNumberLength - 1) + "%"); ObjectSetString(0, "PositionSize", OBJPROP_TEXT, "Pos. Size: " + JustifyRight("", MaxNumberLength) + JustifyRight(DoubleToString(OutputPositionSize, LotStep_digits), MaxNumberLength)); if (tTakeProfitLevel > 0) { string RR, OutputRR; double OutputReward = RoundDown(MathAbs(MathRound((tTakeProfitLevel - tEntryLevel) * UnitCost / TickSize)) * OutputPositionSize, 2); // 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(RoundDown(MathAbs((tTakeProfitLevel - tEntryLevel) / StopLoss), 2), 2); OutputRR = DoubleToString(RoundDown(OutputReward / OutputRiskMoney, 2), 2); } else { RR = "Invalid TP"; OutputRR = RR; } if (OutputRR == RR) RR = ""; ObjectSetString(0, "RR", OBJPROP_TEXT, "Reward/Risk: " + JustifyRight(RR, MaxNumberLength) + JustifyRight(OutputRR, MaxNumberLength)); ObjectSetString(0, "PotentialProfit", OBJPROP_TEXT, "Reward: " + JustifyRight(FormatDouble(DoubleToString(RoundDown(RiskMoney * MathAbs((tTakeProfitLevel - tEntryLevel) / StopLoss), 2), 2)), MaxNumberLength) + JustifyRight(FormatDouble(DoubleToString(OutputReward, 2)), MaxNumberLength)); } if (ShowPortfolioRisk) CalculatePortfolioRisk(); if (ShowMargin) CalculateMargin(); } //+------------------------------------------------------------------+ //| Calculates risk size and position size. Sets object values. | //+------------------------------------------------------------------+ void CalculatePortfolioRisk() { double PortfolioLossMoney = 0; if (CountPendingOrders) { int total = OrdersTotal(); for (int i = 0; i < total; i++) { double PipsLoss = 0; // Select an order. if (!OrderSelect(OrderGetTicket(i))) continue; // No stop-loss. if (OrderGetDouble(ORDER_SL) == 0) { if (IgnoreOrdersWithoutStopLoss) continue; // Buy orders if ((OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT) || (OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP)) { // Losing all the current value PipsLoss = OrderGetDouble(ORDER_PRICE_OPEN); } // Sell orders else if ((OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_LIMIT) || (OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP)) { // Potential loss is infinite PipsLoss = DBL_MAX; } } else // Some sotp-loss { // Buy orders if ((OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT) || (OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP)) { // Stop-loss below open price. PipsLoss = OrderGetDouble(ORDER_PRICE_OPEN) - OrderGetDouble(ORDER_SL); } // Sell orders else if ((OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_LIMIT) || (OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP)) { // Stop-loss above open price. PipsLoss = OrderGetDouble(ORDER_SL) - OrderGetDouble(ORDER_PRICE_OPEN); } } if (PipsLoss != DBL_MAX) { double UnitCost = SymbolInfoDouble(OrderGetString(ORDER_SYMBOL), SYMBOL_TRADE_TICK_VALUE); double TickSize_local = SymbolInfoDouble(OrderGetString(ORDER_SYMBOL), SYMBOL_TRADE_TICK_SIZE); PortfolioLossMoney += OrderGetDouble(ORDER_VOLUME_CURRENT) * PipsLoss * UnitCost / TickSize_local; } else // Infinite loss { PortfolioLossMoney = DBL_MAX; break; } } } int total = PositionsTotal(); for (int i = total; i >= 0; i--) { if (PortfolioLossMoney == DBL_MAX) break; double PipsLoss = 0; if (PositionSelect(PositionGetSymbol(i))) { // No stop-loss. if (PositionGetDouble(POSITION_SL) == 0) { if (IgnoreOrdersWithoutStopLoss) continue; if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { // Losing all the current value PipsLoss = PositionGetDouble(POSITION_PRICE_OPEN); } // Sell orders else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { // Potential loss is infinite PipsLoss = DBL_MAX; } } else // Some sotp-loss. { if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { // Stop-loss below open price. PipsLoss = PositionGetDouble(POSITION_PRICE_OPEN) - PositionGetDouble(POSITION_SL); } // Sell orders else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { // Stop-loss above open price. PipsLoss = PositionGetDouble(POSITION_SL) - PositionGetDouble(POSITION_PRICE_OPEN); } } if (PipsLoss != DBL_MAX) { double UnitCost = SymbolInfoDouble(PositionGetString(POSITION_SYMBOL), SYMBOL_TRADE_TICK_VALUE); double TickSize_local = SymbolInfoDouble(PositionGetString(POSITION_SYMBOL), SYMBOL_TRADE_TICK_SIZE); PortfolioLossMoney += PositionGetDouble(POSITION_VOLUME) * PipsLoss * UnitCost / TickSize_local; } else // Infinite loss { PortfolioLossMoney = DBL_MAX; break; } } } // If account size did not load yet. if (Size == 0) return; string PLM; if (PortfolioLossMoney == DBL_MAX) PLM = " Infinity"; else PLM = JustifyRight(FormatDouble(DoubleToString(PortfolioLossMoney, 2)), MaxNumberLength); ObjectSetString(0, "CurrentPortfolioMoneyRisk", OBJPROP_TEXT, "Current Portfolio Money Risk: " + PLM); string CPR; if (PortfolioLossMoney == DBL_MAX) CPR = " Infinity"; else CPR = JustifyRight(DoubleToString(PortfolioLossMoney / Size * 100, 2), MaxNumberLength); ObjectSetString(0, "CurrentPortfolioRisk", OBJPROP_TEXT, "Current Portfolio Risk: " + CPR + "%"); string PPMR; if (PortfolioLossMoney == DBL_MAX) PPMR = " Infinity"; else PPMR = JustifyRight(FormatDouble(DoubleToString(PortfolioLossMoney + OutputRiskMoney, 2)), MaxNumberLength); ObjectSetString(0, "PotentialPortfolioMoneyRisk", OBJPROP_TEXT, "Potential Portfolio Money Risk: " + PPMR); string PPR; if (PortfolioLossMoney == DBL_MAX) PPR = " Infinity"; else PPR = JustifyRight(DoubleToString((PortfolioLossMoney + OutputRiskMoney) / Size * 100, 2), MaxNumberLength); ObjectSetString(0, "PotentialPortfolioRisk", OBJPROP_TEXT, "Potential Portfolio Risk: " + PPR + "%"); } //+------------------------------------------------------------------+ //| Calculates margin before and after position. | //+------------------------------------------------------------------+ void CalculateMargin() { ENUM_POSITION_TYPE dir; ENUM_ORDER_TYPE order_type; if (tStopLossLevel < tEntryLevel) { dir = POSITION_TYPE_BUY; order_type = ORDER_TYPE_BUY; } else if (tStopLossLevel > tEntryLevel) { dir = POSITION_TYPE_SELL; order_type = ORDER_TYPE_SELL; } else return; // Used by some margin calculation modes. bool mode = true; MqlTick tick; double initial_margin_rate = 0, maintenance_margin_rate = 0; SymbolInfoMarginRate(Symbol(), order_type, initial_margin_rate, maintenance_margin_rate); if (maintenance_margin_rate == 0) maintenance_margin_rate = initial_margin_rate; double PositionMargin = 0; double _PositionMargin; // Based on initial margin requirements - used when needed for Future Free Margin check. // Multiplication or division by 1 is safe. double CurrencyCorrectionCoefficient = 1; double PriceCorrectionCoefficient = 1; double Leverage = 1; double existing_open_price = 1; // If Initial Margin of the symbol is given, a simple formula is used. if (InitialMargin > 0) ContractSize = MaintenanceMargin; if ((MarginMode == SYMBOL_CALC_MODE_FOREX) || (MarginMode == SYMBOL_CALC_MODE_CFDLEVERAGE)) { Leverage = (double)AccountInfoInteger(ACCOUNT_LEVERAGE); } else if (MarginMode == SYMBOL_CALC_MODE_CFDINDEX) Leverage = TickSize / TickValue; if ((MarginMode == SYMBOL_CALC_MODE_CFD) || (MarginMode == SYMBOL_CALC_MODE_CFDINDEX) || (MarginMode == SYMBOL_CALC_MODE_EXCH_STOCKS) || (MarginMode == SYMBOL_CALC_MODE_CFDLEVERAGE)) { SymbolInfoTick(Symbol(), tick); if (dir == POSITION_TYPE_BUY) PriceCorrectionCoefficient = tick.ask; else if (dir == POSITION_TYPE_SELL) PriceCorrectionCoefficient = tick.bid; } PositionMargin = (OutputPositionSize * ContractSize * PriceCorrectionCoefficient / Leverage) * maintenance_margin_rate; // Otherwise, no need to adjust margin. if (AccountCurrency != MarginCurrency) CalculateAdjustment(tick, mode, dir, CurrencyCorrectionCoefficient); PositionMargin *= CurrencyCorrectionCoefficient; if ((PositionSelect(Symbol())) && (PositionGetInteger(POSITION_TYPE) != dir)) { double existing_volume = PositionGetDouble(POSITION_VOLUME); ENUM_POSITION_TYPE existing_dir = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if ((MarginMode == SYMBOL_CALC_MODE_CFD) || (MarginMode == SYMBOL_CALC_MODE_CFDINDEX) || (MarginMode == SYMBOL_CALC_MODE_EXCH_STOCKS) || (MarginMode == SYMBOL_CALC_MODE_CFDLEVERAGE)) { existing_open_price = PositionGetDouble(POSITION_PRICE_OPEN); } double ExistingPositionMargin = (existing_volume * ContractSize * existing_open_price / Leverage) * maintenance_margin_rate; // Otherwise, no need to adjust margin. double CurrencyCorrectionCoefficient_existing = 1; if (AccountCurrency != MarginCurrency) { // This piece yields inaccurate margin of existing position, but it is the best we can get so far. CurrencyCorrectionCoefficient_existing = GetCurrencyCorrectionCoefficient(mode, existing_dir, tick); } ExistingPositionMargin *= CurrencyCorrectionCoefficient_existing; // If new position will just eat out old margin - adjust it to the previous margin size. if (existing_volume >= OutputPositionSize) PositionMargin = -(OutputPositionSize * ContractSize * existing_open_price * CurrencyCorrectionCoefficient_existing / Leverage) * maintenance_margin_rate; else PositionMargin = -ExistingPositionMargin + ((OutputPositionSize - existing_volume) * ContractSize * PriceCorrectionCoefficient * CurrencyCorrectionCoefficient / Leverage) * maintenance_margin_rate; // If initial margin requirement bigger than maintenance margin requirement, we need to calculate a separate position margin value // to check if we have enough necessary free margin before placing a trade. if ((InitialMargin > MaintenanceMargin) || (initial_margin_rate > maintenance_margin_rate)) { if (InitialMargin > 0) ContractSize = InitialMargin; ExistingPositionMargin = (existing_volume * ContractSize * existing_open_price / Leverage) * initial_margin_rate; ExistingPositionMargin *= CurrencyCorrectionCoefficient_existing; if (existing_volume >= OutputPositionSize) _PositionMargin = -(OutputPositionSize * ContractSize * existing_open_price * CurrencyCorrectionCoefficient_existing / Leverage) * initial_margin_rate; else _PositionMargin = -ExistingPositionMargin + ((OutputPositionSize - existing_volume) * ContractSize * PriceCorrectionCoefficient * CurrencyCorrectionCoefficient / Leverage) * initial_margin_rate; } else { _PositionMargin = PositionMargin; } } else if ((InitialMargin > MaintenanceMargin) || (initial_margin_rate > maintenance_margin_rate)) { if (InitialMargin > 0) ContractSize = InitialMargin; _PositionMargin = (OutputPositionSize * ContractSize * PriceCorrectionCoefficient / Leverage) * initial_margin_rate; _PositionMargin *= CurrencyCorrectionCoefficient; } else { _PositionMargin = PositionMargin; } double UsedMargin = AccountInfoDouble(ACCOUNT_MARGIN) + _PositionMargin; ObjectSetString(0, "PositionMargin", OBJPROP_TEXT, "Position Margin: " + JustifyRight(FormatDouble(DoubleToString(NormalizeDouble(PositionMargin, 2), 2)), MaxNumberLength)); ObjectSetString(0, "FutureUsedMargin", OBJPROP_TEXT, "Future Used Margin: " + JustifyRight(FormatDouble(DoubleToString(UsedMargin, 2)), MaxNumberLength)); color mod_margin_font_color = margin_font_color; double FutureMargin = RoundDown(AccountInfoDouble(ACCOUNT_MARGIN_FREE) - PositionMargin, 2); double _FutureMargin = RoundDown(AccountInfoDouble(ACCOUNT_MARGIN_FREE) - _PositionMargin, 2); string MarginSuffix = ""; // Percentage mode. if (AccStopoutMode == ACCOUNT_STOPOUT_MODE_PERCENT) { double Equity = AccountInfoDouble(ACCOUNT_EQUITY); double ML = 0; if (UsedMargin != 0) ML = Equity / UsedMargin * 100; if ((ML > 0) && (ML <= AccStopoutLevel)) mod_margin_font_color = stopout_font_color; } // Absolute value mode. else { if (_FutureMargin <= AccStopoutLevel) mod_margin_font_color = stopout_font_color; } if (_FutureMargin < 0) mod_margin_font_color = stopout_font_color; if (_FutureMargin < FutureMargin) { if (mod_margin_font_color == stopout_font_color) MarginSuffix = " (" + FormatDouble(DoubleToString(_FutureMargin, 2)) + ")"; } ObjectSetString(0, "FutureFreeMargin", OBJPROP_TEXT, "Future Free Margin: " + JustifyRight(FormatDouble(DoubleToString(FutureMargin, 2)) + MarginSuffix, MaxNumberLength)); ObjectSetInteger(0, "FutureFreeMargin", OBJPROP_COLOR, mod_margin_font_color); } //+-----------------------------------------------------------------------------------+ //| Calculates necessary adjustments for cases when MarginCurrency != AccountCurrency.| //+-----------------------------------------------------------------------------------+ void CalculateAdjustment(MqlTick &tick, bool &mode, const ENUM_POSITION_TYPE dir, double &CurrencyCorrectionCoefficient) { string CalcSymbol = NULL; if (AccountCurrency == ProfitCurrency) { CalcSymbol = Symbol(); mode = true; } else { CalcSymbol = GetSymbolByCurrencies(MarginCurrency, AccountCurrency); mode = true; // Failed. if (CalcSymbol == NULL) { // Reversing currencies. CalcSymbol = GetSymbolByCurrencies(AccountCurrency, MarginCurrency); mode = false; } } if (CalcSymbol == NULL) { Print("Error. Cannot detect proper currency pair for Margin calculation."); return; } SymbolInfoTick(CalcSymbol, tick); CurrencyCorrectionCoefficient = GetCurrencyCorrectionCoefficient(mode, dir, tick); } //+------------------------------------------------------------------+ //| Return symbol with specified margin currency and profit currency.| //+------------------------------------------------------------------+ string GetSymbolByCurrencies(string margin_currency, string profit_currency) { // Cycle through all symbols. for (int s = 0; s < SymbolsTotal(false); s++) { // Get symbol name by number. string symbolname = SymbolName(s, false); // Get its margin currency. string m_cur = SymbolInfoString(symbolname, SYMBOL_CURRENCY_MARGIN); // Get its profit currency (profit on price change). string p_cur = SymbolInfoString(symbolname, SYMBOL_CURRENCY_PROFIT); // If symbol matches both currencies, return symbol name. if ((m_cur == margin_currency) && (p_cur == profit_currency)) { // Try to get the symbol name from the Market Watch. string symbolname2 = SymbolName(s, true); // Not present in Market Watch. if (symbolname2 == "") SymbolSelect(symbolname, true); //Print(symbolname); return(symbolname); } } return(NULL); } //+------------------------------------------------------------------+ //| Get margin correction coefficient based on margin currency, | //| trade direction and current prices. | //+------------------------------------------------------------------+ double GetCurrencyCorrectionCoefficient(const bool mode, const ENUM_POSITION_TYPE dir, MqlTick &tick) { if (dir == POSITION_TYPE_BUY) { // Reverse quote. if (mode) { // Using Buy price for reverse quote. return(tick.ask); } // Direct quote. else { // Using Sell price for direct quote. return(1 / tick.bid); } } else if (dir == POSITION_TYPE_SELL) { // Reverse quote. if (mode) { // Using Sell price for reverse quote. return(tick.bid); } // Direct quote. else { // Using Buy price for direct quote. return(1 / tick.ask); } } return(-1); } //+------------------------------------------------------------------+ //| Round down a double value to a given decimal place. | //+------------------------------------------------------------------+ double RoundDown(const double value, const double digits) { int norm = (int)MathPow(10, digits); return(MathFloor(value * norm) / norm); } //+------------------------------------------------------------------+ //| Round a double value to a given decimal place. | //+------------------------------------------------------------------+ double Round(const double value, const double digits) { int norm = (int)MathPow(10, digits); return(MathRound(value * norm) / norm); } //+------------------------------------------------------------------+ //| Justify a string to the right adding enough spaces to the left. | //| length - target length of the resulting string. | //+------------------------------------------------------------------+ string JustifyRight(string str, const int length = 14) { int difference = length - StringLen(str); if (difference < 0) return("Error: String is longer than target length."); for (int i = 0; i < difference; i++) str = " " + str; return(str); } //+------------------------------------------------------------------+ //| Formats double with thousands separator. | //+------------------------------------------------------------------+ string FormatDouble(const string number) { // Find "." position. int pos = StringFind(number, "."); string integer = StringSubstr(number, 0, pos); string decimal = StringSubstr(number, pos, 3); string formatted = ""; string comma = ""; while (StringLen(integer) > 3) { int length = StringLen(integer); string group = StringSubstr(integer, length - 3); formatted = group + comma + formatted; comma = ","; integer = StringSubstr(integer, 0, length - 3); } if (integer == "-") comma = ""; if (integer != "") formatted = integer + comma + formatted; return(formatted + decimal); } //+------------------------------------------------------------------+ //| Counts decimal places. | //+------------------------------------------------------------------+ int CountDecimalPlaces(double number) { // 100 as maximum length of number. for (int i = 0; i < 100; i++) { if (MathFloor(number) == number) return(i); number *= 10; } return(-1); } //+------------------------------------------------------------------+