//+------------------------------------------------------------------+ //| AF_Engine2_Aggregator.mqh | //| Project : Algo Forge (refactor total) | //| Sumber inspirasi: SniperGold SMC Pro+ (c) Waseem Shahrukh | //| https://www.mql5.com/en/code/75466 | //+------------------------------------------------------------------+ //| Engine 2 - AGREGATOR SINYAL (terpisah dari agen) | //| | //| - Menggabungkan output 4 agen (N/C/E/P) menjadi sinyal final. | //| - Agen TIDAK saling tahu; agregator adalah SATU-SATUNYA tempat | //| output agen dipertemukan. | //| - Bobot dinamis: bobot dasar per agen x confidence, dengan | //| penguatan (boost) untuk agen yang searah dengan mayoritas. | //| - Output: dir BUY/SELL/WAIT + buy/sell/bias/score/confidence + | //| entry/SL/TP (dari bar tertutup & ATR Engine 1) + alasan. | //| - Non-repaint: semua level dihitung dari bar tertutup Engine 1. | //+------------------------------------------------------------------+ #ifndef AF_ENGINE2_AGGREGATOR_MQH #define AF_ENGINE2_AGGREGATOR_MQH #include "AF_Engine2_Agents.mqh" //------------------------------------------------------------------ // Output agregator (untuk Engine 3 display / konsumen sinyal) //------------------------------------------------------------------ struct AFAggOut { int dir; // +1 buy / -1 sell / 0 wait double buy; // dukungan buy agregat [0,1] double sell; // dukungan sell agregat [0,1] double bias; // buy-sell [-1,1] double score; // |bias| kekuatan sinyal [0,1] double confidence; // [0,1] (dukungan + kesepakatan) double entry; // level entry (close bar tertutup) double sl; // stop loss (ATR-based) double tp; // take profit (ATR-based) bool hasLevels; // true bila entry/sl/tp terhitung int activeAgents; // jumlah agen dengan confidence>0.05 double weights[4]; // bobot efektif per agen (N,C,E,P) int agentDir[4]; // arah tiap agen string agentReason[4]; // alasan tiap agen (untuk display) string reason; // ringkasan }; //------------------------------------------------------------------ // AFAggregator - penggabung output 4 agen (bobot dinamis 2-pass) //------------------------------------------------------------------ class AFAggregator { public: void Compute(const AFEngine1MTF &e1,int slotE, const AFSignalOut &n,const AFSignalOut &c, const AFSignalOut &e,const AFSignalOut &p, AFAggOut &out) const; }; void AFAggregator::Compute(const AFEngine1MTF &e1,int slotE, const AFSignalOut &n,const AFSignalOut &c, const AFSignalOut &e,const AFSignalOut &p, AFAggOut &out) const { AFSignalOut ag[4]; ag[0]=n; ag[1]=c; ag[2]=e; ag[3]=p; double baseW[4]; baseW[0]=AF_AGG_W_N; baseW[1]=AF_AGG_W_C; baseW[2]=AF_AGG_W_E; baseW[3]=AF_AGG_W_P; // pass 1: bias awal (bobot dasar x confidence) double w1[4]; double W1=0.0; for(int i=0;i<4;i++) { w1[i]=baseW[i]*MathMax(ag[i].confidence,0.0); W1+=w1[i]; } double bias1=0.0; if(W1>0.0) { for(int i=0;i<4;i++) bias1+=w1[i]*ag[i].bias; bias1/=W1; } int majDir=(bias1> AF_E2_DIR_TOL)? 1 : (bias1< -AF_E2_DIR_TOL)? -1 : 0; // pass 2: bobot dinamis dengan penguatan kesepakatan (agreement) double w2[4]; double W2=0.0; for(int i=0;i<4;i++) { double boost=1.0; if(majDir!=0 && ag[i].dir==majDir) boost=1.5; w2[i]=baseW[i]*MathMax(ag[i].confidence,0.0)*boost; W2+=w2[i]; } double buy=0.0, sell=0.0; if(W2>0.0) { for(int i=0;i<4;i++) { buy +=w2[i]*ag[i].buy; sell+=w2[i]*ag[i].sell; } buy/=W2; sell/=W2; } out.buy =AF_Clamp01(buy); out.sell=AF_Clamp01(sell); out.bias=out.buy-out.sell; out.score=MathAbs(out.bias); out.dir=0; if(out.bias>=AF_AGG_BUY_TH && out.buy>=AF_AGG_MIN_SUP) out.dir=1; else if(out.bias<=-AF_AGG_BUY_TH && out.sell>=AF_AGG_MIN_SUP) out.dir=-1; int agree=0; for(int i=0;i<4;i++) if(out.dir!=0 && ag[i].dir==out.dir) agree++; double confBase=MathMax(out.buy,out.sell); out.confidence=(out.dir==0)? confBase : MathMin(1.0,confBase*(0.7+0.1*agree)); out.activeAgents=0; for(int i=0;i<4;i++) { out.agentDir[i]=ag[i].dir; out.agentReason[i]=ag[i].reason; out.weights[i]=w2[i]; if(ag[i].confidence>0.05) out.activeAgents++; } // level entry/SL/TP (non-repaint: close bar tertutup + ATR Engine 1) out.entry=0.0; out.sl=0.0; out.tp=0.0; out.hasLevels=false; if(e1.IsReady(slotE)) { double close=e1.Close(slotE,0); double atr=e1.ATR(slotE,14); if(close>0.0 && atr>0.0) { out.entry=close; out.hasLevels=true; if(out.dir>0) { out.sl=close-AF_AGG_SL_ATR*atr; out.tp=close+AF_AGG_TP_ATR*atr; } else if(out.dir<0) { out.sl=close+AF_AGG_SL_ATR*atr; out.tp=close-AF_AGG_TP_ATR*atr; } } } // ringkasan out.reason=(out.dir>0)? "BUY" : (out.dir<0)? "SELL" : "WAIT"; out.reason+="[bias="+DoubleToString(out.bias,2)+"]"; for(int i=0;i<4;i++) { string tag=(ag[i].dir>0)? "+" : (ag[i].dir<0)? "-" : "~"; out.reason+=" "+ag[i].name+":"+tag; } out.reason+=" | "; for(int i=0;i<4;i++) { if(ag[i].dir!=0 && ag[i].reason!="") { out.reason+=ag[i].name+"("+ag[i].reason+") "; if(StringLen(out.reason)>160) { out.reason=StringSubstr(out.reason,0,157)+"..."; break; } } } } //+------------------------------------------------------------------+ //| AFEngine2Signals - facade Engine 2 (4 agen + agregator) | //| Satu panggilan: input Engine 1 + 4 slot -> output agen & final. | //+------------------------------------------------------------------+ class AFEngine2Signals { private: AFAgentNarrative m_n; AFAgentContext m_c; AFAgentEntry m_e; AFAgentPriceAction m_p; AFAggregator m_agg; public: void Compute(const AFEngine1MTF &e1,int sN,int sC,int sE,int sP, AFSignalOut &oN,AFSignalOut &oC,AFSignalOut &oE,AFSignalOut &oP, AFAggOut &agg) const; }; void AFEngine2Signals::Compute(const AFEngine1MTF &e1,int sN,int sC,int sE,int sP, AFSignalOut &oN,AFSignalOut &oC,AFSignalOut &oE,AFSignalOut &oP, AFAggOut &agg) const { m_n.Compute(e1,sN,oN); m_c.Compute(e1,sC,oC); m_e.Compute(e1,sE,oE); m_p.Compute(e1,sP,oP); m_agg.Compute(e1,sE,oN,oC,oE,oP,agg); } #endif // AF_ENGINE2_AGGREGATOR_MQH