ICT Code Blueprint
ICT Code Blueprint
//| MySmartMoneyEA |
//| Copyright 2024, [Your Name/Company] |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, [Your Name/Company]"
#property link "https://www.mql5.com"
#property version "1.00"
#property description "Expert Advisor implementing multiple Smart Money Concepts
strategies."
//--- Define constants for array sizes to prevent 'undeclared identifier' errors
#define MAX_BARS_FOR_IMPULSIVE_MOVE 20 // Max bars for impulsive move
(InpImpulsiveMoveMaxBars + buffer)
#define MAX_LOOKBACK_FOR_OB 25 // Max lookback for Order Block
(lookbackBars + 2 + buffer)
#define MAX_LOOKBACK_FOR_BREAKER 60 // Max lookback for Breaker Block
(lookbackBars + 5 + buffer)
//+------------------------------------------------------------------+
//| Helper Function: TimeToDay - Extracts the date part of a datetime |
//+------------------------------------------------------------------+
datetime TimeToDay(datetime inputTime) {
MqlDateTime dt;
TimeToStruct(inputTime, dt);
dt.hour = 0;
dt.min = 0;
dt.sec = 0;
return StructToTime(dt);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
// Initialize strategy names for logging/identification
g_strategyNames[0] = "Liquidity Sweep (Double Top/Bottom)";
g_strategyNames[1] = "Power of Three (Bullish)";
g_strategyNames[2] = "Power of Three (Bearish)";
g_strategyNames[3] = "Break and Retest (S/R Flip)";
g_strategyNames[4] = "Fibonacci Retracement";
g_strategyNames[5] = "Moving Average Pullback";
g_strategyNames[6] = "Trend Line Pullback";
g_strategyNames[7] = "Fair Value Gap (FVG) Pullback";
g_strategyNames[8] = "Institutional Funding Candles Pullback";
g_strategyNames[9] = "Order Blocks Pullback";
g_strategyNames[10] = "Breaker Block Pullback";
g_strategyNames[11] = "RSI Divergence with Confluence";
// Check for a new bar (candle) to ensure strategy evaluation is done on closed
bars
datetime currentBarTime = iTime(Symbol(), Period(), 0);
if (currentBarTime == g_prevBarTime) {
return; // Not a new bar yet, wait for the next tick
}
g_prevBarTime = currentBarTime;
ZeroMemory(request);
request.action = TRADE_ACTION_DEAL;
request.symbol = symbol;
request.volume = lotSize;
request.type = ORDER_TYPE_BUY;
request.price = g_tick.ask; // Use current Ask for Buy Market Order
request.sl = NormalizeDouble(stopLoss, _Digits);
request.tp = NormalizeDouble(takeProfit, _Digits);
request.deviation = 10; // Allowed deviation from requested price
request.type_filling = ORDER_FILLING_FOK; // Fill or Kill
request.comment = comment;
request.magic = g_magicNumberBase; // Use a unique magic number for this EA
if(!OrderSend(request, result)) {
Print("Failed to send BUY order: ", result.retcode, " - ", result.comment);
return false;
}
Print("BUY order sent successfully. Ticket: ", result.deal);
return true;
}
ZeroMemory(request);
request.action = TRADE_ACTION_DEAL;
request.symbol = symbol;
request.volume = lotSize;
request.type = ORDER_TYPE_SELL;
request.price = g_tick.bid; // Use current Bid for Sell Market Order
request.sl = NormalizeDouble(stopLoss, _Digits);
request.tp = NormalizeDouble(takeProfit, _Digits);
request.deviation = 10; // Allowed deviation from requested price
request.type_filling = ORDER_FILLING_FOK; // Fill or Kill
request.comment = comment;
request.magic = g_magicNumberBase; // Use a unique magic number for this EA
if(!OrderSend(request, result)) {
Print("Failed to send SELL order: ", result.retcode, " - ",
result.comment);
return false;
}
Print("SELL order sent successfully. Ticket: ", result.deal);
return true;
}
// Ensure there are enough bars available in total before attempting CopyRates
if (Bars(symbol, timeframe) < (currentBar + swingBars + 1)) {
return false;
}
// Function to identify Swing Low (candle has X higher lows on left and X higher
lows on right)
bool IsSwingLow(string symbol, ENUM_TIMEFRAMES timeframe, int currentBar, int
swingBars) {
// Ensure we have enough historical data to look back 'swingBars' candles on
either side
// And that the currentBar is not too recent to form a swing point
if (currentBar < swingBars || currentBar + swingBars >= Bars(symbol,
timeframe)) {
return false;
}
// Ensure there are enough bars available in total before attempting CopyRates
if (Bars(symbol, timeframe) < (currentBar + swingBars + 1)) {
return false;
}
MqlRates prevCandle;
for (int i = currentBar + 1; i < currentBar + lookbackBars; i++) { // Loop back
from currentBar + 1 (older bars)
if (IsSwingHigh(symbol, timeframe, i, InpSwingPointBars)) {
if (!GetCandleData(symbol, timeframe, i, prevCandle)) continue;
double prevHigh = prevCandle.high;
if (MathAbs(currentHigh - prevHigh) <= pipsDeviation * _Point) {
return true; // Found an equal high
}
}
}
return false;
}
MqlRates prevCandle;
for (int i = currentBar + 1; i < currentBar + lookbackBars; i++) { // Loop back
from currentBar + 1 (older bars)
if (IsSwingLow(symbol, timeframe, i, InpSwingPointBars)) {
if (!GetCandleData(symbol, timeframe, i, prevCandle)) continue;
double prevLow = prevCandle.low;
if (MathAbs(currentLow - prevLow) <= pipsDeviation * _Point) {
return true; // Found an equal low
}
}
}
return false;
}
// Bullish FVG: Low of C1 > High of C3 (gap between C1 low and C3 high)
if (c1.low > c3.high && c2.low > c3.high) { // Ensure C2 also doesn't fill the
gap
fvgHigh = c1.low; // FVG High (upper boundary of the gap)
fvgLow = c3.high; // FVG Low (lower boundary of the gap)
isBullishFVG = true;
return true;
}
// Bearish FVG: High of C1 < Low of C3 (gap between C1 high and C3 low)
else if (c1.high < c3.low && c2.high < c3.low) { // Ensure C2 also doesn't fill
the gap
fvgHigh = c3.low; // FVG High (upper boundary of the gap)
fvgLow = c1.high; // FVG Low (lower boundary of the gap)
isBullishFVG = false;
return true;
}
return false; // No FVG found
}
// Check if any part of the candle (wick or body) has touched or crossed
the FVG zone
if (MathMax(candle.low, fvgLow) <= MathMin(candle.high, fvgHigh)) {
return false; // FVG has been touched/mitigated
}
}
return true; // FVG is untouched
}
// Function to check for specific Candlestick Rejection Patterns (e.g., Pin Bar,
Engulfing)
bool IsRejectionCandle(string symbol, ENUM_TIMEFRAMES timeframe, int currentBar,
bool& isBullishRejection) {
MqlRates current, prev;
if (!GetCandleData(symbol, timeframe, currentBar, current) || !
GetCandleData(symbol, timeframe, currentBar + 1, prev)) return false;
// Helper function to check if price is retesting a zone (used by OB, FVG, Breaker)
bool IsPriceRetestingZone(double currentPrice, double zoneHigh, double zoneLow,
double maxPenetrationATR) {
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return false;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return false;
// Check if any part of the current candle (wick or body) has entered the zone
if (MathMax(currentCandle.low, zoneLow) <= MathMin(currentCandle.high,
zoneHigh)) {
return true; // Current candle is inside or touching the zone
}
// Check if volatility is low (Current ATR below average of last 100 bars)
double longTermATR = GetATR(symbol, timeframe, 100, 1); // Get average ATR over
100 bars
if (longTermATR == 0.0 || currentATR > 0.75 * longTermATR) return false; //
Current ATR is too high
// Iterate from older bars towards current (shift from latest bar is 'i')
// Loop from index 1 to count - 2 to ensure rates[i-1], rates[i], rates[i+1]
are all valid.
for (int i = 1; i < count - 1; i++) {
MqlRates current = rates[i]; // The potential OB candle
MqlRates prev = rates[i+1]; // Candle before potential OB (older)
MqlRates next = rates[i-1]; // Candle after potential OB (newer)
// Check if any part of the candle (wick or body) has touched or crossed
the OB zone
if (MathMax(candle.low, obLow) <= MathMin(candle.high, obHigh)) {
return false; // OB has been touched/mitigated
}
}
return true; // OB is untouched
}
MqlRates rates[MAX_LOOKBACK_FOR_BREAKER];
int count = CopyRates(symbol, timeframe, 0, lookbackBars + 5, rates);
if (count < InpSwingPointBars * 2 + 3) return false; // Need enough bars for at
least two swing points and a break
// Look for a Swing Low/High that has been broken and retested (the "breaker"
is the swing point that was broken)
// Loop ensures that rates[i] and its neighbors (for swing point check) are
within bounds.
// The range for 'i' must ensure 'i + InpSwingPointBars + 1' and 'i - 1' are
valid.
for (int i = InpSwingPointBars + 1; i < count - InpSwingPointBars - 1; i++) {
// Bearish Breaker (price breaks swing low, then retests previous swing
high (now resistance))
// Sequence: Swing High -> Swing Low -> Break of Swing Low -> Retest of
Swing High
if (IsSwingLow(symbol, timeframe, i, InpSwingPointBars)) {
double swlPrice = rates[i].low;
int swlBar = i;
// Look for a Swing High (SWH) *before* this Swing Low (j > swlBar for
older bars)
for (int j = swlBar + InpSwingPointBars + 1; j < count; j++) {
if (IsSwingHigh(symbol, timeframe, j, InpSwingPointBars)) {
double swhPrice = rates[j].high;
int swhBar = j;
if (brokenSwl) {
// The breaker zone is the last candle of the original
Swing High or the Swing High itself.
// For a Bearish Breaker, the original Swing High becomes
resistance.
// We use the range of the candle that formed the swing
high.
breakerHigh = rates[swhBar].high;
breakerLow = rates[swlBar].low; // The breaker zone is the
original swing low that was broken
isBullishBreaker = false; // This is a bearish setup
// Print("Found potential Bearish Breaker from SWH at bar
", swhBar, " and SWL at ", swlBar);
return true;
}
}
}
}
// Bullish Breaker (price breaks swing high, then retests previous swing
low (now support))
// Sequence: Swing Low -> Swing High -> Break of Swing High -> Retest of
Swing Low
if (IsSwingHigh(symbol, timeframe, i, InpSwingPointBars)) {
double swhPrice = rates[i].high;
int swhBar = i;
// Look for a Swing Low (SWL) *before* this Swing High (j > swhBar for
older bars)
for (int j = swhBar + InpSwingPointBars + 1; j < count; j++) {
if (IsSwingLow(symbol, timeframe, j, InpSwingPointBars)) {
double swlPrice = rates[j].low;
int swlBar = j;
if (brokenSwh) {
// The breaker zone is the last candle of the original
Swing Low or the Swing Low itself.
// For a Bullish Breaker, the original Swing Low becomes
support.
// We use the range of the candle that formed the swing
low.
breakerHigh = rates[swhBar].high; // The breaker zone is
the original swing high that was broken
breakerLow = rates[swlBar].low;
isBullishBreaker = true; // This is a bullish setup
// Print("Found potential Bullish Breaker from SWL at bar
", swlBar, " and SWH at ", swhBar);
return true;
}
}
}
}
}
return false;
}
//+------------------------------------------------------------------+
//| Strategy Implementations |
//+------------------------------------------------------------------+
// Find the most recent swing high/low that occurred at least 2 bars ago (to
allow for the sweep candle itself)
for (int i = 2; i <= InpLiquiditySweepReversalBars + InpSwingPointBars + 5; i+
+) {
if (IsSwingHigh(Symbol(), Period(), i, InpSwingPointBars)) {
MqlRates tempRates;
if (GetCandleData(Symbol(), Period(), i, tempRates)) {
lastSwingHigh = tempRates.high;
lastSwingHighBar = i;
break; // Take the most recent one
}
}
}
for (int i = 2; i <= InpLiquiditySweepReversalBars + InpSwingPointBars + 5; i+
+) {
if (IsSwingLow(Symbol(), Period(), i, InpSwingPointBars)) {
MqlRates tempRates;
if (GetCandleData(Symbol(), Period(), i, tempRates)) {
lastSwingLow = tempRates.low;
lastSwingLowBar = i;
break; // Take the most recent one
}
}
}
// Check for Bearish Liquidity Sweep (price sweeps above lastSwingHigh then
reverses)
if (lastSwingHighBar != -1 && IsLiquiditySweep(Symbol(), Period(),
lastSwingHigh, sweepBar, true)) { // isBullishSweep = true (swept upwards)
// Ensure HTF alignment for bearish move
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 1: Bearish sweep, but HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = currentCandle.high + InpATRMagnifier * atr;
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice, false);
// Looking for support
// Check if previous candle swept below consolidation low and closed back above
or near it
if (prevCandle.low < consolidationLow && prevCandle.close > consolidationLow -
InpATRMagnifier * GetATR(Symbol(),Period(),InpATRPeriod,1)) {
// This is a simplified manipulation. A true manipulation would have a
deeper sweep and then reversal.
// Also, the previous candle should ideally be a rejection candle.
bool isRejection;
if (!IsRejectionCandle(Symbol(), Period(), 1, isRejection) || !isRejection)
{ // Check if prevCandle is a bullish rejection
// Print("Strategy 2 (Bullish): Previous candle not a bullish rejection
after sweep.");
return;
}
// Check if previous candle swept above consolidation high and closed back
below or near it
if (prevCandle.high > consolidationHigh && prevCandle.close < consolidationHigh
+ InpATRMagnifier * GetATR(Symbol(),Period(),InpATRPeriod,1)) {
bool isRejection;
if (!IsRejectionCandle(Symbol(), Period(), 1, isRejection) || isRejection)
{ // Check if prevCandle is a bearish rejection
// Print("Strategy 2 (Bearish): Previous candle not a bearish rejection
after sweep.");
return;
}
// Simplified S/R detection: find a strong swing high/low that acted as S/R
for (int i = 5; i < 50; i++) { // Look back 50 bars for S/R
if (IsSwingHigh(Symbol(), Period(), i, InpSwingPointBars)) {
srLevel = iHigh(Symbol(), Period(), i);
// Check if it was resistance and now broken to become support
if (prevCandle.high > srLevel + InpBreakPipsBeyondLevel * _Point) { //
Previous candle broke above
// Ensure the break was significant and body penetration
if (MathAbs(prevCandle.open - prevCandle.close) / (prevCandle.high
- prevCandle.low) >= InpBodyPenetrationRatio) {
// Check if current candle is retesting from above and closes
above or near the level
if (currentCandle.low < srLevel + InpATRMagnifier * atr &&
currentCandle.close > srLevel - InpATRMagnifier * atr) {
isResistanceBroken = true;
break;
}
}
}
}
if (IsSwingLow(Symbol(), Period(), i, InpSwingPointBars)) {
srLevel = iLow(Symbol(), Period(), i);
// Check if it was support and now broken to become resistance
if (prevCandle.low < srLevel - InpBreakPipsBeyondLevel * _Point) { //
Previous candle broke below
// Ensure the break was significant and body penetration
if (MathAbs(prevCandle.open - prevCandle.close) / (prevCandle.high
- prevCandle.low) >= InpBodyPenetrationRatio) {
// Check if current candle is retesting from below and closes
below or near the level
if (currentCandle.high > srLevel - InpATRMagnifier * atr &&
currentCandle.close < srLevel + InpATRMagnifier * atr) {
isSupportBroken = true;
break;
}
}
}
}
}
if (!impulsiveMoveFound) {
// Print("Strategy 4: No impulsive move found.");
return;
}
// Find the actual swing high/low points for the impulsive move
// Need to find the start and end of the impulsive move
int impulseStartBar = -1;
int impulseEndBar = -1; // Usually bar 1 (previous closed bar)
if (isBullishImpulse) {
swingLow = impulseRates[count -1].low; // Start of impulse (oldest candle's
low)
swingHigh = impulseRates[0].high; // End of impulse (most recent candle's
high)
// Find the actual lowest low and highest high within the impulsive move
range
for(int i = 0; i < count; i++) {
if (impulseRates[i].low < swingLow) swingLow = impulseRates[i].low;
if (impulseRates[i].high > swingHigh) swingHigh = impulseRates[i].high;
}
} else { // Bearish impulse
swingHigh = impulseRates[count -1].high; // Start of impulse (oldest
candle's high)
swingLow = impulseRates[0].low; // End of impulse (most recent candle's
low)
// Find the actual highest high and lowest low within the impulsive move
range
for(int i = 0; i < count; i++) {
if (impulseRates[i].high > swingHigh) swingHigh = impulseRates[i].high;
if (impulseRates[i].low < swingLow) swingLow = impulseRates[i].low;
}
}
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
// Bullish scenario: Price above MAs, MAs pointing up, pullback to 20/50 SMA,
bullish rejection
if (sma20 > sma50 && sma20 > sma20_prev && sma50 > sma50_prev) { // Uptrend
confirmed by MA crossover and slope
// Price pulled back to 20 SMA
if (currentCandle.low <= sma20 + _Point * 5 && currentCandle.close > sma20
- _Point * 5) { // Price touches/closes near 20 SMA
bool isRejection;
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) &&
isRejection) { // Bullish rejection
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 5 (Bullish): HTF not aligned for bullish
bias.");
return;
}
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
for (int i = InpSwingPointBars + 1; i < 100; i++) { // Look back for swing lows
(oldest bar is i)
if (IsSwingLow(Symbol(), Period(), i, InpSwingPointBars)) {
MqlRates tempRates;
if (GetCandleData(Symbol(), Period(), i, tempRates)) {
if (firstLowBar == -1) { // Found the first (older) swing low
firstLowBar = i;
firstLowPrice = tempRates.low;
} else { // Found the second (more recent) swing low
secondLowBar = i;
secondLowPrice = tempRates.low;
// Ensure it's an upward sloping line (newer low is higher than
older low)
if (secondLowPrice > firstLowPrice) {
isBullishTrendLine = true;
// Calculate expected price of trendline at currentBar
(shift 0)
// Using linear interpolation: y = y1 + ((y2-y1)/(x2-x1)) *
(x-x1)
// x is bar shift (0 for current), y is price
// For CopyRates, index 0 is current, so older bars have
higher shifts.
// Here, firstLowBar is older (larger shift), secondLowBar
is newer (smaller shift).
// Let x1 = secondLowBar, y1 = secondLowPrice
// Let x2 = firstLowBar, y2 = firstLowPrice
trendLinePrice = secondLowPrice + ((firstLowPrice -
secondLowPrice) / (double)(firstLowBar - secondLowBar)) * (0.0 - secondLowBar);
break;
} else { // Not an ascending trendline, reset older point to
current found swing low
firstLowBar = secondLowBar;
firstLowPrice = secondLowPrice;
secondLowBar = -1; secondLowPrice = 0.0;
}
}
}
}
}
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
if (fvgOriginBar == -1) {
// Print("Strategy 7: No unmitigated FVG found within lookback bars.");
return;
}
bool isRejection;
if (isBullishFVG) { // Bullish FVG (price expected to retest FVG Low and
bounce)
if (IsPriceRetestingZone(currentCandle.close, fvgHigh, fvgLow,
InpATRMagnifier)) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) &&
isRejection) { // Bullish rejection at FVG
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 7 (Bullish): HTF not aligned for bullish
bias.");
return;
}
// Check for large body and significant portion of range (strong momentum
candle)
if (bodySize > InpIFCMinATRBody * atr && bodySize / range > 0.75) {
MqlRates subsequentCandle; // The candle immediately after IFC (closer
to current)
if (!GetCandleData(Symbol(), Period(), i - 1, subsequentCandle))
continue;
bool isRejection;
if (isBullishIFC) { // Bullish IFC pullback
if (IsPriceRetestingZone(currentCandle.close, ifcEntryZoneHigh,
ifcEntryZoneLow, InpATRMagnifier)) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) &&
isRejection) { // Bullish rejection
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 8 (Bullish): HTF not aligned for bullish
bias.");
return;
}
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
// Find the most recent unmitigated Order Block (look back up to 20 bars for
OB)
// Iterate from recent bars (small shift) to older bars (larger shift)
for (int i = 0; i <= 20; i++) { // Check for OBs starting from current bar
(i=0) to 20 bars back
if (FindOrderBlock(Symbol(), Period(), i, isBullishOB, obHigh, obLow)) {
// Corrected call to IsOrderBlockUnmitigated
if (IsOrderBlockUnmitigated(Symbol(), Period(), obHigh, obLow, i)) {
obCreationBar = i;
break; // Found the most recent unmitigated OB
}
}
}
if (obCreationBar == -1) {
// Print("Strategy 9: No unmitigated Order Block found.");
return;
}
bool isRejection;
if (isBullishOB) { // Bullish OB (price expected to retest OB and bounce up)
if (IsPriceRetestingZone(currentCandle.close, obHigh, obLow,
InpATRMagnifier)) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) &&
isRejection) { // Bullish rejection
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 9 (Bullish): HTF not aligned for bullish
bias.");
return;
}
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
// Find the most recent Breaker Block (look back up to 50 bars for Breaker)
for (int i = 0; i <= 50; i++) { // Check recent bars for Breaker formation
if (FindBreakerBlock(Symbol(), Period(), i, isBullishBreaker, breakerHigh,
breakerLow)) {
// Filter breaker by min range
if (MathAbs(breakerHigh - breakerLow) < InpBreakerBlockMinATRRange *
atr) continue;
breakerOriginBar = i;
break; // Found most recent valid breaker
}
}
if (breakerOriginBar == -1) {
// Print("Strategy 10: No Breaker Block found.");
return;
}
bool isRejection;
if (isBullishBreaker) { // Bullish Breaker (price expected to retest and bounce
up)
if (IsPriceRetestingZone(currentCandle.close, breakerHigh, breakerLow,
InpATRMagnifier)) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) &&
isRejection) { // Bullish rejection
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 10 (Bullish): HTF not aligned for bullish
bias.");
return;
}
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
// Find a previous swing high that is lower than currentHighPrice but has a
higher RSI
for (int i = currentHighBar + 1; i < InpRSIDivergenceLookbackBars; i++) {
if (IsSwingHigh(Symbol(), Period(), i, InpSwingPointBars)) {
MqlRates tempCandle;
if (GetCandleData(Symbol(), Period(), i, tempCandle)) {
prevHighPrice = tempCandle.high;
prevRSI = GetRSI(Symbol(), Period(), InpRSIPeriod, PRICE_CLOSE, i);
prevHighBar = i;
// Check Price: newer high > older high AND RSI: newer RSI < older
RSI
if (currentHighPrice > prevHighPrice && rsiCurrent < prevRSI -
InpRSIPointsDeviation) {
// Found Bearish Divergence
bool isRejection;
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) && !
isRejection) { // Bearish rejection
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod,
false)) {
// Print("Strategy 11 (Bearish): HTF not aligned for
bearish bias.");
return;
}
// Find a previous swing low that is higher than currentLowPrice but has a
lower RSI
for (int i = currentLowBar + 1; i < InpRSIDivergenceLookbackBars; i++) {
if (IsSwingLow(Symbol(), Period(), i, InpSwingPointBars)) {
MqlRates tempCandle;
if (GetCandleData(Symbol(), Period(), i, tempCandle)) {
prevLowPrice_bullish = tempCandle.low;
prevRSI_bullish = GetRSI(Symbol(), Period(), InpRSIPeriod,
PRICE_CLOSE, i);
prevLowBar_bullish = i;
// Check Price: newer low < older low AND RSI: newer RSI > older
RSI
if (currentLowPrice < prevLowPrice_bullish && rsiCurrent >
prevRSI_bullish + InpRSIPointsDeviation) {
// Found Bullish Divergence
bool isRejection;
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) &&
isRejection) { // Bullish rejection
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod,
true)) {
// Print("Strategy 11 (Bullish): HTF not aligned for
bullish bias.");
return;
}