topkek

🧩 Syntax:
//+------------------------------------------------------------------+
//| Expert advisor: CandlePatternRiskTrailingEA_v3.mq5              |
//| Buy/Sell X H1 candles, SL/TS in EUR, max 1 Buy & 1 Sell         |
//+------------------------------------------------------------------+
#property strict

//--- inputs
input int    GreenCandlesForBuy  = 3;     // aantal groene candles voor buy
input int    RedCandlesForSell   = 3;     // aantal rode candles voor sell
input double RiskInEuro          = 10.0;  // stop-loss in euro
input double TrailingInEuro      = 5.0;   // trailing-afstand in euro
input double TrailingStartEuro   = 5.0;   // start trailing pas na € winst
input double LotSize             = 0.01;  // vaste lotgrootte
input bool   EnableTrailingStop  = true;  // trailing aan/uit

datetime lastBarTime = 0;

//+------------------------------------------------------------------+
//| Expert tick                                                      |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1) Nieuwe H1-candle?
   int needed = MathMax(GreenCandlesForBuy, RedCandlesForSell) + 2;
   MqlRates rates[];
   if(CopyRates(_Symbol, PERIOD_H1, 0, needed, rates) < needed) return;
   ArraySetAsSeries(rates, true);
   if(rates[0].time == lastBarTime) return;
   lastBarTime = rates[0].time;

   // 2) Check open posities
   bool hasBuy=false, hasSell=false;
   for(int i=0;i<PositionsTotal();i++)
   {
      ulong tk = PositionGetTicket(i);
      if(tk==0 || !PositionSelectByTicket(tk)) continue;
      if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue;
      ENUM_POSITION_TYPE t=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      if(t==POSITION_TYPE_BUY)  hasBuy=true;
      if(t==POSITION_TYPE_SELL) hasSell=true;
   }

   // 3) Signalen
   if(!hasBuy && CountConsecutive(rates,GreenCandlesForBuy,true))
      PlaceOrder(ORDER_TYPE_BUY);
   if(!hasSell && CountConsecutive(rates,RedCandlesForSell,false))
      PlaceOrder(ORDER_TYPE_SELL);

   // 4) Trailing
   if(EnableTrailingStop) ApplyTrailing();
}

//+------------------------------------------------------------------+
//| Tel consecutive bullish/bearish                                  |
//+------------------------------------------------------------------+
bool CountConsecutive(const MqlRates &r[], int cnt, bool bullish)
{
   for(int i=1;i<=cnt;i++)
   {
      if(bullish  && r[i].close<=r[i].open)  return false;
      if(!bullish && r[i].close>=r[i].open)  return false;
   }
   return true;
}

//+------------------------------------------------------------------+
//| Open market­order met SL check                                   |
//+------------------------------------------------------------------+
void PlaceOrder(ENUM_ORDER_TYPE type)
{
   double entry = (type==ORDER_TYPE_BUY)
                  ? SymbolInfoDouble(_Symbol,SYMBOL_ASK)
                  : SymbolInfoDouble(_Symbol,SYMBOL_BID);
   // SL in punten
   double ptsSL = CalcPoints(RiskInEuro);
   // min stops
   int minStops = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)+1;
   if(ptsSL < minStops)
   {
      Print("SL-te-klein bij order, aanpassen van ", ptsSL, " naar ", minStops, " pts");
      ptsSL = minStops;
   }
   double sl = (type==ORDER_TYPE_BUY) ? entry-ptsSL*_Point : entry+ptsSL*_Point;
   sl = NormalizeDouble(sl,_Digits);

   MqlTradeRequest rq; MqlTradeResult rs;
   ZeroMemory(rq); ZeroMemory(rs);
   rq.action       = TRADE_ACTION_DEAL;
   rq.symbol       = _Symbol;
   rq.volume       = LotSize;
   rq.type         = type;
   rq.price        = NormalizeDouble(entry,_Digits);
   rq.sl           = sl;
   rq.tp           = 0;
   rq.deviation    = 10;
   rq.magic        = 20250529;
   rq.type_filling = ORDER_FILLING_IOC;

   if(!OrderSend(rq,rs))
      Print("OrderSend failed: ", rs.retcode);
   else if(rs.retcode!=TRADE_RETCODE_DONE)
      Print("Order rejected: ", rs.retcode, " / ", rs.comment);
   else
      Print("Order opened: ", EnumToString(type),
            " @", rq.price, " SL@", sl);
}

//+------------------------------------------------------------------+
//| Bereken punten uit euro­risico                                  |
//+------------------------------------------------------------------+
double CalcPoints(double euro)
{
   double tv = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE);
   double ts = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
   if(tv<=0||ts<=0) return(1);
   double ppp = tv/ts;
   double pts = euro/(ppp*LotSize);
   return(MathMax(NormalizeDouble(pts,0),1.0));
}

//+------------------------------------------------------------------+
//| Trailing stop update met min-stop-check                         |
//+------------------------------------------------------------------+
void ApplyTrailing()
{
   for(int i=0;i<PositionsTotal();i++)
   {
      ulong tk = PositionGetTicket(i);
      if(tk==0||!PositionSelectByTicket(tk)) continue;
      if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue;

      ENUM_POSITION_TYPE t = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      double openP = PositionGetDouble(POSITION_PRICE_OPEN);
      double curP  = (t==POSITION_TYPE_BUY)
                     ? SymbolInfoDouble(_Symbol,SYMBOL_BID)
                     : SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double curSL = PositionGetDouble(POSITION_SL);
      double vol   = PositionGetDouble(POSITION_VOLUME);

      // winst in EUR
      double diff = MathAbs(curP-openP)/_Point;
      double eur  = diff * (SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE)
                    /SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE)) * vol;

      // debug print
      Print("Pos ",tk," profit€=",DoubleToString(eur,2)," curSL=",DoubleToString(curSL,_Digits));

      if(eur < TrailingStartEuro) 
         continue;

      // bereken gewenste newSL
      double ptsTR = CalcPoints(TrailingInEuro);
      double newSL = (t==POSITION_TYPE_BUY) ? curP-ptsTR*_Point : curP+ptsTR*_Point;
      newSL = NormalizeDouble(newSL,_Digits);

      // min-stop check
      int minStops = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)+1;
      double actual = MathAbs(curP - newSL)/_Point;
      if(actual < minStops)
      {
         Print("Trailing SL te klein (",actual," pts), aanpassen naar ", minStops);
         newSL = (t==POSITION_TYPE_BUY) ? curP-minStops*_Point : curP+minStops*_Point;
         newSL = NormalizeDouble(newSL,_Digits);
      }

      // update als opschuift
      if((t==POSITION_TYPE_BUY  && newSL>curSL) ||
         (t==POSITION_TYPE_SELL && newSL<curSL))
      {
         MqlTradeRequest rq; MqlTradeResult rs;
         ZeroMemory(rq); ZeroMemory(rs);
         rq.action   = TRADE_ACTION_SLTP;
         rq.position = tk;
         rq.sl       = newSL;
         rq.tp       = 0;
         if(!OrderSend(rq,rs))
            Print("Trailing SL failed: ", rs.retcode);
         else
            Print("Trailing SL updated to ",DoubleToString(newSL,_Digits));
      }
   }
}
//+------------------------------------------------------------------+