diff --git a/EMACrossOverBTC/main.mq5 b/EMACrossOverBTC/main.mq5 new file mode 100644 index 0000000..51f3569 --- /dev/null +++ b/EMACrossOverBTC/main.mq5 @@ -0,0 +1,426 @@ +//+------------------------------------------------------------------+ +//| ScoringTrade.mq5 | +//| Generated by ChatGPT | +//| | +//+------------------------------------------------------------------+ +#property strict +#include + +// Input parameters +input int MagicNumber = 42; // Unique identifier for this EA's trades +input int scoreThreshold = 15000; // Minimum score required to enter a trade (increased for BTC's larger price movements) +input int slopeThreshold = 3500; // Minimum EMA slope value to consider trend significant (increased for BTC's steeper trends) +input double maxScore = 25000; // Maximum allowed score before clamping (prevents excessive trade signals) +input int cooldownMinutes = 18; // Minimum time between crossover signals (prevents over-trading) +input int tradeCooldownMinutes = 44; // Minimum time between consecutive trades (trade debounce period) +input ENUM_TIMEFRAMES emaTimeFrame = PERIOD_H1; // Timeframe for EMA calculation (1-hour candles) +input double delayClampAbsolute = 5000; // Score threshold for applying decay (prevents score from growing too large) +input int emaPeriod = 139; // Number of periods for EMA calculation (longer period for smoother trend) +input double crossOverStep = 2500; // Score adjustment when price crosses EMA (increased for BTC's larger moves) +input double slopeThresholdStep = 2000; // Score adjustment for significant slope changes (increased for BTC) +input double emaDistanceStep = 500; // Score adjustment for price distance from EMA (increased for BTC) +input double emaDecayStep = 0; // Score decay rate when no significant signals (0 means no decay) +input double decayMultiplier = 0.08; // Multiplier applied to score when above delayClampAbsolute +input double distanceThreshold = 7100; // Minimum price-EMA distance to trigger score adjustment (increased for BTC) +input double atrMultiplier = 3.9; // Multiplier for dynamic stop loss calculation based on ATR +input double TrailingStop = 10; // Distance in points for trailing stop loss +input bool ApplyTrailingStop = true; // Enable/disable trailing stop functionality +input int maxCrossoverTrades = 14; // Maximum number of trades allowed per crossover signal +input double max_drawdown = 0.1; // Maximum allowed drawdown as percentage of account balance +input bool resetCrossoverTradeOnDistance = false; // Reset trade count when price moves beyond distance threshold +input int resetCrossoverNumber = 0; // Number of trades to reset to when resetCrossoverTradeOnDistance is true +input double minimumLotSize = 0.01; // Minimum trade size allowed +input int maxTimeInPosition = 1; // Maximum time in hours to hold a position +input int tradeLengthThreshold = 31; // Time in minutes before considering a reverse trade +input int reverseTP = 707; // Take profit level for reverse trades (increased for BTC) +input int reverseLotSizeMultiplier = 4; // Multiplier for lot size in reverse trades +input int secondaryPositionHoldTime = 75; // Maximum time in minutes to hold a secondary position +// Global variables +int emaHandle; // EMA handle +double prevScore = 0; // Previous score +double currentScore = 0; // Current score +double emaPrevValue = 0; // Previous EMA value +double emaCurrentValue = 0; // Current EMA value +double emaSlope = 0; // EMA slope value +CTrade trade; // Trading object + +datetime lastCrossoverTime = 0; // Time of last crossover +datetime lastTradeTime = 0; // Time of last trade +int crossoverTradeCount = 0; // Count of trades after each crossover + + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() { + // Create EMA handle (e.g., 14-period EMA on the closing price) + emaHandle = iMA(Symbol(), emaTimeFrame, emaPeriod, 0, MODE_EMA, PRICE_CLOSE); + + if (emaHandle == INVALID_HANDLE) { + Print("Failed to create EMA handle"); + return INIT_FAILED; + } + + return INIT_SUCCEEDED; +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) { + // Release the EMA handle + if (emaHandle != INVALID_HANDLE) { + ExpertRemove(); + } +} + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() { + // Buffer to hold the EMA values + double emaBuffer[]; + + // Get dynamic lot size based on current balance and max drawdown + double lotSize = CalculateLotSize(); + + if(lotSize < minimumLotSize) { + lotSize = minimumLotSize; + } + + // Get the current Ask and Bid prices + double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); + double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); + + // Copy the last 2 EMA values (current and previous) + int copied = CopyBuffer(emaHandle, 0, 0, 2, emaBuffer); + if (copied < 2) { + return; + } + + // Get the current and previous EMA values + emaPrevValue = emaBuffer[1]; // Previous EMA value (index 1) + emaCurrentValue = emaBuffer[0]; // Current EMA value (index 0) + + // Calculate the EMA slope (change in EMA values) + emaSlope = - (emaCurrentValue - emaPrevValue) * 100; + Print("EMA Slope: ", emaSlope); + + // Check for price action crossover with EMA + double closePrev = iClose(Symbol(), Period(), 1); // Close of previous bar + double closeCurr = iClose(Symbol(), Period(), 0); // Close of current bar + + // Check if enough time has passed for the cooldown (cooldownMinutes) + if (TimeCurrent() - lastCrossoverTime >= cooldownMinutes * 60) { + if (closePrev < emaPrevValue && closeCurr > emaCurrentValue) { // Bullish crossover + currentScore += crossOverStep; + crossoverTradeCount = 0; // Reset trade count after new crossover + lastCrossoverTime = TimeCurrent(); // Update the last crossover time + } + else if (closePrev > emaPrevValue && closeCurr < emaCurrentValue) { // Bearish crossover + currentScore -= crossOverStep; + crossoverTradeCount = 0; // Reset trade count after new crossover + lastCrossoverTime = TimeCurrent(); // Update the last crossover time + } + } + + // Check EMA slope + if (emaSlope > slopeThreshold) { // Positive slope + currentScore += slopeThresholdStep; + } + else if (emaSlope < -slopeThreshold) { // Negative slope + currentScore -= slopeThresholdStep; + } + else { + if (MathAbs(currentScore) > delayClampAbsolute) { + currentScore *= decayMultiplier; + } + } + + if(ApplyTrailingStop) { + ApplyTrailingStop(); + } + + // Calculate distance to EMA and adjust score + double priceToEmaDistance = closeCurr - emaCurrentValue; // Distance between the current price and the EMA + Print("Price to EMA Distance: ", priceToEmaDistance); + + if (MathAbs(priceToEmaDistance) > distanceThreshold) { + if (priceToEmaDistance > 0) { // Bullish (price above EMA) + currentScore += emaDistanceStep; + } + else if (priceToEmaDistance < 0) { // Bearish (price below EMA) + currentScore -= emaDistanceStep; + } + } + else { + if (currentScore > 0) { + currentScore -= emaDecayStep; + } + else { + currentScore += emaDecayStep; + } + } + + // Close all positions if score crosses zero + if ((prevScore > 0 && currentScore <= 0) || (prevScore < 0 && currentScore >= 0)) { + Close_Position_MN(MagicNumber); + } + + // Update the previous score + prevScore = currentScore; + + if (crossoverTradeCount > maxCrossoverTrades) { + return; + } + + // Debounce check: Ensure enough time has passed since the last trade + if (TimeCurrent() - lastTradeTime >= tradeCooldownMinutes * 60) { + // Calculate ATR (Average True Range) for stop loss calculation + double atrArray[]; + int atrPeriod = 14; // ATR period (can be adjusted) + int copied = CopyBuffer(iATR(Symbol(), Period(), atrPeriod), 0, 0, 1, atrArray); + if (copied < 1) { + return; + } + + // Get the current price (using Bid price) + double currentPrice = Bid; + // Get ATR value + double atrValue = atrArray[0]; // Latest ATR value + + // Get the minimum stop level and freeze level for the symbol + int stopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL); + int freezeLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL); + + // Calculate the minimum stop loss in price units (converted from pips) + double minStopLoss = stopLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT); + double minFreezeLevel = freezeLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT); + + // Dynamic Stop Loss and Take Profit calculation based on ATR + double dynamicSL = atrValue * atrMultiplier; + double dynamicTP = atrValue * atrMultiplier; + + // Adjust SL and TP if they are smaller than the minimum stop level + dynamicSL = MathMax(dynamicSL, minStopLoss); + dynamicTP = MathMax(dynamicTP, dynamicSL); // Ensure TP is at least the same as SL + + // Trade logic based on the score + if (currentScore > scoreThreshold) { // Buy signal + if (PositionSelect(Symbol()) == false || !PositionGetInteger(POSITION_MAGIC) == MagicNumber && crossoverTradeCount < maxCrossoverTrades) { + // Open buy position with dynamic SL and TP + trade.SetExpertMagicNumber(MagicNumber); + if (trade.Buy(lotSize, Symbol(), currentPrice, Bid - dynamicSL, 0)) { + crossoverTradeCount++; // Increment trade count + lastTradeTime = TimeCurrent(); // Update the last trade time + } + } + } + else if (currentScore < -scoreThreshold) { // Sell signal + if (PositionSelect(Symbol()) == false || !PositionGetInteger(POSITION_MAGIC) == MagicNumber && crossoverTradeCount < maxCrossoverTrades) { + // Open sell position with dynamic SL and TP + trade.SetExpertMagicNumber(MagicNumber); + if (trade.Sell(lotSize, Symbol(), currentPrice, Ask + dynamicSL, 0)) { + crossoverTradeCount++; // Increment trade count + lastTradeTime = TimeCurrent(); // Update the last trade time + } + } + } + } + + // Check existing positions for profit and place reverse trade if needed + CheckPositions(); +} + +//+------------------------------------------------------------------+ +//| Check existing positions for profit and place reverse trade if needed | +//+------------------------------------------------------------------+ +void CheckPositions() { + // Check if there are any open positions + if (PositionsTotal() > 0) { + // Check if there are exactly 2 open positions + if (PositionsTotal() == 2) { + for (int i = 0; i < PositionsTotal(); i++) { + ulong ticket = PositionGetTicket(i); + if (PositionSelectByTicket(ticket)) { + datetime openTime = PositionGetInteger(POSITION_TIME); + int tradeLength = TimeCurrent() - openTime; // Trade duration in seconds + + // Check if the trade has been open for more than the secondaryPositionHoldTime + if (tradeLength > secondaryPositionHoldTime * 60) { // Convert threshold to seconds + // Close all positions + CloseAllPositions(); + return; // Exit the function after closing all positions + } + } + } + } else if (PositionsTotal() < 2) { + for (int i = 0; i < PositionsTotal(); i++) { + ulong ticket = PositionGetTicket(i); + if (PositionSelectByTicket(ticket)) { + double profit = PositionGetDouble(POSITION_PROFIT); + datetime openTime = PositionGetInteger(POSITION_TIME); + int tradeLength = TimeCurrent() - openTime; // Trade duration in seconds + + // Check if the trade has been open for more than the tradeLengthThreshold + if (tradeLength > tradeLengthThreshold * 60) { // Convert threshold to seconds + double lotSize = PositionGetDouble(POSITION_VOLUME); + double newLotSize = lotSize * reverseLotSizeMultiplier; // 10 times the original lot size + + crossoverTradeCount = maxCrossoverTrades + 1; + + // Place a reverse trade + if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { + trade.SetExpertMagicNumber(MagicNumber); + if (trade.Sell(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_BID))) { + } else { + Print("Failed to execute reversal sell order"); + } + } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { + trade.SetExpertMagicNumber(MagicNumber); + if (trade.Buy(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_ASK))) { + } else { + Print("Failed to execute reversal buy order"); + } + } + } + + // Close the trade if profit meets the take profit level + if (profit >= reverseTP) { + Close_Position_MN(MagicNumber); + CloseAllPositions(); + } + + // Check if there is only one position and its volume is lotSize * reverseLotSizeMultiplier + if (PositionsTotal() == 1 && PositionGetDouble(POSITION_VOLUME) == minimumLotSize * reverseLotSizeMultiplier) { + trade.PositionClose(ticket); + } + + // Get the current Ask and Bid prices + double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); + double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); + + // Check if the double down trade is exited by stop loss + if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetDouble(POSITION_SL) > 0 && Bid <= PositionGetDouble(POSITION_SL)) { + // Close the original trade + CloseOriginalTrade(); + } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetDouble(POSITION_SL) > 0 && Ask >= PositionGetDouble(POSITION_SL)) { + // Close the original trade + CloseOriginalTrade(); + } + } + } + } + } +} + +// Function to close the original trade +void CloseOriginalTrade() { + for (int i = PositionsTotal() - 1; i >= 0; i--) { + ulong ticket = PositionGetTicket(i); + if (PositionSelectByTicket(ticket)) { + if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { + trade.PositionClose(ticket); + } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { + trade.PositionClose(ticket); + } + } + } +} + +//+------------------------------------------------------------------+ +//| Function to close all positions | +//+------------------------------------------------------------------+ +void CloseAllPositions() { + // Loop through all positions and close them + for (int i = PositionsTotal() - 1; i >= 0; i--) { + ulong ticket = PositionGetTicket(i); + if (PositionSelectByTicket(ticket)) { + if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { + trade.PositionClose(ticket); + } + else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { + trade.PositionClose(ticket); + } + } + } +} + +void ApplyTrailingStop() +{ + for(int i=PositionsTotal()-1; i>=0; i--) + { + string symbol = PositionGetSymbol(i); + ulong PositionTicket = PositionGetTicket(i); + long trade_type = PositionGetInteger(POSITION_TYPE); + + if(!PositionGetInteger(POSITION_MAGIC) == MagicNumber) { + return; + } + + double POINT = SymbolInfoDouble( symbol, SYMBOL_POINT ); + int DIGIT = (int) SymbolInfoInteger( symbol, SYMBOL_DIGITS ); + + if(trade_type == 0) + { + double Bid = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_BID),DIGIT); + + if(Bid-PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * TrailingStop,DIGIT)) + { + if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * TrailingStop,DIGIT)) + { + trade.PositionModify(PositionTicket,NormalizeDouble(Bid - POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP)); + } + } + } + + if(trade_type == 1) + { + double Ask = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_ASK),DIGIT); + + if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble( POINT * TrailingStop,DIGIT)) + { + if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * TrailingStop,DIGIT)) || (PositionGetDouble(POSITION_SL)==0)) + { + trade.PositionModify(PositionTicket,NormalizeDouble(Ask + POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP)); + } + } + } + } +} + +void Close_Position_MN(ulong magicNumber) +{ + int total = PositionsTotal(); + for(int i = total - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + + // Use PositionSelect by symbol instead of ticket + string symbol = PositionGetSymbol(i); + if(PositionSelect(symbol)) + { + if (PositionGetInteger(POSITION_MAGIC) == magicNumber && PositionGetInteger(POSITION_TICKET) == ticket) + { + if(symbol == _Symbol) // Verify the symbol + { + trade.PositionClose(ticket); + } + } + } + } +} + +//+------------------------------------------------------------------+ +//| Calculate the dynamic lot size based on max drawdown | +//+------------------------------------------------------------------+ +double CalculateLotSize() +{ + double balance = AccountInfoDouble(ACCOUNT_BALANCE); // Get account balance + double allowedDrawdown = balance * max_drawdown; // Calculate allowed drawdown in account currency + double baseDrawdownPerLot = 150; // Assumed drawdown per 0.01 lots as per backtest + + // Calculate lot size based on maximum drawdown + double lotSize = (allowedDrawdown / baseDrawdownPerLot) * 0.01; + return NormalizeDouble(lotSize, 2); // Normalize lot size to 2 decimal places +} diff --git a/EMACrossOverBTC/test-balance.jpg b/EMACrossOverBTC/test-balance.jpg new file mode 100644 index 0000000..0e51e4f Binary files /dev/null and b/EMACrossOverBTC/test-balance.jpg differ diff --git a/README.md b/README.md index 60e72ed..42b0b6e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ - [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) + - [5. RSI Reversal Asian AUDUSD](#5-rsi-reversal-asian-audusd) + - [6. RSI Reversal Asian EURUSD](#6-rsi-reversal-asian-eurusd) + - [7. EMA Crossover BTC](#7-ema-crossover-btc) - [Technical Details](#technical-details) - [Requirements](#requirements) - [Installation](#installation) @@ -288,6 +291,221 @@ A strategy that combines RSI divergence with extreme price points detection for **Balance Sheet (2021-2025):** ![RSI Extrema EURUSD Balance Sheet](RSIDivergenceExtremaEURUSD/test-balance.jpg) +### 5. RSI Reversal Asian AUDUSD + +A strategy specifically designed for the Asian session on AUDUSD, using RSI reversals with optimized parameters for this market condition. + +**Key Features:** +- RSI reversal patterns detection +- Asian session optimization +- Dynamic exit based on RSI thresholds +- Advanced position management +- Session-based trading rules + +**Strategy Settings:** +- Symbol: AUDUSD +- Period: M15 (2021.01.01 - 2025.04.03) +- RSI Period: 14 +- RSI Overbought: 67 +- RSI Oversold: 17 +- Take Profit: 253 pips +- Stop Loss: 429 pips +- Max Lot Size: 0.1 +- Max Spread: 1000 +- Max Duration: 81 +- RSI Exit Level: 49 +- Use Stop Loss: false +- Use Take Profit: true +- Use RSI Exit: true + +**Performance Metrics (2021-2025):** +| Metric | Value | +|--------|-------| +| Total Net Profit | $1,521.11 | +| Gross Profit | $6,147.84 | +| Gross Loss | -$4,626.73 | +| Profit Factor | 1.33 | +| Recovery Factor | 4.30 | +| Expected Payoff | $2.32 | +| Sharpe Ratio | 2.14 | +| AHPR | 1.0022 (0.22%) | +| GHPR | 1.0019 (0.19%) | + +**Trade Statistics (2021-2025):** +| Statistic | Value | +|-----------|-------| +| History Quality | 100% | +| Total Bars | 105,824 | +| Total Ticks | 6,215,660 | +| Total Trades | 657 | +| Total Deals | 1,314 | +| Profit Trades | 491 (74.73%) | +| Loss Trades | 166 (25.27%) | +| Short Trades Won | 75.52% | +| Long Trades Won | 61.11% | +| Largest Profit Trade | $24.20 | +| Largest Loss Trade | -$106.60 | +| Average Profit Trade | $12.52 | +| Average Loss Trade | -$27.87 | +| Max Consecutive Wins | 17 ($266.30) | +| Max Consecutive Losses | 4 (-$124.19) | + +**Drawdown Analysis (2021-2025):** +| Metric | Value | +|--------|-------| +| Balance Drawdown Absolute | $158.27 | +| Equity Drawdown Absolute | $189.77 | +| Balance Drawdown Maximal | $302.68 (16.43%) | +| Equity Drawdown Maximal | $353.92 (20.63%) | +| Balance Drawdown Relative | 26.38% ($158.27) | +| Equity Drawdown Relative | 31.97% ($192.77) | + +**Balance Sheet (2021-2025):** +![RSI Reversal Asian Balance Sheet](RSIReversalAsianAUDUSD/test-balance.jpg) + +### 6. RSI Reversal Asian EURUSD + +A strategy specifically designed for the Asian session on EURUSD, using RSI reversals with optimized parameters for this market condition. + +**Key Features:** +- RSI reversal patterns detection +- Asian session optimization +- Dynamic exit based on RSI thresholds +- Advanced position management +- Session-based trading rules + +**Strategy Settings:** +- Symbol: EURUSD +- Period: M15 (2021.01.01 - 2025.04.03) +- RSI Period: 14 +- RSI Overbought: 77 +- RSI Oversold: 10 +- Take Profit: 116 pips +- Stop Loss: 247 pips +- Max Lot Size: 0.1 +- Max Spread: 1000 +- Max Duration: 67 +- RSI Exit Level: 40 +- Use Stop Loss: true +- Use Take Profit: false +- Use RSI Exit: true + +**Performance Metrics (2021-2025):** +| Metric | Value | +|--------|-------| +| Total Net Profit | $955.26 | +| Gross Profit | $1,961.62 | +| Gross Loss | -$1,006.36 | +| Profit Factor | 1.95 | +| Recovery Factor | 6.70 | +| Expected Payoff | $12.09 | +| Sharpe Ratio | 6.99 | +| AHPR | 1.0132 (1.32%) | +| GHPR | 1.0121 (1.21%) | + +**Trade Statistics (2021-2025):** +| Statistic | Value | +|-----------|-------| +| History Quality | 100% | +| Total Bars | 105,824 | +| Total Ticks | 6,218,391 | +| Total Trades | 79 | +| Total Deals | 158 | +| Profit Trades | 40 (50.63%) | +| Loss Trades | 39 (49.37%) | +| Short Trades Won | 50.00% | +| Long Trades Won | 100.00% | +| Largest Profit Trade | $129.78 | +| Largest Loss Trade | -$30.90 | +| Average Profit Trade | $49.04 | +| Average Loss Trade | -$25.80 | +| Max Consecutive Wins | 5 ($223.98) | +| Max Consecutive Losses | 3 (-$79.60) | + +**Drawdown Analysis (2021-2025):** +| Metric | Value | +|--------|-------| +| Balance Drawdown Absolute | $0.00 | +| Equity Drawdown Absolute | $2.51 | +| Balance Drawdown Maximal | $113.91 (7.03%) | +| Equity Drawdown Maximal | $142.61 (8.74%) | +| Balance Drawdown Relative | 13.81% ($98.24) | +| Equity Drawdown Relative | 16.97% ($122.14) | + +**Balance Sheet (2021-2025):** +![RSI Reversal Asian EURUSD Balance Sheet](RSIReversalAsianEURUSD/test-balance.jpg) + +### 7. EMA Crossover BTC + +A strategy specifically designed for Bitcoin (BTCUSD) using EMA crossovers with advanced scoring and position management. + +**Key Features:** +- EMA crossover detection +- Advanced scoring system +- Trailing stop management +- Position scaling and reversal capabilities +- Dynamic distance thresholds +- Decay multiplier for trend strength + +**Strategy Settings:** +- Symbol: BTCUSD +- Period: H1 (2021.01.01 - 2025.04.03) +- Magic Number: 42 +- Score Threshold: 15000 +- Slope Threshold: 3500 +- Max Score: 25000 +- Cooldown Minutes: 18 +- Trade Cooldown Minutes: 44 +- EMA Time Frame: 16385 +- Delay Clamp Absolute: 5000 +- EMA Period: 139 +- Cross Over Step: 2500 +- Slope Threshold Step: 2000 +- EMA Distance Step: 500 +- EMA Decay Step: 0 +- Decay Multiplier: 0.08 +- Distance Threshold: 7100 +- ATR Multiplier: 3.9 +- Trailing Stop: 10 +- Apply Trailing Stop: true +- Max Crossover Trades: 14 +- Max Drawdown: 10% +- Minimum Lot Size: 0.01 +- Max Time in Position: 1 hour +- Trade Length Threshold: 31 +- Reverse TP: 707 +- Reverse Lot Size Multiplier: 4 +- Secondary Position Hold Time: 75 + +**Performance Metrics (2021-2025):** +| Metric | Value | +|--------|-------| +| Total Net Profit | $632.96 | +| Gross Profit | $849.93 | +| Gross Loss | -$216.97 | +| Profit Factor | 3.92 | +| Recovery Factor | 7.47 | +| Expected Payoff | $0.58 | +| Sharpe Ratio | 7.57 | +| AHPR | 1.0007 (0.07%) | +| GHPR | 1.0007 (0.07%) | + +**Trade Statistics (2021-2025):** +| Statistic | Value | +|-----------|-------| +| History Quality | 98% | +| Total Bars | 35,791 | +| Total Ticks | 8,380,510 | +| Balance Drawdown Absolute | $0.00 | +| Equity Drawdown Absolute | $2.72 | +| Balance Drawdown Maximal | $41.94 (3.52%) | +| Equity Drawdown Maximal | $84.72 (7.13%) | +| Balance Drawdown Relative | 3.52% ($41.94) | +| Equity Drawdown Relative | 7.13% ($84.72) | + +**Balance Sheet (2021-2025):** +![EMA Crossover BTC Balance Sheet](EMACrossOverBTC/test-balance.jpg) + ## Technical Details Each EA is implemented in MQL5 and includes: - Custom strategy implementation diff --git a/RSIReversalAsianAUDUSD/main.mq5 b/RSIReversalAsianAUDUSD/main.mq5 new file mode 100644 index 0000000..96471dc --- /dev/null +++ b/RSIReversalAsianAUDUSD/main.mq5 @@ -0,0 +1,457 @@ +//+------------------------------------------------------------------+ +//| SimpleRSIReversalAUDUSD.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 trade class +#include + +// Input parameters +input int RSIPeriod = 14; // RSI period +input double OverboughtLevel = 67; // Overbought level +input double OversoldLevel = 17; // Oversold level +input int TakeProfitPips = 253; // Take profit in pips +input int StopLossPips = 429; // Stop loss in pips +input double MaxLotSize = 0.1; // Maximum lot size +input int MaxSpread = 1000; // Maximum allowed spread in pips +input int MaxDuration = 81; // Maximum trade duration in hours +input bool UseStopLoss = false; // Use stop loss +input bool UseTakeProfit = true; // Use take profit +input bool UseRSIExit = true; // Use RSI for exit +input double RSIExitLevel = 49; // RSI level to exit (50 = neutral) +input bool CloseOutsideSession = false; // Close trades outside Asian session +input color PanelBackground = clrBlack; // Panel background color +input color PanelText = clrWhite; // Panel text color +input int PanelX = 10; // Panel X position +input int PanelY = 20; // Panel Y position + +// Global variables +CTrade trade; +int rsiHandle; +bool isPositionOpen = false; +double positionOpenPrice = 0; +datetime positionOpenTime = 0; +ENUM_POSITION_TYPE lastPositionType = POSITION_TYPE_BUY; +bool sessionCloseAttempted = false; // Track if we've attempted to close positions for current session + +// Panel objects +string panelName = "RSIPanel"; +int panelWidth = 200; +int panelHeight = 200; +int labelHeight = 20; +int labelSpacing = 5; + +// Session times (UTC) +const int AsianSessionStart = 0; // 00:00 UTC +const int AsianSessionEnd = 8; // 08:00 UTC + +//+------------------------------------------------------------------+ +//| Create panel | +//+------------------------------------------------------------------+ +void CreatePanel() +{ + // Create panel background + ObjectCreate(0, panelName, OBJ_RECTANGLE_LABEL, 0, 0, 0); + ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, PanelX); + ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, PanelY); + ObjectSetInteger(0, panelName, OBJPROP_XSIZE, panelWidth); + ObjectSetInteger(0, panelName, OBJPROP_YSIZE, panelHeight); + ObjectSetInteger(0, panelName, OBJPROP_BGCOLOR, PanelBackground); + ObjectSetInteger(0, panelName, OBJPROP_BORDER_TYPE, BORDER_FLAT); + ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, panelName, OBJPROP_COLOR, PanelText); + ObjectSetInteger(0, panelName, OBJPROP_STYLE, STYLE_SOLID); + ObjectSetInteger(0, panelName, OBJPROP_WIDTH, 1); + ObjectSetInteger(0, panelName, OBJPROP_BACK, false); + ObjectSetInteger(0, panelName, OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, panelName, OBJPROP_SELECTED, false); + ObjectSetInteger(0, panelName, OBJPROP_HIDDEN, true); + ObjectSetInteger(0, panelName, OBJPROP_ZORDER, 0); + + // Create title label + ObjectCreate(0, panelName + "Title", OBJ_LABEL, 0, 0, 0); + ObjectSetInteger(0, panelName + "Title", OBJPROP_XDISTANCE, PanelX + 5); + ObjectSetInteger(0, panelName + "Title", OBJPROP_YDISTANCE, PanelY + 5); + ObjectSetInteger(0, panelName + "Title", OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetString(0, panelName + "Title", OBJPROP_TEXT, "RSI Reversal"); + ObjectSetInteger(0, panelName + "Title", OBJPROP_COLOR, PanelText); + ObjectSetInteger(0, panelName + "Title", OBJPROP_FONTSIZE, 10); + + // Create score labels + CreateScoreLabel("RSI", "RSI: ", 0); + CreateScoreLabel("Position", "Position: ", 1); + CreateScoreLabel("Spread", "Spread: ", 2); + CreateScoreLabel("Session", "Session: ", 3); + CreateScoreLabel("SL", "Stop Loss: ", 4); + CreateScoreLabel("TP", "Take Profit: ", 5); +} + +//+------------------------------------------------------------------+ +//| Create score label | +//+------------------------------------------------------------------+ +void CreateScoreLabel(string name, string text, int index) +{ + ObjectCreate(0, panelName + name, OBJ_LABEL, 0, 0, 0); + ObjectSetInteger(0, panelName + name, OBJPROP_XDISTANCE, PanelX + 5); + ObjectSetInteger(0, panelName + name, OBJPROP_YDISTANCE, PanelY + 30 + index * (labelHeight + labelSpacing)); + ObjectSetInteger(0, panelName + name, OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetString(0, panelName + name, OBJPROP_TEXT, text); + ObjectSetInteger(0, panelName + name, OBJPROP_COLOR, PanelText); + ObjectSetInteger(0, panelName + name, OBJPROP_FONTSIZE, 8); +} + +//+------------------------------------------------------------------+ +//| Update panel values | +//+------------------------------------------------------------------+ +void UpdatePanel(double rsi, string position, int spread, string session, double sl, double tp) +{ + ObjectSetString(0, panelName + "RSI", OBJPROP_TEXT, "RSI: " + DoubleToString(rsi, 2)); + ObjectSetString(0, panelName + "Position", OBJPROP_TEXT, "Position: " + position); + ObjectSetString(0, panelName + "Spread", OBJPROP_TEXT, "Spread: " + IntegerToString(spread) + " pips"); + ObjectSetString(0, panelName + "Session", OBJPROP_TEXT, "Session: " + session); + ObjectSetString(0, panelName + "SL", OBJPROP_TEXT, "Stop Loss: " + IntegerToString(StopLossPips) + " pips"); + ObjectSetString(0, panelName + "TP", OBJPROP_TEXT, "Take Profit: " + IntegerToString(TakeProfitPips) + " pips"); +} + +//+------------------------------------------------------------------+ +//| Check if current time is in Asian session | +//+------------------------------------------------------------------+ +bool IsAsianSession() +{ + datetime currentTime = TimeCurrent(); + MqlDateTime timeStruct; + TimeToStruct(currentTime, timeStruct); + + return (timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd); +} + +//+------------------------------------------------------------------+ +//| Get current session name | +//+------------------------------------------------------------------+ +string GetCurrentSession() +{ + datetime currentTime = TimeCurrent(); + MqlDateTime timeStruct; + TimeToStruct(currentTime, timeStruct); + + if(timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd) + return "Asian"; + else if(timeStruct.hour >= 8 && timeStruct.hour < 16) + return "London"; + else if(timeStruct.hour >= 13 && timeStruct.hour < 21) + return "New York"; + else + return "Other"; +} + +//+------------------------------------------------------------------+ +//| Check if trading is allowed | +//+------------------------------------------------------------------+ +bool IsTradingAllowed() +{ + // Check if market is open + if(!SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_FULL) + { + Print("Trading is not allowed for ", _Symbol); + return false; + } + + // Check if we have enough money + if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0) + { + Print("Not enough free margin"); + return false; + } + + return true; +} + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + // Initialize RSI indicator + rsiHandle = iRSI(_Symbol, PERIOD_M15, RSIPeriod, PRICE_CLOSE); + + if(rsiHandle == INVALID_HANDLE) + { + Print("Failed to create RSI indicator handle"); + return(INIT_FAILED); + } + + // Create panel + CreatePanel(); + + Print("Expert Advisor initialized successfully"); + Print("Trading symbol: ", _Symbol); + Print("Account balance: ", AccountInfoDouble(ACCOUNT_BALANCE)); + Print("Account leverage: ", AccountInfoInteger(ACCOUNT_LEVERAGE)); + + return(INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + // Release indicator handles + IndicatorRelease(rsiHandle); + + // Remove panel objects + ObjectsDeleteAll(0, panelName); +} + +//+------------------------------------------------------------------+ +//| Close all trades for the current symbol | +//+------------------------------------------------------------------+ +bool CloseAllTrades(string reason = "") +{ + bool allClosed = true; + int totalPositions = PositionsTotal(); + + if(totalPositions == 0) + return true; + + Print("Attempting to close all positions", (reason != "" ? " - " + reason : "")); + + for(int i = totalPositions - 1; i >= 0; i--) + { + if(PositionGetSymbol(i) == _Symbol) + { + // Try to close position with retry logic + int retryCount = 0; + bool positionClosed = false; + + while(retryCount < 3 && !positionClosed) + { + if(trade.PositionClose(_Symbol)) + { + Print("Position closed successfully"); + isPositionOpen = false; + positionClosed = true; + } + else + { + int error = GetLastError(); + Print("Failed to close position. Error: ", error, " Retry: ", retryCount + 1); + + // If error is 4756 (Trade disabled), wait longer before retry + if(error == 4756) + { + Sleep(5000); // Wait 5 seconds before retry + retryCount++; + } + else + { + // For other errors, break the loop + break; + } + } + } + + if(!positionClosed) + { + Print("Failed to close position after all retries"); + allClosed = false; + } + } + } + + return allClosed; +} + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() +{ + // Check if trading is allowed + if(!IsTradingAllowed()) + { + Print("Trading is not allowed at the moment"); + return; + } + + // Check if we're in Asian session + if(!IsAsianSession()) + { + Print("Not in Asian session"); + + // Close all positions if outside Asian session and CloseOutsideSession is true + if(CloseOutsideSession && !sessionCloseAttempted) + { + CloseAllTrades("Outside Asian session"); + sessionCloseAttempted = true; + } + return; + } + else + { + // Reset the session close attempt flag when we enter Asian session + sessionCloseAttempted = false; + } + + // Get current spread + double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID); + int spreadInPips = (int)(spread / _Point); + + // Check if spread is too high + if(spreadInPips > MaxSpread) + { + Print("Spread too high: ", spreadInPips, " pips"); + return; + } + + // Get RSI value + double rsi[]; + ArraySetAsSeries(rsi, true); + + if(CopyBuffer(rsiHandle, 0, 0, 1, rsi) != 1) + return; + + // Get current prices + double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID); + double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK); + + // Get position status + string positionStatus = "None"; + for(int i = 0; i < PositionsTotal(); i++) + { + if(PositionGetSymbol(i) == _Symbol) + { + ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + positionStatus = (posType == POSITION_TYPE_BUY) ? "Long" : "Short"; + break; + } + } + + // Calculate stop loss and take profit levels + double sl = 0; + double tp = 0; + + // Update panel + UpdatePanel(rsi[0], positionStatus, spreadInPips, GetCurrentSession(), sl, tp); + + // Check for open position + bool hasOpenPosition = false; + for(int i = 0; i < PositionsTotal(); i++) + { + if(PositionGetSymbol(i) == _Symbol) + { + hasOpenPosition = true; + + // Get position details + double positionProfit = PositionGetDouble(POSITION_PROFIT); + double positionVolume = PositionGetDouble(POSITION_VOLUME); + double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); + ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + + // Check for RSI exit if enabled + if(UseRSIExit) + { + bool shouldExit = false; + + // For long positions, exit when RSI reaches or exceeds exit level + if(posType == POSITION_TYPE_BUY && rsi[0] >= RSIExitLevel) + { + Print("Closing long position due to RSI exit. RSI: ", rsi[0], " Exit Level: ", RSIExitLevel); + shouldExit = true; + } + // For short positions, exit when RSI reaches or falls below exit level + else if(posType == POSITION_TYPE_SELL && rsi[0] <= RSIExitLevel) + { + Print("Closing short position due to RSI exit. RSI: ", rsi[0], " Exit Level: ", RSIExitLevel); + shouldExit = true; + } + + if(shouldExit) + { + CloseAllTrades("RSI Exit"); + return; + } + } + + // Check for timeout + if(TimeCurrent() - positionOpenTime > MaxDuration * 3600) + { + Print("Closing position due to timeout"); + CloseAllTrades("Timeout"); + return; + } + + break; + } + } + + // If no position is open, look for entry signals + if(!hasOpenPosition) + { + // Place buy order if RSI is oversold + if(rsi[0] <= OversoldLevel) + { + double sl = UseStopLoss ? currentBid - StopLossPips * _Point : 0; + double tp = UseTakeProfit ? currentBid + TakeProfitPips * _Point : 0; + + if(UseStopLoss && sl >= currentBid) + return; + if(UseTakeProfit && tp <= currentBid) + return; + + // Set trade parameters + trade.SetDeviationInPoints(3); + trade.SetTypeFilling(ORDER_FILLING_IOC); + trade.SetExpertMagicNumber(123456); + + // Place buy order using CTrade + if(!trade.Buy(MaxLotSize, _Symbol, currentAsk, sl, tp, "RSI Buy")) + { + Print("Buy order failed. Error code: ", GetLastError()); + } + else + { + Print("Buy order placed. RSI: ", rsi[0]); + isPositionOpen = true; + positionOpenPrice = currentAsk; + positionOpenTime = TimeCurrent(); + lastPositionType = POSITION_TYPE_BUY; + } + } + // Place sell order if RSI is overbought + else if(rsi[0] >= OverboughtLevel) + { + double sl = UseStopLoss ? currentAsk + StopLossPips * _Point : 0; + double tp = UseTakeProfit ? currentAsk - TakeProfitPips * _Point : 0; + + if(UseStopLoss && sl <= currentAsk) + return; + if(UseTakeProfit && tp >= currentAsk) + return; + + // Set trade parameters + trade.SetDeviationInPoints(3); + trade.SetTypeFilling(ORDER_FILLING_IOC); + trade.SetExpertMagicNumber(123456); + + // Place sell order using CTrade + if(!trade.Sell(MaxLotSize, _Symbol, currentBid, sl, tp, "RSI Sell")) + { + Print("Sell order failed. Error code: ", GetLastError()); + } + else + { + Print("Sell order placed. RSI: ", rsi[0]); + isPositionOpen = true; + positionOpenPrice = currentBid; + positionOpenTime = TimeCurrent(); + lastPositionType = POSITION_TYPE_SELL; + } + } + } +} diff --git a/RSIReversalAsianAUDUSD/test-balance.jpg b/RSIReversalAsianAUDUSD/test-balance.jpg new file mode 100644 index 0000000..1255eba Binary files /dev/null and b/RSIReversalAsianAUDUSD/test-balance.jpg differ diff --git a/RSIReversalAsianEURUSD/main.mq5 b/RSIReversalAsianEURUSD/main.mq5 new file mode 100644 index 0000000..c3ed4a6 --- /dev/null +++ b/RSIReversalAsianEURUSD/main.mq5 @@ -0,0 +1,457 @@ +//+------------------------------------------------------------------+ +//| SimpleRSIReversalAUDUSD.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 trade class +#include + +// Input parameters +input int RSIPeriod = 14; // RSI period +input double OverboughtLevel = 77; // Overbought level +input double OversoldLevel = 10; // Oversold level +input int TakeProfitPips = 116; // Take profit in pips +input int StopLossPips = 247; // Stop loss in pips +input double MaxLotSize = 0.1; // Maximum lot size +input int MaxSpread = 1000; // Maximum allowed spread in pips +input int MaxDuration = 67; // Maximum trade duration in hours +input bool UseStopLoss = true; // Use stop loss +input bool UseTakeProfit = false; // Use take profit +input bool UseRSIExit = true; // Use RSI for exit +input double RSIExitLevel = 40; // RSI level to exit (50 = neutral) +input bool CloseOutsideSession = false; // Close trades outside Asian session +input color PanelBackground = clrBlack; // Panel background color +input color PanelText = clrWhite; // Panel text color +input int PanelX = 10; // Panel X position +input int PanelY = 20; // Panel Y position + +// Global variables +CTrade trade; +int rsiHandle; +bool isPositionOpen = false; +double positionOpenPrice = 0; +datetime positionOpenTime = 0; +ENUM_POSITION_TYPE lastPositionType = POSITION_TYPE_BUY; +bool sessionCloseAttempted = false; // Track if we've attempted to close positions for current session + +// Panel objects +string panelName = "RSIPanel"; +int panelWidth = 200; +int panelHeight = 200; +int labelHeight = 20; +int labelSpacing = 5; + +// Session times (UTC) +const int AsianSessionStart = 0; // 00:00 UTC +const int AsianSessionEnd = 8; // 08:00 UTC + +//+------------------------------------------------------------------+ +//| Create panel | +//+------------------------------------------------------------------+ +void CreatePanel() +{ + // Create panel background + ObjectCreate(0, panelName, OBJ_RECTANGLE_LABEL, 0, 0, 0); + ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, PanelX); + ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, PanelY); + ObjectSetInteger(0, panelName, OBJPROP_XSIZE, panelWidth); + ObjectSetInteger(0, panelName, OBJPROP_YSIZE, panelHeight); + ObjectSetInteger(0, panelName, OBJPROP_BGCOLOR, PanelBackground); + ObjectSetInteger(0, panelName, OBJPROP_BORDER_TYPE, BORDER_FLAT); + ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, panelName, OBJPROP_COLOR, PanelText); + ObjectSetInteger(0, panelName, OBJPROP_STYLE, STYLE_SOLID); + ObjectSetInteger(0, panelName, OBJPROP_WIDTH, 1); + ObjectSetInteger(0, panelName, OBJPROP_BACK, false); + ObjectSetInteger(0, panelName, OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, panelName, OBJPROP_SELECTED, false); + ObjectSetInteger(0, panelName, OBJPROP_HIDDEN, true); + ObjectSetInteger(0, panelName, OBJPROP_ZORDER, 0); + + // Create title label + ObjectCreate(0, panelName + "Title", OBJ_LABEL, 0, 0, 0); + ObjectSetInteger(0, panelName + "Title", OBJPROP_XDISTANCE, PanelX + 5); + ObjectSetInteger(0, panelName + "Title", OBJPROP_YDISTANCE, PanelY + 5); + ObjectSetInteger(0, panelName + "Title", OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetString(0, panelName + "Title", OBJPROP_TEXT, "RSI Reversal"); + ObjectSetInteger(0, panelName + "Title", OBJPROP_COLOR, PanelText); + ObjectSetInteger(0, panelName + "Title", OBJPROP_FONTSIZE, 10); + + // Create score labels + CreateScoreLabel("RSI", "RSI: ", 0); + CreateScoreLabel("Position", "Position: ", 1); + CreateScoreLabel("Spread", "Spread: ", 2); + CreateScoreLabel("Session", "Session: ", 3); + CreateScoreLabel("SL", "Stop Loss: ", 4); + CreateScoreLabel("TP", "Take Profit: ", 5); +} + +//+------------------------------------------------------------------+ +//| Create score label | +//+------------------------------------------------------------------+ +void CreateScoreLabel(string name, string text, int index) +{ + ObjectCreate(0, panelName + name, OBJ_LABEL, 0, 0, 0); + ObjectSetInteger(0, panelName + name, OBJPROP_XDISTANCE, PanelX + 5); + ObjectSetInteger(0, panelName + name, OBJPROP_YDISTANCE, PanelY + 30 + index * (labelHeight + labelSpacing)); + ObjectSetInteger(0, panelName + name, OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetString(0, panelName + name, OBJPROP_TEXT, text); + ObjectSetInteger(0, panelName + name, OBJPROP_COLOR, PanelText); + ObjectSetInteger(0, panelName + name, OBJPROP_FONTSIZE, 8); +} + +//+------------------------------------------------------------------+ +//| Update panel values | +//+------------------------------------------------------------------+ +void UpdatePanel(double rsi, string position, int spread, string session, double sl, double tp) +{ + ObjectSetString(0, panelName + "RSI", OBJPROP_TEXT, "RSI: " + DoubleToString(rsi, 2)); + ObjectSetString(0, panelName + "Position", OBJPROP_TEXT, "Position: " + position); + ObjectSetString(0, panelName + "Spread", OBJPROP_TEXT, "Spread: " + IntegerToString(spread) + " pips"); + ObjectSetString(0, panelName + "Session", OBJPROP_TEXT, "Session: " + session); + ObjectSetString(0, panelName + "SL", OBJPROP_TEXT, "Stop Loss: " + IntegerToString(StopLossPips) + " pips"); + ObjectSetString(0, panelName + "TP", OBJPROP_TEXT, "Take Profit: " + IntegerToString(TakeProfitPips) + " pips"); +} + +//+------------------------------------------------------------------+ +//| Check if current time is in Asian session | +//+------------------------------------------------------------------+ +bool IsAsianSession() +{ + datetime currentTime = TimeCurrent(); + MqlDateTime timeStruct; + TimeToStruct(currentTime, timeStruct); + + return (timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd); +} + +//+------------------------------------------------------------------+ +//| Get current session name | +//+------------------------------------------------------------------+ +string GetCurrentSession() +{ + datetime currentTime = TimeCurrent(); + MqlDateTime timeStruct; + TimeToStruct(currentTime, timeStruct); + + if(timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd) + return "Asian"; + else if(timeStruct.hour >= 8 && timeStruct.hour < 16) + return "London"; + else if(timeStruct.hour >= 13 && timeStruct.hour < 21) + return "New York"; + else + return "Other"; +} + +//+------------------------------------------------------------------+ +//| Check if trading is allowed | +//+------------------------------------------------------------------+ +bool IsTradingAllowed() +{ + // Check if market is open + if(!SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_FULL) + { + Print("Trading is not allowed for ", _Symbol); + return false; + } + + // Check if we have enough money + if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0) + { + Print("Not enough free margin"); + return false; + } + + return true; +} + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + // Initialize RSI indicator + rsiHandle = iRSI(_Symbol, PERIOD_M15, RSIPeriod, PRICE_CLOSE); + + if(rsiHandle == INVALID_HANDLE) + { + Print("Failed to create RSI indicator handle"); + return(INIT_FAILED); + } + + // Create panel + CreatePanel(); + + Print("Expert Advisor initialized successfully"); + Print("Trading symbol: ", _Symbol); + Print("Account balance: ", AccountInfoDouble(ACCOUNT_BALANCE)); + Print("Account leverage: ", AccountInfoInteger(ACCOUNT_LEVERAGE)); + + return(INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + // Release indicator handles + IndicatorRelease(rsiHandle); + + // Remove panel objects + ObjectsDeleteAll(0, panelName); +} + +//+------------------------------------------------------------------+ +//| Close all trades for the current symbol | +//+------------------------------------------------------------------+ +bool CloseAllTrades(string reason = "") +{ + bool allClosed = true; + int totalPositions = PositionsTotal(); + + if(totalPositions == 0) + return true; + + Print("Attempting to close all positions", (reason != "" ? " - " + reason : "")); + + for(int i = totalPositions - 1; i >= 0; i--) + { + if(PositionGetSymbol(i) == _Symbol) + { + // Try to close position with retry logic + int retryCount = 0; + bool positionClosed = false; + + while(retryCount < 3 && !positionClosed) + { + if(trade.PositionClose(_Symbol)) + { + Print("Position closed successfully"); + isPositionOpen = false; + positionClosed = true; + } + else + { + int error = GetLastError(); + Print("Failed to close position. Error: ", error, " Retry: ", retryCount + 1); + + // If error is 4756 (Trade disabled), wait longer before retry + if(error == 4756) + { + Sleep(5000); // Wait 5 seconds before retry + retryCount++; + } + else + { + // For other errors, break the loop + break; + } + } + } + + if(!positionClosed) + { + Print("Failed to close position after all retries"); + allClosed = false; + } + } + } + + return allClosed; +} + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() +{ + // Check if trading is allowed + if(!IsTradingAllowed()) + { + Print("Trading is not allowed at the moment"); + return; + } + + // Check if we're in Asian session + if(!IsAsianSession()) + { + Print("Not in Asian session"); + + // Close all positions if outside Asian session and CloseOutsideSession is true + if(CloseOutsideSession && !sessionCloseAttempted) + { + CloseAllTrades("Outside Asian session"); + sessionCloseAttempted = true; + } + return; + } + else + { + // Reset the session close attempt flag when we enter Asian session + sessionCloseAttempted = false; + } + + // Get current spread + double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID); + int spreadInPips = (int)(spread / _Point); + + // Check if spread is too high + if(spreadInPips > MaxSpread) + { + Print("Spread too high: ", spreadInPips, " pips"); + return; + } + + // Get RSI value + double rsi[]; + ArraySetAsSeries(rsi, true); + + if(CopyBuffer(rsiHandle, 0, 0, 1, rsi) != 1) + return; + + // Get current prices + double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID); + double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK); + + // Get position status + string positionStatus = "None"; + for(int i = 0; i < PositionsTotal(); i++) + { + if(PositionGetSymbol(i) == _Symbol) + { + ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + positionStatus = (posType == POSITION_TYPE_BUY) ? "Long" : "Short"; + break; + } + } + + // Calculate stop loss and take profit levels + double sl = 0; + double tp = 0; + + // Update panel + UpdatePanel(rsi[0], positionStatus, spreadInPips, GetCurrentSession(), sl, tp); + + // Check for open position + bool hasOpenPosition = false; + for(int i = 0; i < PositionsTotal(); i++) + { + if(PositionGetSymbol(i) == _Symbol) + { + hasOpenPosition = true; + + // Get position details + double positionProfit = PositionGetDouble(POSITION_PROFIT); + double positionVolume = PositionGetDouble(POSITION_VOLUME); + double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); + ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + + // Check for RSI exit if enabled + if(UseRSIExit) + { + bool shouldExit = false; + + // For long positions, exit when RSI reaches or exceeds exit level + if(posType == POSITION_TYPE_BUY && rsi[0] >= RSIExitLevel) + { + Print("Closing long position due to RSI exit. RSI: ", rsi[0], " Exit Level: ", RSIExitLevel); + shouldExit = true; + } + // For short positions, exit when RSI reaches or falls below exit level + else if(posType == POSITION_TYPE_SELL && rsi[0] <= RSIExitLevel) + { + Print("Closing short position due to RSI exit. RSI: ", rsi[0], " Exit Level: ", RSIExitLevel); + shouldExit = true; + } + + if(shouldExit) + { + CloseAllTrades("RSI Exit"); + return; + } + } + + // Check for timeout + if(TimeCurrent() - positionOpenTime > MaxDuration * 3600) + { + Print("Closing position due to timeout"); + CloseAllTrades("Timeout"); + return; + } + + break; + } + } + + // If no position is open, look for entry signals + if(!hasOpenPosition) + { + // Place buy order if RSI is oversold + if(rsi[0] <= OversoldLevel) + { + double sl = UseStopLoss ? currentBid - StopLossPips * _Point : 0; + double tp = UseTakeProfit ? currentBid + TakeProfitPips * _Point : 0; + + if(UseStopLoss && sl >= currentBid) + return; + if(UseTakeProfit && tp <= currentBid) + return; + + // Set trade parameters + trade.SetDeviationInPoints(3); + trade.SetTypeFilling(ORDER_FILLING_IOC); + trade.SetExpertMagicNumber(123456); + + // Place buy order using CTrade + if(!trade.Buy(MaxLotSize, _Symbol, currentAsk, sl, tp, "RSI Buy")) + { + Print("Buy order failed. Error code: ", GetLastError()); + } + else + { + Print("Buy order placed. RSI: ", rsi[0]); + isPositionOpen = true; + positionOpenPrice = currentAsk; + positionOpenTime = TimeCurrent(); + lastPositionType = POSITION_TYPE_BUY; + } + } + // Place sell order if RSI is overbought + else if(rsi[0] >= OverboughtLevel) + { + double sl = UseStopLoss ? currentAsk + StopLossPips * _Point : 0; + double tp = UseTakeProfit ? currentAsk - TakeProfitPips * _Point : 0; + + if(UseStopLoss && sl <= currentAsk) + return; + if(UseTakeProfit && tp >= currentAsk) + return; + + // Set trade parameters + trade.SetDeviationInPoints(3); + trade.SetTypeFilling(ORDER_FILLING_IOC); + trade.SetExpertMagicNumber(123456); + + // Place sell order using CTrade + if(!trade.Sell(MaxLotSize, _Symbol, currentBid, sl, tp, "RSI Sell")) + { + Print("Sell order failed. Error code: ", GetLastError()); + } + else + { + Print("Sell order placed. RSI: ", rsi[0]); + isPositionOpen = true; + positionOpenPrice = currentBid; + positionOpenTime = TimeCurrent(); + lastPositionType = POSITION_TYPE_SELL; + } + } + } +} diff --git a/RSIReversalAsianEURUSD/test-balance.jpg b/RSIReversalAsianEURUSD/test-balance.jpg new file mode 100644 index 0000000..c3695c2 Binary files /dev/null and b/RSIReversalAsianEURUSD/test-balance.jpg differ