diff --git a/README.md b/README.md index 13b0eb8..f337732 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,72 @@ A strategy that uses Exponential Moving Average (EMA) crossovers with advanced s **Additional Test Results (2008):** ![EMA Balance Sheet 2008](EMACrossOverSkirmish/balance-2008.jpg) +### 3. RSI Divergence Extrema + +A strategy that combines RSI divergence with extreme price points detection for enhanced market reversal signals. + +**Key Features:** +- RSI divergence patterns detection +- Extreme price points identification +- Dynamic exit based on RSI thresholds +- Advanced risk management + +**Strategy Settings:** +- Symbol: AUDUSD +- Period: H1 (2021.01.01 - 2025.04.11) +- RSI Period: 14 +- RSI Overbought: 70 +- RSI Oversold: 30 +- Base Lot Size: 0.01 +- Exit Buy RSI Threshold: 60.0 +- Exit Sell RSI Threshold: 40.0 +- Bar Time Frame: 16385 + +**Performance Metrics (2021-2025):** +| Metric | Value | +|--------|-------| +| Total Net Profit | $141.61 | +| Gross Profit | $508.54 | +| Gross Loss | -$366.93 | +| Profit Factor | 1.39 | +| Recovery Factor | 3.37 | +| Expected Payoff | $0.59 | +| Sharpe Ratio | 1.72 | +| AHPR | 1.0009 (0.09%) | +| GHPR | 1.0009 (0.09%) | + +**Trade Statistics (2021-2025):** +| Statistic | Value | +|-----------|-------| +| History Quality | 82% real ticks | +| Total Bars | 26,604 | +| Total Ticks | 116,467,811 | +| Total Trades | 240 | +| Total Deals | 480 | +| Profit Trades | 158 (65.83%) | +| Loss Trades | 82 (34.17%) | +| Short Trades Won | 70.73% | +| Long Trades Won | 60.68% | +| Largest Profit Trade | $12.35 | +| Largest Loss Trade | -$18.76 | +| Average Profit Trade | $3.22 | +| Average Loss Trade | -$4.47 | +| Max Consecutive Wins | 11 ($35.96) | +| Max Consecutive Losses | 4 (-$26.61) | + +**Drawdown Analysis (2021-2025):** +| Metric | Value | +|--------|-------| +| Balance Drawdown Absolute | $6.16 | +| Equity Drawdown Absolute | $14.13 | +| Balance Drawdown Maximal | $34.06 (4.50%) | +| Equity Drawdown Maximal | $42.06 (5.54%) | +| Balance Drawdown Relative | 4.55% ($32.84) | +| Equity Drawdown Relative | 5.54% ($42.06) | + +**Balance Sheet (2021-2025):** +![RSI Extrema Balance Sheet](RSIDivergenceExtrema/test-balance.jpg) + ## Technical Details Each EA is implemented in MQL5 and includes: - Custom strategy implementation diff --git a/RSIDivergenceExtrema/main.mq5 b/RSIDivergenceExtrema/main.mq5 new file mode 100644 index 0000000..edde8be --- /dev/null +++ b/RSIDivergenceExtrema/main.mq5 @@ -0,0 +1,347 @@ +//+------------------------------------------------------------------+ +//| RSIDivergenceRebound.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" +#property strict + +#include // Include CTrade class + +// Input Parameters +input int RSI_Period = 14; // RSI Period +input int RSI_Overbought = 70; // RSI Overbought Level +input int RSI_Oversold = 30; // RSI Oversold Level +input double BaseLotSize = 0.01; // Base Lot Size +input ENUM_TIMEFRAMES BarTimeFrame = PERIOD_H1; // Timeframe for bar updates +input double ExitBuyRSIThreshold = 60; // RSI level to exit buy positions +input double ExitSellRSIThreshold = 40; // RSI level to exit sell positions + +// Global Variables +int rsiHandle; // RSI indicator handle +CTrade trade; // Trade object +datetime lastBarTime = 0; // Last bar time +double RSILastThree = 0; // Third last RSI value +double RSILastTwo = 0; // Second last RSI value +double RSILast = 0; // Last RSI value +bool hasFirstExtrema = false; // Flag for first extrema +bool hasSecondExtrema = false; // Flag for second extrema +bool hasThirdExtrema = false; // Flag for third extrema +bool isOverboughtExtrema = false; // Flag for extrema type +double priceFirstExtrema = 0; // Price at first extrema +double rsiFirstExtrema = 0; // RSI at first extrema +double priceSecondExtrema = 0; // Price at second extrema +double rsiSecondExtrema = 0; // RSI at second extrema +double priceThirdExtrema = 0; // Price at third extrema +double rsiThirdExtrema = 0; // RSI at third extrema +string extremaPrefix = "Ext_"; // Prefix for extrema objects +datetime firstExtremaTime = 0; // Time of first extrema +datetime secondExtremaTime = 0; // Time of second extrema +datetime thirdExtremaTime = 0; // Time of third extrema + +//+------------------------------------------------------------------+ +//| Draw extrema point | +//+------------------------------------------------------------------+ +void DrawExtremaPoint(string name, datetime time, double price, color clr, int shape, string label) +{ + // Create the point + ObjectCreate(0, name, OBJ_ARROW, 0, time, price); + ObjectSetInteger(0, name, OBJPROP_ARROWCODE, shape); + ObjectSetInteger(0, name, OBJPROP_COLOR, clr); + ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); + ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, name, OBJPROP_HIDDEN, false); + ObjectSetInteger(0, name, OBJPROP_BACK, true); + + // Add label + string labelName = name + "_Label"; + ObjectCreate(0, labelName, OBJ_TEXT, 0, time, price); + ObjectSetString(0, labelName, OBJPROP_TEXT, label); + ObjectSetInteger(0, labelName, OBJPROP_COLOR, clr); + ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 8); + ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, labelName, OBJPROP_HIDDEN, false); + ObjectSetInteger(0, labelName, OBJPROP_BACK, true); +} + +//+------------------------------------------------------------------+ +//| Clean up extrema objects | +//+------------------------------------------------------------------+ +void CleanupExtremaObjects() +{ + for(int i = ObjectsTotal(0, 0, -1) - 1; i >= 0; i--) + { + string name = ObjectName(0, i, 0, -1); + if(StringFind(name, extremaPrefix) == 0) + { + ObjectDelete(0, name); + } + } +} + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + // Initialize RSI indicator + rsiHandle = iRSI(_Symbol, BarTimeFrame, RSI_Period, PRICE_CLOSE); + + if(rsiHandle == INVALID_HANDLE) + { + Print("Error creating RSI indicator"); + return(INIT_FAILED); + } + + // Initialize trade object + trade.SetExpertMagicNumber(123456); + + Print("RSI Divergence Rebound Strategy Initialized"); + Print("RSI Period: ", RSI_Period); + Print("Overbought Level: ", RSI_Overbought); + Print("Oversold Level: ", RSI_Oversold); + + // Clean up any existing extrema objects + CleanupExtremaObjects(); + + return(INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + // Clean up extrema objects + CleanupExtremaObjects(); + + IndicatorRelease(rsiHandle); +} + +//+------------------------------------------------------------------+ +//| Check for local extrema in RSI | +//+------------------------------------------------------------------+ +bool IsLocalExtrema(double rsi1, double rsi2, double rsi3, bool& isMaxima) +{ + if(rsi2 > rsi1 && rsi2 > rsi3) + { + isMaxima = true; + return true; + } + else if(rsi2 < rsi1 && rsi2 < rsi3) + { + isMaxima = false; + return true; + } + return false; +} + +//+------------------------------------------------------------------+ +//| Check for divergence patterns | +//+------------------------------------------------------------------+ +bool CheckDivergence(double price1, double rsi1, double price2, double rsi2, bool isOverbought) +{ + if(isOverbought) + { + // Bearish divergence (price makes higher high, RSI makes lower high) + if(price2 > price1 && rsi2 < rsi1) + return true; + // Hidden bearish divergence (price makes lower high, RSI makes higher high) + if(price2 < price1 && rsi2 > rsi1) + return true; + } + else + { + // Bullish divergence (price makes lower low, RSI makes higher low) + if(price2 < price1 && rsi2 > rsi1) + return true; + // Hidden bullish divergence (price makes higher low, RSI makes lower low) + if(price2 > price1 && rsi2 < rsi1) + return true; + } + return false; +} + +//+------------------------------------------------------------------+ +//| Check if market is open | +//+------------------------------------------------------------------+ +bool IsMarketOpen() +{ + MqlDateTime dt; + TimeCurrent(dt); + + // Check if it's a weekend + if(dt.day_of_week == 0 || dt.day_of_week == 6) + return false; + + // Check if it's within trading hours (assuming 24/5 market) + // You can modify these hours based on your broker's trading hours + int hour = dt.hour; + int minute = dt.min; + + // Market is open 24/5 except weekends + return true; +} + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() +{ + // Check if market is open + if(!IsMarketOpen()) + { + Print("Market is closed - resetting extrema"); + ResetExtrema(); + return; + } + + // Check for new bar + datetime currentBarTime = iTime(_Symbol, BarTimeFrame, 0); + if(currentBarTime == lastBarTime) + return; + lastBarTime = currentBarTime; + + // Get current RSI value + double rsiBuffer[]; + ArraySetAsSeries(rsiBuffer, true); + if(CopyBuffer(rsiHandle, 0, 0, 1, rsiBuffer) != 1) + { + Print("Error copying RSI buffer"); + return; + } + + // Update RSI queue + RSILastThree = RSILastTwo; + RSILastTwo = RSILast; + RSILast = rsiBuffer[0]; + + // Check if we have enough RSI values + if(RSILastThree == 0 || RSILastTwo == 0) + return; + + // Check for local extrema + bool isMaxima; + if(IsLocalExtrema(RSILastThree, RSILastTwo, RSILast, isMaxima)) + { + // First extrema (must be overbought/oversold) + if(!hasFirstExtrema) + { + if((isMaxima && RSILastTwo >= RSI_Overbought) || (!isMaxima && RSILastTwo <= RSI_Oversold)) + { + hasFirstExtrema = true; + isOverboughtExtrema = isMaxima; + priceFirstExtrema = iClose(_Symbol, BarTimeFrame, 1); + rsiFirstExtrema = RSILastTwo; + firstExtremaTime = iTime(_Symbol, BarTimeFrame, 1); + + // Draw first extrema + string firstExtremaName = extremaPrefix + "First_" + TimeToString(firstExtremaTime); + DrawExtremaPoint(firstExtremaName, firstExtremaTime, priceFirstExtrema, + isMaxima ? clrRed : clrGreen, 234, "1st " + (isMaxima ? "OB" : "OS")); + + Print("First extrema detected - Type: ", isMaxima ? "Overbought" : "Oversold", + ", RSI: ", rsiFirstExtrema, ", Price: ", priceFirstExtrema); + } + } + // Second extrema (check for divergence) + else if(!hasSecondExtrema) + { + priceSecondExtrema = iClose(_Symbol, BarTimeFrame, 1); + rsiSecondExtrema = RSILastTwo; + secondExtremaTime = iTime(_Symbol, BarTimeFrame, 1); + + if(CheckDivergence(priceFirstExtrema, rsiFirstExtrema, priceSecondExtrema, rsiSecondExtrema, isOverboughtExtrema)) + { + hasSecondExtrema = true; + + // Draw second extrema + string secondExtremaName = extremaPrefix + "Second_" + TimeToString(secondExtremaTime); + DrawExtremaPoint(secondExtremaName, secondExtremaTime, priceSecondExtrema, + clrBlue, 233, "2nd Div"); + + Print("Second extrema detected - Divergence found", + ", RSI: ", rsiSecondExtrema, ", Price: ", priceSecondExtrema); + } + } + // Third extrema (must be between overbought/oversold levels) + else if(!hasThirdExtrema) + { + if(RSILastTwo > RSI_Oversold && RSILastTwo < RSI_Overbought) + { + hasThirdExtrema = true; + priceThirdExtrema = iClose(_Symbol, BarTimeFrame, 1); + rsiThirdExtrema = RSILastTwo; + thirdExtremaTime = iTime(_Symbol, BarTimeFrame, 1); + + // Draw third extrema + string thirdExtremaName = extremaPrefix + "Third_" + TimeToString(thirdExtremaTime); + DrawExtremaPoint(thirdExtremaName, thirdExtremaTime, priceThirdExtrema, + clrMagenta, 232, "3rd Entry"); + + Print("Third extrema detected - Trade signal", + ", RSI: ", rsiThirdExtrema, ", Price: ", priceThirdExtrema); + + // Enter trade + if(isOverboughtExtrema) + { + if(!trade.Sell(BaseLotSize, _Symbol, 0, 0, 0, "RSI Divergence Sell")) + { + Print("Failed to execute sell order - resetting extrema"); + ResetExtrema(); + } + } + else + { + if(!trade.Buy(BaseLotSize, _Symbol, 0, 0, 0, "RSI Divergence Buy")) + { + Print("Failed to execute buy order - resetting extrema"); + ResetExtrema(); + } + } + } + } + } + + // Check for exit conditions + if(PositionSelect(_Symbol)) + { + ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + if(posType == POSITION_TYPE_BUY && RSILast >= ExitBuyRSIThreshold) + { + trade.PositionClose(_Symbol); + ResetExtrema(); + } + else if(posType == POSITION_TYPE_SELL && RSILast <= ExitSellRSIThreshold) + { + trade.PositionClose(_Symbol); + ResetExtrema(); + } + } +} + +//+------------------------------------------------------------------+ +//| Reset extrema flags and values | +//+------------------------------------------------------------------+ +void ResetExtrema() +{ + // Clean up existing objects + CleanupExtremaObjects(); + + hasFirstExtrema = false; + hasSecondExtrema = false; + hasThirdExtrema = false; + isOverboughtExtrema = false; + priceFirstExtrema = 0; + rsiFirstExtrema = 0; + priceSecondExtrema = 0; + rsiSecondExtrema = 0; + priceThirdExtrema = 0; + rsiThirdExtrema = 0; + firstExtremaTime = 0; + secondExtremaTime = 0; + thirdExtremaTime = 0; +} +//+------------------------------------------------------------------+ diff --git a/RSIDivergenceExtrema/test-balance.jpg b/RSIDivergenceExtrema/test-balance.jpg new file mode 100644 index 0000000..fcc5804 Binary files /dev/null and b/RSIDivergenceExtrema/test-balance.jpg differ