203 lines
6.9 KiB
MQL5
203 lines
6.9 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 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
|