Files
2026-04-24 14:14:09 +02:00

452 lines
14 KiB
Plaintext

//+------------------------------------------------------------------+
//| 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);
}
}
//+------------------------------------------------------------------+