#property strict #property version "1.00" #include input group "=== Market ===" input string InpSymbol = "BTCUSD"; input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15; input double InpLots = 0.01; input int InpSlippagePoints = 30; input int InpMagic = 930101; input int InpMaxPositions = 6; input bool InpDebugLogs = true; input group "=== EMA Trend State ===" input int InpEmaPeriod = 200; input int InpTrendLookbackBars = 12; input double InpTrendMinPoints = 120; // total EMA delta over lookback input double InpFlatMaxPoints = 40; // dead-flat band over lookback input group "=== RSI Entries ===" input int InpRsiPeriod = 14; input double InpRsiDipLevel = 35.0; // buy dip in uptrend input double InpRsiSurgeLevel = 65.0; // sell surge in downtrend input bool InpUseCrossSignal = true; // true=cross, false=state-based input group "=== Risk ===" input bool InpUseHardSLTP = false; input double InpSLPoints = 2500; input double InpTPPoints = 4500; enum TrendState { TREND_FLAT = 0, TREND_UP = 1, TREND_DOWN = -1 }; CTrade trade; datetime g_lastBarTime = 0; void DebugLog(const string msg) { if(InpDebugLogs) Print("[EMARSIWarm] ", msg); } bool IsNewBar(const string symbol, ENUM_TIMEFRAMES tf) { datetime t = iTime(symbol, tf, 0); if(t <= 0 || t == g_lastBarTime) return false; g_lastBarTime = t; return true; } double GetIndicatorValue(const int handle, const int bufferIdx, const int shift) { if(handle == INVALID_HANDLE) return 0.0; double v[1]; if(CopyBuffer(handle, bufferIdx, shift, 1, v) <= 0) return 0.0; return v[0]; } double GetEma(const string symbol, ENUM_TIMEFRAMES tf, const int period, const int shift) { int h = iMA(symbol, tf, period, 0, MODE_EMA, PRICE_CLOSE); double val = GetIndicatorValue(h, 0, shift); if(h != INVALID_HANDLE) IndicatorRelease(h); return val; } double GetRsi(const string symbol, ENUM_TIMEFRAMES tf, const int period, const int shift) { int h = iRSI(symbol, tf, period, PRICE_CLOSE); double val = GetIndicatorValue(h, 0, shift); if(h != INVALID_HANDLE) IndicatorRelease(h); return val; } TrendState GetTrendState() { double emaNow = GetEma(InpSymbol, InpTimeframe, InpEmaPeriod, 1); double emaPast = GetEma(InpSymbol, InpTimeframe, InpEmaPeriod, 1 + InpTrendLookbackBars); if(emaNow == 0.0 || emaPast == 0.0) return TREND_FLAT; double deltaPts = (emaNow - emaPast) / _Point; if(MathAbs(deltaPts) <= InpFlatMaxPoints) return TREND_FLAT; if(deltaPts >= InpTrendMinPoints) return TREND_UP; if(deltaPts <= -InpTrendMinPoints) return TREND_DOWN; return TREND_FLAT; } int CountPositionsByMagic(const string symbol, const int magic) { int count = 0; for(int i = PositionsTotal() - 1; i >= 0; --i) { ulong t = PositionGetTicket(i); if(t == 0) continue; if(PositionGetString(POSITION_SYMBOL) == symbol && (int)PositionGetInteger(POSITION_MAGIC) == magic) count++; } return count; } string TrendStateToString(const TrendState s) { if(s == TREND_UP) return "UP"; if(s == TREND_DOWN) return "DOWN"; return "FLAT"; } void CloseAllByMagic(const string symbol, const int magic) { for(int i = PositionsTotal() - 1; i >= 0; --i) { ulong t = PositionGetTicket(i); if(t == 0) continue; if(PositionGetString(POSITION_SYMBOL) == symbol && (int)PositionGetInteger(POSITION_MAGIC) == magic) trade.PositionClose(t); } } void ComputeSLTP(const bool isBuy, const double entry, double &sl, double &tp) { if(!InpUseHardSLTP) { sl = 0.0; tp = 0.0; return; } if(isBuy) { sl = entry - InpSLPoints * _Point; tp = entry + InpTPPoints * _Point; } else { sl = entry + InpSLPoints * _Point; tp = entry - InpTPPoints * _Point; } } bool BuySignal() { double r1 = GetRsi(InpSymbol, InpTimeframe, InpRsiPeriod, 1); double r2 = GetRsi(InpSymbol, InpTimeframe, InpRsiPeriod, 2); if(r1 == 0.0 || r2 == 0.0) return false; if(InpUseCrossSignal) return (r2 > InpRsiDipLevel && r1 <= InpRsiDipLevel); // fresh dip return (r1 <= InpRsiDipLevel); } bool SellSignal() { double r1 = GetRsi(InpSymbol, InpTimeframe, InpRsiPeriod, 1); double r2 = GetRsi(InpSymbol, InpTimeframe, InpRsiPeriod, 2); if(r1 == 0.0 || r2 == 0.0) return false; if(InpUseCrossSignal) return (r2 < InpRsiSurgeLevel && r1 >= InpRsiSurgeLevel); // fresh surge return (r1 >= InpRsiSurgeLevel); } void OnTick() { if(_Symbol != InpSymbol) { static datetime lastMismatchLog = 0; datetime nowBar = iTime(_Symbol, PERIOD_M1, 0); if(nowBar != lastMismatchLog) { lastMismatchLog = nowBar; DebugLog(StringFormat("Skipped: chart symbol=%s but InpSymbol=%s. Attach EA to %s chart or set InpSymbol=%s.", _Symbol, InpSymbol, InpSymbol, _Symbol)); } return; } if(!IsNewBar(InpSymbol, InpTimeframe)) return; TrendState state = GetTrendState(); double rsi1 = GetRsi(InpSymbol, InpTimeframe, InpRsiPeriod, 1); double rsi2 = GetRsi(InpSymbol, InpTimeframe, InpRsiPeriod, 2); int posCount = CountPositionsByMagic(InpSymbol, InpMagic); DebugLog(StringFormat("Bar=%s state=%s rsi1=%.2f rsi2=%.2f positions=%d", TimeToString(iTime(InpSymbol, InpTimeframe, 1), TIME_DATE|TIME_MINUTES), TrendStateToString(state), rsi1, rsi2, posCount)); // Core idea: when EMA is "dead flat", flatten everything. if(state == TREND_FLAT) { DebugLog("Action: EMA flat -> closing all positions for this magic."); CloseAllByMagic(InpSymbol, InpMagic); return; } if(posCount >= InpMaxPositions) { DebugLog(StringFormat("Skipped: max positions reached (%d).", InpMaxPositions)); return; } MqlTick tick; if(!SymbolInfoTick(InpSymbol, tick)) { DebugLog("Skipped: SymbolInfoTick failed."); return; } double sl = 0.0, tp = 0.0; trade.SetExpertMagicNumber(InpMagic); trade.SetDeviationInPoints(InpSlippagePoints); if(state == TREND_UP && BuySignal()) { ComputeSLTP(true, tick.ask, sl, tp); if(trade.Buy(InpLots, InpSymbol, tick.ask, sl, tp, "EMAUp_RSIDip_Buy")) DebugLog(StringFormat("BUY opened lots=%.2f price=%.2f sl=%.2f tp=%.2f", InpLots, tick.ask, sl, tp)); else DebugLog(StringFormat("BUY failed retcode=%d", trade.ResultRetcode())); } else if(state == TREND_DOWN && SellSignal()) { ComputeSLTP(false, tick.bid, sl, tp); if(trade.Sell(InpLots, InpSymbol, tick.bid, sl, tp, "EMADown_RSISurge_Sell")) DebugLog(StringFormat("SELL opened lots=%.2f price=%.2f sl=%.2f tp=%.2f", InpLots, tick.bid, sl, tp)); else DebugLog(StringFormat("SELL failed retcode=%d", trade.ResultRetcode())); } else { if(state == TREND_UP) DebugLog("No entry: UP trend but RSI dip condition not met."); else if(state == TREND_DOWN) DebugLog("No entry: DOWN trend but RSI surge condition not met."); } }