//+------------------------------------------------------------------+ //| TDI_Momentum_Pulse.mq5 | //| Generated by Ruben Works | //| Based on Master Trading Plan v1 | //+------------------------------------------------------------------+ #property copyright "Ruben Works" #property link "" #property version "1.00" // Include Standard Libraries #include #include #include //--- Input Parameters input group "Strategy Settings" input double InpLots = 0.02; // Lot Size (Reduced for 750 pip risk) input int InpStopLoss = 7500; // Stop Loss (Points, 750 Pips - Safe Mode) input int InpTakeProfit = 3000; // Take Profit (Points, 300 Pips) input int InpMagic = 123456; // Magic Number input int InpTimeExitHours = 16; // Max Holding Time (Hours) input group "Trailing Stop Settings" input bool InpUseTrailing = true; // Enable Trailing Stop input int InpTrailingStart = 500; // Start Trailing After Profit (Points) input int InpTrailingDist = 500; // Trailing Distance (Points) input int InpTrailingStep = 50; // Trailing Step (Points) input group "TDI Settings" input int InpRSI_Period = 13; // RSI Period input int InpBand_Period = 34; // Volatility Band Period input double InpBand_Std = 1.6185; // Volatility Band Std Dev input int InpGreen_Period = 2; // RSI Price Line (Green) input int InpRed_Period = 7; // Trade Signal Line (Red) input group "Filter Settings" input int InpEMA_Period = 10; // Momentum Filter (EMA) input int InpTrend_Period = 200; // Trend Filter (SMA) //--- Global Objects CTrade m_trade; CPositionInfo m_position; COrderInfo m_order; //--- Indicator Handles int hRSI; // Base RSI int hTDI_Green; // SMA(2) of RSI int hTDI_Red; // SMA(7) of RSI int hStoch; // Stochastic int hEMA; // Price EMA 10 int hTrend; // Price SMA 200 //--- Buffers (Resizing managed automatically by CopyBuffer) double bufRSI[]; double bufGreen[]; double bufRed[]; double bufStochK[]; double bufStochD[]; double bufEMA[]; double bufTrend[]; //--- Time Management datetime lastBarTime = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // 1. Initialize Trade Object m_trade.SetExpertMagicNumber(InpMagic); m_trade.SetMarginMode(); m_trade.SetTypeFillingBySymbol(Symbol()); // 2. Indicator Handles // --- TDI Construction --- // Base RSI hRSI = iRSI(Symbol(), Period(), InpRSI_Period, PRICE_CLOSE); if(hRSI == INVALID_HANDLE) { Print("Failed to create RSI handle"); return INIT_FAILED; } // Green Line: MA(2) applied to RSI handle hTDI_Green = iMA(Symbol(), Period(), InpGreen_Period, 0, MODE_SMA, hRSI); if(hTDI_Green == INVALID_HANDLE) { Print("Failed to create TDI Green handle"); return INIT_FAILED; } // Red Line: MA(7) applied to RSI handle hTDI_Red = iMA(Symbol(), Period(), InpRed_Period, 0, MODE_SMA, hRSI); if(hTDI_Red == INVALID_HANDLE) { Print("Failed to create TDI Red handle"); return INIT_FAILED; } // Note: We don't strictly *need* the bands for the specific Buy Signal logic (Green Cross Red), // but if you wanted Squeeze logic, we'd add Bands here. For Master Plan v1, Cross is sufficient. // --- Stochastic --- hStoch = iStochastic(Symbol(), Period(), 5, 3, 3, MODE_SMA, STO_LOWHIGH); if(hStoch == INVALID_HANDLE) { Print("Failed to create Stoch handle"); return INIT_FAILED; } // --- Filters --- hEMA = iMA(Symbol(), Period(), InpEMA_Period, 0, MODE_EMA, PRICE_CLOSE); if(hEMA == INVALID_HANDLE) { Print("Failed to create EMA handle"); return INIT_FAILED; } hTrend = iMA(Symbol(), Period(), InpTrend_Period, 0, MODE_SMA, PRICE_CLOSE); if(hTrend == INVALID_HANDLE) { Print("Failed to create Trend handle"); return INIT_FAILED; } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(hRSI); IndicatorRelease(hTDI_Green); IndicatorRelease(hTDI_Red); IndicatorRelease(hStoch); IndicatorRelease(hEMA); IndicatorRelease(hTrend); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // 1. Check For Time Exits (Every Tick) CheckTimeExits(); // 1b. Check Trailing Stop (Every Tick) if(InpUseTrailing) CheckTrailingStop(); // 2. Check for New Bar (Entry Logic) if(!IsNewBar()) return; // 3. Data Buffers Copy (We need Index 1 and 2 for crossovers) // We copy 3 values: [0] current, [1] closed, [2] prev-closed if(CopyBuffer(hTDI_Green, 0, 0, 3, bufGreen) < 3) return; if(CopyBuffer(hTDI_Red, 0, 0, 3, bufRed) < 3) return; if(CopyBuffer(hStoch, 0, 0, 3, bufStochK) < 3) return; if(CopyBuffer(hStoch, 1, 0, 3, bufStochD) < 3) return; // Buffer 1 is Signal line in Stoch if(CopyBuffer(hEMA, 0, 0, 3, bufEMA) < 3) return; if(CopyBuffer(hTrend, 0, 0, 3, bufTrend) < 3) return; // MQL5 Arrays are index 0 = oldest by default for CopyBuffer? // Actually defaults are 0 = start index. // Using ArraySetAsSeries(true) makes 0 = newest (Current Bar). ArraySetAsSeries(bufGreen, true); ArraySetAsSeries(bufRed, true); ArraySetAsSeries(bufStochK, true); ArraySetAsSeries(bufStochD, true); ArraySetAsSeries(bufEMA, true); ArraySetAsSeries(bufTrend, true); // Get Price Data for Candle 1 double close1 = iClose(Symbol(), Period(), 1); double high1 = iHigh(Symbol(), Period(), 1); double low1 = iLow(Symbol(), Period(), 1); // --- FRIDAY FILTER --- MqlDateTime dt; TimeCurrent(dt); if(dt.day_of_week == 5) return; // Do not enter on Friday // --- SIGNAL LOGIC --- // 1. TDI Cross UP (Green crosses Red) // Current Closed (1) > Red AND Prev (2) <= Red bool tdiCross = (bufGreen[1] > bufRed[1]) && (bufGreen[2] <= bufRed[2]); // 2. Stoch Cross UP bool stochCross = (bufStochK[1] > bufStochD[1]) && (bufStochK[2] <= bufStochD[2]); // 3. EMA Filter (Close > EMA) bool momentumOk = (close1 > bufEMA[1]); // 4. Trend Filter (Close > SMA 200) bool trendOk = (close1 > bufTrend[1]); // --- EXECUTION --- if(tdiCross && stochCross && momentumOk && trendOk) { // --- SPLIT ENTRY LOGIC --- double sl_price = close1 - (InpStopLoss * _Point); double tp_price = close1 + (InpTakeProfit * _Point); // Calculate Normalized Prices double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); sl_price = NormalizeDouble(sl_price, _Digits); tp_price = NormalizeDouble(tp_price, _Digits); // Trade A: Market Buy (50% Lots) // Note: We assume InpLots is the Total Risk. So we divide by 2? // Or InpLots is per trade. Let's assume InpLots is TOTAL SIZE. double splitLot = NormalizeDouble(InpLots / 2.0, 2); if(splitLot < SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN)) splitLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); m_trade.Buy(splitLot, Symbol(), ask, sl_price, tp_price, "TDI Market"); // Trade B: Limit Buy (50% Lots) // FIX: Add Spread Buffer to ensure fill. // Ask must hit Limit. If we place at Bid MidPrice, Ask is higher by Spread. // So we lift the limit by the Spread. // Use Ask-Bid to get the exact spread at this moment. double market_spread = ask - SymbolInfoDouble(Symbol(), SYMBOL_BID); double midPrice = (high1 + low1) / 2.0; // Add Spread to Limit to ensure Ask touches it when Bid touches Mid midPrice = midPrice + market_spread; midPrice = NormalizeDouble(midPrice, _Digits); // Limit SL/TP double limit_sl = midPrice - (InpStopLoss * _Point); double limit_tp = midPrice + (InpTakeProfit * _Point); limit_sl = NormalizeDouble(limit_sl, _Digits); limit_tp = NormalizeDouble(limit_tp, _Digits); // Expiration: 4 Hours (16200 sec approx, or TimeCurrent + 4*3600) datetime expiry = TimeCurrent() + (4 * 3600); m_trade.BuyLimit(splitLot, midPrice, Symbol(), limit_sl, limit_tp, ORDER_TIME_SPECIFIED, expiry, "TDI Pullback"); } } //+------------------------------------------------------------------+ //| Check if New Bar | //+------------------------------------------------------------------+ bool IsNewBar() { datetime currTime = iTime(Symbol(), Period(), 0); if(currTime != lastBarTime) { lastBarTime = currTime; return true; } return false; } //+------------------------------------------------------------------+ //| Check Time Exits | //+------------------------------------------------------------------+ void CheckTimeExits() { // Loop all open positions for(int i = PositionsTotal() - 1; i >= 0; i--) { if(m_position.SelectByIndex(i)) { if(m_position.Symbol() == Symbol() && m_position.Magic() == InpMagic) { // Calculate duration in seconds long duration = TimeCurrent() - m_position.Time(); // Convert hours to seconds if(duration > (InpTimeExitHours * 3600)) { m_trade.PositionClose(m_position.Ticket()); Print("Time Exit Triggered for Ticket: ", m_position.Ticket()); } } } } } //+------------------------------------------------------------------+ //| Check Trailing Stop Logic | //+------------------------------------------------------------------+ void CheckTrailingStop() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(m_position.SelectByIndex(i)) { if(m_position.Symbol() == Symbol() && m_position.Magic() == InpMagic) { if(m_position.PositionType() == POSITION_TYPE_BUY) { double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID); double openPrice = m_position.PriceOpen(); double currentProfitPoints = (currentPrice - openPrice) / SymbolInfoDouble(Symbol(), SYMBOL_POINT); // Start Trailing if Profit > TrailingStart if(currentProfitPoints > InpTrailingStart) { double newSL = currentPrice - (InpTrailingDist * SymbolInfoDouble(Symbol(), SYMBOL_POINT)); double currentSL = m_position.StopLoss(); // Check if New SL is an improvement (Higher than current) // And check if change is significant enough (TrailingStep) if(newSL > currentSL + (InpTrailingStep * SymbolInfoDouble(Symbol(), SYMBOL_POINT))) { // Normalize newSL = NormalizeDouble(newSL, _Digits); double tp = m_position.TakeProfit(); // Modify if(m_trade.PositionModify(m_position.Ticket(), newSL, tp)) { // Success } } } } } } } } //+------------------------------------------------------------------+