472 lines
16 KiB
Plaintext
472 lines
16 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| RSIMidPointHijackStrategy.mqh |
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool IsNewBar(string symbol)
|
|
{
|
|
datetime time[];
|
|
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
|
{
|
|
if(time[0] != rmData.lastBarTime)
|
|
{
|
|
rmData.lastBarTime = time[0];
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsWithinTradingHours(int startHour, int endHour)
|
|
{
|
|
MqlDateTime currentTime;
|
|
TimeToStruct(TimeCurrent(), currentTime);
|
|
|
|
if(startHour <= endHour)
|
|
return (currentTime.hour >= startHour && currentTime.hour < endHour);
|
|
else
|
|
return (currentTime.hour >= startHour || currentTime.hour < endHour);
|
|
}
|
|
|
|
bool HasPosition(string symbol, int magic)
|
|
{
|
|
return PositionExistsByMagic(symbol, magic);
|
|
}
|
|
|
|
bool HasProfitablePosition(int excludeMagic)
|
|
{
|
|
bool hasProfitable = false;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(rmData.positionInfo.SelectByIndex(i))
|
|
{
|
|
if(rmData.positionInfo.Magic() != excludeMagic)
|
|
{
|
|
double profit = rmData.positionInfo.Profit();
|
|
if(profit > RM_InpLockProfitThreshold * _Point)
|
|
{
|
|
hasProfitable = true;
|
|
if(RM_InpCloseOppositeTrades)
|
|
{
|
|
if((excludeMagic == RM_InpMagicNumberRSIFollow && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse) ||
|
|
(excludeMagic == RM_InpMagicNumberRSIReverse && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow) ||
|
|
(excludeMagic == RM_InpMagicNumberEMACross && (rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse || rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow)) ||
|
|
((excludeMagic == RM_InpMagicNumberRSIFollow || excludeMagic == RM_InpMagicNumberRSIReverse) && rmData.positionInfo.Magic() == RM_InpMagicNumberEMACross))
|
|
{
|
|
ClosePosition(rmData.symbol, (int)rmData.positionInfo.Magic());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hasProfitable;
|
|
}
|
|
|
|
bool IsRSIReverseInCooldown(string symbol)
|
|
{
|
|
if(RM_InpRSIReverseCooldownBars <= 0)
|
|
return false;
|
|
|
|
if(!rmData.rsiReverseInCooldown)
|
|
return false;
|
|
|
|
datetime time[];
|
|
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
|
{
|
|
datetime currentBarTime = time[0];
|
|
datetime cooldownEndTime = rmData.rsiReverseLastCloseTime + RM_InpRSIReverseCooldownBars * PeriodSeconds(RM_InpTimeframe);
|
|
|
|
if(currentBarTime >= cooldownEndTime)
|
|
{
|
|
rmData.rsiReverseInCooldown = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CheckRSIFollowStrategy(string symbol)
|
|
{
|
|
if(!IsWithinTradingHours(RM_InpRSIFollowStartHour, RM_InpRSIFollowEndHour))
|
|
{
|
|
if(RM_InpRSIFollowCloseOutsideHours)
|
|
{
|
|
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
|
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIFollow))
|
|
return;
|
|
|
|
if(rmData.lastBarRSI > RM_InpRSIOverbought)
|
|
rmData.rsiOverbought = true;
|
|
else if(rmData.lastBarRSI < RM_InpRSIOversold)
|
|
rmData.rsiOversold = true;
|
|
|
|
if(rmData.rsiOverbought && rmData.lastBarRSI < RM_InpRSIExitLevel)
|
|
{
|
|
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
|
{
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
|
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
|
|
}
|
|
rmData.rsiOverbought = false;
|
|
}
|
|
else if(rmData.rsiOversold && rmData.lastBarRSI > RM_InpRSIExitLevel)
|
|
{
|
|
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
|
{
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
|
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Follow");
|
|
}
|
|
rmData.rsiOversold = false;
|
|
}
|
|
}
|
|
|
|
void CheckRSIReverseStrategy(string symbol)
|
|
{
|
|
if(!IsWithinTradingHours(RM_InpRSIReverseStartHour, RM_InpRSIReverseEndHour))
|
|
{
|
|
if(RM_InpRSIReverseCloseOutsideHours)
|
|
{
|
|
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
|
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIReverse))
|
|
return;
|
|
|
|
if(IsRSIReverseInCooldown(symbol))
|
|
return;
|
|
|
|
if(rmData.lastBarRSIReverse > RM_InpRSIReverseOverbought)
|
|
rmData.rsiReverseOverbought = true;
|
|
else if(rmData.lastBarRSIReverse < RM_InpRSIReverseOversold)
|
|
rmData.rsiReverseOversold = true;
|
|
|
|
if(rmData.rsiReverseOverbought && rmData.lastBarRSIReverse < RM_InpRSIReverseCrossLevel)
|
|
{
|
|
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
|
{
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
|
|
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
|
|
}
|
|
rmData.rsiReverseOverbought = false;
|
|
}
|
|
else if(rmData.rsiReverseOversold && rmData.lastBarRSIReverse > RM_InpRSIReverseCrossLevel)
|
|
{
|
|
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
|
{
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
|
|
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "RSI Reverse");
|
|
}
|
|
rmData.rsiReverseOversold = false;
|
|
}
|
|
}
|
|
|
|
void CheckEMACrossStrategy(string symbol)
|
|
{
|
|
if(!IsWithinTradingHours(RM_InpEMACrossStartHour, RM_InpEMACrossEndHour))
|
|
{
|
|
if(RM_InpEMACrossCloseOutsideHours)
|
|
{
|
|
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
|
|
ClosePosition(symbol, RM_InpMagicNumberEMACross);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberEMACross))
|
|
return;
|
|
|
|
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
|
|
{
|
|
rmData.emaCrossBuySignal = true;
|
|
rmData.emaCrossSellSignal = false;
|
|
rmData.emaCrossSignalBar = 0;
|
|
}
|
|
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
|
|
{
|
|
rmData.emaCrossSellSignal = true;
|
|
rmData.emaCrossBuySignal = false;
|
|
rmData.emaCrossSignalBar = 0;
|
|
}
|
|
|
|
if(RM_InpUseEMADistanceEntry)
|
|
{
|
|
if(rmData.emaCrossBuySignal)
|
|
{
|
|
bool distanceConditionMet = true;
|
|
double emaHistory[], closeHistory[];
|
|
ArraySetAsSeries(emaHistory, true);
|
|
ArraySetAsSeries(closeHistory, true);
|
|
|
|
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
|
|
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
|
|
{
|
|
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
|
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
|
|
{
|
|
double distance = (closeHistory[i] - emaHistory[i]) / point;
|
|
if(distance < RM_InpEMADistancePips)
|
|
{
|
|
distanceConditionMet = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
|
|
{
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
|
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
|
|
rmData.emaCrossBuySignal = false;
|
|
}
|
|
}
|
|
}
|
|
else if(rmData.emaCrossSellSignal)
|
|
{
|
|
bool distanceConditionMet = true;
|
|
double emaHistory[], closeHistory[];
|
|
ArraySetAsSeries(emaHistory, true);
|
|
ArraySetAsSeries(closeHistory, true);
|
|
|
|
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
|
|
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
|
|
{
|
|
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
|
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
|
|
{
|
|
double distance = (emaHistory[i] - closeHistory[i]) / point;
|
|
if(distance < RM_InpEMADistancePips)
|
|
{
|
|
distanceConditionMet = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
|
|
{
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
|
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross Distance");
|
|
rmData.emaCrossSellSignal = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
|
|
{
|
|
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
|
|
{
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
|
rmData.trade.Buy(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
|
|
}
|
|
}
|
|
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
|
|
{
|
|
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
|
|
{
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
|
|
rmData.trade.Sell(g_RM_LotSize, symbol, 0, 0, 0, "EMA Cross");
|
|
}
|
|
}
|
|
}
|
|
|
|
if(rmData.emaCrossBuySignal || rmData.emaCrossSellSignal)
|
|
{
|
|
rmData.emaCrossSignalBar++;
|
|
if(rmData.emaCrossSignalBar > RM_InpEMADistancePeriod * 2)
|
|
{
|
|
rmData.emaCrossBuySignal = false;
|
|
rmData.emaCrossSellSignal = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CheckExitConditions(string symbol)
|
|
{
|
|
if(RM_InpEnableRSIFollow)
|
|
{
|
|
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
|
|
{
|
|
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIFollow))
|
|
{
|
|
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSI < RM_InpRSIExitLevel) ||
|
|
(posType == POSITION_TYPE_SELL && rmData.lastBarRSI > RM_InpRSIExitLevel))
|
|
{
|
|
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(RM_InpEnableRSIReverse)
|
|
{
|
|
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
|
|
{
|
|
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIReverse))
|
|
{
|
|
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSIReverse < RM_InpRSIReverseExitLevel) ||
|
|
(posType == POSITION_TYPE_SELL && rmData.lastBarRSIReverse > RM_InpRSIReverseExitLevel))
|
|
{
|
|
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(RM_InpEnableEMACross)
|
|
{
|
|
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
|
|
{
|
|
if(PositionSelectByMagic(symbol, RM_InpMagicNumberEMACross))
|
|
{
|
|
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
if((posType == POSITION_TYPE_BUY && rmData.lastBarEMA > rmData.lastBarClose) ||
|
|
(posType == POSITION_TYPE_SELL && rmData.lastBarEMA < rmData.lastBarClose))
|
|
{
|
|
ClosePosition(symbol, RM_InpMagicNumberEMACross);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClosePosition(string symbol, int magic)
|
|
{
|
|
if(!PositionExistsByMagic(symbol, magic))
|
|
return;
|
|
|
|
ulong ticket = GetPositionTicketByMagic(symbol, magic);
|
|
if(ticket == 0)
|
|
return;
|
|
|
|
if(magic == RM_InpMagicNumberRSIReverse)
|
|
{
|
|
if(PositionSelectByTicketSymbolAndMagic(ticket, symbol, magic))
|
|
{
|
|
datetime time[];
|
|
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
|
{
|
|
rmData.rsiReverseLastCloseTime = time[0];
|
|
double profit = PositionGetDouble(POSITION_PROFIT);
|
|
if(!RM_InpRSIReverseCooldownOnLoss || profit < 0)
|
|
{
|
|
rmData.rsiReverseInCooldown = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ClosePositionByMagic(rmData.trade, symbol, magic);
|
|
}
|
|
|
|
bool InitRSIMidPointHijack(string symbol)
|
|
{
|
|
rmData.symbol = symbol;
|
|
rmData.rsiOverbought = false;
|
|
rmData.rsiOversold = false;
|
|
rmData.rsiReverseOverbought = false;
|
|
rmData.rsiReverseOversold = false;
|
|
rmData.emaCrossBuySignal = false;
|
|
rmData.emaCrossSellSignal = false;
|
|
rmData.emaCrossSignalBar = 0;
|
|
rmData.rsiReverseInCooldown = false;
|
|
rmData.lastBarRSI = 0;
|
|
rmData.lastBarRSIReverse = 0;
|
|
rmData.lastBarEMA = 0;
|
|
rmData.lastBarClose = 0;
|
|
rmData.lastBarEMAPrev = 0;
|
|
rmData.lastBarClosePrev = 0;
|
|
|
|
// Check if symbol exists
|
|
if(!SymbolSelect(symbol, true))
|
|
{
|
|
Print("RSIMidPointHijack: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
|
return false;
|
|
}
|
|
|
|
Sleep(100); // Wait for symbol to be ready
|
|
|
|
rmData.rsiHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIPeriod, PRICE_CLOSE);
|
|
rmData.rsiReverseHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIReversePeriod, PRICE_CLOSE);
|
|
rmData.emaHandle = iMA(symbol, RM_InpTimeframe, RM_InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
if(rmData.rsiHandle == INVALID_HANDLE || rmData.rsiReverseHandle == INVALID_HANDLE || rmData.emaHandle == INVALID_HANDLE)
|
|
{
|
|
Print("RSIMidPointHijack: Error creating indicators for '", symbol, "'");
|
|
return false;
|
|
}
|
|
|
|
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
|
|
rmData.trade.SetMarginMode();
|
|
rmData.trade.SetTypeFillingBySymbol(symbol);
|
|
rmData.trade.SetDeviationInPoints(10);
|
|
|
|
datetime time[];
|
|
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
|
|
rmData.lastBarTime = time[0];
|
|
|
|
rmData.isInitialized = true;
|
|
Print("RSIMidPointHijack: Successfully initialized for symbol '", symbol, "'");
|
|
return true;
|
|
}
|
|
|
|
void DeinitRSIMidPointHijack()
|
|
{
|
|
if(rmData.rsiHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiHandle);
|
|
if(rmData.rsiReverseHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiReverseHandle);
|
|
if(rmData.emaHandle != INVALID_HANDLE) IndicatorRelease(rmData.emaHandle);
|
|
}
|
|
|
|
void ProcessRSIMidPointHijack(string symbol)
|
|
{
|
|
// Skip if not initialized (symbol not available)
|
|
if(!rmData.isInitialized)
|
|
return;
|
|
|
|
rmData.symbol = symbol; // Update symbol in case it changed
|
|
if(!IsNewBar(rmData.symbol))
|
|
return;
|
|
|
|
double rsi[], rsiReverse[], ema[], close[];
|
|
ArraySetAsSeries(rsi, true);
|
|
ArraySetAsSeries(rsiReverse, true);
|
|
ArraySetAsSeries(ema, true);
|
|
ArraySetAsSeries(close, true);
|
|
|
|
rmData.lastBarEMAPrev = rmData.lastBarEMA;
|
|
rmData.lastBarClosePrev = rmData.lastBarClose;
|
|
|
|
if(CopyBuffer(rmData.rsiHandle, 0, 0, 1, rsi) > 0)
|
|
rmData.lastBarRSI = rsi[0];
|
|
|
|
if(CopyBuffer(rmData.rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
|
|
rmData.lastBarRSIReverse = rsiReverse[0];
|
|
|
|
if(CopyBuffer(rmData.emaHandle, 0, 0, 1, ema) > 0)
|
|
rmData.lastBarEMA = ema[0];
|
|
|
|
if(CopyClose(rmData.symbol, RM_InpTimeframe, 0, 1, close) > 0)
|
|
rmData.lastBarClose = close[0];
|
|
|
|
if(RM_InpEnableRSIFollow)
|
|
CheckRSIFollowStrategy(rmData.symbol);
|
|
if(RM_InpEnableRSIReverse)
|
|
CheckRSIReverseStrategy(rmData.symbol);
|
|
if(RM_InpEnableEMACross)
|
|
CheckEMACrossStrategy(rmData.symbol);
|
|
|
|
CheckExitConditions(rmData.symbol);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|