mql5/Indicators/RoundLevels.mq5

216 lines
8.8 KiB
MQL5
Raw Permalink Normal View History

Module Integration Summary for External Trade Management Overview To fully integrate the enhanced external trade management system, updates are required to 5 out of 7 existing modules. The updates maintain backward compatibility while adding new functionality for external trade handling. Module Update Requirements 🟢 No Updates Required (2 modules) TechnicalAnalysis.mqh - Already provides necessary calculations EntrySystem.mqh - Only handles EA's own entry signals 🟡 Minor Updates (2 modules) DataTypes.mqh - Add external trade structures and fields Utilities.mqh - Enhanced logging for external trades 🟠 Moderate Updates (3 modules) RiskManager.mqh - Enhanced risk enforcement methods TradeManager.mqh - Improved stop management for externals Dashboard.mqh - Display external trade information Integration Steps Phase 1: Data Structures (DataTypes.mqh) Add ENUM_EXTERNAL_STATUS enumeration Extend ManagedTrade structure with external-specific fields Add ExternalTradeStats structure for metrics Update DashboardConfig with show_external flag Key additions: external_status - Track state of external trade source_name - Identify where trade came from stops_modified - Track if we modified the trade original_sl/tp - Store original values for comparison Phase 2: Risk Management (RiskManager.mqh) Add EnforceRiskRulesEnhanced() method Implement GetExternalExposure() for risk aggregation Add UpdateExternalStats() for tracking Enhance ValidateAndAdjustRiskExternal() method Key features: Separate risk calculation for external trades Cache mechanism for performance Statistical tracking of external positions Smart risk adjustment without closing trades Phase 3: Trade Management (TradeManager.mqh) Add ApplyDefaultStopsEnhanced() with better logic Implement OverrideExternalStops() with smart override Create ManageExternalTrade() with different rules Add ApplyBreakevenExternal() with wider triggers Key features: Smart stop override (only improve, never worsen) Different management rules for external trades Respect minimum broker distances Track modification success/failure rates Phase 4: User Interface (Dashboard.mqh) Add CreateExternalSection() for display area Implement UpdateExternalSection() for real-time updates Add SetCustomText() for flexible display Create ShowExternalTrades() toggle method Key features: Real-time external trade count and risk Color-coded risk warnings List of active external positions Modification statistics display Phase 5: Logging (Utilities.mqh) Add LogExternalTrade() for detailed event logging Create separate CSV log for external trades Enhance GenerateReportEnhanced() with external section Add IdentifyTradeSource() for magic number interpretation Key features: Separate CSV log for external trade events Detailed tracking of all modifications Source identification from magic numbers Enhanced reporting with external statistics
2025-08-27 14:21:02 +01:00
//+------------------------------------------------------------------+
//| RoundLevels.mq5 |
//| https://t.me/ForexEaPremium |
//+------------------------------------------------------------------+
#property copyright "https://t.me/ForexEaPremium"
#property link "https://t.me/ForexEaPremium"
#property version "1.03"
#property description "Generates round level zone background shading on chart."
#property indicator_chart_window
#property indicator_plots 0
input int Levels = 5; // Levels - number of level zones in each direction.
input int Interval = 50; // Interval between zones in points.
input int ZoneWidth = 10; // Zone width in points.
input color ColorUp = clrFireBrick;
input color ColorDn = clrDarkGreen;
input bool InvertZones = false; // Invert zones to shade the areas between round numbers.
input bool ZonesAsBackground = true;
input bool DrawLines = false; // Draw lines on levels.
input color LineColor = clrDarkGray;
input int LineWidth = 1;
input ENUM_LINE_STYLE LineStyle = STYLE_DASHDOT;
input bool LinesAsBackground = false;
input bool ShowLineLabels = false;
input color LineLabelColor = clrWhite;
input string ObjectPrefix = "RoundLevels";
input string Comment_1 = "======================="; // Notification Options
input bool EnableNotify = false; // Enable Notifications Feature
input bool SendAlert = true; // Send Alert Notification
input bool SendApp = false; // Send Notification to Mobile
input bool SendEmail = false; // Send Notification via Email
input int AlertDelay = 5; // Alert Delay, seconds
enum direction
{
Up,
Down
};
datetime LastNotificationTime;
void OnInit()
{
LastNotificationTime = TimeCurrent();
}
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, ObjectPrefix);
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int& spread[])
{
double starting_price = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);
for (int i = 0; i < Levels; i++)
{
// Calculate price levels below and above the current price.
double lvl_down = FindNextLevel(NormalizeDouble(starting_price - i * Interval * _Point, _Digits), Down);
double lvl_up = FindNextLevel(NormalizeDouble(starting_price + i * Interval * _Point, _Digits), Up);
// Calculate and draw rectangle below current price.
string name = ObjectPrefix + "D" + IntegerToString(i);
double price1, price2;
if (InvertZones)
{
price1 = lvl_down - (ZoneWidth / 2 * _Point);
price2 = lvl_down - ((Interval - ZoneWidth / 2) * _Point);
}
else
{
price1 = lvl_down + (ZoneWidth / 2 * _Point);
price2 = lvl_down - (ZoneWidth / 2 * _Point);
}
DrawRectangle(name, price1, price2, ColorDn);
name = ObjectPrefix + "LD" + IntegerToString(i);
if (DrawLines) DrawLine(name, lvl_down);
Notify(price1, price2);
// Calculate and draw rectangle above current price.
name = ObjectPrefix + "U" + IntegerToString(i);
if (InvertZones)
{
price1 = lvl_up + ((Interval - ZoneWidth / 2) * _Point);
price2 = lvl_up + (ZoneWidth / 2 * _Point);
}
else
{
price1 = lvl_up + (ZoneWidth / 2 * _Point);
price2 = lvl_up - (ZoneWidth / 2 * _Point);
}
DrawRectangle(name, price1, price2, ColorUp);
name = ObjectPrefix + "LU" + IntegerToString(i);
if (DrawLines) DrawLine(name, lvl_up);
Notify(price1, price2);
}
// Center level required for inverted zones.
if (InvertZones)
{
double lvl_down = FindNextLevel(NormalizeDouble(starting_price, _Digits), Down);
double lvl_up = FindNextLevel(NormalizeDouble(starting_price, _Digits), Up);
string name = ObjectPrefix + "C";
double price1 = lvl_up - (ZoneWidth / 2 * _Point);
double price2 = lvl_down + (ZoneWidth / 2 * _Point);
DrawRectangle(name, price1, price2, (ColorDn + ColorUp) / 2);
Notify(price1, price2);
}
return 0;
}
double FindNextLevel(const double sp, const direction dir)
{
// Multiplier for getting number of points in the price.
double multiplier = MathPow(10, _Digits);
// Integer price (nubmer of points in the price).
int integer_price = (int)MathRound(sp * MathPow(10, _Digits));
// Distance from the next round number down.
int distance = integer_price % Interval;
if (dir == Down)
{
return NormalizeDouble(MathRound(integer_price - distance) / multiplier, _Digits);
}
else if (dir == Up)
{
return NormalizeDouble((integer_price + (Interval - distance)) / multiplier, _Digits);
}
return EMPTY_VALUE;
}
void DrawRectangle(const string name, const double price1, const double price2, const color colour)
{
if (ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_RECTANGLE, 0, 0, 0);
ObjectSetDouble(0, name, OBJPROP_PRICE, 0, price1);
ObjectSetDouble(0, name, OBJPROP_PRICE, 1, price2);
ObjectSetInteger(0, name, OBJPROP_TIME, 0, D'1970.01.01');
ObjectSetInteger(0, name, OBJPROP_TIME, 1, D'3000.12.31');
ObjectSetInteger(0, name, OBJPROP_COLOR, colour);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_BACK, ZonesAsBackground);
ObjectSetInteger(0, name, OBJPROP_FILL, true);
if ((!DrawLines) && (ShowLineLabels))
{
DrawLineLabel(name + "_LL", (price1 + price2) / 2);
}
}
void DrawLine(const string name, const double price)
{
if (ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_HLINE, 0, 0, 0);
ObjectSetDouble(0, name, OBJPROP_PRICE, 0, price);
ObjectSetInteger(0, name, OBJPROP_COLOR, LineColor);
ObjectSetInteger(0, name, OBJPROP_STYLE, LineStyle);
ObjectSetInteger(0, name, OBJPROP_WIDTH, LineWidth);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_BACK, LinesAsBackground);
if (ShowLineLabels)
{
DrawLineLabel(name + "_LL", price);
}
}
void DrawLineLabel(const string name, const double price)
{
if (ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_ARROW_RIGHT_PRICE, 0, 0, 0);
ObjectSetDouble(0, name, OBJPROP_PRICE, 0, price);
ObjectSetInteger(0, name, OBJPROP_TIME, 0, iTime(Symbol(), Period(), 0));
ObjectSetInteger(0, name, OBJPROP_COLOR, LineLabelColor);
ObjectSetInteger(0, name, OBJPROP_WIDTH, LineWidth);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_BACK, false);
}
void Notify(double price1, double price2)
{
if ((!EnableNotify) || ((!SendAlert) && (!SendApp) && (!SendEmail))) return;
if (TimeCurrent() - LastNotificationTime < AlertDelay) return;
if ((SymbolInfoDouble(Symbol(), SYMBOL_BID) > MathMax(price2, price1)) || (SymbolInfoDouble(Symbol(), SYMBOL_BID) < MathMin(price2, price1))) return;
string EmailSubject = "RoundLevels " + Symbol() + " Notification";
string EmailBody = AccountInfoString(ACCOUNT_COMPANY) + " - " + AccountInfoString(ACCOUNT_NAME) + " - " + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) + "\r\nRoundLevels Notification for " + Symbol() + " @ " + EnumToString((ENUM_TIMEFRAMES)Period()) + "\r\n";
string AlertText = "";
string AppText = AccountInfoString(ACCOUNT_COMPANY) + " - " + AccountInfoString(ACCOUNT_NAME) + " - " + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) + " - RoundLevels - " + Symbol() + " @ " + EnumToString((ENUM_TIMEFRAMES)Period()) + " - ";
string Text = "Bid Price (" + DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_BID), _Digits) + ") inside a Zone (" + DoubleToString(price2, _Digits) + "-" + DoubleToString(price1, _Digits) + ")";
EmailBody += Text;
AlertText += Text;
AppText += Text;
if (SendAlert) Alert(AlertText);
if (SendEmail)
{
if (!SendMail(EmailSubject, EmailBody)) Print("Error sending email " + IntegerToString(GetLastError()));
}
if (SendApp)
{
if (!SendNotification(AppText)) Print("Error sending notification " + IntegerToString(GetLastError()));
}
LastNotificationTime = TimeCurrent();
}
//+------------------------------------------------------------------+