//+------------------------------------------------------------------+
//| 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."
//--- Includes (Standard Libraries - essential for basic MQL5 functions)
// Corrected include paths to standard MQL5 locations
#include <stdlib.mqh> // For MQL5 standard library functions like NormalizeDouble,
MathAbs
#include <Trade\Trade.mqh> // For CTrade class or MqlTradeRequest/MqlTradeResult
structures
#include <Math\Math.mqh> // For mathematical functions like MathAtan
// If 'file not found' errors persist for standard includes like stdlib.mqh or
Math\Math.mqh,
// it often indicates an issue with your MetaTrader 5 installation or corrupt
files.
// Please verify your MT5 installation and ensure these files exist in your MQL5\
Include directory.
//--- 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)
//--- Global Variables ---
datetime g_lastTradeDay[12]; // Array to track the last trading day for each
strategy (index 0-11 for 12 strategies)
string g_strategyNames[12]; // Array to map strategy index to its name
int g_magicNumberBase = 12345; // Base magic number for trades, unique for
each EA instance
//--- Helper Variables for OnTick logic ---
datetime g_prevBarTime = 0; // To detect new bars
MqlTick g_tick; // To get current tick data (Bid/Ask)
//--- EA Inputs (configurable by user) ---
input double InpLotSize = 0.05; // Fixed lot size for all trades
input bool InpEnableStrategy1 = true; // Liquidity Sweep (Double Top/Bottom
Based)
input bool InpEnableStrategy2 = true; // Power of Three
input bool InpEnableStrategy3 = true; // Break and Retest (S/R Flip)
input bool InpEnableStrategy4 = true; // Fibonacci Retracement
input bool InpEnableStrategy5 = true; // Moving Average (20/50 SMA) Pullback
input bool InpEnableStrategy6 = true; // Trend Line Pullback
input bool InpEnableStrategy7 = true; // Fair Value Gap (FVG) Pullback
input bool InpEnableStrategy8 = true; // Institutional Funding Candles (IFC)
Pullback
input bool InpEnableStrategy9 = true; // Order Blocks Pullback
input bool InpEnableStrategy10 = true; // Breaker Block Pullback
input bool InpEnableStrategy11 = true; // RSI Divergence with Confluence
// --- Common Strategy Parameters ---
input int InpSwingPointBars = 3; // Bars required on each side for swing
high/low detection
input double InpEqualHighLowPipsDeviation = 5.0; // Pips deviation for equal
highs/lows
input int InpLiquiditySweepReversalBars = 2; // Max candles to confirm reversal
after sweep
input int InpATRPeriod = 14; // Period for ATR calculation (used for
volatility, pips conversion)
input double InpATRMagnifier = 0.25; // ATR multiplier for minor penetration, SL
distance, etc. (0.25 = 25% of ATR)
input ENUM_TIMEFRAMES InpHTFPeriod = PERIOD_H4; // Higher Time Frame for alignment
check (e.g., H4)
input int InpMovingAveragePeriod = 20; // Period for 20 SMA
input int InpLongMovingAveragePeriod = 50; // Period for 50 SMA
input double InpADXPeriod = 14; // Period for ADX calculation
input double InpADXThreshold = 25.0; // ADX threshold for trending market
input int InpRSIPeriod = 14; // Period for RSI
input int InpRSIDivergenceLookbackBars = 20; // Max bars between points for RSI
divergence
input double InpRSIPointsDeviation = 3.0; // Min RSI points deviation for
divergence
input double InpMinRiskRewardRatio = 2.0; // Minimum R:R for TP calculation
// --- Specific Strategy Parameters ---
input double InpConsolidationATRMultiplier = 1.5; // ATR multiplier for
consolidation range size
input int InpConsolidationMinBars = 10; // Min bars to check for consolidation
input int InpConsolidationMaxBars = 20; // Max bars to check for consolidation
input double InpBreakPipsBeyondLevel = 5.0; // Min pips for S/R break confirmation
(Break and Retest)
input double InpBodyPenetrationRatio = 0.75; // Min ratio of candle body
penetrating S/R for a break (0.75 = 75%)
input double InpMinImpulsiveMoveATR = 3.0; // Min ATR for strong impulsive move
(Fibonacci)
input int InpImpulsiveMoveMaxBars = 15; // Max bars for impulsive move
(Fibonacci)
input double InpFVGMinATRHeight = 0.5; // Min FVG height in ATR for filtering
input int InpFVGMaxBarsFromOrigin = 10; // Max bars from origin for FVG filter
(closer to origin)
input double InpIFCMinATRBody = 1.5; // Min IFC body size in ATR
input double InpIFCSubsequentMoveATR = 3.0; // Min subsequent move size after IFC
in ATR
input int InpOrderBlockUnmitigatedBodyPenetration = 25; // Max % body
penetration to consider OB unmitigated
input double InpBreakerBlockMinATRRange = 1.0; // Min range for Breaker Block zone
(in ATR)
//+------------------------------------------------------------------+
//| 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";
// Initialize lastTradeDay array to 0 (no trades taken yet)
for (int i = 0; i < 12; i++) { // Corrected size to 12
g_lastTradeDay[i] = 0;
}
// Check if the symbol is XAUUSDm (or allow any symbol if flexibility is
desired)
// Removed strict check for XAUUSDm to allow flexibility.
Print("MySmartMoneyEA Initialized Successfully! Current Symbol: ", Symbol());
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
Print("MySmartMoneyEA Deinitialized. Reason: ", reason);
// Add any cleanup code here, e.g., closing open positions if desired (careful
with this!)
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// Get current tick info for real-time Bid/Ask
if (!SymbolInfoTick(Symbol(), g_tick)) {
Print("Error getting tick data for ", Symbol());
return;
}
// 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;
// --- Daily Reset Logic ---
// This ensures the daily trade limit is reset at the start of a new day
OnStartOfNewDay();
// --- Strategy Evaluation ---
// Iterate through each strategy and check for trade setups
// Only one trade per strategy per day is allowed
// Strategy 1: Liquidity Sweep (Double Top/Bottom Based)
if (InpEnableStrategy1 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[0])) {
CheckStrategy_LiquiditySweep_DoubleTopBottom();
}
// Strategy 2: Power of Three (Bullish)
if (InpEnableStrategy2 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[1])) {
CheckStrategy_PowerOfThree_Bullish();
}
// Strategy 2: Power of Three (Bearish) - Using separate daily flag
if (InpEnableStrategy2 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[2])) {
CheckStrategy_PowerOfThree_Bearish();
}
// Strategy 3: Break and Retest (S/R Flip)
if (InpEnableStrategy3 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[3])) {
CheckStrategy_BreakAndRetest();
}
// Strategy 4: Fibonacci Retracement
if (InpEnableStrategy4 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[4])) {
CheckStrategy_FibonacciRetracement();
}
// Strategy 5: Moving Average (20/50 SMA) Pullback
if (InpEnableStrategy5 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[5])) {
CheckStrategy_MAPullback();
}
// Strategy 6: Trend Line Pullback
if (InpEnableStrategy6 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[6])) {
CheckStrategy_TrendLinePullback();
}
// Strategy 7: Fair Value Gap (FVG) Pullback
if (InpEnableStrategy7 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[7])) {
CheckStrategy_FVGPullback();
}
// Strategy 8: Institutional Funding Candles (IFC) Pullback
if (InpEnableStrategy8 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[8])) {
CheckStrategy_IFCPullback();
}
// Strategy 9: Order Blocks Pullback
if (InpEnableStrategy9 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[9])) {
CheckStrategy_OrderBlockPullback();
}
// Strategy 10: Breaker Block Pullback
if (InpEnableStrategy10 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[10])) {
CheckStrategy_BreakerBlockPullback();
}
// Strategy 11: RSI Divergence with Confluence
if (InpEnableStrategy11 && TimeToDay(TimeCurrent()) !=
TimeToDay(g_lastTradeDay[11])) {
CheckStrategy_RSIDivergence();
}
}
// --- Order Management ---
// Function to send a buy order
bool SendBuyOrder(string symbol, double lotSize, double price, double stopLoss,
double takeProfit, ENUM_TIMEFRAMES timeframe, string comment) {
MqlTradeRequest request;
MqlTradeResult result;
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;
}
// Function to send a sell order
bool SendSellOrder(string symbol, double lotSize, double price, double stopLoss,
double takeProfit, ENUM_TIMEFRAMES timeframe, string comment) {
MqlTradeRequest request;
MqlTradeResult result;
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;
}
// --- Market Data & Indicator Functions ---
// Get candlestick data for a specific symbol and timeframe
// Returns true on success and populates 'rates', false on failure
bool GetCandleData(string symbol, ENUM_TIMEFRAMES timeframe, int shift, MqlRates&
rates) {
MqlRates temp_rates[1];
if (CopyRates(symbol, timeframe, shift, 1, temp_rates) > 0) {
rates = temp_rates[0];
return true;
}
// Print("Failed to get candle data for ", symbol, " ", timeframe, " shift ",
shift); // Often too much logging
ZeroMemory(rates); // Ensure rates is zeroed on failure
return false;
}
// Get ATR value
double GetATR(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift) {
int atr_handle = iATR(symbol, timeframe, period);
if (atr_handle == INVALID_HANDLE) {
Print("Failed to get ATR handle for ", symbol, " ", timeframe, " period ",
period);
return 0.0;
}
double atr_value[];
if (CopyBuffer(atr_handle, 0, shift, 1, atr_value) <= 0) {
// Print("Failed to copy ATR buffer for ", symbol, " ", timeframe, " shift
", shift); // Often too much logging
return 0.0;
}
return atr_value[0];
}
// Get SMA value
double GetSMA(string symbol, ENUM_TIMEFRAMES timeframe, int period, ENUM_MA_METHOD
method, int shift) {
int ma_handle = iMA(symbol, timeframe, period, 0, method, PRICE_CLOSE);
if (ma_handle == INVALID_HANDLE) {
Print("Failed to get MA handle for ", symbol, " ", timeframe, " period ",
period);
return 0.0;
}
double ma_value[];
if (CopyBuffer(ma_handle, 0, shift, 1, ma_value) <= 0) {
// Print("Failed to copy MA buffer for ", symbol, " ", timeframe, " shift
", shift); // Often too much logging
return 0.0;
}
return ma_value[0];
}
// Get ADX value
double GetADX(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift) {
int adx_handle = iADX(symbol, timeframe, period);
if (adx_handle == INVALID_HANDLE) {
Print("Failed to get ADX handle for ", symbol, " ", timeframe, " period ",
period);
return 0.0;
}
double adx_value[];
if (CopyBuffer(adx_handle, MAIN_LINE, shift, 1, adx_value) <= 0) {
// Print("Failed to copy ADX buffer for ", symbol, " ", timeframe, " shift
", shift); // Often too much logging
return 0.0;
}
return adx_value[0];
}
// Get RSI value
double GetRSI(string symbol, ENUM_TIMEFRAMES timeframe, int period,
ENUM_APPLIED_PRICE appliedPrice, int shift) {
int rsi_handle = iRSI(symbol, timeframe, period, appliedPrice);
if (rsi_handle == INVALID_HANDLE) {
Print("Failed to get RSI handle for ", symbol, " ", timeframe, " period ",
period);
return 0.0;
}
double rsi_value[];
if (CopyBuffer(rsi_handle, 0, shift, 1, rsi_value) <= 0) {
// Print("Failed to copy RSI buffer for ", symbol, " ", timeframe, " shift
", shift); // Often too much logging
return 0.0;
}
return rsi_value[0];
}
// --- Geometric & Pattern Detection ---
// Function to identify Swing High (candle has X lower highs on left and X lower
highs on right)
bool IsSwingHigh(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 rates[2 * swingBars + 1];
// CopyRates should use currentBar - swingBars as the start index (oldest bar
in window)
// and copy 2*swingBars + 1 bars (total length of the window).
int copied_count = CopyRates(symbol, timeframe, currentBar - swingBars, 2 *
swingBars + 1, rates);
if (copied_count < (2 * swingBars + 1)) { // Not enough bars copied
return false;
}
double currentHigh = rates[swingBars].high; // The high of the potential swing
point (middle of the array)
for (int i = 0; i < swingBars; i++) {
if (rates[i].high >= currentHigh || rates[swingBars + 1 + i].high >=
currentHigh) {
return false; // Found a higher high on left or right
}
}
return true; // It's a swing high
}
// 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 rates[2 * swingBars + 1];
// CopyRates should use currentBar - swingBars as the start index (oldest bar
in window)
// and copy 2*swingBars + 1 bars (total length of the window).
int copied_count = CopyRates(symbol, timeframe, currentBar - swingBars, 2 *
swingBars + 1, rates);
if (copied_count < (2 * swingBars + 1)) { // Not enough bars copied
return false;
}
double currentLow = rates[swingBars].low; // The low of the potential swing
point (middle of the array)
for (int i = 0; i < swingBars; i++) {
if (rates[i].low <= currentLow || rates[swingBars + 1 + i].low <=
currentLow) {
return false; // Found a lower low on left or right
}
// Check for same low within tiny tolerance (important for flat lows)
if (MathAbs(rates[i].low - currentLow) < _Point * 0.1 ||
MathAbs(rates[swingBars + 1 + i].low - currentLow) < _Point * 0.1) {
// Consider exact same low as not lower, so it's a swing
}
}
return true; // It's a swing low
}
// Function to identify Equal Highs (two swing highs within deviation)
bool IsEqualHighs(string symbol, ENUM_TIMEFRAMES timeframe, int currentBar, int
lookbackBars, double pipsDeviation) {
MqlRates currentCandle;
if (!GetCandleData(symbol, timeframe, currentBar, currentCandle)) return false;
double currentHigh = currentCandle.high;
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;
}
// Function to identify Equal Lows (two swing lows within deviation)
bool IsEqualLows(string symbol, ENUM_TIMEFRAMES timeframe, int currentBar, int
lookbackBars, double pipsDeviation) {
MqlRates currentCandle;
if (!GetCandleData(symbol, timeframe, currentBar, currentCandle)) return false;
double currentLow = currentCandle.low;
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;
}
// Function to determine if a Liquidity Sweep occurred
// Takes potential sweep high/low and returns true if confirmed reversal after
sweep
bool IsLiquiditySweep(string symbol, ENUM_TIMEFRAMES timeframe, double sweepLevel,
int sweepBar, bool isBullishSweep) {
MqlRates sweepCandle;
if (!GetCandleData(symbol, timeframe, sweepBar, sweepCandle)) return false;
// Check if price indeed moved beyond sweepLevel at sweepBar
bool swept = false;
if (isBullishSweep) { // Looking for bearish reversal (bullish sweep)
if (sweepCandle.high > sweepLevel) swept = true;
} else { // Looking for bullish reversal (bearish sweep)
if (sweepCandle.low < sweepLevel) swept = true;
}
if (!swept) return false;
// Check if subsequent candles (within InpLiquiditySweepReversalBars) close
back inside/decisively against sweep direction
MqlRates subsequentCandle;
for (int i = 0; i < InpLiquiditySweepReversalBars; i++) { // Check from current
bar (0)
if (!GetCandleData(symbol, timeframe, i, subsequentCandle)) continue;
if (isBullishSweep) { // Bearish reversal after bullish sweep
if (subsequentCandle.close < sweepLevel) { // Closed below the swept
level
return true;
}
} else { // Bullish reversal after bearish sweep
if (subsequentCandle.close > sweepLevel) { // Closed above the swept
level
return true;
}
}
}
return false;
}
// Function to identify a Fair Value Gap (FVG)
// Returns true if FVG found and populates fvgHigh, fvgLow. Else returns false.
// currentBar: The shift of the latest candle of the 3-candle FVG pattern (C3)
bool FindFVG(string symbol, ENUM_TIMEFRAMES timeframe, int currentBar, bool&
isBullishFVG, double& fvgHigh, double& fvgLow) {
fvgHigh = 0.0; // Initialize output parameters
fvgLow = 0.0;
isBullishFVG = false;
MqlRates c1, c2, c3;
// C1 is the oldest (currentBar + 2), C2 is middle (currentBar + 1), C3 is the
most recent (currentBar)
if (!GetCandleData(symbol, timeframe, currentBar + 2, c1) ||
!GetCandleData(symbol, timeframe, currentBar + 1, c2) ||
!GetCandleData(symbol, timeframe, currentBar, c3)) {
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
}
// Function to check if FVG is unmitigated
// fvgCreationBar is the shift of the third candle (C3 in FindFVG)
bool IsFVGUnmitigated(string symbol, ENUM_TIMEFRAMES timeframe, double fvgHigh,
double fvgLow, int fvgCreationBar) {
MqlRates candle;
for (int i = 0; i < fvgCreationBar; i++) { // Loop from current bar (0) up to
the FVG creation bar (exclusive)
if (!GetCandleData(symbol, timeframe, i, candle)) continue;
// 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;
double bodySize = MathAbs(current.open - current.close);
double totalRange = current.high - current.low;
if (totalRange == 0) return false; // Avoid division by zero
// --- Pin Bar ---
// Bullish Pin Bar: Small body, long lower wick (at least 2x body), closes near
high
if (current.close > current.open && // Bullish candle
(current.open - current.low) > (2 * bodySize) && // Lower wick is long
(current.high - current.close) < (0.5 * bodySize) && // Small upper wick
bodySize < (0.5 * totalRange)) { // Body is small (less than half total
range)
isBullishRejection = true;
return true;
}
// Bearish Pin Bar: Small body, long upper wick (at least 2x body), closes near
low
if (current.close < current.open && // Bearish candle
(current.high - current.open) > (2 * bodySize) && // Upper wick is long
(current.close - current.low) < (0.5 * bodySize) && // Small lower wick
bodySize < (0.5 * totalRange)) { // Body is small (less than half total
range)
isBullishRejection = false;
return true;
}
// --- Engulfing Pattern ---
// Bullish Engulfing: Current bullish body fully engulfs previous bearish body
if (current.close > current.open && prev.close < prev.open &&
current.open < prev.close && current.close > prev.open) {
isBullishRejection = true;
return true;
}
// Bearish Engulfing: Current bearish body fully engulfs previous bullish body
if (current.close < current.open && prev.close > prev.open &&
current.open > prev.close && current.close < prev.open) {
isBullishRejection = false;
return true;
}
return false; // No rejection pattern found
}
// Function to find the "Next Major S/R Level" for TP
double FindNextMajorSR(string symbol, ENUM_TIMEFRAMES timeframe, double
currentPrice, bool isLookingForResistance) {
double targetLevel = 0.0;
MqlRates rates[100]; // Look back 100 bars
int count = CopyRates(symbol, timeframe, 0, 100, rates);
if (count <= 0) return 0.0;
for (int i = 1; i < count; i++) { // Start from 1 as 0 is current bar
if (isLookingForResistance) {
// Find a swing high above currentPrice
if (rates[i].high > currentPrice && IsSwingHigh(symbol, timeframe, i,
InpSwingPointBars)) {
targetLevel = rates[i].high;
break; // Take the first one found (most recent)
}
} else {
// Find a swing low below currentPrice
if (rates[i].low < currentPrice && IsSwingLow(symbol, timeframe, i,
InpSwingPointBars)) {
targetLevel = rates[i].low;
break; // Take the first one found (most recent)
}
}
}
return targetLevel; // This needs to be much more sophisticated in real code
}
// Function for Higher Time Frame (HTF) Alignment
bool CheckHTFAlignment(string symbol, ENUM_TIMEFRAMES mainTimeframe,
ENUM_TIMEFRAMES htfTimeframe, bool isBullishBias) {
// Get HTF MAs
double sma20_htf = GetSMA(symbol, htfTimeframe, InpMovingAveragePeriod,
MODE_SMA, 1); // 1 for previous closed bar
double sma50_htf = GetSMA(symbol, htfTimeframe, InpLongMovingAveragePeriod,
MODE_SMA, 1);
// Check for valid MA values (e.g., not 0.0 from error)
if (sma20_htf == 0.0 || sma50_htf == 0.0) return false;
// Check MA slopes
double sma20_htf_prev = GetSMA(symbol, htfTimeframe, InpMovingAveragePeriod,
MODE_SMA, 2);
double sma50_htf_prev = GetSMA(symbol, htfTimeframe,
InpLongMovingAveragePeriod, MODE_SMA, 2);
if (sma20_htf_prev == 0.0 || sma50_htf_prev == 0.0) return false;
bool maSlopeOk = false;
if (isBullishBias) {
if (sma20_htf > sma20_htf_prev && sma50_htf > sma50_htf_prev) maSlopeOk =
true;
} else {
if (sma20_htf < sma20_htf_prev && sma50_htf < sma50_htf_prev) maSlopeOk =
true;
}
if (!maSlopeOk) return false;
// Check ADX on HTF
double adx_htf = GetADX(symbol, htfTimeframe, InpADXPeriod, 1);
if (adx_htf < InpADXThreshold) return false;
// Check HTF price position relative to HTF MAs
MqlRates htf_candle;
if (!GetCandleData(symbol, htfTimeframe, 1, htf_candle)) return false;
bool maPositionOk = false;
if (isBullishBias) {
if (htf_candle.close > sma20_htf && sma20_htf > sma50_htf) maPositionOk =
true;
} else {
if (htf_candle.close < sma20_htf && sma20_htf < sma50_htf) maPositionOk =
true;
}
if (!maPositionOk) return false;
return true; // HTF alignment confirmed
}
// 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 for minor wick penetration beyond the zone
double upperPenetration = currentCandle.high - zoneHigh;
double lowerPenetration = zoneLow - currentCandle.low;
if (upperPenetration > 0 && upperPenetration <= maxPenetrationATR * atr &&
currentCandle.close < zoneHigh) {
return true; // Wick penetrated up, but closed back below zone high
(rejection from resistance)
}
if (lowerPenetration > 0 && lowerPenetration <= maxPenetrationATR * atr &&
currentCandle.close > zoneLow) {
return true; // Wick penetrated down, but closed back above zone low
(rejection from support)
}
return false; // Not retesting according to rules
}
// Helper function to define consolidation for Power of Three
bool IsConsolidation(string symbol, ENUM_TIMEFRAMES timeframe, int minBars, int
maxBars, double atrMultiplier) {
MqlRates rates[50]; // Increased size for maxBars + margin
int count = CopyRates(symbol, timeframe, 1, maxBars, rates); // Start from 1
for closed bars
if (count < minBars) return false; // Need at least minBars for consolidation
check
if (count == 0) return false; // Ensure there's data before accessing rates[0]
double currentATR = GetATR(symbol, timeframe, InpATRPeriod, 1);
if (currentATR == 0.0) return false;
// 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
// Check if price is within a tight percentage of recent high/low over X bars
double highestHigh = 0;
double lowestLow = 9999999.9; // Initialize with a very high value for XAUUSDm
for (int i = 0; i < count; i++) { // Iterate through copied bars
if (rates[i].high > highestHigh) highestHigh = rates[i].high;
if (rates[i].low < lowestLow) lowestLow = rates[i].low;
}
double range = highestHigh - lowestLow;
if (range > atrMultiplier * currentATR) return false; // Range is too wide for
consolidation
return true; // Likely in consolidation
}
// Function to calculate Risk-Reward Ratio
double CalculateRiskRewardRatio(double entryPrice, double stopLoss, double
takeProfit, bool isBuy) {
if (isBuy) {
double risk = entryPrice - stopLoss;
double reward = takeProfit - entryPrice;
if (risk <= 0) return 0.0; // Risk cannot be zero or negative
return reward / risk;
} else { // Sell
double risk = stopLoss - entryPrice;
double reward = entryPrice - takeProfit;
if (risk <= 0) return 0.0; // Risk cannot be zero or negative
return reward / risk;
}
}
// Function to reset daily trade flags
void OnStartOfNewDay() {
datetime now = TimeCurrent();
datetime today = TimeToDay(now);
for (int i = 0; i < 12; i++) { // Corrected size to 12
if (TimeToDay(g_lastTradeDay[i]) != today) {
g_lastTradeDay[i] = 0; // Reset for new day
}
}
}
// Function to detect an impulsive move
// Returns true if there's a strong directional move over recent bars
bool IsImpulsiveMove(string symbol, ENUM_TIMEFRAMES timeframe, int maxBars, double
minATR, bool& isBullish) {
MqlRates rates[MAX_BARS_FOR_IMPULSIVE_MOVE];
int count = CopyRates(symbol, timeframe, 1, maxBars, rates); // Copy previous
maxBars candles
if (count < maxBars) return false;
if (count == 0) return false; // Ensure there's data
double currentATR = GetATR(symbol, timeframe, InpATRPeriod, 1);
if (currentATR == 0.0) return false;
double firstClose = rates[count - 1].close;
double lastClose = rates[0].close;
double totalMovement = MathAbs(lastClose - firstClose);
if (totalMovement < minATR * currentATR) {
return false; // Not enough movement
}
// Check for mostly unidirectional movement
int bullishCount = 0;
int bearishCount = 0;
for (int i = 0; i < count; i++) {
if (rates[i].close > rates[i].open) bullishCount++;
else if (rates[i].close < rates[i].open) bearishCount++;
}
if (bullishCount > bearishCount * 2) { // Significantly more bullish candles
isBullish = true;
return true;
} else if (bearishCount > bullishCount * 2) { // Significantly more bearish
candles
isBullish = false;
return true;
}
return false; // Not a clear impulsive move
}
// Function to find an Order Block
// Returns true if found and populates obHigh, obLow, isBullishOB. Else returns
false.
bool FindOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int lookbackBars,
bool& isBullishOB, double& obHigh, double& obLow) {
obHigh = 0.0;
obLow = 0.0;
isBullishOB = false;
// The array needs to be large enough to hold 'lookbackBars + 2' bars.
// For safer indexing (i-1, i, i+1), we need at least 3 bars (i=1, i-1=0,
i+1=2)
// The loop should go from i=1 to count-2
// So, CopyRates needs at least 3 bars (or more if lookbackBars is larger)
MqlRates rates[MAX_LOOKBACK_FOR_OB];
int count = CopyRates(symbol, timeframe, 0, lookbackBars + 2, rates);
if (count < 3) return false; // Need at least 3 bars for the pattern (i-1, i,
i+1)
// 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)
// Bullish Order Block: Last down candle before an impulsive move up
// Conditions:
// 1. Current candle is bearish (close < open)
// 2. Next candle is strongly bullish and breaks above 'current' candle's
high
// 3. 'current' candle is part of a recent swing low formation (simplified)
if (current.close < current.open && // Current candle is bearish
next.close > current.high && // Next candle closes above current
high (impulsive move start)
next.open < current.high && // Next candle started below current
high
IsSwingLow(symbol, timeframe, i, InpSwingPointBars)) // 'current' is a
swing low (use 'i' as bar shift)
{
obHigh = current.high;
obLow = current.low;
isBullishOB = true;
// Print("Found potential Bullish OB at bar ", i, ": High=", obHigh, ",
Low=", obLow);
return true;
}
// Bearish Order Block: Last up candle before an impulsive move down
// Conditions:
// 1. Current candle is bullish (close > open)
// 2. Next candle is strongly bearish and breaks below 'current' candle's
low
// 3. 'current' candle is part of a recent swing high formation
(simplified)
if (current.close > current.open && // Current candle is bullish
next.close < current.low && // Next candle closes below current low
(impulsive move start)
next.open > current.low && // Next candle started above current
low
IsSwingHigh(symbol, timeframe, i, InpSwingPointBars)) // 'current' is a
swing high (use 'i' as bar shift)
{
obHigh = current.high;
obLow = current.low;
isBullishOB = false;
// Print("Found potential Bearish OB at bar ", i, ": High=", obHigh, ",
Low=", obLow);
return true;
}
}
return false;
}
// Function to check if an Order Block is unmitigated
// obCreationBar is the shift of the candle that formed the OB
bool IsOrderBlockUnmitigated(string symbol, ENUM_TIMEFRAMES timeframe, double
obHigh, double obLow, int obCreationBar) {
MqlRates candle;
// Iterate from current bar (shift 0) up to the OB creation bar (exclusive)
for (int i = 0; i < obCreationBar; i++) {
if (!GetCandleData(symbol, timeframe, i, candle)) continue;
// 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
}
// Function to find a Breaker Block
// Returns true if found and populates breakerHigh, breakerLow, isBullishBreaker.
Else returns false.
bool FindBreakerBlock(string symbol, ENUM_TIMEFRAMES timeframe, int lookbackBars,
bool& isBullishBreaker, double& breakerHigh, double& breakerLow) {
breakerHigh = 0.0;
breakerLow = 0.0;
isBullishBreaker = false;
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;
// Check if SWL was broken (close below it after its formation)
bool brokenSwl = false;
for (int k = swlBar - 1; k >= 0; k--) { // Check from candle
after SWL towards current (k=0 is current bar)
if (rates[k].close < swlPrice - InpBreakPipsBeyondLevel *
_Point) { // Significant break
brokenSwl = true;
break;
}
}
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;
// Check if SWH was broken (close above it after its formation)
bool brokenSwh = false;
for (int k = swhBar - 1; k >= 0; k--) { // Check from candle
after SWH towards current
if (rates[k].close > swhPrice + InpBreakPipsBeyondLevel *
_Point) { // Significant break
brokenSwh = true;
break;
}
}
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;
}
// Function to get the angle of a trend line (simple approximation)
double GetTrendLineAngle(string symbol, ENUM_TIMEFRAMES timeframe, int
point1_shift, int point2_shift, double point1_price, double point2_price) {
if (point1_shift == point2_shift) return 0.0;
if (point1_price == point2_price) return 0.0; // Flat line
// Calculate time difference in minutes
// double casts below are for clarity, as MQL5 arithmetic typically handles int
to double promotion
double barsDiff = MathAbs((double)point1_shift - (double)point2_shift);
if (barsDiff == 0) return 0.0; // Same bar
double priceDiff = point1_price - point2_price;
// This line might be the source of "MathAtan - undeclared identifier" if
Math.mqh is not found.
// The "some operator expected" might be a cascading error from MathAtan not
being recognized.
double angle = MathAtan(priceDiff / (barsDiff * SymbolInfoDouble(symbol,
SYMBOL_POINT)));
return angle * 180 / M_PI; // Convert to degrees
}
//+------------------------------------------------------------------+
//| Strategy Implementations |
//+------------------------------------------------------------------+
// Strategy 1: Liquidity Sweep (Double Top/Bottom Based)
void CheckStrategy_LiquiditySweep_DoubleTopBottom() {
if (!InpEnableStrategy1 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[0])) return;
int sweepBar = 1; // The latest completed bar
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return; // Current
closed candle
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
// Simplified approach for finding recent swing highs/lows to sweep
// For a more robust implementation, you would need functions like:
// bool FindDoubleTop(string symbol, ENUM_TIMEFRAMES timeframe, int lookback,
double pipsDeviation, double& firstTopPrice, double& secondTopPrice, int&
secondTopBar);
// bool FindDoubleBottom(string symbol, ENUM_TIMEFRAMES timeframe, int
lookback, double pipsDeviation, double& firstBottomPrice, double&
secondBottomPrice, int& secondBottomBar);
double lastSwingHigh = 0.0;
double lastSwingLow = 0.0;
int lastSwingHighBar = -1;
int lastSwingLowBar = -1;
// 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
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[0])) {
g_lastTradeDay[0] = TimeCurrent();
Print("Strategy 1: Bearish Liquidity Sweep Trade taken. SL: ",
DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 1: Bearish Liquidity Sweep setup found, but R:R or TP
invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, false), 2));
}
}
// Check for Bullish Liquidity Sweep (price sweeps below lastSwingLow then
reverses)
else if (lastSwingLowBar != -1 && IsLiquiditySweep(Symbol(), Period(),
lastSwingLow, sweepBar, false)) { // isBullishSweep = false (swept downwards)
// Ensure HTF alignment for bullish move
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 1: Bullish sweep, but HTF not aligned for bullish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = currentCandle.low - InpATRMagnifier * atr;
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true); // Looking for resistance
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[0])) {
g_lastTradeDay[0] = TimeCurrent();
Print("Strategy 1: Bullish Liquidity Sweep Trade taken. SL: ",
DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 1: Bullish Liquidity Sweep setup found, but R:R or TP
invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, true), 2));
}
}
}
// Strategy 2: Power of Three - Bullish
void CheckStrategy_PowerOfThree_Bullish() {
if (!InpEnableStrategy2 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[1])) return;
MqlRates currentCandle, prevCandle, twoPrevCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle) ||
!GetCandleData(Symbol(), Period(), 1, prevCandle) ||
!GetCandleData(Symbol(), Period(), 2, twoPrevCandle)) {
return;
}
// Power of Three (Accumulation, Manipulation, Distribution) - Bullish scenario
// 1. Consolidation (Accumulation) - check previous 10-20 bars
if (!IsConsolidation(Symbol(), Period(), InpConsolidationMinBars,
InpConsolidationMaxBars, InpConsolidationATRMultiplier)) {
// Print("Strategy 2 (Bullish): Not in consolidation.");
return;
}
// 2. Manipulation (False Breakout/Sweep lower)
// The previous candle (prevCandle) should have swept below the consolidation
low.
// The candle before that (twoPrevCandle) should ideally be part of the
consolidation.
// Calculate consolidation range
MqlRates consolidationRates[InpConsolidationMaxBars];
int count = CopyRates(Symbol(), Period(), 2, InpConsolidationMaxBars,
consolidationRates);
if (count < InpConsolidationMinBars) return;
if (count == 0) return; // Ensure consolidationRates has data
double consolidationLow = consolidationRates[0].low; // Initial lowest
for (int i=0; i<count; i++) {
if (consolidationRates[i].low < consolidationLow) consolidationLow =
consolidationRates[i].low;
}
// 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;
}
// 3. Distribution (Impulsive move up, i.e., current candle)
// Current candle should be a strong bullish candle, closing significantly
higher.
if (currentCandle.close > currentCandle.open &&
(currentCandle.close - currentCandle.open) > (0.5 * GetATR(Symbol(),
Period(), InpATRPeriod, 1))) { // Strong bullish candle
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 2 (Bullish): HTF not aligned for bullish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = prevCandle.low - InpATRMagnifier * GetATR(Symbol(),
Period(), InpATRPeriod, 1); // Below manipulation low
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[1])) {
g_lastTradeDay[1] = TimeCurrent();
Print("Strategy 2 (Bullish): Power of Three Trade taken. SL: ",
DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 2 (Bullish): Power of Three setup found, but R:R or
TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, true), 2));
}
}
}
}
// Strategy 2: Power of Three - Bearish
void CheckStrategy_PowerOfThree_Bearish() {
if (!InpEnableStrategy2 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[2])) return;
MqlRates currentCandle, prevCandle, twoPrevCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle) ||
!GetCandleData(Symbol(), Period(), 1, prevCandle) ||
!GetCandleData(Symbol(), Period(), 2, twoPrevCandle)) {
return;
}
// Power of Three (Accumulation, Manipulation, Distribution) - Bearish scenario
// 1. Consolidation (Accumulation) - check previous 10-20 bars
if (!IsConsolidation(Symbol(), Period(), InpConsolidationMinBars,
InpConsolidationMaxBars, InpConsolidationATRMultiplier)) {
// Print("Strategy 2 (Bearish): Not in consolidation.");
return;
}
// 2. Manipulation (False Breakout/Sweep higher)
// The previous candle (prevCandle) should have swept above the consolidation
high.
MqlRates consolidationRates[InpConsolidationMaxBars];
int count = CopyRates(Symbol(), Period(), 2, InpConsolidationMaxBars,
consolidationRates);
if (count < InpConsolidationMinBars) return;
if (count == 0) return; // Ensure consolidationRates has data
double consolidationHigh = consolidationRates[0].high; // Initial highest
for (int i=0; i<count; i++) {
if (consolidationRates[i].high > consolidationHigh) consolidationHigh =
consolidationRates[i].high;
}
// 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;
}
// 3. Distribution (Impulsive move down, i.e., current candle)
// Current candle should be a strong bearish candle, closing significantly
lower.
if (currentCandle.close < currentCandle.open &&
(currentCandle.open - currentCandle.close) > (0.5 * GetATR(Symbol(),
Period(), InpATRPeriod, 1))) { // Strong bearish candle
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 2 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = prevCandle.high + InpATRMagnifier * GetATR(Symbol(),
Period(), InpATRPeriod, 1); // Above manipulation high
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[2])) {
g_lastTradeDay[2] = TimeCurrent();
Print("Strategy 2 (Bearish): Power of Three Trade taken. SL: ",
DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 2 (Bearish): Power of Three setup found, but R:R or
TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, false), 2));
}
}
}
}
// Strategy 3: Break and Retest (S/R Flip)
void CheckStrategy_BreakAndRetest() {
if (!InpEnableStrategy3 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[3])) return;
MqlRates currentCandle, prevCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle) || !
GetCandleData(Symbol(), Period(), 1, prevCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
double srLevel = 0.0;
bool isResistanceBroken = false; // True if resistance was broken (now support)
bool isSupportBroken = false; // True if support was broken (now resistance)
// 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;
}
}
}
}
}
// Bullish Re-test (Resistance becomes Support)
if (isResistanceBroken) {
bool isRejection;
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) && isRejection) {
// Bullish rejection at the level
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 3 (Bullish): HTF not aligned for bullish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = srLevel - InpATRMagnifier * atr; // Below the flipped
support
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[3])) {
g_lastTradeDay[3] = TimeCurrent();
Print("Strategy 3 (Bullish): Break and Retest (R -> S) Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 3 (Bullish): Break and Retest (R -> S) setup found,
but R:R or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true), 2));
}
}
}
// Bearish Re-test (Support becomes Resistance)
else if (isSupportBroken) {
bool isRejection;
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) && !isRejection)
{ // Bearish rejection at the level
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 3 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = srLevel + InpATRMagnifier * atr; // Above the flipped
resistance
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[3])) {
g_lastTradeDay[3] = TimeCurrent();
Print("Strategy 3 (Bearish): Break and Retest (S -> R) Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 3 (Bearish): Break and Retest (S -> R) setup found,
but R:R or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false), 2));
}
}
}
}
// Strategy 4: Fibonacci Retracement
void CheckStrategy_FibonacciRetracement() {
if (!InpEnableStrategy4 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[4])) return;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
// 1. Identify a recent impulsive move
bool isBullishImpulse = false;
bool impulsiveMoveFound = IsImpulsiveMove(Symbol(), Period(),
InpImpulsiveMoveMaxBars, InpMinImpulsiveMoveATR, isBullishImpulse);
if (!impulsiveMoveFound) {
// Print("Strategy 4: No impulsive move found.");
return;
}
double swingHigh = 0.0;
double swingLow = 0.0;
// 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)
MqlRates impulseRates[InpImpulsiveMoveMaxBars + 1];
int count = CopyRates(Symbol(), Period(), 0, InpImpulsiveMoveMaxBars + 1,
impulseRates);
if (count <= 0) return;
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;
}
}
if (swingHigh == 0.0 || swingLow == 0.0 || swingHigh == swingLow) return;
// Define Fibonacci levels (e.g., 0.5, 0.618, 0.786)
double fib50 = 0.0;
double fib618 = 0.0;
double fib786 = 0.0;
if (isBullishImpulse) { // From Low to High
fib50 = swingLow + (swingHigh - swingLow) * 0.50;
fib618 = swingLow + (swingHigh - swingLow) * 0.618;
fib786 = swingLow + (swingHigh - swingLow) * 0.786;
} else { // From High to Low
fib50 = swingHigh - (swingHigh - swingLow) * 0.50;
fib618 = swingHigh - (swingHigh - swingLow) * 0.618;
fib786 = swingHigh - (swingHigh - swingLow) * 0.786;
}
// Check for pullback to Fibonacci levels AND a rejection candle
bool isRejection;
if (isBullishImpulse) { // Looking for bullish entry on pullback (price drops
to fib level)
if (currentCandle.low <= fib618 && currentCandle.close > fib50 && // Price
retested between 50-61.8% or deeper
IsRejectionCandle(Symbol(), Period(), 0, isRejection) && isRejection) {
// Bullish rejection
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 4 (Bullish): HTF not aligned for bullish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = swingLow - InpATRMagnifier * atr; // Below the swing
low of the impulse
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true); // Target next resistance
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[4])) {
g_lastTradeDay[4] = TimeCurrent();
Print("Strategy 4 (Bullish): Fibonacci Retracement Trade taken.
SL: ", DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit,
_Digits));
}
} else {
Print("Strategy 4 (Bullish): Fibonacci Retracement setup found, but
R:R or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true), 2));
}
}
} else { // Bearish impulse, looking for bearish entry on pullback (price rises
to fib level)
if (currentCandle.high >= fib618 && currentCandle.close < fib50 && // Price
retested between 50-61.8% or deeper
IsRejectionCandle(Symbol(), Period(), 0, isRejection) && !isRejection)
{ // Bearish rejection
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 4 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = swingHigh + InpATRMagnifier * atr; // Above the swing
high of the impulse
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false); // Target next support
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice, stopLoss,
takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[4])) {
g_lastTradeDay[4] = TimeCurrent();
Print("Strategy 4 (Bearish): Fibonacci Retracement Trade taken.
SL: ", DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit,
_Digits));
}
} else {
Print("Strategy 4 (Bearish): Fibonacci Retracement setup found, but
R:R or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false), 2));
}
}
}
}
// Strategy 5: Moving Average (20/50 SMA) Pullback
void CheckStrategy_MAPullback() {
if (!InpEnableStrategy5 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[5])) return;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
double sma20 = GetSMA(Symbol(), Period(), InpMovingAveragePeriod, MODE_SMA, 1);
// Previous closed bar
double sma50 = GetSMA(Symbol(), Period(), InpLongMovingAveragePeriod, MODE_SMA,
1);
double sma20_prev = GetSMA(Symbol(), Period(), InpMovingAveragePeriod,
MODE_SMA, 2);
double sma50_prev = GetSMA(Symbol(), Period(), InpLongMovingAveragePeriod,
MODE_SMA, 2);
if (sma20 == 0.0 || sma50 == 0.0 || sma20_prev == 0.0 || sma50_prev == 0.0)
return; // Ensure valid MA data
// 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;
}
double entryPrice = currentCandle.close;
double stopLoss = MathMin(currentCandle.low, sma50) -
InpATRMagnifier * atr; // Below 50 SMA or rejection low
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[5])) {
g_lastTradeDay[5] = TimeCurrent();
Print("Strategy 5 (Bullish): MA Pullback Trade taken. SL:
", DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit,
_Digits));
}
} else {
Print("Strategy 5 (Bullish): MA Pullback setup found, but R:R
or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true), 2));
}
}
}
}
// Bearish scenario: Price below MAs, MAs pointing down, pullback to 20/50 SMA,
bearish rejection
else if (sma20 < sma50 && sma20 < sma20_prev && sma50 < sma50_prev) { //
Downtrend confirmed by MA crossover and slope
// Price pulled back to 20 SMA
if (currentCandle.high >= sma20 - _Point * 5 && currentCandle.close < sma20
+ _Point * 5) { // Price touches/closes near 20 SMA
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 5 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = MathMax(currentCandle.high, sma50) +
InpATRMagnifier * atr; // Above 50 SMA or rejection high
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[5])) {
g_lastTradeDay[5] = TimeCurrent();
Print("Strategy 5 (Bearish): MA Pullback Trade taken. SL:
", DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit,
_Digits));
}
} else {
Print("Strategy 5 (Bearish): MA Pullback setup found, but R:R
or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false), 2));
}
}
}
}
}
// Strategy 6: Trend Line Pullback
void CheckStrategy_TrendLinePullback() {
if (!InpEnableStrategy6 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[6])) return;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
double trendLinePrice = 0.0;
bool isBullishTrendLine = false; // True for uptrend, false for downtrend
// Simplified Trend Line Detection: Find two recent swing highs/lows
// Bullish Trend Line (connecting two swing lows)
int firstLowBar = -1, secondLowBar = -1;
double firstLowPrice = 0.0, secondLowPrice = 0.0;
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;
}
}
}
}
}
// Bearish Trend Line (connecting two swing highs)
if (!isBullishTrendLine) { // Only check for bearish if no bullish found
int firstHighBar = -1, secondHighBar = -1;
double firstHighPrice = 0.0, secondHighPrice = 0.0;
for (int i = InpSwingPointBars + 1; i < 100; i++) {
if (IsSwingHigh(Symbol(), Period(), i, InpSwingPointBars)) {
MqlRates tempRates;
if (GetCandleData(Symbol(), Period(), i, tempRates)) {
if (firstHighBar == -1) {
firstHighBar = i;
firstHighPrice = tempRates.high;
} else {
secondHighBar = i;
secondHighPrice = tempRates.high;
// Ensure it's a downward sloping line (newer high is lower
than older high)
if (secondHighPrice < firstHighPrice) {
isBullishTrendLine = false; // Explicitly set false for
bearish
trendLinePrice = secondHighBar + ((firstHighPrice -
secondHighPrice) / (double)(firstHighBar - secondHighBar)) * (0.0 - secondHighBar);
// Corrected calculation
break;
} else {
firstHighBar = secondHighBar;
firstHighPrice = secondHighPrice;
secondHighBar = -1; secondHighPrice = 0.0;
}
}
}
}
}
}
if (trendLinePrice == 0.0) return; // No valid trend line found
// Check for pullback and rejection
bool isRejection;
if (isBullishTrendLine) { // Bullish Trend Line (support)
// Price should be near or touching the trend line from above, and form
bullish rejection
if (currentCandle.low <= trendLinePrice + InpATRMagnifier * atr &&
currentCandle.close > trendLinePrice - InpATRMagnifier * atr) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) &&
isRejection) { // Bullish rejection
// Check HTF alignment for bullish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, true)) {
// Print("Strategy 6 (Bullish): HTF not aligned for bullish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = currentCandle.low - InpATRMagnifier * atr;
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[6])) {
g_lastTradeDay[6] = TimeCurrent();
Print("Strategy 6 (Bullish): Trend Line Pullback Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 6 (Bullish): Trend Line Pullback setup found,
but R:R or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true), 2));
}
}
}
} else { // Bearish Trend Line (resistance)
// Price should be near or touching the trend line from below, and form
bearish rejection
if (currentCandle.high >= trendLinePrice - InpATRMagnifier * atr &&
currentCandle.close < trendLinePrice + InpATRMagnifier * atr) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) && !
isRejection) { // Bearish rejection
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 6 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = currentCandle.high + InpATRMagnifier * atr;
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[6])) {
g_lastTradeDay[6] = TimeCurrent();
Print("Strategy 6 (Bearish): Trend Line Pullback Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 6 (Bearish): Trend Line Pullback setup found,
but R:R or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false), 2));
}
}
}
}
}
// Strategy 7: Fair Value Gap (FVG) Pullback
void CheckStrategy_FVGPullback() {
if (!InpEnableStrategy7 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[7])) return;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
double fvgHigh = 0.0;
double fvgLow = 0.0;
bool isBullishFVG = false;
int fvgOriginBar = -1; // Bar where the FVG was created
// Find the most recent unmitigated FVG within lookback bars
for (int i = 0; i < InpFVGMaxBarsFromOrigin; i++) { // Check recent bars for
FVG formation (i is shift for C3)
if (FindFVG(Symbol(), Period(), i, isBullishFVG, fvgHigh, fvgLow)) {
// Filter by FVG height (must be significant enough)
if (MathAbs(fvgHigh - fvgLow) < InpFVGMinATRHeight * atr) continue;
if (IsFVGUnmitigated(Symbol(), Period(), fvgHigh, fvgLow, i)) {
fvgOriginBar = i;
break; // Take the most recent unmitigated FVG
}
}
}
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;
}
double entryPrice = currentCandle.close;
double stopLoss = fvgLow - InpATRMagnifier * atr; // Below FVG low
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[7])) {
g_lastTradeDay[7] = TimeCurrent();
Print("Strategy 7 (Bullish): FVG Pullback Trade taken. SL:
", DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit,
_Digits));
}
} else {
Print("Strategy 7 (Bullish): FVG Pullback setup found, but R:R
or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true), 2));
}
}
}
} else { // Bearish FVG (price expected to retest FVG High and fall)
if (IsPriceRetestingZone(currentCandle.close, fvgHigh, fvgLow,
InpATRMagnifier)) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) && !
isRejection) { // Bearish rejection at FVG
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 7 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = fvgHigh + InpATRMagnifier * atr; // Above FVG
high
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[7])) {
g_lastTradeDay[7] = TimeCurrent();
Print("Strategy 7 (Bearish): FVG Pullback Trade taken. SL:
", DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit,
_Digits));
}
} else {
Print("Strategy 7 (Bearish): FVG Pullback setup found, but R:R
or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false), 2));
}
}
}
}
}
// Strategy 8: Institutional Funding Candles (IFC) Pullback
void CheckStrategy_IFCPullback() {
if (!InpEnableStrategy8 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[8])) return;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
double ifcOpen = 0.0, ifcClose = 0.0, ifcHigh = 0.0, ifcLow = 0.0;
bool isBullishIFC = false;
int ifcBar = -1; // Shift of the IFC candle
for (int i = 1; i <= InpImpulsiveMoveMaxBars; i++) { // Look for an IFC within
max bars (i is shift of potential IFC)
MqlRates candle;
if (!GetCandleData(Symbol(), Period(), i, candle)) continue;
double bodySize = MathAbs(candle.open - candle.close);
double range = candle.high - candle.low;
if (range == 0) continue; // Avoid division by zero
// 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;
if (candle.close > candle.open) { // Bullish IFC
// Check if subsequent candle moved significantly away
if (subsequentCandle.close > candle.high && (subsequentCandle.close
- candle.close) > InpIFCSubsequentMoveATR * atr) {
ifcOpen = candle.open;
ifcClose = candle.close;
ifcHigh = candle.high;
ifcLow = candle.low;
isBullishIFC = true;
ifcBar = i;
break;
}
} else { // Bearish IFC
// Check if subsequent candle moved significantly away
if (subsequentCandle.close < candle.low && (candle.close -
subsequentCandle.close) > InpIFCSubsequentMoveATR * atr) {
ifcOpen = candle.open;
ifcClose = candle.close;
ifcHigh = candle.high;
ifcLow = candle.low;
isBullishIFC = false;
ifcBar = i;
break;
}
}
}
}
if (ifcBar == -1) return; // No IFC found
// Now check for pullback to the IFC's range (e.g., 50% level or open/close)
// The entry zone for IFC is usually the open of the IFC candle.
double ifcEntryZoneHigh = isBullishIFC ? ifcClose : ifcOpen;
double ifcEntryZoneLow = isBullishIFC ? ifcOpen : ifcClose;
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;
}
double entryPrice = currentCandle.close;
double stopLoss = ifcLow - InpATRMagnifier * atr;
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[8])) {
g_lastTradeDay[8] = TimeCurrent();
Print("Strategy 8 (Bullish): IFC Pullback Trade taken. SL:
", DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit,
_Digits));
}
} else {
Print("Strategy 8 (Bullish): IFC Pullback setup found, but R:R
or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true), 2));
}
}
}
} else { // Bearish IFC pullback
if (IsPriceRetestingZone(currentCandle.close, ifcEntryZoneHigh,
ifcEntryZoneLow, InpATRMagnifier)) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) && !
isRejection) { // Bearish rejection
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 8 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = ifcHigh + InpATRMagnifier * atr;
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[8])) {
g_lastTradeDay[8] = TimeCurrent();
Print("Strategy 8 (Bearish): IFC Pullback Trade taken. SL:
", DoubleToString(stopLoss, _Digits), ", TP: ", DoubleToString(takeProfit,
_Digits));
}
} else {
Print("Strategy 8 (Bearish): IFC Pullback setup found, but R:R
or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false), 2));
}
}
}
}
}
// Strategy 9: Order Blocks Pullback
void CheckStrategy_OrderBlockPullback() {
if (!InpEnableStrategy9 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[9])) return;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
double obHigh = 0.0;
double obLow = 0.0;
bool isBullishOB = false;
int obCreationBar = -1;
// 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;
}
double entryPrice = currentCandle.close;
double stopLoss = obLow - InpATRMagnifier * atr; // Below OB low
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[9])) {
g_lastTradeDay[9] = TimeCurrent();
Print("Strategy 9 (Bullish): Order Block Pullback Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 9 (Bullish): Order Block Pullback setup found,
but R:R or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true), 2));
}
}
}
} else { // Bearish OB (price expected to retest OB and fall down)
if (IsPriceRetestingZone(currentCandle.close, obHigh, obLow,
InpATRMagnifier)) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) && !
isRejection) { // Bearish rejection
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 9 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = obHigh + InpATRMagnifier * atr; // Above OB high
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[9])) {
g_lastTradeDay[9] = TimeCurrent();
Print("Strategy 9 (Bearish): Order Block Pullback Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 9 (Bearish): Order Block Pullback setup found,
but R:R or TP invalid. R:R = ", DoubleToString(CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false), 2));
}
}
}
}
}
// Strategy 10: Breaker Block Pullback
void CheckStrategy_BreakerBlockPullback() {
if (!InpEnableStrategy10 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[10])) return;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
double breakerHigh = 0.0;
double breakerLow = 0.0;
bool isBullishBreaker = false;
int breakerOriginBar = -1;
// 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;
}
double entryPrice = currentCandle.close;
double stopLoss = breakerLow - InpATRMagnifier * atr; // Below
Breaker low
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
true);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, true) >= InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[10])) {
g_lastTradeDay[10] = TimeCurrent();
Print("Strategy 10 (Bullish): Breaker Block Pullback Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 10 (Bullish): Breaker Block Pullback setup
found, but R:R or TP invalid. R:R = ",
DoubleToString(CalculateRiskRewardRatio(entryPrice, stopLoss, takeProfit, true),
2));
}
}
}
} else { // Bearish Breaker (price expected to retest and fall down)
if (IsPriceRetestingZone(currentCandle.close, breakerHigh, breakerLow,
InpATRMagnifier)) {
if (IsRejectionCandle(Symbol(), Period(), 0, isRejection) && !
isRejection) { // Bearish rejection
// Check HTF alignment for bearish bias
if (!CheckHTFAlignment(Symbol(), Period(), InpHTFPeriod, false)) {
// Print("Strategy 10 (Bearish): HTF not aligned for bearish
bias.");
return;
}
double entryPrice = currentCandle.close;
double stopLoss = breakerHigh + InpATRMagnifier * atr; // Above
Breaker high
double takeProfit = FindNextMajorSR(Symbol(), Period(), entryPrice,
false);
if (takeProfit != 0.0 && CalculateRiskRewardRatio(entryPrice,
stopLoss, takeProfit, false) >= InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice, stopLoss,
takeProfit, Period(), g_strategyNames[10])) {
g_lastTradeDay[10] = TimeCurrent();
Print("Strategy 10 (Bearish): Breaker Block Pullback Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 10 (Bearish): Breaker Block Pullback setup
found, but R:R or TP invalid. R:R = ",
DoubleToString(CalculateRiskRewardRatio(entryPrice, stopLoss, takeProfit, false),
2));
}
}
}
}
}
// Strategy 11: RSI Divergence with Confluence
void CheckStrategy_RSIDivergence() {
if (!InpEnableStrategy11 || TimeToDay(TimeCurrent()) ==
TimeToDay(g_lastTradeDay[11])) return;
MqlRates currentCandle;
if (!GetCandleData(Symbol(), Period(), 0, currentCandle)) return;
double atr = GetATR(Symbol(), Period(), InpATRPeriod, 1);
if (atr == 0.0) return;
double rsiCurrent = GetRSI(Symbol(), Period(), InpRSIPeriod, PRICE_CLOSE, 0);
if (rsiCurrent == 0.0) return;
// Bearish Divergence (Price Higher High, RSI Lower High)
int currentHighBar = iHighest(Symbol(), Period(), MODE_HIGH,
InpRSIDivergenceLookbackBars, 0); // Recent highest high
double currentHighPrice = iHigh(Symbol(), Period(), currentHighBar);
int prevHighBar = -1;
double prevHighPrice = 0.0;
double prevRSI = 0.0;
// 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;
}
double entryPrice = currentCandle.close;
double stopLoss = currentCandle.high + InpATRMagnifier *
atr;
double takeProfit = FindNextMajorSR(Symbol(), Period(),
entryPrice, false);
if (takeProfit != 0.0 &&
CalculateRiskRewardRatio(entryPrice, stopLoss, takeProfit, false) >=
InpMinRiskRewardRatio) {
if (SendSellOrder(Symbol(), InpLotSize, entryPrice,
stopLoss, takeProfit, Period(), g_strategyNames[11])) {
g_lastTradeDay[11] = TimeCurrent();
Print("Strategy 11 (Bearish): RSI Divergence Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 11 (Bearish): RSI Divergence setup
found, but R:R or TP invalid. R:R = ",
DoubleToString(CalculateRiskRewardRatio(entryPrice, stopLoss, takeProfit, false),
2));
}
}
return; // Only look for one divergence
}
}
}
}
// Bullish Divergence (Price Lower Low, RSI Higher Low)
int currentLowBar = iLowest(Symbol(), Period(), MODE_LOW,
InpRSIDivergenceLookbackBars, 0); // Recent lowest low
double currentLowPrice = iLow(Symbol(), Period(), currentLowBar);
int prevLowBar_bullish = -1; // Declared for this specific block
double prevLowPrice_bullish = 0.0; // Declared for this specific block
double prevRSI_bullish = 0.0; // Declared for this specific block
// 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;
}
double entryPrice = currentCandle.close;
double stopLoss = currentCandle.low - InpATRMagnifier *
atr;
double takeProfit = FindNextMajorSR(Symbol(), Period(),
entryPrice, true);
if (takeProfit != 0.0 &&
CalculateRiskRewardRatio(entryPrice, stopLoss, takeProfit, true) >=
InpMinRiskRewardRatio) {
if (SendBuyOrder(Symbol(), InpLotSize, entryPrice,
stopLoss, takeProfit, Period(), g_strategyNames[11])) {
g_lastTradeDay[11] = TimeCurrent();
Print("Strategy 11 (Bullish): RSI Divergence Trade
taken. SL: ", DoubleToString(stopLoss, _Digits), ", TP: ",
DoubleToString(takeProfit, _Digits));
}
} else {
Print("Strategy 11 (Bullish): RSI Divergence setup
found, but R:R or TP invalid. R:R = ",
DoubleToString(CalculateRiskRewardRatio(entryPrice, stopLoss, takeProfit, true),
2));
}
}
return; // Only look for one divergence
}
}
}
}
}