Skip to content

Commit

Permalink
3.04
Browse files Browse the repository at this point in the history
1. Added a dark theme mode, which can be set via the DarkMode input parameter.
2. Added an option to display breakeven lines at levels where the Position Sizer will apply breakeven to existing positions.
3. Added the Entry line label to display the distance from the current price to the Entry level for pending orders.
4. Added input parameters (ShowMaxParametersOnTrading, ShowFusesOnTrading, and ShowCheckboxesOnTrading) to make the Trading tab more compact.
5. Added separate total and per-symbol fields to control volume, risk, and number of trades on the Trading tab.
6. Added an input parameter (SettingsFile) to let users load their own custom settings file with panel fields configured according to their needs. The EA won't delete custom settings files.
7. Added two new hotkeys — to set a stop-loss (SetStopLossHotKey) and a take-profit (SetTakeProfitHotKey) to the price level at the mouse pointer's current position.
8. Added an option to change the translation of the panel's interface in MetaTrader 5. Language files are currently only available for Ukrainian and Russian. Users can create and use their own translation files.
9. Changed the breakeven mechanism to take into account the size of the commission if UseCommissionToSetTPDistance is set to true.
10. Changed the additional TP fields to appear with some non-zero value if the main TP is non-zero.
11. Fixed a bug when switching the chart's symbol could result in line labels disappearing if SymbolChange was set to Keep panel as is.
12. Fixed a bug in the MT5 version that resulted in the trade type not resetting properly when SymbolChange was set to Reset to defaults on symbol change.
13. Fixed a bug that resulted in false warnings and incorrect position size calculation when the EA failed to get the symbol information at the first attempt.
14. Fixed a bug that resulted in some of the +/- buttons to remain on the chart when the panel was minimized.
15. Fixed a bug that resulted in the stop-loss not keeping its correct distance when SLDistanceInPoints was set to true.
16. Fixed a bug when the additional funds asterisk remained visible even when the panel was minimized.
17. Fixed a bug in the MT5 version that resulted in the risk field resetting to default when the user clicked on the position size field after setting it to a custom value.
18. Fixed a bug that resulted in the stop-loss line sometimes disappearing on a symbol change.
19. Fixed a bug in the MT5 version that resulted in the Stop Price line appearing after switching from Stop Limit to Instant and setting the DisableStopLimit input parameter to true.
20. Fixed a bug that would result in wrong value appearing in the account size field after clicking on it and then clicking outside if the value contained a thousands separator.
21. Fixed a bug in the MT5 version that caused a critical 'array out of range' error when clicking on the +/- buttons for a single take-profit field.
22. Fixed a bug in the MT4 version that caused a critical 'array out of range' error when switching the chart symbol to a one with more additional TPs saved in a settings file.
23. Fixed a bug that caused the risk to be calculated based on the percentage value instead of the money value when Custom Balance was updated even if the MoneyRisk parameter was greater than 0.
24. Fixed a bug with ATR timeframe button not working.
25. Fixed a bug with incorrect volume share allocation when adding a new TP.
26. Fixed a bug in the MT5 version that caused a critical 'stack overflow' error when the EA failed to convert a symbol's profit or margin currency.
27. Fixed a bug that would cause TP values set in points to shift when dragging doing some unrelated chart manipulations.
  • Loading branch information
EarnForex authored Jul 20, 2023
1 parent 45e7c67 commit e23e2f2
Show file tree
Hide file tree
Showing 13 changed files with 2,878 additions and 985 deletions.
33 changes: 25 additions & 8 deletions MQL4/Experts/Position Sizer/Defines.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@
#include <Controls\Label.mqh>
#include <Arrays\List.mqh>

#define CONTROLS_EDIT_COLOR_ENABLE C'255,255,255'
#define CONTROLS_EDIT_COLOR_DISABLE C'221,221,211'
color CONTROLS_EDIT_COLOR_ENABLE = C'255,255,255';
color CONTROLS_EDIT_COLOR_DISABLE = C'221,221,211';

#define CONTROLS_BUTTON_COLOR_ENABLE C'200,200,200'
#define CONTROLS_BUTTON_COLOR_DISABLE C'224,224,224'
color CONTROLS_BUTTON_COLOR_ENABLE = C'200,200,200';
color CONTROLS_BUTTON_COLOR_DISABLE = C'224,224,224';

color DARKMODE_BG_DARK_COLOR = 0x444444;
color DARKMODE_CONTROL_BRODER_COLOR = 0x888888;
color DARKMODE_MAIN_AREA_BORDER_COLOR = 0x333333;
color DARKMODE_MAIN_AREA_BG_COLOR = 0x666666;
color DARKMODE_EDIT_BG_COLOR = 0xAAAAAA;
color DARKMODE_BUTTON_BG_COLOR = 0xA19999;
color DARKMODE_TEXT_COLOR = 0x000000;;

enum ENTRY_TYPE
{
Expand Down Expand Up @@ -82,6 +90,13 @@ enum COMMISSION_TYPE
COMMISSION_PERCENT, // Percentage
};

enum CALCULATE_RISK_FOR_TRADING_TAB
{
CALCULATE_RISK_FOR_TRADING_TAB_NO, // Normal calculation
CALCULATE_RISK_FOR_TRADING_TAB_TOTAL, // For Trading tab - total
CALCULATE_RISK_FOR_TRADING_TAB_PER_SYMBOL // For Trading tab - per symbol
};

struct Settings
{
ENTRY_TYPE EntryType;
Expand Down Expand Up @@ -116,7 +131,6 @@ struct Settings
int MaxSpread;
int MaxEntrySLDistance;
int MinEntrySLDistance;
double MaxPositionSize;
// For SL/TP distance modes:
int StopLoss;
int TakeProfit;
Expand All @@ -131,9 +145,12 @@ struct Settings
bool CommentAutoSuffix;
int TrailingStopPoints;
int BreakEvenPoints;
int MaxNumberOfTrades;
bool AllSymbols;
double MaxTotalRisk;
int MaxNumberOfTradesTotal;
int MaxNumberOfTradesPerSymbol;
double MaxPositionSizeTotal;
double MaxPositionSizePerSymbol;
double MaxRiskTotal;
double MaxRiskPerSymbol;
// For ATR:
int ATRPeriod;
double ATRMultiplierSL;
Expand Down
Binary file added MQL4/Experts/Position Sizer/EF-Icon-64x64px.ico
Binary file not shown.
156 changes: 135 additions & 21 deletions MQL4/Experts/Position Sizer/Position Sizer Trading.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -98,34 +98,39 @@ void Trade()
}
}

if (sets.MaxNumberOfTrades > 0)
if ((sets.MaxNumberOfTradesTotal > 0) || (sets.MaxNumberOfTradesPerSymbol > 0))
{
int total = OrdersTotal();
int cnt = 0;
int cnt = 0, persymbol_cnt = 0;
for (int i = 0; i < total; i++)
{
if (!OrderSelect(i, SELECT_BY_POS)) continue;
if ((sets.MagicNumber != 0) && (OrderMagicNumber() != sets.MagicNumber)) continue;
if ((!sets.AllSymbols) && (OrderSymbol() != Symbol())) continue;
if (OrderSymbol() == Symbol()) persymbol_cnt++;
cnt++;
}
if (cnt >= sets.MaxNumberOfTrades)
if ((cnt + sets.TakeProfitsNumber > sets.MaxNumberOfTradesTotal) && (sets.MaxNumberOfTradesTotal > 0))
{
Alert("Not taking a trade - current # of traes (", cnt, ") >= maximum number of trades (", sets.MaxNumberOfTrades, ").");
Alert("Not taking a trade - current total # of trades (", cnt, ") + number of trades in execution (", sets.TakeProfitsNumber, ") > maximum total number of trades allowed (", sets.MaxNumberOfTradesTotal, ").");
return;
}
if ((persymbol_cnt + sets.TakeProfitsNumber > sets.MaxNumberOfTradesPerSymbol) && (sets.MaxNumberOfTradesPerSymbol > 0))
{
Alert("Not taking a trade - current # of trades per symbol (", persymbol_cnt, ") + number of trades in execution (", sets.TakeProfitsNumber, ") > maximum number of trades per symbol allowed (", sets.MaxNumberOfTradesPerSymbol, ").");
return;
}
}

if (sets.MaxTotalRisk > 0)
if (sets.MaxRiskTotal > 0)
{
CalculatePortfolioRisk(true);
CalculatePortfolioRisk(CALCULATE_RISK_FOR_TRADING_TAB_TOTAL);
double risk;
if (PortfolioLossMoney != DBL_MAX)
{
risk = (PortfolioLossMoney + OutputRiskMoney) / AccSize * 100;
if (risk > sets.MaxTotalRisk)
if (risk > sets.MaxRiskTotal)
{
Alert("Not taking a trade - total potential risk (", DoubleToString(risk, 2), ") >= maximum risk (", DoubleToString(sets.MaxTotalRisk, 2), ").");
Alert("Not taking a trade - total potential risk (", DoubleToString(risk, 2), ") >= maximum total risk allowed (", DoubleToString(sets.MaxRiskTotal, 2), ").");
return;
}
}
Expand All @@ -135,6 +140,25 @@ void Trade()
return;
}
}
if (sets.MaxRiskPerSymbol > 0)
{
CalculatePortfolioRisk(CALCULATE_RISK_FOR_TRADING_TAB_PER_SYMBOL);
double risk;
if (PortfolioLossMoney != DBL_MAX)
{
risk = (PortfolioLossMoney + OutputRiskMoney) / AccSize * 100;
if (risk > sets.MaxRiskPerSymbol)
{
Alert("Not taking a trade - potential risk per symbol (", DoubleToString(risk, 2), ") >= maximum risk per symbol allowed (", DoubleToString(sets.MaxRiskPerSymbol, 2), ").");
return;
}
}
else
{
Alert("Not taking a trade - infinite potential risk per symbol.");
return;
}
}

ENUM_SYMBOL_TRADE_EXECUTION Execution_Mode = (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_EXEMODE);
string warning_suffix = "";
Expand Down Expand Up @@ -189,20 +213,25 @@ void Trade()
}
}

if (sets.MaxPositionSize > 0)
if ((sets.MaxPositionSizeTotal > 0) && (sets.MaxPositionSizePerSymbol > 0))
{
int total = OrdersTotal();
double volume = 0;
double volume = 0, volume_persymbol = 0;
for (int i = 0; i < total; i++)
{
if (!OrderSelect(i, SELECT_BY_POS)) continue;
if ((sets.MagicNumber != 0) && (OrderMagicNumber() != sets.MagicNumber)) continue;
if ((!sets.AllSymbols) && (OrderSymbol() != Symbol())) continue;
if (OrderSymbol() == Symbol()) volume_persymbol += OrderLots();
volume += OrderLots();
}
if (volume + PositionSize > sets.MaxPositionSize)
if ((volume + PositionSize > sets.MaxPositionSizeTotal) && (sets.MaxPositionSizeTotal > 0))
{
Alert("Not taking a trade - current total volume (", DoubleToString(volume, LotStep_digits), ") + new position volume (", DoubleToString(PositionSize, LotStep_digits), ") >= maximum total volume (", sets.MaxPositionSize, ").");
Alert("Not taking a trade - current total volume (", DoubleToString(volume, LotStep_digits), ") + new position volume (", DoubleToString(PositionSize, LotStep_digits), ") >= maximum total volume allowed (", DoubleToString(sets.MaxPositionSizeTotal, LotStep_digits), ").");
return;
}
if ((volume_persymbol + PositionSize > sets.MaxPositionSizePerSymbol) && (sets.MaxPositionSizePerSymbol > 0))
{
Alert("Not taking a trade - current volume per symbol (", DoubleToString(volume_persymbol, LotStep_digits), ") + new position volume (", DoubleToString(PositionSize, LotStep_digits), ") >= maximum volume per symbol allowed (", DoubleToString(sets.MaxPositionSizePerSymbol, LotStep_digits), ").");
return;
}
}
Expand Down Expand Up @@ -480,7 +509,38 @@ void DoTrailingStop()
// Sets SL to breakeven based on the Magic number and symbol.
void DoBreakEven()
{
if ((!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) || (!TerminalInfoInteger(TERMINAL_CONNECTED)) || (!MQLInfoInteger(MQL_TRADE_ALLOWED))) return;
if (!TerminalInfoInteger(TERMINAL_CONNECTED)) return;

// Delete old BE lines if necessary.
if (be_line_color != clrNONE)
{
int obj_total = ObjectsTotal(ChartID(), -1, OBJ_HLINE);
for (int i = obj_total - 1; i >= 0; i--)
{
string obj_name = ObjectName(ChartID(), i, -1, OBJ_HLINE);
if (StringFind(obj_name, ObjectPrefix + "BE") == -1) continue; // Skip all other horizontal lines.
int ticket = (int)StringToInteger(StringSubstr(obj_name, StringLen(ObjectPrefix + "BE")));
if (!OrderSelect(ticket, SELECT_BY_TICKET)) // No longer exists.
{
ObjectDelete(ChartID(), obj_name); // Delete the line.
if (ShowLineLabels) ObjectDelete(ChartID(), ObjectPrefix + "BEL" + IntegerToString(ticket)); // Delete the label.
}
else // Check if already triggered. Order selected.
{
double be_price = NormalizeDouble(StringToDouble(ObjectGetString(ChartID(), obj_name, OBJPROP_TOOLTIP)), _Digits);
if (((OrderType() == OP_BUY) && (OrderStopLoss() >= be_price)) // Already triggered.
|| ((OrderType() == OP_SELL) && (OrderStopLoss() <= be_price) && (OrderStopLoss() != 0)))
{
ObjectDelete(ChartID(), obj_name); // Delete the line.
if (ShowLineLabels) ObjectDelete(ChartID(), ObjectPrefix + "BEL" + IntegerToString(ticket)); // Delete the label.
}
}
}
}

if (sets.BreakEvenPoints <= 0) return;

if ((!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) || (!MQLInfoInteger(MQL_TRADE_ALLOWED))) return;

for (int i = 0; i < OrdersTotal(); i++)
{
Expand All @@ -489,25 +549,44 @@ void DoBreakEven()
else
{
if ((OrderSymbol() != Symbol()) || (OrderMagicNumber() != sets.MagicNumber)) continue;

// Based on the commission if UseCommissionToSetTPDistance is set to true.
double extra_be_distance = 0;
if ((UseCommissionToSetTPDistance) && (sets.CommissionPerLot != 0))
{
// Calculate real commission in currency units.
double commission = CalculateCommission();

// Extra BE Distance = Commission Size / Point_value.
// Commission Size = Commission * 2.
// Extra BE Distance = Commission * 2 / Point_value.
if ((UnitCost_reward != 0) && (TickSize != 0))
extra_be_distance = commission * 2 / (UnitCost_reward / TickSize);
}

if (OrderType() == OP_BUY)
{
double BE = NormalizeDouble(OrderOpenPrice() + sets.BreakEvenPoints * _Point, _Digits);
if ((Bid >= BE) && (OrderOpenPrice() > OrderStopLoss())) // Only move to breakeven if the current stop-loss is lower.
double BE_threshold = NormalizeDouble(OrderOpenPrice() + sets.BreakEvenPoints * _Point, _Digits);
double BE_price = NormalizeDouble(OrderOpenPrice() + extra_be_distance, _Digits);
if ((be_line_color != clrNONE) && (BE_price > OrderStopLoss())) DrawBELine(OrderTicket(), BE_threshold, BE_price); // Only draw if not triggered yet.
if ((Bid >= BE_threshold) && (Bid >= BE_price) && (BE_price > OrderStopLoss())) // Only move to BE if the price reached the necessary threshold, the price is above the calculated BE price, and the current stop-loss is lower.
{
// Write Open price to the SL field.
if (!OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), OrderExpiration()))
if (!OrderModify(OrderTicket(), OrderOpenPrice(), BE_price, OrderTakeProfit(), OrderExpiration()))
Print("OrderModify Buy BE failed " + ErrorDescription(GetLastError()) + ".");
else
Print("Breakeven was applied to position - " + Symbol() + " BUY-order #" + IntegerToString(OrderTicket()) + " Lotsize = " + DoubleToString(OrderLots(), LotStep_digits) + ", OpenPrice = " + DoubleToString(OrderOpenPrice(), _Digits) + ", Stop-Loss was moved from " + DoubleToString(OrderStopLoss(), _Digits) + ".");
}
}
else if (OrderType() == OP_SELL)
{
double BE = NormalizeDouble(OrderOpenPrice() - sets.BreakEvenPoints * _Point, _Digits);
if ((Ask <= BE) && ((OrderOpenPrice() < OrderStopLoss()) || (OrderStopLoss() == 0))) // Only move to breakeven if the current stop-loss is higher (or zero).
double BE_threshold = NormalizeDouble(OrderOpenPrice() - sets.BreakEvenPoints * _Point, _Digits);
double BE_price = NormalizeDouble(OrderOpenPrice() - extra_be_distance, _Digits);
if ((be_line_color != clrNONE) && ((BE_price < OrderStopLoss()) || (OrderStopLoss() == 0))) DrawBELine(OrderTicket(), BE_threshold, BE_price);
if ((Ask <= BE_threshold) && (Ask <= BE_price) && ((BE_price < OrderStopLoss()) || (OrderStopLoss() == 0))) // Only move to BE if the price reached the necessary threshold, the price below the calculated BE price, and the current stop-loss is higher (or zero).
{
// Write Open price to the SL field.
if (!OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), OrderExpiration()))
if (!OrderModify(OrderTicket(), OrderOpenPrice(), BE_price, OrderTakeProfit(), OrderExpiration()))
Print("OrderModify Sell BE failed " + ErrorDescription(GetLastError()) + ".");
else
Print("Breakeven was applied to position - " + Symbol() + " SELL-order #" + IntegerToString(OrderTicket()) + " Lotsize = " + DoubleToString(OrderLots(), LotStep_digits) + ", OpenPrice = " + DoubleToString(OrderOpenPrice(), _Digits) + ", Stop-Loss was moved from " + DoubleToString(OrderStopLoss(), _Digits) + ".");
Expand All @@ -516,4 +595,39 @@ void DoBreakEven()
}
}
}

void DrawBELine(int ticket, double be_threshold, double be_price)
{
string obj_name = ObjectPrefix + "BE" + IntegerToString(ticket); // Line.
ObjectCreate(ChartID(), obj_name, OBJ_HLINE, 0, TimeCurrent(), be_threshold);
ObjectSetDouble(ChartID(), obj_name, OBJPROP_PRICE, be_threshold);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_STYLE, be_line_style);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_COLOR, be_line_color);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_WIDTH, be_line_width);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_BACK, true);
ObjectSetString(ChartID(), obj_name, OBJPROP_TOOLTIP, DoubleToString(be_price, _Digits)); // Store BE price in the tooltip.

if (sets.ShowLines) ObjectSetInteger(ChartID(), obj_name, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
else ObjectSetInteger(ChartID(), obj_name, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);

if (ShowLineLabels)
{
obj_name = ObjectPrefix + "BEL" + IntegerToString(ticket); // Label.
ObjectCreate(ChartID(), obj_name, OBJ_LABEL, 0, 0, 0);
if (sets.ShowLines) ObjectSetInteger(ChartID(), obj_name, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
else ObjectSetInteger(ChartID(), obj_name, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_COLOR, clrNONE);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_HIDDEN, false);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(ChartID(), obj_name, OBJPROP_BACK, DrawTextAsBackground);
ObjectSetString(ChartID(), obj_name, OBJPROP_TOOLTIP, "");
string text = "BE for ";
if (OrderType() == OP_BUY) text += "Buy";
else if (OrderType() == OP_SELL) text += "Sell";
text += " #" + IntegerToString(ticket);
DrawLineLabel(obj_name, text, be_threshold, be_line_color, false, -6);
}
}
//+------------------------------------------------------------------+
Loading

0 comments on commit e23e2f2

Please sign in to comment.