//+------------------------------------------------------------------+ //| RSIScalpingStrategy.mqh | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| RSI Scalping Strategy Data Structure | //+------------------------------------------------------------------+ struct RSIScalpingData { string symbol; bool isInitialized; CTrade trade; int rsi_handle; double rsi_buffer[]; double rsi_prev; double rsi_current; double rsi_two_bars_ago; bool position_open; ulong position_ticket; ENUM_POSITION_TYPE current_position_type; datetime last_bar_time; bool rsi_against_position; int bars_against_count; }; string ErrorDescription(int errorCode) { switch(errorCode) { case 4801: return "Symbol not found"; case 4802: return "Symbol not selected"; case 4803: return "Symbol not visible"; case 4804: return "Symbol not available"; case 4805: return "Cannot load indicator - insufficient history data"; default: return "Unknown error " + IntegerToString(errorCode); } } bool InitRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period, ENUM_APPLIED_PRICE RSI_Applied_Price, int MagicNumber, int Slippage) { data.symbol = symbol; data.isInitialized = false; // Check if symbol exists if(!SymbolSelect(symbol, true)) { Print("RSIScalping: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name."); return false; // Return false but don't fail entire EA } // Wait a bit for symbol to be ready Sleep(100); // Try to create RSI indicator with retry logic (for insufficient history in backtesting) data.rsi_handle = INVALID_HANDLE; int retryCount = 0; int maxRetries = 5; while(retryCount < maxRetries && data.rsi_handle == INVALID_HANDLE) { data.rsi_handle = iRSI(symbol, TimeFrame, RSI_Period, RSI_Applied_Price); if(data.rsi_handle == INVALID_HANDLE) { int error = GetLastError(); // Error 4805 = insufficient history - wait longer and retry if(error == 4805 && retryCount < maxRetries - 1) { Sleep(1000); // Wait 1 second for history to load retryCount++; continue; } Print("RSIScalping: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", ErrorDescription(error), ")"); return false; // Return false but don't fail entire EA } } if(data.rsi_handle == INVALID_HANDLE) { Print("RSIScalping: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries"); return false; } data.trade.SetExpertMagicNumber(MagicNumber); data.trade.SetDeviationInPoints(Slippage); data.trade.SetTypeFilling(ORDER_FILLING_FOK); ArraySetAsSeries(data.rsi_buffer, true); data.position_open = false; data.position_ticket = 0; data.rsi_against_position = false; data.bars_against_count = 0; data.isInitialized = true; Print("RSIScalping: Successfully initialized for symbol '", symbol, "'"); return true; } void DeinitRSIScalping(RSIScalpingData& data) { if(data.rsi_handle != INVALID_HANDLE) IndicatorRelease(data.rsi_handle); } bool UpdateRSI(RSIScalpingData& data) { if(CopyBuffer(data.rsi_handle, 0, 0, 3, data.rsi_buffer) < 3) return false; data.rsi_current = data.rsi_buffer[0]; data.rsi_prev = data.rsi_buffer[1]; data.rsi_two_bars_ago = data.rsi_buffer[2]; return true; } void CheckExistingPosition(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber, double RSI_Oversold, double RSI_Overbought, double RSI_Target_Buy, double RSI_Target_Sell, int BarsToWait) { // Always check if position exists, even if tracking says it doesn't bool positionExists = PositionExistsByMagic(data.symbol, MagicNumber); if(!positionExists && data.position_open) { // Position was closed externally, reset tracking data.position_open = false; data.position_ticket = 0; data.rsi_against_position = false; data.bars_against_count = 0; return; } if(!positionExists) return; // Update tracking if we have a position but tracking was lost if(!data.position_open && positionExists) { ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber); if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber)) { data.position_ticket = ticket; data.position_open = true; data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); } } // Verify our tracked position still exists if(data.position_open && data.position_ticket > 0) { if(!PositionSelectByTicketSymbolAndMagic(data.position_ticket, data.symbol, MagicNumber)) { // Try to find the position again ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber); if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber)) { data.position_ticket = ticket; data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); } else { // Position doesn't exist, reset tracking data.position_open = false; data.position_ticket = 0; data.rsi_against_position = false; data.bars_against_count = 0; return; } } else { // Update position type in case it changed (shouldn't happen, but be safe) data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); } } if(data.current_position_type == POSITION_TYPE_BUY) { if(data.rsi_current < RSI_Oversold) { if(!data.rsi_against_position) { data.rsi_against_position = true; data.bars_against_count = 1; } else { data.bars_against_count++; } if(data.bars_against_count >= BarsToWait) { ClosePosition(data, MagicNumber); return; } } else { if(data.rsi_against_position) { data.rsi_against_position = false; data.bars_against_count = 0; } if(data.rsi_current >= RSI_Target_Buy) { ClosePosition(data, MagicNumber); } } } else if(data.current_position_type == POSITION_TYPE_SELL) { if(data.rsi_current > RSI_Overbought) { if(!data.rsi_against_position) { data.rsi_against_position = true; data.bars_against_count = 1; } else { data.bars_against_count++; } if(data.bars_against_count >= BarsToWait) { ClosePosition(data, MagicNumber); return; } } else { if(data.rsi_against_position) { data.rsi_against_position = false; data.bars_against_count = 0; } if(data.rsi_current <= RSI_Target_Sell) { ClosePosition(data, MagicNumber); } } } } void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber, double RSI_Oversold, double RSI_Overbought, double LotSize) { if(data.rsi_two_bars_ago <= RSI_Oversold && data.rsi_prev > RSI_Oversold) { OpenBuyPosition(data, MagicNumber, LotSize); } if(data.rsi_two_bars_ago >= RSI_Overbought && data.rsi_prev < RSI_Overbought) { OpenSellPosition(data, MagicNumber, LotSize); } } //+------------------------------------------------------------------+ //| Normalize Lot Size According to Symbol Properties | //+------------------------------------------------------------------+ double NormalizeLotSize(string symbol, double lotSize) { double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); // Round to lot step if(lotStep > 0) lotSize = MathFloor(lotSize / lotStep) * lotStep; // Apply min/max constraints if(lotSize < minLot) lotSize = minLot; if(lotSize > maxLot) lotSize = maxLot; return lotSize; } void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize) { if(PositionExistsByMagic(data.symbol, MagicNumber)) return; // Normalize lot size according to symbol properties double normalizedLot = NormalizeLotSize(data.symbol, LotSize); double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK); if(data.trade.Buy(normalizedLot, data.symbol, ask, 0, 0, "RSI Scalping Buy")) { ulong new_ticket = data.trade.ResultOrder(); if(new_ticket > 0) { if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber)) { data.position_ticket = new_ticket; data.position_open = true; data.current_position_type = POSITION_TYPE_BUY; } } } } void OpenSellPosition(RSIScalpingData& data, int MagicNumber, double LotSize) { if(PositionExistsByMagic(data.symbol, MagicNumber)) return; // Normalize lot size according to symbol properties double normalizedLot = NormalizeLotSize(data.symbol, LotSize); double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID); if(data.trade.Sell(normalizedLot, data.symbol, bid, 0, 0, "RSI Scalping Sell")) { ulong new_ticket = data.trade.ResultOrder(); if(new_ticket > 0) { if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber)) { data.position_ticket = new_ticket; data.position_open = true; data.current_position_type = POSITION_TYPE_SELL; } } } } void ClosePosition(RSIScalpingData& data, int MagicNumber) { // First verify position still exists if(!PositionExistsByMagic(data.symbol, MagicNumber)) { // Position doesn't exist, reset tracking data.position_open = false; data.position_ticket = 0; data.rsi_against_position = false; data.bars_against_count = 0; return; } // Try to close by ticket first (more reliable) bool closed = false; if(data.position_ticket > 0) { if(PositionSelectByTicket(data.position_ticket)) { // Verify it's our position if(PositionGetString(POSITION_SYMBOL) == data.symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { closed = data.trade.PositionClose(data.position_ticket); if(!closed) { Print("RSIScalping: Failed to close position by ticket ", data.position_ticket, " - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")"); } } } } // If ticket method failed, try magic number method if(!closed) { closed = ClosePositionByMagic(data.trade, data.symbol, MagicNumber); if(!closed) { Print("RSIScalping: Failed to close position by magic number for '", data.symbol, "' - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")"); } } // Verify position is actually closed if(closed) { // Wait a moment and verify Sleep(50); if(!PositionExistsByMagic(data.symbol, MagicNumber)) { data.position_open = false; data.position_ticket = 0; data.rsi_against_position = false; data.bars_against_count = 0; Print("RSIScalping: Position successfully closed for '", data.symbol, "'"); } else { Print("RSIScalping: Warning - Close returned success but position still exists for '", data.symbol, "'"); // Try one more time Sleep(100); if(PositionExistsByMagic(data.symbol, MagicNumber)) { ClosePositionByMagic(data.trade, data.symbol, MagicNumber); } // Reset tracking anyway to prevent getting stuck data.position_open = false; data.position_ticket = 0; data.rsi_against_position = false; data.bars_against_count = 0; } } else { // Close failed, but reset tracking to prevent getting stuck // The position might have been closed externally data.position_open = false; data.position_ticket = 0; data.rsi_against_position = false; data.bars_against_count = 0; } } void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period, ENUM_APPLIED_PRICE RSI_Applied_Price, double RSI_Overbought, double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell, int BarsToWait, double LotSize, int MagicNumber) { // Skip if not initialized (symbol not available) if(!data.isInitialized) return; data.symbol = symbol; // Update symbol in case it changed if(Bars(data.symbol, TimeFrame) < RSI_Period + 2) return; datetime current_bar_time = iTime(data.symbol, TimeFrame, 0); if(current_bar_time == data.last_bar_time) return; data.last_bar_time = current_bar_time; if(!UpdateRSI(data)) return; CheckExistingPosition(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, RSI_Target_Buy, RSI_Target_Sell, BarsToWait); if(!data.position_open && !PositionExistsByMagic(data.symbol, MagicNumber)) { CheckEntrySignals(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, LotSize); } } //+------------------------------------------------------------------+