SniperGold_ML/MQL5/Include/AlgoForge/AF_Engine3_Render.mqh

483 lines
23 KiB
MQL5

//+------------------------------------------------------------------+
//| AF_Engine3_Render.mqh |
//| Project : Algo Forge (refactor total) |
//| Sumber inspirasi: SniperGold SMC Pro+ (c) Waseem Shahrukh |
//| https://www.mql5.com/en/code/75466 |
//+------------------------------------------------------------------+
//| Engine 3 - RENDER DISPLAY (murni menggambar, TIDAK menghitung) |
//| |
//| - HANYA membaca output Engine 1 (AFEngine1MTF) dan Engine 2 |
//| (AFSignalOut, AFAggOut, AFDisplayData). |
//| - Tidak ada perhitungan analisis di sini: semua struktur, zona, |
//| level, bias sudah disiapkan oleh Engine 2 (AF_BuildDisplayData |
//| di AF_Engine2_Display.mqh). |
//| - Meniru tampilan kode asli 75466: garis struktur (BOS/CHoCH), |
//| swing point (HH/HL/LH/LL), Order Block, FVG, premium/discount, |
//| level MTF (PDH/PDL/PWH/PWL), sinyal (entry/SL/TP + panah), |
//| dan dashboard (bias, ringkasan struktur, likuiditas, konteks, |
//| level kunci, alignment N/C/E/P, trade setup, legenda). |
//| - Non-repaint: renderer dipanggil konsumen HANYA saat bar |
//| tertutup baru (lihat AF_Engine3_Display.mq5). |
//+------------------------------------------------------------------+
#ifndef AF_ENGINE3_RENDER_MQH
#define AF_ENGINE3_RENDER_MQH
#include "AF_Engine2_Display.mqh"
//------------------------------------------------------------------
// Konfigurasi render (diisi konsumen: indikator / EA uji)
//------------------------------------------------------------------
struct AFRenderCfg
{
string prefix;
bool showStructure;
bool showSwingPoints;
bool showOB;
int obCount;
bool showFVG;
int fvgCount;
bool showPD;
bool showLevels;
bool showSignals;
bool showDashboard;
int fontSize;
string fontName;
color bull, bear;
color obLineBull, obLineBear, obFillBull, obFillBear;
color fvgLineBull, fvgLineBear, fvgFillBull, fvgFillBear;
color premiumLine, premiumFill;
color equilLine, equilFill;
color discountLine, discountFill;
color mtfLevel;
color textCol, accent, panelBG, panelBorder;
bool panelTransparent;
color sigEntry, sigSL, sigTP;
};
// Default (warna LuxAlgo-style, sama dengan kode asli 75466)
void AFR_DefaultCfg(AFRenderCfg &cfg)
{
cfg.prefix=AF_E3_PFX;
cfg.showStructure=true;
cfg.showSwingPoints=true;
cfg.showOB=true; cfg.obCount=AF_E3_OB_DEFAULT;
cfg.showFVG=true; cfg.fvgCount=AF_E3_FVG_DEFAULT;
cfg.showPD=true;
cfg.showLevels=true;
cfg.showSignals=true;
cfg.showDashboard=true;
cfg.fontSize=9;
cfg.fontName="Segoe UI";
cfg.bull=C'8,153,129';
cfg.bear=C'242,54,69';
cfg.obLineBull=C'91,156,246';
cfg.obLineBear=C'247,124,128';
cfg.obFillBull=C'9,15,28';
cfg.obFillBear=C'28,13,15';
cfg.fvgLineBull=C'0,255,104';
cfg.fvgLineBear=C'255,0,8';
cfg.fvgFillBull=C'7,26,16';
cfg.fvgFillBear=C'27,8,10';
cfg.premiumLine=C'242,54,69';
cfg.premiumFill=C'27,10,12';
cfg.equilLine=C'178,181,190';
cfg.equilFill=C'20,21,24';
cfg.discountLine=C'8,153,129';
cfg.discountFill=C'7,24,16';
cfg.mtfLevel=C'33,87,243';
cfg.textCol=clrGainsboro;
cfg.accent=C'33,87,243';
cfg.panelBG=C'12,16,22';
cfg.panelBorder=C'40,50,62';
cfg.panelTransparent=true;
cfg.sigEntry=C'8,153,129';
cfg.sigSL=clrTomato;
cfg.sigTP=clrLimeGreen;
}
//------------------------------------------------------------------
// Helper gambar
//------------------------------------------------------------------
void AFR_TrendLine(string name,datetime t1,double p1,datetime t2,double p2,
color c,int width,ENUM_LINE_STYLE style)
{
if(ObjectFind(0,name)<0) ObjectCreate(0,name,OBJ_TREND,0,t1,p1,t2,p2);
else { ObjectMove(0,name,0,t1,p1); ObjectMove(0,name,1,t2,p2); }
ObjectSetInteger(0,name,OBJPROP_COLOR,c);
ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
ObjectSetInteger(0,name,OBJPROP_STYLE,style);
ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
}
void AFR_Text(string name,datetime t,double price,string text,color c,int fs,
ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT)
{
if(ObjectFind(0,name)<0) ObjectCreate(0,name,OBJ_TEXT,0,t,price);
else ObjectMove(0,name,0,t,price);
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fs);
ObjectSetInteger(0,name,OBJPROP_COLOR,c);
ObjectSetInteger(0,name,OBJPROP_ANCHOR,anchor);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
}
void AFR_Box(string name,datetime t1,double p1,datetime t2,double p2,
color fill,color border,bool back)
{
if(ObjectFind(0,name)<0) ObjectCreate(0,name,OBJ_RECTANGLE,0,t1,p1,t2,p2);
else { ObjectMove(0,name,0,t1,p1); ObjectMove(0,name,1,t2,p2); }
// OBJ_RECTANGLE: border = OBJPROP_COLOR, fill = OBJPROP_BGCOLOR + FILL
ObjectSetInteger(0,name,OBJPROP_COLOR,border);
ObjectSetInteger(0,name,OBJPROP_BGCOLOR,fill);
ObjectSetInteger(0,name,OBJPROP_FILL,true);
ObjectSetInteger(0,name,OBJPROP_BACK,back);
ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
}
void AFR_Panel(const AFRenderCfg &cfg,string name,ENUM_BASE_CORNER corner,
int x,int y,int w,int h)
{
if(ObjectFind(0,name)<0) ObjectCreate(0,name,OBJ_RECTANGLE_LABEL,0,0,0);
ObjectSetInteger(0,name,OBJPROP_CORNER,corner);
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
ObjectSetInteger(0,name,OBJPROP_XSIZE,w);
ObjectSetInteger(0,name,OBJPROP_YSIZE,h);
ObjectSetInteger(0,name,OBJPROP_BGCOLOR,cfg.panelTransparent? clrNONE : cfg.panelBG);
ObjectSetInteger(0,name,OBJPROP_BORDER_TYPE,BORDER_FLAT);
ObjectSetInteger(0,name,OBJPROP_COLOR,cfg.panelBorder);
ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
}
void AFR_PanelLabel(const AFRenderCfg &cfg,string name,ENUM_BASE_CORNER corner,
int x,int y,string text,color c,int fs,bool bold=false,
ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER)
{
if(ObjectFind(0,name)<0) ObjectCreate(0,name,OBJ_LABEL,0,0,0);
ObjectSetInteger(0,name,OBJPROP_CORNER,corner);
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
ObjectSetInteger(0,name,OBJPROP_ANCHOR,anchor);
ObjectSetString(0,name,OBJPROP_TEXT,text);
ObjectSetString(0,name,OBJPROP_FONT,bold? cfg.fontName+" Bold" : cfg.fontName);
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fs);
ObjectSetInteger(0,name,OBJPROP_COLOR,c);
ObjectSetInteger(0,name,OBJPROP_BACK,false);
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
}
//------------------------------------------------------------------
// Dashboard (meniru layout kode asli 75466)
//------------------------------------------------------------------
void AFR_DrawDashboard(const AFRenderCfg &cfg,const AFDisplayData &dd,const AFAggOut &agg)
{
string P=cfg.prefix;
int fs=cfg.fontSize;
color bc=(dd.structure=="Bullish")? cfg.bull :
(dd.structure=="Bearish")? cfg.bear : clrSilver;
// ---- panel bias (kiri-atas) ----
AFR_Panel(cfg,P+"P_BIAS",CORNER_LEFT_UPPER,12,30,252,180);
AFR_PanelLabel(cfg,P+"L_bt",CORNER_LEFT_UPPER,24,40,
"AF BIAS: "+(dd.structure=="Range"? "RANGE" : dd.structure),
bc,fs+2,true);
int y=72, dy=22, xk=24, xv=132;
AFR_PanelLabel(cfg,P+"L_b1k",CORNER_LEFT_UPPER,xk,y,"Structure:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_b1v",CORNER_LEFT_UPPER,xv,y,dd.structure,bc,fs); y+=dy;
AFR_PanelLabel(cfg,P+"L_b2k",CORNER_LEFT_UPPER,xk,y,"EQ Pos:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_b2v",CORNER_LEFT_UPPER,xv,y,dd.eqPos,
dd.eqPos=="Discount"? cfg.bull :
(dd.eqPos=="Premium"? cfg.bear : clrSilver),fs); y+=dy;
AFR_PanelLabel(cfg,P+"L_b3k",CORNER_LEFT_UPPER,xk,y,"Liquidity:",cfg.textCol,fs);
string liq=(dd.eqhSwept||dd.eqlSwept)? "Swept" : "Intact";
AFR_PanelLabel(cfg,P+"L_b3v",CORNER_LEFT_UPPER,xv,y,liq,
(dd.eqhSwept||dd.eqlSwept)? cfg.accent : clrSilver,fs); y+=dy;
AFR_PanelLabel(cfg,P+"L_b4k",CORNER_LEFT_UPPER,xk,y,"Signal:",cfg.textCol,fs);
string sig=(agg.dir>0)? "BUY" : (agg.dir<0)? "SELL" : "WAIT";
color sc=(agg.dir>0)? cfg.bull : (agg.dir<0)? cfg.bear : clrSilver;
AFR_PanelLabel(cfg,P+"L_b4v",CORNER_LEFT_UPPER,xv,y,sig,sc,fs); y+=dy;
AFR_PanelLabel(cfg,P+"L_b5k",CORNER_LEFT_UPPER,xk,y,"Score:",cfg.textCol,fs);
int pct=(int)(agg.score*100.0);
AFR_PanelLabel(cfg,P+"L_b5v",CORNER_LEFT_UPPER,xv,y,IntegerToString(pct)+"%",
pct>=60? cfg.bull : (pct>=30? clrGold : cfg.bear),fs);
// ---- panel bawah-kiri: ringkasan struktur / likuiditas / konteks / level / alignment ----
int bw=180, bh=78, gap=10, bx=14, by=14;
AFR_Panel(cfg,P+"P_d1",CORNER_LEFT_LOWER,bx,by+bh,bw,bh);
AFR_PanelLabel(cfg,P+"L_d1t",CORNER_LEFT_LOWER,bx+12,by+bh-12,"STRUCTURE SUMMARY",cfg.textCol,fs,true);
AFR_PanelLabel(cfg,P+"L_d1a",CORNER_LEFT_LOWER,bx+12,by+bh-34,"HH: "+IntegerToString(dd.HH),cfg.bull,fs);
AFR_PanelLabel(cfg,P+"L_d1b",CORNER_LEFT_LOWER,bx+95,by+bh-34,"HL: "+IntegerToString(dd.HL),cfg.bull,fs);
AFR_PanelLabel(cfg,P+"L_d1c",CORNER_LEFT_LOWER,bx+12,by+bh-56,"LH: "+IntegerToString(dd.LH),cfg.bear,fs);
AFR_PanelLabel(cfg,P+"L_d1d",CORNER_LEFT_LOWER,bx+95,by+bh-56,"LL: "+IntegerToString(dd.LL),cfg.bear,fs);
int x2=bx+bw+gap;
AFR_Panel(cfg,P+"P_d2",CORNER_LEFT_LOWER,x2,by+bh,bw,bh);
AFR_PanelLabel(cfg,P+"L_d2t",CORNER_LEFT_LOWER,x2+12,by+bh-12,"LIQUIDITY STATUS",cfg.textCol,fs,true);
AFR_PanelLabel(cfg,P+"L_d2a",CORNER_LEFT_LOWER,x2+12,by+bh-40,"EQH Swept:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_d2av",CORNER_LEFT_LOWER,x2+108,by+bh-40,
dd.eqhSwept? "Yes":"No", dd.eqhSwept? cfg.accent : clrGray,fs);
AFR_PanelLabel(cfg,P+"L_d2b",CORNER_LEFT_LOWER,x2+12,by+bh-62,"EQL Swept:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_d2bv",CORNER_LEFT_LOWER,x2+108,by+bh-62,
dd.eqlSwept? "Yes":"No", dd.eqlSwept? cfg.accent : clrGray,fs);
int x3=x2+bw+gap;
AFR_Panel(cfg,P+"P_d3",CORNER_LEFT_LOWER,x3,by+bh,bw,bh);
AFR_PanelLabel(cfg,P+"L_d3t",CORNER_LEFT_LOWER,x3+12,by+bh-12,"MARKET CONTEXT",cfg.textCol,fs,true);
AFR_PanelLabel(cfg,P+"L_d3a",CORNER_LEFT_LOWER,x3+12,by+bh-40,"Price in:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_d3av",CORNER_LEFT_LOWER,x3+95,by+bh-40,dd.eqPos,
dd.eqPos=="Discount"? cfg.bull :
(dd.eqPos=="Premium"? cfg.bear : clrSilver),fs);
AFR_PanelLabel(cfg,P+"L_d3b",CORNER_LEFT_LOWER,x3+12,by+bh-62,"Bias:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_d3bv",CORNER_LEFT_LOWER,x3+95,by+bh-62,dd.structure,bc,fs);
int x4=x3+bw+gap;
AFR_Panel(cfg,P+"P_d4",CORNER_LEFT_LOWER,x4,by+bh,bw+30,bh);
AFR_PanelLabel(cfg,P+"L_d4t",CORNER_LEFT_LOWER,x4+12,by+bh-12,"KEY LEVELS",cfg.textCol,fs,true);
AFR_PanelLabel(cfg,P+"L_d4a",CORNER_LEFT_LOWER,x4+12,by+bh-40,
"PWH: "+(dd.pwh>0? DoubleToString(dd.pwh,_Digits) : "-"),cfg.accent,fs);
AFR_PanelLabel(cfg,P+"L_d4b",CORNER_LEFT_LOWER,x4+118,by+bh-40,
"PDL: "+(dd.pdl>0? DoubleToString(dd.pdl,_Digits) : "-"),cfg.accent,fs);
AFR_PanelLabel(cfg,P+"L_d4c",CORNER_LEFT_LOWER,x4+12,by+bh-62,
"PDH: "+(dd.pdh>0? DoubleToString(dd.pdh,_Digits) : "-"),cfg.accent,fs);
AFR_PanelLabel(cfg,P+"L_d4d",CORNER_LEFT_LOWER,x4+118,by+bh-62,
"PWL: "+(dd.pwl>0? DoubleToString(dd.pwl,_Digits) : "-"),cfg.accent,fs);
int x5=x4+bw+30+gap;
int bh5=bh+22;
AFR_Panel(cfg,P+"P_d5",CORNER_LEFT_LOWER,x5,by+bh5,bw,bh5);
AFR_PanelLabel(cfg,P+"L_d5t",CORNER_LEFT_LOWER,x5+12,by+bh5-12,"ALIGNMENT (N/C/E/P)",cfg.textCol,fs,true);
AFR_PanelLabel(cfg,P+"L_d5a",CORNER_LEFT_LOWER,x5+12,by+bh5-36,"Narr:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_d5av",CORNER_LEFT_LOWER,x5+95,by+bh5-36,dd.nBias,
dd.nBias=="Bullish"? cfg.bull : (dd.nBias=="Bearish"? cfg.bear : clrSilver),fs);
AFR_PanelLabel(cfg,P+"L_d5b",CORNER_LEFT_LOWER,x5+12,by+bh5-58,"Cont:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_d5bv",CORNER_LEFT_LOWER,x5+95,by+bh5-58,dd.cBias,
dd.cBias=="Bullish"? cfg.bull : (dd.cBias=="Bearish"? cfg.bear : clrSilver),fs);
AFR_PanelLabel(cfg,P+"L_d5c",CORNER_LEFT_LOWER,x5+12,by+bh5-80,"Entry:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_d5cv",CORNER_LEFT_LOWER,x5+95,by+bh5-80,dd.eBias,
dd.eBias=="Bullish"? cfg.bull : (dd.eBias=="Bearish"? cfg.bear : clrSilver),fs);
AFR_PanelLabel(cfg,P+"L_d5d",CORNER_LEFT_LOWER,x5+12,by+bh5-102,"PA:",cfg.textCol,fs);
AFR_PanelLabel(cfg,P+"L_d5dv",CORNER_LEFT_LOWER,x5+95,by+bh5-102,dd.pBias,
dd.pBias=="Bullish"? cfg.bull : (dd.pBias=="Bearish"? cfg.bear : clrSilver),fs);
// ---- trade setup (kanan-atas) ----
bool active=(agg.dir>0 || agg.dir<0);
int rows=active? 5 : 1;
int pw=212, px=14, py=30, sdy=21;
int ph=64+rows*sdy+8;
AFR_Panel(cfg,P+"P_sig",CORNER_RIGHT_UPPER,px,py,pw,ph);
AFR_PanelLabel(cfg,P+"L_st",CORNER_RIGHT_UPPER,px+pw-14,py+10,"TRADE SETUP",cfg.textCol,fs,true,ANCHOR_RIGHT_UPPER);
int sy=py+38, kx=px+pw-14, vx=px+12;
AFR_PanelLabel(cfg,P+"L_s0",CORNER_RIGHT_UPPER,kx,sy,"Signal:",cfg.textCol,fs+1,true,ANCHOR_RIGHT_UPPER);
AFR_PanelLabel(cfg,P+"L_s0v",CORNER_RIGHT_UPPER,vx,sy,sig,sc,fs+1,true,ANCHOR_RIGHT_UPPER);
sy+=sdy+6;
if(active)
{
AFR_PanelLabel(cfg,P+"SR_2k",CORNER_RIGHT_UPPER,kx,sy,"Entry:",cfg.textCol,fs,false,ANCHOR_RIGHT_UPPER);
AFR_PanelLabel(cfg,P+"SR_2v",CORNER_RIGHT_UPPER,vx,sy,DoubleToString(agg.entry,_Digits),cfg.textCol,fs,false,ANCHOR_RIGHT_UPPER); sy+=sdy;
AFR_PanelLabel(cfg,P+"SR_3k",CORNER_RIGHT_UPPER,kx,sy,"SL:",cfg.textCol,fs,false,ANCHOR_RIGHT_UPPER);
AFR_PanelLabel(cfg,P+"SR_3v",CORNER_RIGHT_UPPER,vx,sy,DoubleToString(agg.sl,_Digits),cfg.sigSL,fs,false,ANCHOR_RIGHT_UPPER); sy+=sdy;
AFR_PanelLabel(cfg,P+"SR_4k",CORNER_RIGHT_UPPER,kx,sy,"TP:",cfg.textCol,fs,false,ANCHOR_RIGHT_UPPER);
AFR_PanelLabel(cfg,P+"SR_4v",CORNER_RIGHT_UPPER,vx,sy,DoubleToString(agg.tp,_Digits),cfg.sigTP,fs,false,ANCHOR_RIGHT_UPPER); sy+=sdy;
double rr=0.0;
if(agg.dir>0 && agg.entry>agg.sl && agg.sl>0) rr=(agg.tp-agg.entry)/(agg.entry-agg.sl);
if(agg.dir<0 && agg.sl>agg.entry && agg.entry>0) rr=(agg.entry-agg.tp)/(agg.sl-agg.entry);
AFR_PanelLabel(cfg,P+"SR_5k",CORNER_RIGHT_UPPER,kx,sy,"R:R:",cfg.textCol,fs,false,ANCHOR_RIGHT_UPPER);
AFR_PanelLabel(cfg,P+"SR_5v",CORNER_RIGHT_UPPER,vx,sy,"1 : "+DoubleToString(rr,1),
rr>=2.0? cfg.bull : clrGold,fs,false,ANCHOR_RIGHT_UPPER);
}
else
{
string note=agg.reason;
if(StringLen(note)>60) note=StringSubstr(note,0,57)+"...";
AFR_PanelLabel(cfg,P+"SR_w",CORNER_RIGHT_UPPER,kx,sy,note,clrSilver,fs-1,false,ANCHOR_RIGHT_UPPER);
}
// ---- legenda (kanan-bawah) ----
int lw=185, lh=140, lx=14, ly=14+bh+gap;
AFR_Panel(cfg,P+"P_leg",CORNER_RIGHT_LOWER,lx,ly+lh,lw,lh);
AFR_PanelLabel(cfg,P+"L_legt",CORNER_RIGHT_LOWER,lx+lw-12,ly+lh-12,"LEGEND STATUS",cfg.textCol,fs,true,ANCHOR_RIGHT_UPPER);
string it[5]={"Structure","Order Blocks","FVG","Premium/Discount","Liquidity"};
bool on[5]={cfg.showStructure||cfg.showSwingPoints,cfg.showOB,cfg.showFVG,cfg.showPD,cfg.showLevels};
int yy=ly+lh-36;
for(int i=0;i<5;i++)
{
AFR_PanelLabel(cfg,P+"L_le"+IntegerToString(i)+"a",CORNER_RIGHT_LOWER,lx+lw-12,yy,it[i],cfg.textCol,fs,false,ANCHOR_RIGHT_UPPER);
AFR_PanelLabel(cfg,P+"L_le"+IntegerToString(i)+"b",CORNER_RIGHT_LOWER,lx+12,yy,on[i]? "On":"Off",
on[i]? cfg.bull : clrGray,fs,false,ANCHOR_RIGHT_UPPER);
yy-=22;
}
}
//------------------------------------------------------------------
// Gambar SEMUA elemen display (dipanggil konsumen per bar tertutup)
//------------------------------------------------------------------
void AFR_DrawAll(const AFRenderCfg &cfg,const AFEngine1MTF &e1,int sDisp,
const AFDisplayData &dd,const AFSignalOut &n,const AFSignalOut &c,
const AFSignalOut &e,const AFSignalOut &p,const AFAggOut &agg)
{
string P=cfg.prefix;
datetime tR=dd.dispEnd;
if(tR<=0) tR=TimeCurrent();
// ---- struktur (BOS/CHoCH) ----
if(cfg.showStructure)
{
int nL=ArraySize(dd.structLines);
for(int i=0;i<nL;i++)
{
string nm=P+"ST_"+IntegerToString(i);
color col=(dd.structLines[i].dir>0)? cfg.bull : cfg.bear;
AFR_TrendLine(nm,dd.structLines[i].t1,dd.structLines[i].price,
dd.structLines[i].t2,dd.structLines[i].price,col,2,STYLE_SOLID);
AFR_Text(nm+"_t",(datetime)((dd.structLines[i].t1+dd.structLines[i].t2)/2),
dd.structLines[i].price+20*_Point,dd.structLines[i].lbl,col,
cfg.fontSize-1,ANCHOR_LOWER);
}
}
// ---- swing points (HH/HL/LH/LL) ----
if(cfg.showSwingPoints)
{
int nP=ArraySize(dd.pivots);
for(int i=0;i<nP;i++)
{
string nm=P+"SP_"+IntegerToString(i);
color col=(dd.pivots[i].tag=="HH"||dd.pivots[i].tag=="HL")? cfg.bull : cfg.bear;
double y=dd.pivots[i].isHigh? dd.pivots[i].price+28*_Point : dd.pivots[i].price-28*_Point;
AFR_Text(nm,dd.pivots[i].time,y,dd.pivots[i].tag,col,cfg.fontSize,
dd.pivots[i].isHigh? ANCHOR_LOWER : ANCHOR_UPPER);
}
}
// ---- order blocks ----
if(cfg.showOB)
{
int nZ=ArraySize(dd.ob);
int nDraw=MathMin(nZ,cfg.obCount);
for(int i=0;i<nDraw;i++)
{
string nm=P+"OB_"+IntegerToString(i);
color line=dd.ob[i].bull? cfg.obLineBull : cfg.obLineBear;
color fill=dd.ob[i].bull? cfg.obFillBull : cfg.obFillBear;
AFR_Box(nm,dd.ob[i].t,dd.ob[i].top,tR,dd.ob[i].bot,fill,line,true);
AFR_Text(nm+"_t",dd.ob[i].t,(dd.ob[i].top+dd.ob[i].bot)/2.0,
(dd.ob[i].bull? "Bullish OB " : "Bearish OB ")+
AF_E2_TfName(e1.TfOf(sDisp)),line,cfg.fontSize-1,ANCHOR_LEFT);
}
}
// ---- FVG ----
if(cfg.showFVG)
{
int nZ=ArraySize(dd.fvg);
int nDraw=MathMin(nZ,cfg.fvgCount);
for(int i=0;i<nDraw;i++)
{
string nm=P+"FVG_"+IntegerToString(i);
color line=dd.fvg[i].bull? cfg.fvgLineBull : cfg.fvgLineBear;
color fill=dd.fvg[i].bull? cfg.fvgFillBull : cfg.fvgFillBear;
AFR_Box(nm,dd.fvg[i].t,dd.fvg[i].top,tR,dd.fvg[i].bot,fill,line,true);
AFR_Text(nm+"_t",dd.fvg[i].t,(dd.fvg[i].top+dd.fvg[i].bot)/2.0,
(dd.fvg[i].bull? "Bullish FVG " : "Bearish FVG ")+
AF_E2_TfName(e1.TfOf(sDisp)),line,cfg.fontSize-1,ANCHOR_LEFT);
}
}
// ---- premium / discount ----
if(cfg.showPD && dd.swHigh>dd.swLow)
{
double rng=dd.swHigh-dd.swLow;
double pp=AF_E3_PD_PREMIUM_PCT/100.0;
double eqh=AF_E3_PD_EQUIL_PCT/2.0/100.0;
datetime t1=dd.dispStart;
if(t1<=0) t1=(datetime)((long)tR-86400);
AFR_Box(P+"PD_PR",t1,dd.swHigh,tR,dd.swLow+(1.0-pp)*rng,cfg.premiumFill,cfg.premiumLine,true);
AFR_Box(P+"PD_EQ",t1,dd.swLow+(0.5+eqh)*rng,tR,dd.swLow+(0.5-eqh)*rng,cfg.equilFill,cfg.equilLine,true);
AFR_Box(P+"PD_DC",t1,dd.swLow+pp*rng,tR,dd.swLow,cfg.discountFill,cfg.discountLine,true);
}
// ---- level MTF (PDH/PDL/PWH/PWL) ----
if(cfg.showLevels)
{
datetime t1=dd.dispStart;
if(t1<=0) t1=(datetime)((long)tR-86400);
if(dd.pdh>0.0)
{
AFR_TrendLine(P+"LV_PDH",t1,dd.pdh,tR,dd.pdh,cfg.mtfLevel,1,STYLE_SOLID);
AFR_Text(P+"LV_PDH_t",tR,dd.pdh,"PDH "+DoubleToString(dd.pdh,_Digits),cfg.mtfLevel,cfg.fontSize-1,ANCHOR_LEFT);
}
if(dd.pdl>0.0)
{
AFR_TrendLine(P+"LV_PDL",t1,dd.pdl,tR,dd.pdl,cfg.mtfLevel,1,STYLE_SOLID);
AFR_Text(P+"LV_PDL_t",tR,dd.pdl,"PDL "+DoubleToString(dd.pdl,_Digits),cfg.mtfLevel,cfg.fontSize-1,ANCHOR_LEFT);
}
if(dd.pwh>0.0)
{
AFR_TrendLine(P+"LV_PWH",t1,dd.pwh,tR,dd.pwh,cfg.mtfLevel,1,STYLE_DASH);
AFR_Text(P+"LV_PWH_t",tR,dd.pwh,"PWH "+DoubleToString(dd.pwh,_Digits),cfg.mtfLevel,cfg.fontSize-1,ANCHOR_LEFT);
}
if(dd.pwl>0.0)
{
AFR_TrendLine(P+"LV_PWL",t1,dd.pwl,tR,dd.pwl,cfg.mtfLevel,1,STYLE_DASH);
AFR_Text(P+"LV_PWL_t",tR,dd.pwl,"PWL "+DoubleToString(dd.pwl,_Digits),cfg.mtfLevel,cfg.fontSize-1,ANCHOR_LEFT);
}
}
// ---- sinyal (entry/SL/TP + panah) ----
if(cfg.showSignals)
{
datetime t1=dd.dispStart;
if(t1<=0) t1=(datetime)((long)tR-86400);
if(agg.dir>0 || agg.dir<0)
{
bool buy=(agg.dir>0);
color cE=buy? cfg.sigEntry : cfg.bear;
AFR_TrendLine(P+"SIG_E",t1,agg.entry,tR,agg.entry,cE,2,STYLE_SOLID);
AFR_Text(P+"SIG_E_t",tR,agg.entry,
(buy? "BUY @ " : "SELL @ ")+DoubleToString(agg.entry,_Digits),
cE,cfg.fontSize-1,ANCHOR_LEFT);
if(agg.sl>0.0)
{
AFR_TrendLine(P+"SIG_SL",t1,agg.sl,tR,agg.sl,cfg.sigSL,1,STYLE_DASH);
AFR_Text(P+"SIG_SL_t",tR,agg.sl,"SL "+DoubleToString(agg.sl,_Digits),cfg.sigSL,cfg.fontSize-1,ANCHOR_LEFT);
}
if(agg.tp>0.0)
{
AFR_TrendLine(P+"SIG_TP",t1,agg.tp,tR,agg.tp,cfg.sigTP,1,STYLE_DOT);
AFR_Text(P+"SIG_TP_t",tR,agg.tp,"TP "+DoubleToString(agg.tp,_Digits),cfg.sigTP,cfg.fontSize-1,ANCHOR_LEFT);
}
datetime tSig=(e1.IsReady(sDisp))? e1.Time(sDisp,0) : tR;
string nm=P+"SIG_AR";
if(ObjectFind(0,nm)<0) ObjectCreate(0,nm,OBJ_ARROW,0,tSig,agg.entry);
else ObjectMove(0,nm,0,tSig,agg.entry);
ObjectSetInteger(0,nm,OBJPROP_ARROWCODE,buy? 233 : 234);
ObjectSetInteger(0,nm,OBJPROP_COLOR,cE);
ObjectSetInteger(0,nm,OBJPROP_WIDTH,2);
ObjectSetInteger(0,nm,OBJPROP_ANCHOR,buy? ANCHOR_TOP : ANCHOR_BOTTOM);
ObjectSetInteger(0,nm,OBJPROP_SELECTABLE,false);
}
}
// ---- dashboard ----
if(cfg.showDashboard) AFR_DrawDashboard(cfg,dd,agg);
}
//------------------------------------------------------------------
// Bersihkan semua objek Engine 3 dari chart
//------------------------------------------------------------------
void AFR_Clear(const AFRenderCfg &cfg)
{
ObjectsDeleteAll(0,cfg.prefix);
}
#endif // AF_ENGINE3_RENDER_MQH