216 lines
8.8 KiB
MQL5
216 lines
8.8 KiB
MQL5
|
//+------------------------------------------------------------------+
|
||
|
//| 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();
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|