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
385 lines
No EOL
15 KiB
MQL5
385 lines
No EOL
15 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| QQE.mq5 |
|
|
//| Copyright © 2010-2022, https://t.me/ForexEaPremium |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "https://t.me/ForexEaPremium"
|
|
#property link "https://t.me/ForexEaPremium"
|
|
#property version "1.03"
|
|
|
|
#property description "QQE - Qualitative Quantitative Estimation."
|
|
#property description "Calculated as two indicators:"
|
|
#property description "1) MA on RSI"
|
|
#property description "2) Difference of MA on RSI and MA of MA of ATR of MA of RSI"
|
|
#property description "The signal for buy is when blue line crosses level 50 from below after crossing the yellow line from below."
|
|
#property description "The signal for sell is when blue line crosses level 50 from above after crossing the yellow line from above."
|
|
|
|
#property indicator_separate_window
|
|
#property indicator_buffers 6
|
|
#property indicator_plots 2
|
|
#property indicator_width1 2
|
|
#property indicator_color1 clrDodgerBlue
|
|
#property indicator_type1 DRAW_LINE
|
|
#property indicator_label1 "RSI MA"
|
|
#property indicator_color2 clrYellow
|
|
#property indicator_type2 DRAW_LINE
|
|
#property indicator_style2 STYLE_DOT
|
|
#property indicator_label2 "Smoothed"
|
|
#property indicator_level1 50
|
|
#property indicator_levelcolor clrAqua
|
|
#property indicator_levelstyle STYLE_DOT
|
|
|
|
// Inputs
|
|
input int SF = 5; // Smoothing Factor
|
|
input bool AlertOnCrossover = false;
|
|
input bool AlertOnLevel = false;
|
|
input int AlertLevel = 50;
|
|
input bool ArrowsOnCrossover = true;
|
|
input color CrossoverUpArrow = clrGreen;
|
|
input color CrossoverDnArrow = clrRed;
|
|
input bool ArrowsOnLevel = true;
|
|
input color LevelUpArrow = clrGreen;
|
|
input color LevelDnArrow = clrRed;
|
|
input bool NativeAlerts = true;
|
|
input bool EmailAlerts = false;
|
|
input bool NotificationAlerts = true;
|
|
input ENUM_TIMEFRAMES UpperTimeframe = PERIOD_CURRENT;
|
|
input string ObjectPrefix = "QQE-";
|
|
|
|
// Global variables:
|
|
int RSI_Period = 14;
|
|
int Wilders_Period;
|
|
int StartBar;
|
|
datetime LastAlertTimeCross, LastAlertTimeLevel;
|
|
|
|
// For MTF support:
|
|
int QQE_handle;
|
|
|
|
// Buffers:
|
|
double TrLevelSlow[];
|
|
double AtrRsi[];
|
|
double MaAtrRsi[];
|
|
double Rsi[];
|
|
double RsiMa[];
|
|
double MaMaAtrRsi[];
|
|
|
|
// Indicator handles
|
|
int myRSI;
|
|
|
|
void OnInit()
|
|
{
|
|
LastAlertTimeCross = 0;
|
|
LastAlertTimeLevel = 0;
|
|
Wilders_Period = RSI_Period * 2 - 1;
|
|
StartBar = MathMax(SF, Wilders_Period);
|
|
|
|
SetIndexBuffer(0, RsiMa, INDICATOR_DATA);
|
|
SetIndexBuffer(1, TrLevelSlow, INDICATOR_DATA);
|
|
SetIndexBuffer(2, AtrRsi, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(3, MaAtrRsi, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(4, Rsi, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(5, MaMaAtrRsi, INDICATOR_CALCULATIONS);
|
|
|
|
ArraySetAsSeries(RsiMa, true);
|
|
ArraySetAsSeries(TrLevelSlow, true);
|
|
ArraySetAsSeries(AtrRsi, true);
|
|
ArraySetAsSeries(MaAtrRsi, true);
|
|
ArraySetAsSeries(Rsi, true);
|
|
ArraySetAsSeries(MaMaAtrRsi, true);
|
|
|
|
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0);
|
|
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0);
|
|
|
|
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, StartBar);
|
|
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, StartBar);
|
|
|
|
IndicatorSetInteger(INDICATOR_DIGITS, 2);
|
|
|
|
myRSI = iRSI(Symbol(), Period(), RSI_Period, PRICE_CLOSE);
|
|
|
|
if (PeriodSeconds(UpperTimeframe) > PeriodSeconds())
|
|
{
|
|
string IndicatorFileName = MQLInfoString(MQL_PROGRAM_NAME);
|
|
QQE_handle = iCustom(Symbol(), UpperTimeframe, IndicatorFileName, SF, false, false, AlertLevel, false, LevelUpArrow, LevelDnArrow, false, CrossoverUpArrow, CrossoverDnArrow, false, false, false, UpperTimeframe, ObjectPrefix);
|
|
}
|
|
else
|
|
{
|
|
QQE_handle = INVALID_HANDLE;
|
|
if (PeriodSeconds(UpperTimeframe) < PeriodSeconds())
|
|
{
|
|
Print("UpperTimeframe should be above the current timeframe.");
|
|
}
|
|
}
|
|
|
|
IndicatorSetString(INDICATOR_SHORTNAME, "QQE(" + IntegerToString(SF) + ")");
|
|
}
|
|
|
|
void OnDeinit(const int reason)
|
|
{
|
|
ObjectsDeleteAll(ChartID(), 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[])
|
|
{
|
|
ArraySetAsSeries(Time, true);
|
|
ArraySetAsSeries(High, true);
|
|
ArraySetAsSeries(Low, true);
|
|
|
|
if (rates_total <= StartBar) return 0;
|
|
|
|
int counted = prev_calculated - 1;
|
|
if (counted < 1)
|
|
{
|
|
for (int i = rates_total - StartBar; i < rates_total; i++)
|
|
{
|
|
TrLevelSlow[i] = 0.0;
|
|
AtrRsi[i] = 0.0;
|
|
MaAtrRsi[i] = 0.0;
|
|
Rsi[i] = 0.0;
|
|
RsiMa[i] = 0.0;
|
|
MaMaAtrRsi[i] = 0.0;
|
|
}
|
|
}
|
|
|
|
bool rec_only_latest_upper_bar = false; // Recalculate only the latest upper timeframe bar.
|
|
if ((counted > 0) && (QQE_handle != INVALID_HANDLE))
|
|
{
|
|
counted = prev_calculated - PeriodSeconds(UpperTimeframe) / PeriodSeconds(); // Make the indicator redraw all current bars that constitute the upper timeframe bar.
|
|
rec_only_latest_upper_bar = true;
|
|
}
|
|
else
|
|
{
|
|
counted = rates_total - counted - 1;
|
|
if (counted > rates_total - StartBar - 1) counted = rates_total - StartBar - 1;
|
|
}
|
|
|
|
if (QQE_handle == INVALID_HANDLE)
|
|
{
|
|
if (CopyBuffer(myRSI, 0, 0, counted + 2, Rsi) != counted + 2) return 0;
|
|
|
|
// Fills "counted" cells of RsiMA with EMA of Rsi.
|
|
CalculateEMA(counted + 1, SF, Rsi, RsiMa);
|
|
|
|
for (int i = counted; i >= 0; i--)
|
|
{
|
|
AtrRsi[i] = MathAbs(RsiMa[i + 1] - RsiMa[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = counted; i >= 0; i--)
|
|
{
|
|
double buf[1];
|
|
if (rec_only_latest_upper_bar)
|
|
if (Time[i] < iTime(Symbol(), UpperTimeframe, 0)) continue; // Skip bars older than the upper current bar.
|
|
int n = CopyBuffer(QQE_handle, 0, Time[i], 1, buf);
|
|
if (n == 1) RsiMa[i] = buf[0];
|
|
else return prev_calculated;
|
|
}
|
|
}
|
|
|
|
int i = counted + 1;
|
|
double tr = 0, rsi1 = 0;
|
|
|
|
if (QQE_handle == INVALID_HANDLE)
|
|
{
|
|
// Fills "counted" cells of MaAtrRsi with EMA of AtrRsi.
|
|
CalculateEMA(counted, Wilders_Period, AtrRsi, MaAtrRsi);
|
|
tr = TrLevelSlow[i];
|
|
rsi1 = RsiMa[i];
|
|
CalculateEMA(counted, Wilders_Period, MaAtrRsi, MaMaAtrRsi);
|
|
}
|
|
|
|
while (i > 0)
|
|
{
|
|
i--;
|
|
if (QQE_handle == INVALID_HANDLE)
|
|
{
|
|
double rsi0 = RsiMa[i];
|
|
double dar = MaMaAtrRsi[i] * 4.236;
|
|
double dv = tr;
|
|
|
|
if (rsi0 < tr)
|
|
{
|
|
tr = rsi0 + dar;
|
|
if ((rsi1 < dv) && (tr > dv)) tr = dv;
|
|
}
|
|
else if (rsi0 > tr)
|
|
{
|
|
tr = rsi0 - dar;
|
|
if ((rsi1 > dv) && (tr < dv)) tr = dv;
|
|
}
|
|
|
|
TrLevelSlow[i] = tr;
|
|
rsi1 = rsi0;
|
|
}
|
|
else
|
|
{
|
|
double buf[1];
|
|
if (rec_only_latest_upper_bar)
|
|
if (Time[i] < iTime(Symbol(), UpperTimeframe, 0)) continue; // Skip bars older than the upper current bar.
|
|
int n = CopyBuffer(QQE_handle, 1, Time[i], 1, buf);
|
|
if (n == 1) TrLevelSlow[i] = buf[0];
|
|
else return prev_calculated;
|
|
}
|
|
|
|
// Arrows:
|
|
if ((i > 0) || (QQE_handle != INVALID_HANDLE)) // In MTF mode, check as soon as possible.
|
|
{
|
|
// Prepare for multi-timeframe mode.
|
|
int cur_i = i;
|
|
int pre_i = i + 1;
|
|
// Actual MTF (to avoid non-existing signals):
|
|
if ((QQE_handle != INVALID_HANDLE) && (i < PeriodSeconds(UpperTimeframe) / PeriodSeconds())) // Can safely skip this step if processing old bars.
|
|
{
|
|
// Find the bar that corresponds to the upper timeframe's latest finished bar.
|
|
int customIndex = iBarShift(Symbol(), UpperTimeframe, Time[cur_i]);
|
|
cur_i++;
|
|
while ((cur_i < rates_total) && (iBarShift(Symbol(), UpperTimeframe, Time[cur_i]) == customIndex))
|
|
{
|
|
cur_i++;
|
|
}
|
|
if (cur_i == rates_total) return prev_calculated;
|
|
// Find the bar that corresponds to the upper timeframe's pre-latest finished bar.
|
|
customIndex = iBarShift(Symbol(), UpperTimeframe, Time[cur_i]);
|
|
pre_i = cur_i + 1;
|
|
while ((pre_i < rates_total) && (iBarShift(Symbol(), UpperTimeframe, Time[pre_i])) == customIndex)
|
|
{
|
|
pre_i++;
|
|
}
|
|
if (pre_i == rates_total) return prev_calculated;
|
|
cur_i = pre_i - 1; // Use oldest lower timeframe bar inside that upper timeframe bar.
|
|
}
|
|
|
|
if (ArrowsOnCrossover)
|
|
{
|
|
string name = ObjectPrefix + "CArrow" + TimeToString(Time[cur_i]);
|
|
if ((RsiMa[pre_i] < TrLevelSlow[pre_i]) && (RsiMa[cur_i] > TrLevelSlow[cur_i]))
|
|
{
|
|
ObjectCreate(ChartID(), name, OBJ_ARROW_THUMB_UP, 0, Time[cur_i], Low[cur_i] - 1 * _Point);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_COLOR, LevelUpArrow);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_ANCHOR, ANCHOR_TOP);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_WIDTH, 5);
|
|
}
|
|
else if ((RsiMa[pre_i] > TrLevelSlow[pre_i]) && (RsiMa[cur_i] < TrLevelSlow[cur_i]))
|
|
{
|
|
ObjectCreate(ChartID(), name, OBJ_ARROW_THUMB_DOWN, 0, Time[cur_i], High[cur_i] + 1 * _Point);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_COLOR, LevelDnArrow);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_WIDTH, 5);
|
|
}
|
|
}
|
|
if (ArrowsOnLevel)
|
|
{
|
|
string name = ObjectPrefix + "LArrow" + TimeToString(Time[cur_i]);
|
|
if ((RsiMa[pre_i] < AlertLevel) && (RsiMa[cur_i] > AlertLevel))
|
|
{
|
|
ObjectCreate(ChartID(), name, OBJ_ARROW_UP, 0, Time[cur_i], Low[cur_i] - 1 * _Point);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_COLOR, LevelUpArrow);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_ANCHOR, ANCHOR_TOP);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_WIDTH, 5);
|
|
}
|
|
else if ((RsiMa[pre_i] > AlertLevel) && (RsiMa[cur_i] < AlertLevel))
|
|
{
|
|
ObjectCreate(ChartID(), name, OBJ_ARROW_DOWN, 0, Time[cur_i], High[cur_i] + 1 * _Point);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_COLOR, LevelDnArrow);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
|
|
ObjectSetInteger(ChartID(), name, OBJPROP_WIDTH, 5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((!NativeAlerts) && (!EmailAlerts) && (!NotificationAlerts)) return rates_total;
|
|
if ((!AlertOnCrossover) && (!AlertOnLevel)) return rates_total;
|
|
|
|
// Prepare for multi-timeframe mode.
|
|
int pre_i = 2;
|
|
if (QQE_handle != INVALID_HANDLE)
|
|
{
|
|
// Find the bar that corresponds to the upper timeframe's latest finished bar.
|
|
int cnt = 1;
|
|
int customIndex = iBarShift(Symbol(), UpperTimeframe, Time[0]);
|
|
while ((cnt < rates_total) && (iBarShift(Symbol(), UpperTimeframe, Time[cnt]) == customIndex))
|
|
{
|
|
cnt++;
|
|
}
|
|
i = cnt;
|
|
// Find the bar that corresponds to the upper timeframe's pre-latest finished bar.
|
|
customIndex = iBarShift(Symbol(), UpperTimeframe, Time[cnt]);
|
|
cnt++;
|
|
while ((cnt < rates_total) && (iBarShift(Symbol(), UpperTimeframe, Time[cnt]) == customIndex))
|
|
{
|
|
cnt++;
|
|
}
|
|
pre_i = cnt;
|
|
}
|
|
else i = 1; // Non-MTF.
|
|
|
|
if (AlertOnLevel)
|
|
{
|
|
if ((LastAlertTimeLevel > 0) && (((RsiMa[pre_i] < AlertLevel) && (RsiMa[i] > AlertLevel)) || ((RsiMa[pre_i] > AlertLevel) && (RsiMa[i] < AlertLevel))) && (Time[i - 1] > LastAlertTimeLevel))
|
|
{
|
|
string TextNative = IntegerToString(AlertLevel) + " level Cross ";
|
|
if ((RsiMa[pre_i] > AlertLevel) && (RsiMa[i] < AlertLevel)) TextNative += "Down";
|
|
else TextNative += "Up";
|
|
string Text = "QQE " + Symbol() + ", TF: " + TimeframeToString((ENUM_TIMEFRAMES)Period()) + " " + TextNative;
|
|
DoAlerts(Text, TextNative);
|
|
LastAlertTimeLevel = Time[i - 1];
|
|
}
|
|
}
|
|
|
|
if (AlertOnCrossover)
|
|
{
|
|
if ((LastAlertTimeCross > 0) && (((RsiMa[pre_i] < TrLevelSlow[pre_i]) && (RsiMa[i] > TrLevelSlow[i])) || ((RsiMa[pre_i] > TrLevelSlow[pre_i]) && (RsiMa[i] < TrLevelSlow[i]))) && (Time[i - 1] > LastAlertTimeCross))
|
|
{
|
|
string TextNative = "RSI MA crossed Smoothed Line from ";
|
|
if ((RsiMa[pre_i] > TrLevelSlow[pre_i]) && (RsiMa[i] < TrLevelSlow[i])) TextNative += "above.";
|
|
else TextNative += "below.";
|
|
string Text = "QQE " + Symbol() + ", TF: " + TimeframeToString((ENUM_TIMEFRAMES)Period()) + " " + TextNative;
|
|
DoAlerts(Text, TextNative);
|
|
LastAlertTimeCross = Time[i - 1];
|
|
}
|
|
}
|
|
|
|
if (LastAlertTimeLevel == 0) LastAlertTimeLevel = Time[0];
|
|
if (LastAlertTimeCross == 0) LastAlertTimeCross = Time[0];
|
|
|
|
return rates_total;
|
|
}
|
|
|
|
void DoAlerts(string Text, string TextNative)
|
|
{
|
|
if (NativeAlerts) Alert(TextNative);
|
|
if (EmailAlerts) SendMail(Text, Text);
|
|
if (NotificationAlerts) SendNotification(Text);
|
|
}
|
|
|
|
string TimeframeToString(ENUM_TIMEFRAMES P)
|
|
{
|
|
return StringSubstr(EnumToString(P), 7);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Exponential Moving Average |
|
|
//| Fills the buffer array with EMA values. |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateEMA(int begin, int period, const double &price[], double &result[])
|
|
{
|
|
double SmoothFactor = 2.0 / (1.0 + period);
|
|
|
|
for (int i = begin; i >= 0; i--)
|
|
{
|
|
if (price[i] == EMPTY_VALUE) result[i] = 0;
|
|
else result[i] = price[i] * SmoothFactor + result[i + 1] * (1.0 - SmoothFactor);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+ |