diff --git a/README.md b/README.md index deaf705..51f1ff4 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ - [6. RSI Reversal Asian EURUSD](#6-rsi-reversal-asian-eurusd) - [7. EMA Crossover BTC](#7-ema-crossover-btc) - [8. Smart RSI BTC](#8-smart-rsi-btc) + - [9. RSI MidPoint Hijack](#9-rsi-midpoint-hijack) - [Technical Details](#technical-details) - [Requirements](#requirements) - [Installation](#installation) @@ -573,6 +574,71 @@ A strategy that implements a smart RSI-based trading system specifically optimiz **Balance Sheet:** ![Smart RSI BTC Balance Sheet](SmartRSIBTC/test-balance.jpg) +### 9. RSI MidPoint Hijack + +A strategy that combines RSI analysis with midpoint-based entry and exit signals for enhanced trading performance. + +**Key Features:** +- RSI-based trend analysis +- Midpoint-based entry and exit signals +- Dynamic position sizing +- Advanced risk management + +**Strategy Settings:** +- Symbol: XAUUSD +- Period: H1 +- RSI Period: 14 +- RSI Overbought: 70 +- RSI Oversold: 30 +- Base Lot Size: 0.01 +- Max Spread: 50 +- Max Risk Percent: 2% +- Max Drawdown Percent: 10% +- Max Consecutive Losses: 3 +- Max Lot Size: 0.1 + +**Performance Metrics:** +| Metric | Value | +|--------|-------| +| Total Net Profit | $1,344.74 | +| Gross Profit | $4,410.51 | +| Gross Loss | -$3,065.77 | +| Profit Factor | 1.44 | +| Recovery Factor | 4.91 | +| Expected Payoff | $4.87 | +| Sharpe Ratio | 1.69 | +| AHPR | 1.0033 (0.33%) | +| GHPR | 1.0031 (0.31%) | + +**Trade Statistics:** +| Statistic | Value | +|-----------|-------| +| Total Trades | 276 | +| Total Deals | 552 | +| Profit Trades | 81 (29.35%) | +| Loss Trades | 195 (70.65%) | +| Short Trades Won | 26.28% | +| Long Trades Won | 32.37% | +| Largest Profit Trade | $144.46 | +| Largest Loss Trade | -$46.66 | +| Average Profit Trade | $54.45 | +| Average Loss Trade | -$15.72 | +| Max Consecutive Wins | 3 ($179.71) | +| Max Consecutive Losses | 15 (-$159.89) | + +**Drawdown Analysis:** +| Metric | Value | +|--------|-------| +| Balance Drawdown Absolute | $18.62 | +| Equity Drawdown Absolute | $19.45 | +| Balance Drawdown Maximal | $231.79 (10.21%) | +| Equity Drawdown Maximal | $273.75 (11.93%) | +| Balance Drawdown Relative | 13.47% ($210.14) | +| Equity Drawdown Relative | 15.85% ($250.78) | + +**Balance Sheet:** +![RSI MidPoint Hijack Balance Sheet](RSIMidPointHijack/test-balance.jpg) + ## Technical Details Each EA is implemented in MQL5 and includes: diff --git a/RSIMidPointHijack/main.mq5 b/RSIMidPointHijack/main.mq5 new file mode 100644 index 0000000..296d752 --- /dev/null +++ b/RSIMidPointHijack/main.mq5 @@ -0,0 +1,586 @@ +//+------------------------------------------------------------------+ +//| RSIFollowReverseEMACrossOver.mq5 | +//| Copyright 2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2024, MetaQuotes Ltd." +#property link "https://www.mql5.com" +#property version "1.00" + +#include +#include + +// Input Parameters +input group "General Settings" +input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Trading Timeframe +input double InpLotSize = 0.01; // Lot Size +input int InpMagicNumberRSIFollow = 1001; // Magic Number RSI Follow +input int InpMagicNumberRSIReverse = 1002;// Magic Number RSI Reverse +input int InpMagicNumberEMACross = 1003; // Magic Number EMA Cross + +input group "Strategy Switches" +input bool InpEnableRSIFollow = false; // Enable RSI Follow Strategy +input bool InpEnableRSIReverse = true; // Enable RSI Reverse Strategy +input bool InpEnableEMACross = false; // Enable EMA Cross Strategy +input bool InpEnableStrategyLock = false; // Enable Strategy Lock +input double InpLockProfitThreshold = 0.0; // Lock Profit Threshold (pips) +input bool InpCloseOppositeTrades = false; // Close Opposite Trades When Profiting + +input group "RSI Follow Strategy" +input int InpRSIPeriod = 14; // RSI Period +input int InpRSIOverbought = 70; // RSI Overbought Level +input int InpRSIOversold = 30; // RSI Oversold Level +input int InpRSIExitLevel = 50; // RSI Exit Level +input int InpRSIFollowStartHour = 0; // RSI Follow Start Hour (0-23) +input int InpRSIFollowEndHour = 23; // RSI Follow End Hour (0-23) +input bool InpRSIFollowCloseOutsideHours = true; // Close trades outside trading hours + +input group "RSI Reverse Strategy" +input int InpRSIReversePeriod = 59; // RSI Period +input int InpRSIReverseOverbought = 51; // RSI Overbought Level +input int InpRSIReverseOversold = 49; // RSI Oversold Level +input int InpRSIReverseCrossLevel = 53; // RSI Cross Level +input int InpRSIReverseExitLevel = 48; // RSI Exit Level +input int InpRSIReverseStartHour = 7; // RSI Reverse Start Hour (0-23) +input int InpRSIReverseEndHour = 13; // RSI Reverse End Hour (0-23) +input bool InpRSIReverseCloseOutsideHours = false; // Close trades outside trading hours +input int InpRSIReverseCooldownBars = 15; // RSI Reverse Cooldown (bars) +input bool InpRSIReverseCooldownOnLoss = true; // Apply cooldown only on loss + +input group "EMA Cross Strategy" +input int InpEMAPeriod = 20; // EMA Period +input int InpEMACrossStartHour = 0; // EMA Cross Start Hour (0-23) +input int InpEMACrossEndHour = 23; // EMA Cross End Hour (0-23) +input bool InpEMACrossCloseOutsideHours = true; // Close trades outside trading hours +input bool InpUseEMADistanceEntry = false; // Use EMA Distance Entry +input double InpEMADistancePips = 10.0; // EMA Distance Threshold (pips) +input int InpEMADistancePeriod = 3; // EMA Distance Period (bars) + +// Global Variables +int rsiHandle; +int rsiReverseHandle; +int emaHandle; +bool rsiOverbought = false; +bool rsiOversold = false; +bool rsiReverseOverbought = false; +bool rsiReverseOversold = false; +CTrade trade; +CPositionInfo positionInfo; +bool emaCrossBuySignal = false; +bool emaCrossSellSignal = false; +int emaCrossSignalBar = 0; +datetime lastBarTime = 0; +datetime rsiReverseLastCloseTime = 0; +bool rsiReverseInCooldown = false; + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + // Initialize indicators + rsiHandle = iRSI(_Symbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE); + rsiReverseHandle = iRSI(_Symbol, InpTimeframe, InpRSIReversePeriod, PRICE_CLOSE); + emaHandle = iMA(_Symbol, InpTimeframe, InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE); + + if(rsiHandle == INVALID_HANDLE || rsiReverseHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE) + { + Print("Error creating indicators"); + return INIT_FAILED; + } + + // Initialize trade settings + trade.SetExpertMagicNumber(InpMagicNumberRSIFollow); + trade.SetMarginMode(); + trade.SetTypeFillingBySymbol(_Symbol); + trade.SetDeviationInPoints(10); + + // Initialize last bar time + datetime time[]; + if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0) + { + lastBarTime = time[0]; + } + + return(INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +//| Check if new bar has formed | +//+------------------------------------------------------------------+ +bool IsNewBar() +{ + datetime time[]; + if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0) + { + if(time[0] != lastBarTime) + { + lastBarTime = time[0]; + return true; + } + } + return false; +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + // Release indicator handles + IndicatorRelease(rsiHandle); + IndicatorRelease(rsiReverseHandle); + IndicatorRelease(emaHandle); +} + +//+------------------------------------------------------------------+ +//| Check if current time is within trading hours | +//+------------------------------------------------------------------+ +bool IsWithinTradingHours(int startHour, int endHour) +{ + MqlDateTime currentTime; + TimeToStruct(TimeCurrent(), currentTime); + + if(startHour <= endHour) + { + return (currentTime.hour >= startHour && currentTime.hour < endHour); + } + else + { + return (currentTime.hour >= startHour || currentTime.hour < endHour); + } +} + +//+------------------------------------------------------------------+ +//| Check if position exists for given magic number | +//+------------------------------------------------------------------+ +bool HasPosition(int magic) +{ + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + if(positionInfo.SelectByIndex(i)) + { + if(positionInfo.Magic() == magic) + return true; + } + } + return false; +} + +//+------------------------------------------------------------------+ +//| Check if any strategy has profitable position | +//+------------------------------------------------------------------+ +bool HasProfitablePosition(int excludeMagic) +{ + bool hasProfitable = false; + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + if(positionInfo.SelectByIndex(i)) + { + if(positionInfo.Magic() != excludeMagic) + { + double profit = positionInfo.Profit(); + if(profit > InpLockProfitThreshold * _Point) + { + hasProfitable = true; + // If enabled, close opposite trades + if(InpCloseOppositeTrades) + { + // Check if this is an opposite trade to the excluded magic number + if((excludeMagic == InpMagicNumberRSIFollow && positionInfo.Magic() == InpMagicNumberRSIReverse) || + (excludeMagic == InpMagicNumberRSIReverse && positionInfo.Magic() == InpMagicNumberRSIFollow) || + (excludeMagic == InpMagicNumberEMACross && (positionInfo.Magic() == InpMagicNumberRSIReverse || positionInfo.Magic() == InpMagicNumberRSIFollow)) || + ((excludeMagic == InpMagicNumberRSIFollow || excludeMagic == InpMagicNumberRSIReverse) && positionInfo.Magic() == InpMagicNumberEMACross)) + { + ClosePosition(positionInfo.Magic()); + } + } + } + } + } + } + return hasProfitable; +} + +//+------------------------------------------------------------------+ +//| Check for RSI Follow Strategy signals | +//+------------------------------------------------------------------+ +void CheckRSIFollowStrategy() +{ + // Check if within trading hours + if(!IsWithinTradingHours(InpRSIFollowStartHour, InpRSIFollowEndHour)) + { + if(InpRSIFollowCloseOutsideHours) + { + if(HasPosition(InpMagicNumberRSIFollow)) + { + ClosePosition(InpMagicNumberRSIFollow); + } + } + return; + } + + // Check strategy lock + if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberRSIFollow)) + return; + + double rsi[]; + ArraySetAsSeries(rsi, true); + CopyBuffer(rsiHandle, 0, 0, 3, rsi); + + if(ArraySize(rsi) < 3) return; + + // Check for overbought condition + if(rsi[1] > InpRSIOverbought) + rsiOverbought = true; + else if(rsi[1] < InpRSIOversold) + rsiOversold = true; + + // Check for entry signals + if(rsiOverbought && rsi[1] < rsi[0] && rsi[1] < InpRSIExitLevel) + { + // Sell signal + if(!HasPosition(InpMagicNumberRSIFollow)) + { + trade.SetExpertMagicNumber(InpMagicNumberRSIFollow); + trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "RSI Follow"); + } + rsiOverbought = false; + } + else if(rsiOversold && rsi[1] > rsi[0] && rsi[1] > InpRSIExitLevel) + { + // Buy signal + if(!HasPosition(InpMagicNumberRSIFollow)) + { + trade.SetExpertMagicNumber(InpMagicNumberRSIFollow); + trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "RSI Follow"); + } + rsiOversold = false; + } +} + +//+------------------------------------------------------------------+ +//| Check if RSI Reverse is in cooldown | +//+------------------------------------------------------------------+ +bool IsRSIReverseInCooldown() +{ + if(InpRSIReverseCooldownBars <= 0) + return false; + + if(!rsiReverseInCooldown) + return false; + + datetime time[]; + if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0) + { + datetime currentBarTime = time[0]; + datetime cooldownEndTime = rsiReverseLastCloseTime + InpRSIReverseCooldownBars * PeriodSeconds(InpTimeframe); + + if(currentBarTime >= cooldownEndTime) + { + rsiReverseInCooldown = false; + return false; + } + } + + return true; +} + +//+------------------------------------------------------------------+ +//| Check for RSI Reverse Strategy signals | +//+------------------------------------------------------------------+ +void CheckRSIReverseStrategy() +{ + // Check if within trading hours + if(!IsWithinTradingHours(InpRSIReverseStartHour, InpRSIReverseEndHour)) + { + if(InpRSIReverseCloseOutsideHours) + { + if(HasPosition(InpMagicNumberRSIReverse)) + { + ClosePosition(InpMagicNumberRSIReverse); + } + } + return; + } + + // Check strategy lock + if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberRSIReverse)) + return; + + // Check cooldown + if(IsRSIReverseInCooldown()) + return; + + double rsi[]; + ArraySetAsSeries(rsi, true); + CopyBuffer(rsiReverseHandle, 0, 0, 3, rsi); + + if(ArraySize(rsi) < 3) return; + + // Check for overbought/oversold conditions + if(rsi[1] > InpRSIReverseOverbought) + rsiReverseOverbought = true; + else if(rsi[1] < InpRSIReverseOversold) + rsiReverseOversold = true; + + // Check for entry signals + if(rsiReverseOverbought && rsi[1] < InpRSIReverseCrossLevel) + { + // Sell signal + if(!HasPosition(InpMagicNumberRSIReverse)) + { + trade.SetExpertMagicNumber(InpMagicNumberRSIReverse); + trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "RSI Reverse"); + } + rsiReverseOverbought = false; + } + else if(rsiReverseOversold && rsi[1] > InpRSIReverseCrossLevel) + { + // Buy signal + if(!HasPosition(InpMagicNumberRSIReverse)) + { + trade.SetExpertMagicNumber(InpMagicNumberRSIReverse); + trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "RSI Reverse"); + } + rsiReverseOversold = false; + } +} + +//+------------------------------------------------------------------+ +//| Check for EMA Cross Strategy signals | +//+------------------------------------------------------------------+ +void CheckEMACrossStrategy() +{ + // Check if within trading hours + if(!IsWithinTradingHours(InpEMACrossStartHour, InpEMACrossEndHour)) + { + if(InpEMACrossCloseOutsideHours) + { + if(HasPosition(InpMagicNumberEMACross)) + { + ClosePosition(InpMagicNumberEMACross); + } + } + return; + } + + // Check strategy lock + if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberEMACross)) + return; + + double ema[], close[]; + ArraySetAsSeries(ema, true); + ArraySetAsSeries(close, true); + + CopyBuffer(emaHandle, 0, 0, InpEMADistancePeriod + 2, ema); + CopyClose(_Symbol, InpTimeframe, 0, InpEMADistancePeriod + 2, close); + + if(ArraySize(ema) < InpEMADistancePeriod + 2 || ArraySize(close) < InpEMADistancePeriod + 2) return; + + // Check for cross signals + if(ema[1] < close[1] && ema[0] > close[0]) + { + // Buy cross signal + emaCrossBuySignal = true; + emaCrossSellSignal = false; + emaCrossSignalBar = 0; + } + else if(ema[1] > close[1] && ema[0] < close[0]) + { + // Sell cross signal + emaCrossSellSignal = true; + emaCrossBuySignal = false; + emaCrossSignalBar = 0; + } + + // Check for distance entry conditions + if(InpUseEMADistanceEntry) + { + if(emaCrossBuySignal) + { + // Check if price has moved above EMA by the required distance for the required period + bool distanceConditionMet = true; + for(int i = 0; i < InpEMADistancePeriod; i++) + { + double distance = (close[i] - ema[i]) / _Point; + if(distance < InpEMADistancePips) + { + distanceConditionMet = false; + break; + } + } + + if(distanceConditionMet && !HasPosition(InpMagicNumberEMACross)) + { + trade.SetExpertMagicNumber(InpMagicNumberEMACross); + trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross Distance"); + emaCrossBuySignal = false; + } + } + else if(emaCrossSellSignal) + { + // Check if price has moved below EMA by the required distance for the required period + bool distanceConditionMet = true; + for(int i = 0; i < InpEMADistancePeriod; i++) + { + double distance = (ema[i] - close[i]) / _Point; + if(distance < InpEMADistancePips) + { + distanceConditionMet = false; + break; + } + } + + if(distanceConditionMet && !HasPosition(InpMagicNumberEMACross)) + { + trade.SetExpertMagicNumber(InpMagicNumberEMACross); + trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross Distance"); + emaCrossSellSignal = false; + } + } + } + else + { + // Original cross entry logic + if(ema[1] < close[1] && ema[0] > close[0]) + { + // Buy signal + if(!HasPosition(InpMagicNumberEMACross)) + { + trade.SetExpertMagicNumber(InpMagicNumberEMACross); + trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross"); + } + } + else if(ema[1] > close[1] && ema[0] < close[0]) + { + // Sell signal + if(!HasPosition(InpMagicNumberEMACross)) + { + trade.SetExpertMagicNumber(InpMagicNumberEMACross); + trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross"); + } + } + } + + // Increment signal bar counter + if(emaCrossBuySignal || emaCrossSellSignal) + { + emaCrossSignalBar++; + // Reset signals if they're too old (optional, can be removed if not needed) + if(emaCrossSignalBar > InpEMADistancePeriod * 2) + { + emaCrossBuySignal = false; + emaCrossSellSignal = false; + } + } +} + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() +{ + // Only process on new bar + if(!IsNewBar()) + return; + + // Check for new signals + if(InpEnableRSIFollow) + CheckRSIFollowStrategy(); + if(InpEnableRSIReverse) + CheckRSIReverseStrategy(); + if(InpEnableEMACross) + CheckEMACrossStrategy(); + + // Check for exit conditions + CheckExitConditions(); +} + +//+------------------------------------------------------------------+ +//| Check exit conditions for all strategies | +//+------------------------------------------------------------------+ +void CheckExitConditions() +{ + double rsi[], rsiReverse[], ema[], close[]; + ArraySetAsSeries(rsi, true); + ArraySetAsSeries(rsiReverse, true); + ArraySetAsSeries(ema, true); + ArraySetAsSeries(close, true); + + if(InpEnableRSIFollow) + { + CopyBuffer(rsiHandle, 0, 0, 1, rsi); + // Check RSI Follow exit conditions + if(HasPosition(InpMagicNumberRSIFollow)) + { + if((positionInfo.PositionType() == POSITION_TYPE_BUY && rsi[0] < InpRSIExitLevel) || + (positionInfo.PositionType() == POSITION_TYPE_SELL && rsi[0] > InpRSIExitLevel)) + { + ClosePosition(InpMagicNumberRSIFollow); + } + } + } + + if(InpEnableRSIReverse) + { + CopyBuffer(rsiReverseHandle, 0, 0, 1, rsiReverse); + // Check RSI Reverse exit conditions + if(HasPosition(InpMagicNumberRSIReverse)) + { + if((positionInfo.PositionType() == POSITION_TYPE_BUY && rsiReverse[0] < InpRSIReverseExitLevel) || + (positionInfo.PositionType() == POSITION_TYPE_SELL && rsiReverse[0] > InpRSIReverseExitLevel)) + { + ClosePosition(InpMagicNumberRSIReverse); + } + } + } + + if(InpEnableEMACross) + { + CopyBuffer(emaHandle, 0, 0, 2, ema); + CopyClose(_Symbol, InpTimeframe, 0, 2, close); + // Check EMA Cross exit conditions + if(HasPosition(InpMagicNumberEMACross)) + { + if((positionInfo.PositionType() == POSITION_TYPE_BUY && ema[0] > close[0]) || + (positionInfo.PositionType() == POSITION_TYPE_SELL && ema[0] < close[0])) + { + ClosePosition(InpMagicNumberEMACross); + } + } + } +} + +//+------------------------------------------------------------------+ +//| Close position by magic number | +//+------------------------------------------------------------------+ +void ClosePosition(int magic) +{ + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + if(positionInfo.SelectByIndex(i)) + { + if(positionInfo.Magic() == magic) + { + // Check if this is RSI Reverse position and update cooldown + if(magic == InpMagicNumberRSIReverse) + { + datetime time[]; + if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0) + { + rsiReverseLastCloseTime = time[0]; + // Only enter cooldown if it's a loss or if cooldown on loss is disabled + if(!InpRSIReverseCooldownOnLoss || positionInfo.Profit() < 0) + { + rsiReverseInCooldown = true; + } + } + } + + trade.PositionClose(positionInfo.Ticket()); + break; + } + } + } +} diff --git a/RSIMidPointHijack/test-balance.jpg b/RSIMidPointHijack/test-balance.jpg new file mode 100644 index 0000000..6b96132 Binary files /dev/null and b/RSIMidPointHijack/test-balance.jpg differ