//+------------------------------------------------------------------+ //| StEngines.mqh | //| Copyright 2026, Allan NiƱal | //| https://www.allanninal.dev/mql5 | //+------------------------------------------------------------------+ //| The three signal engines of Signal Trader (UT Bot, SuperTrend, | //| Range Filter), embedded so nothing else has to be installed. | //| Each is the calculation loop of the indicator moved into a class | //| that is fed ONE CLOSED BAR at a time. Range Filter uses RF.mqh. | //| A signal is reported for the bar just fed, which is always | //| closed, so nothing repaints. The Python statement of the three | //| engines is tests/reference.py. | //+------------------------------------------------------------------+ // SPDX-License-Identifier: MPL-2.0 // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not // distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/ // Origin of the maths: UT Bot idea by Yo_adriiiiaan (from an original idea by HPotter), TradingView "UT Bot Alerts" by // QuantNomad; SuperTrend follows the widely published TradingView ATR-band script; Range Filter is an own implementation // of the published range-filter method. #ifndef ST_ENGINES_MQH #define ST_ENGINES_MQH #include "RF.mqh" #define ST_ATR_N 14 // the stop distance unit bool StAbove(const double a,const double b) { return(a-b>1e-10*MathAbs(b)); } bool StBelow(const double a,const double b) { return(b-a>1e-10*MathAbs(b)); } //--- bars fed so far plus Wilder ATR(14) (seeded with the SMA of the first 14 true ranges) class CEng { public: double o[],h[],l[],c[],atr[]; int n,cap; CEng(void) { n=0; cap=0; } virtual ~CEng(void) {} void Clear(void) { n=0; cap=0; ArrayResize(o,0); ArrayResize(h,0); ArrayResize(l,0); ArrayResize(c,0); ArrayResize(atr,0); OnClear(); } virtual void OnClear(void) {} virtual void Grow(void) { if(n0 ? g_line[i-1] : 0.0); if(g_atr[i]==EMPTY_VALUE) { g_line[i]=EMPTY_VALUE; return(0); } if(prev==EMPTY_VALUE) prev=0.0; double src=c[i],p1=(i>0 ? c[i-1] : src),nloss=InpKey*g_atr[i]; if(src>prev && p1>prev) g_line[i]=MathMax(prev,src-nloss); else if(srcprev ? src-nloss : src+nloss); if(i==0 || g_line[i-1]==EMPTY_VALUE) return(0); bool up=(src>g_line[i] && p1<=g_line[i-1]); bool dn=(src=g_line[i-1]); if(InpUseEMA) { up=up && g_ema[i]!=EMPTY_VALUE && c[i]>g_ema[i]; dn=dn && g_ema[i]!=EMPTY_VALUE && c[i]=InpATR-1) { if(i==InpATR-1) { double s=0; for(int k=i-InpATR+1; k<=i; k++) s+=TR(k); g_atr[i]=s/InpATR; } else g_atr[i]=(g_atr[i-1]*(InpATR-1)+tr)/InpATR; } if(i0 && StAbove(c[i-1],up1)) up=MathMax(up,up1); if(i>0 && StBelow(c[i-1],dn1)) dn=MathMin(dn,dn1); double trend=(first ? 1.0 : g_trend[i-1]); if(trend==-1.0 && StAbove(c[i],dn1)) trend=1.0; else if(trend==1.0 && StBelow(c[i],up1)) trend=-1.0; g_up[i]=up; g_dn[i]=dn; g_trend[i]=trend; if(first) return(0); bool buy=(trend==1.0 && g_trend[i-1]==-1.0); bool sell=(trend==-1.0 && g_trend[i-1]==1.0); if(InpUseEMA) { buy=buy && g_ema[i]!=EMPTY_VALUE && c[i]>g_ema[i]; sell=sell && g_ema[i]!=EMPTY_VALUE && c[i]