//+------------------------------------------------------------------+ //| Cdl.mqh | //| Copyright 2026, Allan NiƱal | //| https://www.allanninal.dev/mql5 | //+------------------------------------------------------------------+ //| Candlestick engine, one CLOSED bar per step (no repaint), shared | //| by Candlestick Signal Alerts. | //| 21 patterns; the exact rules are tests/reference.py (this file | //| is checked against it bar by bar). Trend before a pattern: close | //| vs EMA(20). Averages: body and range of the 10 bars before. | //| Statistics: the move N bars later in ATR(14) units, signed by | //| the pattern's direction, and the same for every bar (baseline). | //+------------------------------------------------------------------+ // SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE) #ifndef CDL_MQH #define CDL_MQH #define CD_NP 13 #define CD_N 64 // ring of recent bars (forward bars + 4 must stay below it) #define CD_EMA 20 #define CD_AVG 10 string CDName(const int p) { string n[CD_NP]={"Bullish Engulfing","Bearish Engulfing","Hammer","Hanging Man","Inverted Hammer","Shooting Star", "Doji","Morning Star","Evening Star","Bullish Harami","Bearish Harami","Piercing Line", "Dark Cloud Cover" }; return(p>=0 && p=0 && ps.o[CDi(k)]); } bool CDBear(const CDState &s,const int k) { return(s.c[CDi(k)]=y-g_cd_tol); } bool LE(const double x,const double y) { return(x<=y+g_cd_tol); } bool GT(const double x,const double y) { return(x>y+g_cd_tol); } bool LT(const double x,const double y) { return(x=CD_AVG && k>=3) { double ab=0,ar=0; for(int j=k-CD_AVG; j0) CDAdd(s,1,-1,use); if(GT(r,0)) { bool hammer=(GE(w,2*b) && LE(u,0.1*r) && GE(w,0.6*r)); bool inv=(GE(u,2*b) && LE(w,0.1*r) && GE(u,0.6*r)); if(hammer && t1<0) CDAdd(s,2,1,use); if(hammer && t1>0) CDAdd(s,3,-1,use); if(inv && t1<0) CDAdd(s,4,1,use); if(inv && t1>0) CDAdd(s,5,-1,use); if(LE(b,0.05*r) && GE(r,0.5*ar) && t1!=0) CDAdd(s,6,-t1,use); } double midq=(CDO(s,q)+CDC(s,q))/2,midp=(CDO(s,p)+CDC(s,p))/2; if(CDBear(s,q) && GE(CDBody(s,q),ab) && LE(CDBody(s,p),0.3*CDBody(s,q)) && LE(MathMin(CDO(s,p),CDC(s,p)),CDC(s,q)) && CDBull(s,k) && GT(c,midq) && t3<0) CDAdd(s,7,1,use); if(CDBull(s,q) && GE(CDBody(s,q),ab) && LE(CDBody(s,p),0.3*CDBody(s,q)) && GE(MathMax(CDO(s,p),CDC(s,p)),CDC(s,q)) && CDBear(s,k) && LT(c,midq) && t3>0) CDAdd(s,8,-1,use); if(CDBear(s,p) && GE(CDBody(s,p),ab) && CDBull(s,k) && GE(o,CDC(s,p)) && LE(c,CDO(s,p)) && LT(b,CDBody(s,p)) && t2<0) CDAdd(s,9,1,use); if(CDBull(s,p) && GE(CDBody(s,p),ab) && CDBear(s,k) && LE(o,CDC(s,p)) && GE(c,CDO(s,p)) && LT(b,CDBody(s,p)) && t2>0) CDAdd(s,10,-1,use); if(CDBear(s,p) && GE(CDBody(s,p),ab) && CDBull(s,k) && LE(o,CDC(s,p)) && GT(c,midp) && LT(c,CDO(s,p)) && t2<0) CDAdd(s,11,1,use); if(CDBull(s,p) && GE(CDBody(s,p),ab) && CDBear(s,k) && GE(o,CDC(s,p)) && LT(c,midp) && GT(c,CDO(s,p)) && t2>0) CDAdd(s,12,-1,use); } //--- Free: the events of this bar are in ev_mask / ev_dir return(s.ev_mask!=0); } //--- "Bullish Engulfing, Doji" from a mask (only the given direction when dir != 0) string CDNames(const CDState &s,const int mask,const int dir) { string r=""; for(int i=0; i