//+------------------------------------------------------------------+ //| AF_Engine2_Display.mqh | //| Project : Algo Forge (refactor total) | //| Sumber inspirasi: SniperGold SMC Pro+ (c) Waseem Shahrukh | //| https://www.mql5.com/en/code/75466 | //+------------------------------------------------------------------+ //| Engine 2 - PEMBANGUN KONTEKS DISPLAY (konsumsi Engine 3) | //| | //| - Engine 3 (display) TIDAK menghitung sendiri. Semua data | //| struktur/zona/level yang perlu DIGAMBAR disiapkan DI SINI | //| (layer Engine 2), memakai helper analisis murni yang SAMA | //| dengan agen N/C/E/P (AF_BuildSwing, AF_FindOrderBlock, dll). | //| - AF_BuildDisplayData membaca HANYA: | //| * Engine 1 (AFEngine1MTF) - bar tertutup (closed-bar lock) | //| * Output Engine 2 (AFSignalOut) - bias & alasan per agen | //| lalu mengisi AFDisplayData untuk renderer Engine 3. | //| - Non-repaint: semua nilai dihitung dari bar tertutup Engine 1. | //| - Konsisten dengan agen: struktur (BOS/CHoCH), swing point | //| (HH/HL/LH/LL), OB, FVG, premium/discount, level MTF (PDH/PDL/ | //| PWH/PWL), bias agen - setara dengan display kode asli 75466. | //+------------------------------------------------------------------+ #ifndef AF_ENGINE2_DISPLAY_MQH #define AF_ENGINE2_DISPLAY_MQH #include "AF_Engine2_Aggregator.mqh" //------------------------------------------------------------------ // Zona display (OB / FVG) //------------------------------------------------------------------ struct AFDispZone { datetime t; // waktu bar zona double top; double bot; bool bull; // true = zona bullish (support), false = bearish (resistance) string tag; // "OB" / "FVG" }; // Garis struktur (BOS / CHoCH) struct AFDispLine { datetime t1; double price; datetime t2; string lbl; // "BOS" / "CHoCH" int dir; // +1 bullish, -1 bearish }; // Swing point (dengan klasifikasi) struct AFDispPivot { datetime time; double price; bool isHigh; string tag; // HH/HL/LH/LL }; //------------------------------------------------------------------ // Data display lengkap (diisi AF_BuildDisplayData; digambar Engine 3) //------------------------------------------------------------------ struct AFDisplayData { string structure; // "Bullish" / "Bearish" / "Range" string eqPos; // "Premium" / "Discount" / "Equilibrium" int HH, HL, LH, LL; bool eqhSwept; // buy-side liquidity (EQH) tersapu -> intent bearish bool eqlSwept; // sell-side liquidity (EQL) tersapu -> intent bullish double swHigh; // range tertinggi (jendela display) double swLow; // range terendah double rangePos; // posisi close di range [0,1] double atr; // ATR slot display datetime dispStart; // waktu bar tertua yang digambar datetime dispEnd; // ujung kanan (bar tertutup terbaru + shift) AFDispPivot pivots[]; // swing points (dengan tag) AFDispLine structLines[]; // BOS/CHoCH AFDispZone ob[]; // order blocks (belum mitigated) AFDispZone fvg[]; // FVG (belum mitigated) double pdh, pdl, pwh, pwl; // level MTF (0 = belum tersedia) string nBias, cBias, eBias, pBias; // bias per agen (Bullish/Bearish/Range) string nReason, cReason, eReason, pReason; // alasan per agen void Reset() { structure=""; eqPos=""; HH=0; HL=0; LH=0; LL=0; eqhSwept=false; eqlSwept=false; swHigh=0.0; swLow=0.0; rangePos=0.5; atr=0.0; dispStart=0; dispEnd=0; ArrayResize(pivots,0); ArrayResize(structLines,0); ArrayResize(ob,0); ArrayResize(fvg,0); pdh=0.0; pdl=0.0; pwh=0.0; pwl=0.0; nBias=""; cBias=""; eBias=""; pBias=""; nReason=""; cReason=""; eReason=""; pReason=""; } }; //------------------------------------------------------------------ // Bantu: string bias dari arah agen //------------------------------------------------------------------ string AF_DirBias(int dir) { return (dir>0)? "Bullish" : (dir<0)? "Bearish" : "Range"; } //------------------------------------------------------------------ // Klasifikasi swing point menjadi HH/HL/LH/LL (urutan tertua->terbaru) // tags[]: [0..nHigh-1] = highs, [nHigh..nHigh+nLow-1] = lows //------------------------------------------------------------------ void AF_ClassifyPivots(const AFSwingData &sd,int &HH,int &HL,int &LH,int &LL,string &tags[]) { HH=0; HL=0; LH=0; LL=0; double lastH=0.0, lastL=0.0; bool haveH=false, haveL=false; for(int i=sd.nHigh-1; i>=0; i--) { string tag; if(!haveH || sd.highs[i].price>lastH) { tag="HH"; HH++; } else { tag="LH"; LH++; } lastH=sd.highs[i].price; haveH=true; tags[i]=tag; } for(int i=sd.nLow-1; i>=0; i--) { string tag; if(!haveL || sd.lows[i].price>lastL) { tag="HL"; HL++; } else { tag="LL"; LL++; } lastL=sd.lows[i].price; haveL=true; tags[sd.nHigh+i]=tag; } } //------------------------------------------------------------------ // Garis struktur BOS/CHoCH dari pivot fractal (kronologis) //------------------------------------------------------------------ void AF_BuildStructLines(const AFEngine1MTF &e1,int slot,AFDispLine &lines[]) { ArrayResize(lines,0); if(!e1.IsReady(slot)) return; int cnt=e1.Count(slot); if(cnt<5) return; double upTarget=DBL_MAX, dnTarget=-DBL_MAX; int upBar=-1, dnBar=-1; int trend=0; // kronologis: i = cnt-3 (tertua) -> 2 (terbaru), index 0 = bar terbaru for(int i=cnt-3; i>=2; i--) { double h=e1.High(slot,i); double l=e1.Low(slot,i); if(h>e1.High(slot,i-1) && h>e1.High(slot,i-2) && h>e1.High(slot,i+1) && h>e1.High(slot,i+2)) { upTarget=h; upBar=i; } if(l=0 && upBar!=i && e1.Close(slot,i)>upTarget) { if(ArraySize(lines)=0 && dnBar!=i && e1.Close(slot,i)0)? "CHoCH" : "BOS"; lines[n].dir=-1; } trend=-1; dnTarget=-DBL_MAX; dnBar=-1; } } } //------------------------------------------------------------------ // Kumpulkan Order Block (bar lawan sebelum move kuat), tanpa MIT //------------------------------------------------------------------ void AF_CollectOBs(const AFEngine1MTF &e1,int slot,int maxN,double atr,AFDispZone &zones[]) { ArrayResize(zones,0); if(!e1.IsReady(slot) || maxN<=0 || atr<=0.0) return; int cnt=e1.Count(slot); if(cnt<5) return; double avg=AF_AvgBody(e1,slot,AF_E2_LOOKBACK_AVG); if(avg<=0.0) return; int maxIdx=MathMin(cnt-3,AF_E3_DISP_BARS); for(int i=0; i lama) { int obBar=i+1; // bar lawan tepat sebelum move if(obBar>cnt-2) continue; double moveBody=MathAbs(e1.Close(slot,i)-e1.Open(slot,i)); if(moveBodye1.Open(slot,i)); bool prevBear=(e1.Close(slot,obBar)e1.Open(slot,obBar)); if(!prevBear && !prevBull) continue; bool bull; if(moveUp && prevBear) bull=true; else if(!moveUp && prevBull) bull=false; else continue; double top=e1.High(slot,obBar); double bot=e1.Low(slot,obBar); if((top-bot)=0; b--) { if(bull && e1.Close(slot,b)top) { mit=true; break; } } if(mit) continue; // dedupe sederhana: zona hampir sama sudah ada int nz=ArraySize(zones); bool dup=false; for(int k=0; k=maxN) break; ArrayResize(zones,nz+1); zones[nz].t=e1.Time(slot,obBar); zones[nz].top=top; zones[nz].bot=bot; zones[nz].bull=bull; zones[nz].tag="OB"; } } //------------------------------------------------------------------ // Kumpulkan FVG (imbalance) terbaru, tanpa MIT //------------------------------------------------------------------ void AF_CollectFVG(const AFEngine1MTF &e1,int slot,int maxN,double atr,AFDispZone &zones[]) { ArrayResize(zones,0); if(!e1.IsReady(slot) || maxN<=0 || atr<=0.0) return; int cnt=e1.Count(slot); if(cnt<3) return; int maxIdx=MathMin(cnt-3,AF_E3_DISP_BARS); for(int i=0; i<=maxIdx; i++) { double lo0=e1.Low(slot,i); double hi2=e1.High(slot,i+2); double hi0=e1.High(slot,i); double lo2=e1.Low(slot,i+2); double top=0.0, bot=0.0; bool bull=false, found=false; if(lo0>hi2) { bull=true; top=lo0; bot=hi2; found=true; } else if(hi0=0; b--) { if(bull && e1.Low(slot,b)<=bot) { mit=true; break; } // wick full-fill (S-9) if(!bull && e1.High(slot,b)>=top) { mit=true; break; } // wick full-fill (S-9) } if(mit) continue; int nz=ArraySize(zones); if(nz>=maxN) break; ArrayResize(zones,nz+1); zones[nz].t=e1.Time(slot,i); zones[nz].top=top; zones[nz].bot=bot; zones[nz].bull=bull; zones[nz].tag="FVG"; } } //------------------------------------------------------------------ // Level MTF: previous day/week high/low dari slot D1/W1 Engine 1 //------------------------------------------------------------------ void AF_MTFLevels(const AFEngine1MTF &e1,int sD1,int sW1, double &pdh,double &pdl,double &pwh,double &pwl) { pdh=0.0; pdl=0.0; pwh=0.0; pwl=0.0; if(e1.IsReady(sD1) && e1.Count(sD1)>=2) { pdh=e1.High(sD1,1); pdl=e1.Low(sD1,1); } if(e1.IsReady(sW1) && e1.Count(sW1)>=2) { pwh=e1.High(sW1,1); pwl=e1.Low(sW1,1); } } //------------------------------------------------------------------ // Pembangun data display (SELURUH komputasi untuk Engine 3 di sini) //------------------------------------------------------------------ void AF_BuildDisplayData(const AFEngine1MTF &e1, int sN,int sC,int sE,int sP,int sDisp,int sD1,int sW1, int maxOB,int maxFVG, const AFSignalOut &oN,const AFSignalOut &oC, const AFSignalOut &oE,const AFSignalOut &oP, AFDisplayData &dd) { dd.Reset(); // bias & alasan agen (dari output Engine 2 - tidak dihitung ulang) dd.nBias=AF_DirBias(oN.dir); dd.cBias=AF_DirBias(oC.dir); dd.eBias=AF_DirBias(oE.dir); dd.pBias=AF_DirBias(oP.dir); dd.nReason=oN.reason; dd.cReason=oC.reason; dd.eReason=oE.reason; dd.pReason=oP.reason; if(!e1.IsReady(sDisp)) return; int cnt=e1.Count(sDisp); if(cnt<5) return; dd.atr=e1.ATR(sDisp,14); // rentang & posisi close (premium/discount) double rmin=0.0, rmax=0.0, pos=0.5; AF_RangeStat(e1,sDisp,AF_E3_DISP_BARS,rmin,rmax,pos); dd.swHigh=rmax; dd.swLow=rmin; dd.rangePos=pos; dd.eqPos=(pos<0.38)? "Discount" : (pos>0.62)? "Premium" : "Equilibrium"; // struktur (tren) dari pivot fractal AFSwingData sd; AF_BuildSwing(e1,sDisp,sd,AF_E3_DISP_BARS); int trend=0; double clarity=0.0; AF_TrendFromSwing(sd,trend,clarity); dd.structure=(trend>0)? "Bullish" : (trend<0)? "Bearish" : "Range"; // klasifikasi HH/HL/LH/LL + daftar swing point string tags[]; ArrayResize(tags,2*AF_E2_MAX_PIVOTS); AF_ClassifyPivots(sd,dd.HH,dd.HL,dd.LH,dd.LL,tags); int nP=MathMin(sd.nHigh+sd.nLow,AF_E3_MAX_PIVOTS); ArrayResize(dd.pivots,nP); int k=0; for(int i=0; i0); dd.eqhSwept=(sweep<0); // garis struktur BOS/CHoCH AF_BuildStructLines(e1,sDisp,dd.structLines); // zona OB & FVG AF_CollectOBs(e1,sDisp,MathMax(1,maxOB),dd.atr,dd.ob); AF_CollectFVG(e1,sDisp,MathMax(1,maxFVG),dd.atr,dd.fvg); // level MTF AF_MTFLevels(e1,sD1,sW1,dd.pdh,dd.pdl,dd.pwh,dd.pwl); // jendela waktu display int lastIdx=MathMin(cnt-1,AF_E3_DISP_BARS); dd.dispStart=e1.Time(sDisp,lastIdx); int sec=AFEngine1MTF::PeriodSecondsOf(e1.TfOf(sDisp)); if(sec<=0) sec=3600; dd.dispEnd=e1.Time(sDisp,0)+(datetime)MathMax(sec*4,3600); } //------------------------------------------------------------------ // Pembanding data display (untuk uji non-repaint) //------------------------------------------------------------------ bool AF_SameDisplayData(const AFDisplayData &a,const AFDisplayData &b) { if(a.structure!=b.structure) return false; if(a.eqPos!=b.eqPos) return false; if(a.HH!=b.HH || a.HL!=b.HL || a.LH!=b.LH || a.LL!=b.LL) return false; if(a.eqhSwept!=b.eqhSwept || a.eqlSwept!=b.eqlSwept) return false; if(MathAbs(a.swHigh-b.swHigh)>1e-12) return false; if(MathAbs(a.swLow-b.swLow)>1e-12) return false; if(MathAbs(a.rangePos-b.rangePos)>1e-12) return false; if(MathAbs(a.atr-b.atr)>1e-12) return false; if(a.dispStart!=b.dispStart || a.dispEnd!=b.dispEnd) return false; if(ArraySize(a.pivots)!=ArraySize(b.pivots)) return false; if(ArraySize(a.structLines)!=ArraySize(b.structLines)) return false; if(ArraySize(a.ob)!=ArraySize(b.ob)) return false; if(ArraySize(a.fvg)!=ArraySize(b.fvg)) return false; if(MathAbs(a.pdh-b.pdh)>1e-12 || MathAbs(a.pdl-b.pdl)>1e-12) return false; if(MathAbs(a.pwh-b.pwh)>1e-12 || MathAbs(a.pwl-b.pwl)>1e-12) return false; if(a.nBias!=b.nBias || a.cBias!=b.cBias || a.eBias!=b.eBias || a.pBias!=b.pBias) return false; if(a.nReason!=b.nReason || a.cReason!=b.cReason) return false; if(a.eReason!=b.eReason || a.pReason!=b.pReason) return false; return true; } #endif // AF_ENGINE2_DISPLAY_MQH