diff --git a/README.md b/README.md index f337732..60e72ed 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,18 @@ +# Table of Contents +- [Table of Contents](#table-of-contents) +- [Profitable Expert Advisors Collection](#profitable-expert-advisors-collection) + - [Project Overview](#project-overview) + - [Available Expert Advisors](#available-expert-advisors) + - [1. RSI Divergence Rebound](#1-rsi-divergence-rebound) + - [2. EMA Crossover Skirmish](#2-ema-crossover-skirmish) + - [3. RSI Divergence Extrema AUDUSD](#3-rsi-divergence-extrema-audusd) + - [4. RSI Divergence Extrema EURUSD](#4-rsi-divergence-extrema-eurusd) + - [Technical Details](#technical-details) + - [Requirements](#requirements) + - [Installation](#installation) + - [Disclaimer](#disclaimer) + + # Profitable Expert Advisors Collection ## Project Overview @@ -73,9 +88,6 @@ A strategy that combines RSI (Relative Strength Index) divergence detection with **Balance Sheet (2021-2025):** ![RSI Balance Sheet](RSIDivergenceRebound/test-balance.jpg) -**Additional Test Results (2008):** -![RSI Balance Sheet 2008](RSIDivergenceRebound/balance-2008.jpg) - ### 2. EMA Crossover Skirmish A strategy that uses Exponential Moving Average (EMA) crossovers with advanced scoring and position management. @@ -139,10 +151,8 @@ A strategy that uses Exponential Moving Average (EMA) crossovers with advanced s **Balance Sheet (2021-2025):** ![EMA Balance Sheet](EMACrossOverSkirmish/test-balance.jpg) -**Additional Test Results (2008):** -![EMA Balance Sheet 2008](EMACrossOverSkirmish/balance-2008.jpg) -### 3. RSI Divergence Extrema +### 3. RSI Divergence Extrema AUDUSD A strategy that combines RSI divergence with extreme price points detection for enhanced market reversal signals. @@ -206,7 +216,77 @@ A strategy that combines RSI divergence with extreme price points detection for | Equity Drawdown Relative | 5.54% ($42.06) | **Balance Sheet (2021-2025):** -![RSI Extrema Balance Sheet](RSIDivergenceExtrema/test-balance.jpg) +![RSI Extrema Balance Sheet](RSIDivergenceExtremaAUDUSD/test-balance.jpg) + +### 4. RSI Divergence Extrema EURUSD + +A strategy that combines RSI divergence with extreme price points detection for enhanced market reversal signals on EURUSD. + +**Key Features:** +- RSI divergence patterns detection +- Extreme price points identification +- Dynamic exit based on RSI thresholds +- Advanced risk management with hedging +- Stuck trade detection and management + +**Strategy Settings:** +- Symbol: EURUSD +- Period: H6 (2021.01.01 - 2025.04.11) +- RSI Period: 14 +- RSI Overbought: 71 +- RSI Oversold: 33 +- Base Lot Size: 0.01 +- Exit Buy RSI Threshold: 60 +- Exit Sell RSI Threshold: 40 +- Bar Time Frame: 16390 +- Extrema Expiry Bars: 45 +- Stuck Trade Bars: 6 +- Hedge Lot Multiplier: 6 + +**Performance Metrics (2021-2025):** +| Metric | Value | +|--------|-------| +| Total Net Profit | $510.13 | +| Gross Profit | $1,033.91 | +| Gross Loss | -$523.78 | +| Profit Factor | 1.97 | +| Recovery Factor | 2.87 | +| Expected Payoff | $4.15 | +| Sharpe Ratio | 2.80 | +| AHPR | 1.0053 (0.53%) | +| GHPR | 1.0050 (0.50%) | + +**Trade Statistics (2021-2025):** +| Statistic | Value | +|-----------|-------| +| History Quality | 82% real ticks | +| Total Bars | 4,436 | +| Total Ticks | 107,013,176 | +| Total Trades | 123 | +| Total Deals | 246 | +| Profit Trades | 66 (53.66%) | +| Loss Trades | 57 (46.34%) | +| Short Trades Won | 69.49% | +| Long Trades Won | 39.06% | +| Largest Profit Trade | $76.90 | +| Largest Loss Trade | -$71.89 | +| Average Profit Trade | $15.67 | +| Average Loss Trade | -$9.19 | +| Max Consecutive Wins | 4 ($32.75) | +| Max Consecutive Losses | 3 (-$85.50) | + +**Drawdown Analysis (2021-2025):** +| Metric | Value | +|--------|-------| +| Balance Drawdown Absolute | $0.00 | +| Equity Drawdown Absolute | $14.91 | +| Balance Drawdown Maximal | $85.50 (8.28%) | +| Equity Drawdown Maximal | $177.46 (16.45%) | +| Balance Drawdown Relative | 8.28% ($85.50) | +| Equity Drawdown Relative | 16.45% ($177.46) | + +**Balance Sheet (2021-2025):** +![RSI Extrema EURUSD Balance Sheet](RSIDivergenceExtremaEURUSD/test-balance.jpg) ## Technical Details Each EA is implemented in MQL5 and includes: diff --git a/RSIDivergenceExtrema/main.mq5 b/RSIDivergenceExtremaAUDUSD/main.mq5 similarity index 100% rename from RSIDivergenceExtrema/main.mq5 rename to RSIDivergenceExtremaAUDUSD/main.mq5 diff --git a/RSIDivergenceExtrema/test-balance.jpg b/RSIDivergenceExtremaAUDUSD/test-balance.jpg similarity index 100% rename from RSIDivergenceExtrema/test-balance.jpg rename to RSIDivergenceExtremaAUDUSD/test-balance.jpg diff --git a/RSIDivergenceExtremaEURUSD/main.mq5 b/RSIDivergenceExtremaEURUSD/main.mq5 new file mode 100644 index 0000000..21d05bf --- /dev/null +++ b/RSIDivergenceExtremaEURUSD/main.mq5 @@ -0,0 +1,655 @@ +//+------------------------------------------------------------------+ +//| 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 = 71; // RSI Overbought Level +input int RSI_Oversold = 33; // RSI Oversold Level +input double BaseLotSize = 0.01; // Base Lot Size +input ENUM_TIMEFRAMES BarTimeFrame = PERIOD_H6; // Timeframe for bar updates +input double ExitBuyRSIThreshold = 60; // RSI level to exit buy positions +input double ExitSellRSIThreshold = 40; // RSI level to exit sell positions +input int ExtremaExpiryBars = 45; // Number of bars before extrema expire +input int StuckTradeBars = 6; // Number of bars before considering trade stuck +input double HedgeLotMultiplier = 6.0; // Multiplier for hedge position lot size + +// 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 +datetime extremaStartTime = 0; // Time when first extrema was detected +datetime positionOpenTime = 0; // Time when position was opened +bool isHedged = false; // Flag for hedge position + +//+------------------------------------------------------------------+ +//| 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); + } + } +} + +//+------------------------------------------------------------------+ +//| Check if trade is stuck | +//+------------------------------------------------------------------+ +bool IsTradeStuck() +{ + if(!PositionSelect(_Symbol)) + { + Print("No position selected - cannot check if trade is stuck"); + return false; + } + + if(positionOpenTime == 0) + { + Print("Position open time not set - cannot check if trade is stuck"); + return false; + } + + datetime currentTime = iTime(_Symbol, BarTimeFrame, 0); + int barsPassed = (int)((currentTime - positionOpenTime) / PeriodSeconds(BarTimeFrame)); + + Print("Trade Stuck Check - Current Time: ", TimeToString(currentTime), + ", Position Open Time: ", TimeToString(positionOpenTime), + ", Bars Passed: ", barsPassed, + ", Stuck Trade Bars: ", StuckTradeBars); + + return barsPassed >= StuckTradeBars; +} + +//+------------------------------------------------------------------+ +//| Place hedge trade | +//+------------------------------------------------------------------+ +void PlaceHedgeTrade() +{ + if(isHedged) + { + Print("Hedge position already exists - skipping"); + return; + } + + if(!PositionSelect(_Symbol)) + { + Print("No position selected - cannot place hedge"); + return; + } + + ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + double currentLot = PositionGetDouble(POSITION_VOLUME); + double hedgeLot = currentLot * HedgeLotMultiplier; + + Print("Placing hedge trade - Current Position: ", EnumToString(posType), + ", Current Lot: ", currentLot, + ", Hedge Lot: ", hedgeLot); + + // Set different magic number for hedge positions + trade.SetExpertMagicNumber(654321); + + if(posType == POSITION_TYPE_BUY) + { + if(trade.Sell(hedgeLot, _Symbol, 0, 0, 0, "RSI Hedge Sell")) + { + isHedged = true; + Print("Hedge sell position opened with lot size: ", hedgeLot); + } + else + { + Print("Failed to open hedge sell position"); + } + } + else if(posType == POSITION_TYPE_SELL) + { + if(trade.Buy(hedgeLot, _Symbol, 0, 0, 0, "RSI Hedge Buy")) + { + isHedged = true; + Print("Hedge buy position opened with lot size: ", hedgeLot); + } + else + { + Print("Failed to open hedge buy position"); + } + } + + // Reset magic number back to original + trade.SetExpertMagicNumber(123456); +} + +//+------------------------------------------------------------------+ +//| Close all positions | +//+------------------------------------------------------------------+ +void CloseAllPositions() +{ + Print("Starting to close all positions"); + + // Close all positions for the symbol + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) + { + Print("Failed to get position ticket for index ", i); + continue; + } + + if(!PositionSelectByTicket(ticket)) + { + Print("Failed to select position with ticket ", ticket); + continue; + } + + if(PositionGetString(POSITION_SYMBOL) != _Symbol) + { + Print("Position ", ticket, " is not for symbol ", _Symbol); + continue; + } + + Print("Closing position - Ticket: ", ticket, + ", Magic: ", PositionGetInteger(POSITION_MAGIC), + ", Type: ", EnumToString((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE))); + + if(!trade.PositionClose(ticket)) + { + Print("Failed to close position with ticket ", ticket); + } + else + { + Print("Successfully closed position with ticket ", ticket); + } + } + + isHedged = false; + positionOpenTime = 0; + Print("All positions closed"); +} + +//+------------------------------------------------------------------+ +//| 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(); + + positionOpenTime = 0; + isHedged = false; + + return(INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + // Clean up extrema objects + CleanupExtremaObjects(); + + IndicatorRelease(rsiHandle); + + CloseAllPositions(); +} + +//+------------------------------------------------------------------+ +//| 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; +} + +//+------------------------------------------------------------------+ +//| Check if extrema has expired | +//+------------------------------------------------------------------+ +bool HasExtremaExpired() +{ + if(extremaStartTime == 0) + return false; + + datetime currentTime = iTime(_Symbol, BarTimeFrame, 0); + int barsPassed = (int)((currentTime - extremaStartTime) / PeriodSeconds(BarTimeFrame)); + + return barsPassed >= ExtremaExpiryBars; +} + +//+------------------------------------------------------------------+ +//| Check if loss is resolved after hedging | +//+------------------------------------------------------------------+ +bool IsLossResolved() +{ + if(!isHedged) + { + Print("Loss Resolution Check - No hedge position exists"); + return false; + } + + double originalProfit = 0; + double hedgeProfit = 0; + bool foundOriginal = false; + bool foundHedge = false; + + Print("Loss Resolution Check - Starting position scan"); + + // Calculate total profit from all positions + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) + { + Print("Loss Resolution Check - Failed to get position ticket for index ", i); + continue; + } + + if(!PositionSelectByTicket(ticket)) + { + Print("Loss Resolution Check - Failed to select position with ticket ", ticket); + continue; + } + + if(PositionGetString(POSITION_SYMBOL) != _Symbol) + { + Print("Loss Resolution Check - Position ", ticket, " is not for symbol ", _Symbol); + continue; + } + + double profit = PositionGetDouble(POSITION_PROFIT); + int magic = (int)PositionGetInteger(POSITION_MAGIC); + + Print("Loss Resolution Check - Position ", ticket, + ", Magic: ", magic, + ", Profit: ", profit); + + if(magic == 123456) // Original position + { + originalProfit = profit; + foundOriginal = true; + Print("Loss Resolution Check - Found original position with profit: ", profit); + } + else if(magic == 654321) // Hedge position + { + hedgeProfit = profit; + foundHedge = true; + Print("Loss Resolution Check - Found hedge position with profit: ", profit); + } + } + + if(!foundOriginal) + Print("Loss Resolution Check - Warning: Original position not found"); + if(!foundHedge) + Print("Loss Resolution Check - Warning: Hedge position not found"); + + double totalProfit = originalProfit + hedgeProfit; + Print("Loss Resolution Check - Final Calculation -", + "\nOriginal Profit: ", originalProfit, + "\nHedge Profit: ", hedgeProfit, + "\nTotal Profit: ", totalProfit, + "\nIs Resolved: ", totalProfit >= 0); + + return totalProfit >= 0; +} + +//+------------------------------------------------------------------+ +//| Check if main trade is in loss | +//+------------------------------------------------------------------+ +bool IsMainTradeInLoss() +{ + if(!PositionSelect(_Symbol)) + { + Print("No position selected - cannot check for loss"); + return false; + } + + if(PositionGetInteger(POSITION_MAGIC) != 123456) + { + Print("Not a main trade position - cannot check for loss"); + return false; + } + + double profit = PositionGetDouble(POSITION_PROFIT); + Print("Main Trade Profit Check - Profit: ", profit); + + return profit < 0; +} + +//+------------------------------------------------------------------+ +//| 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)) + { + + if(!hasFirstExtrema) + { + // For overbought condition, we need a maxima + if(isMaxima && RSILastTwo >= RSI_Overbought) + { + hasFirstExtrema = true; + isOverboughtExtrema = true; + priceFirstExtrema = iClose(_Symbol, BarTimeFrame, 1); + rsiFirstExtrema = RSILastTwo; + firstExtremaTime = iTime(_Symbol, BarTimeFrame, 1); + extremaStartTime = firstExtremaTime; + + // Draw first extrema + string firstExtremaName = extremaPrefix + "First_" + TimeToString(firstExtremaTime); + DrawExtremaPoint(firstExtremaName, firstExtremaTime, priceFirstExtrema, + clrRed, 234, "1st OB"); + + Print("First extrema detected - Type: Overbought", + ", RSI: ", rsiFirstExtrema, ", Price: ", priceFirstExtrema); + } + // For oversold condition, we need a minima + else if(!isMaxima && RSILastTwo <= RSI_Oversold) + { + hasFirstExtrema = true; + isOverboughtExtrema = false; + priceFirstExtrema = iClose(_Symbol, BarTimeFrame, 1); + rsiFirstExtrema = RSILastTwo; + firstExtremaTime = iTime(_Symbol, BarTimeFrame, 1); + extremaStartTime = firstExtremaTime; + + // Draw first extrema + string firstExtremaName = extremaPrefix + "First_" + TimeToString(firstExtremaTime); + DrawExtremaPoint(firstExtremaName, firstExtremaTime, priceFirstExtrema, + clrGreen, 234, "1st OS"); + + Print("First extrema detected - Type: 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 + { + positionOpenTime = iTime(_Symbol, BarTimeFrame, 0); + Print("Sell position opened at: ", TimeToString(positionOpenTime)); + } + } + else + { + if(!trade.Buy(BaseLotSize, _Symbol, 0, 0, 0, "RSI Divergence Buy")) + { + Print("Failed to execute buy order - resetting extrema"); + ResetExtrema(); + } + else + { + positionOpenTime = iTime(_Symbol, BarTimeFrame, 0); + Print("Buy position opened at: ", TimeToString(positionOpenTime)); + } + } + } + } + } + + // Check for exit conditions and hedge + if(PositionSelect(_Symbol)) + { + ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + + // Check if trade is stuck and in loss + if(IsTradeStuck() && IsMainTradeInLoss()) + { + Print("Trade is stuck and in loss - placing hedge"); + PlaceHedgeTrade(); + } + + // Check if loss is resolved after hedging + if(isHedged && IsLossResolved()) + { + Print("Loss resolved - closing all positions"); + CloseAllPositions(); + ResetExtrema(); + return; + } + + // Check RSI exit conditions + if(posType == POSITION_TYPE_BUY && RSILast >= ExitBuyRSIThreshold) + { + CloseAllPositions(); + ResetExtrema(); + } + else if(posType == POSITION_TYPE_SELL && RSILast <= ExitSellRSIThreshold) + { + CloseAllPositions(); + 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; + extremaStartTime = 0; + positionOpenTime = 0; + isHedged = false; +} +//+------------------------------------------------------------------+ diff --git a/RSIDivergenceExtremaEURUSD/test-balance.jpg b/RSIDivergenceExtremaEURUSD/test-balance.jpg new file mode 100644 index 0000000..8ff0293 Binary files /dev/null and b/RSIDivergenceExtremaEURUSD/test-balance.jpg differ