SniperGold_ML/MQL5/Include/AlgoForge/AF_Engine2_Aggregator.mqh

203 lines
6.9 KiB
MQL5
Raw Permalink Normal View History

//+------------------------------------------------------------------+
//| 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