forked from chiki2bum2/SniperGold_ML
191 lines
8.1 KiB
MQL5
191 lines
8.1 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| AlgoForge_Engine1_UnitTest.mq5 |
| |||
//| Project : Algo Forge (refactor total) |
| |||
//| Sumber inspirasi: SniperGold SMC Pro+ (c) Waseem Shahrukh |
| |||
//| https://www.mql5.com/en/code/75466 |
| |||
//+------------------------------------------------------------------+
| |||
//| Unit test minimal Engine 1 (Data Collector Bar MTF + cache). |
| |||
//| Jalankan di Strategy Tester (mis. XAUUSDc M15, model 1-min OHLC).|
| |||
//| Semua hasil dicetak di journal dengan prefix "AFTEST". |
| |||
//| |
| |||
//| CEK YANG DIJALANKAN: |
| |||
//| T1 - cache ready & count>0 untuk tiap TF terdaftar |
| |||
//| T2 - validitas OHLC setiap bar |
| |||
//| T3 - urutan waktu strict (menurun dari index 0) |
| |||
//| T4 - closed-bar lock: SEMUA bar di cache sudah tertutup |
| |||
//| T5 - anti-freeze: CopyRates HANYA saat Bars() berubah |
| |||
//| T6 - konsistensi Count() vs Bars() |
| |||
//| T7 - independensi chart-TF: Engine memakai TF eksplisit |
| |||
//| (PERIOD_CURRENT ditolak saat Register; verifikasi statis |
| |||
//| dengan grep di kode sumber) |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Algo Forge - refactor SniperGold SMC Pro+ (Waseem Shahrukh, https://www.mql5.com/en/code/75466)"
| |||
#property version "1.00"
| |||
#property strict
| |||
#property description "Unit test Engine 1: MTF bar collector + cache index reusable. Jalankan di Strategy Tester; hasil di journal (prefix AFTEST)."
| |||
| |||
#include <AlgoForge\AF_Engine1_MTFData.mqh>
| |||
| |||
input group "===== ENGINE 1 UNIT TEST ====="
| |||
input ENUM_TIMEFRAMES InpTF1 = PERIOD_M1; // TF uji #1
| |||
input ENUM_TIMEFRAMES InpTF2 = PERIOD_M5; // TF uji #2
| |||
input ENUM_TIMEFRAMES InpTF3 = PERIOD_M15; // TF uji #3
| |||
input ENUM_TIMEFRAMES InpTF4 = PERIOD_H1; // TF uji #4
| |||
input int InpMaxBars = 300; // Kapasitas cache per TF
| |||
input bool InpVerbose = false; // Log detail per bar (5 bar terbaru tiap slot)
| |||
| |||
AFEngine1MTF g_e1;
| |||
int g_s[4];
| |||
long g_lastBars[4];
| |||
int g_histAtLast[4];
| |||
int g_pass=0, g_fail=0;
| |||
bool g_initDone=false;
| |||
bool g_verbose=false;
| |||
| |||
//+------------------------------------------------------------------+
| |||
string SlotName(int i)
| |||
{
| |||
switch(i)
| |||
{
| |||
case 0: return EnumToString(InpTF1);
| |||
case 1: return EnumToString(InpTF2);
| |||
case 2: return EnumToString(InpTF3);
| |||
case 3: return EnumToString(InpTF4);
| |||
}
| |||
return "?";
| |||
}
| |||
| |||
void Pass(const string what) { g_pass++; if(g_verbose) Print("AFTEST PASS: "+what); }
| |||
void Fail(const string what) { g_fail++; Print("AFTEST FAIL: "+what); }
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Cek menyeluruh satu slot (dipanggil saat init & tiap bar baru) |
| |||
//+------------------------------------------------------------------+
| |||
void CheckSlot(int slot,const string name)
| |||
{
| |||
if(slot<0) { Fail(name+": slot registrasi gagal"); return; }
| |||
if(!g_e1.IsReady(slot))
| |||
{
| |||
PrintFormat("AFTEST [%s] SKIP: cache belum ready (Bars=%d)",name,(int)Bars(_Symbol,g_e1.TfOf(slot)));
| |||
return;
| |||
}
| |||
int cnt=g_e1.Count(slot);
| |||
if(cnt<=0) { Fail(name+": T1 count<=0"); return; }
| |||
Pass(name+": T1 ready, count="+IntegerToString(cnt));
| |||
| |||
bool sane=true, ordered=true, closed=true;
| |||
datetime now=TimeCurrent();
| |||
datetime newest=g_e1.Time(slot,0);
| |||
for(int i=0;i<cnt;i++)
| |||
{
| |||
AFBar b;
| |||
if(!g_e1.GetBar(slot,i,b)) { sane=false; continue; }
| |||
// T2 - validitas OHLC
| |||
if(b.high < MathMax(b.open,b.close)-1e-9) sane=false;
| |||
if(b.low > MathMin(b.open,b.close)+1e-9) sane=false;
| |||
if(b.high < b.low-1e-9) sane=false;
| |||
if(b.open<=0 || b.high<=0 || b.low<=0 || b.close<=0) sane=false;
| |||
// T3 - urutan waktu strict (index bertambah = makin tua)
| |||
if(i+1<cnt)
| |||
{
| |||
AFBar b2;
| |||
if(g_e1.GetBar(slot,i+1,b2) && b.time<=b2.time) ordered=false;
| |||
}
| |||
// T4 - closed-bar lock
| |||
if(!g_e1.IsBarClosed(b.time,g_e1.TfOf(slot),now)) closed=false;
| |||
}
| |||
if(sane) Pass(name+": T2 OHLC valid"); else Fail(name+": T2 OHLC invalid");
| |||
if(ordered) Pass(name+": T3 urutan waktu strict"); else Fail(name+": T3 urutan waktu rusak");
| |||
if(closed) Pass(name+": T4 semua bar tertutup (newest="+TimeToString(newest)+")");
| |||
else Fail(name+": T4 ada bar terbuka di cache");
| |||
| |||
// T6 - konsistensi dengan Bars()
| |||
long barsAll=Bars(_Symbol,g_e1.TfOf(slot));
| |||
if(barsAll>0 && cnt<=barsAll) Pass(name+": T6 count<=Bars ("+IntegerToString(cnt)+"/"+IntegerToString(barsAll)+")");
| |||
else if(barsAll>0) Fail(name+": T6 count>Bars (tidak mungkin)");
| |||
| |||
if(g_verbose) PrintFormat("AFTEST [%s] stat: count=%d bars=%d historyCalls=%d refresh=%d",
| |||
name,cnt,(int)barsAll,g_e1.HistoryCalls(slot),g_e1.RefreshCount(slot));
| |||
if(g_verbose)
| |||
{
| |||
for(int i=0;i<MathMin(5,cnt);i++)
| |||
{
| |||
AFBar b;
| |||
if(g_e1.GetBar(slot,i,b))
| |||
PrintFormat("AFTEST [%s] bar[%d] %s O=%.5f H=%.5f L=%.5f C=%.5f V=%d",
| |||
name,i,TimeToString(b.time),b.open,b.high,b.low,b.close,b.tick_volume);
| |||
}
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Init |
| |||
//+------------------------------------------------------------------+
| |||
int OnInit()
| |||
{
| |||
Print("AFTEST ============================================================");
| |||
Print("AFTEST Algo Forge Engine1 UnitTest v"+AF_PROJECT_VERSION);
| |||
Print("AFTEST Atribusi: "+AF_SOURCE_TITLE+" (c) "+AF_SOURCE_AUTHOR+" - "+AF_SOURCE_URL);
| |||
g_verbose=InpVerbose;
| |||
| |||
g_s[0]=g_e1.Register(InpTF1,InpMaxBars);
| |||
g_s[1]=g_e1.Register(InpTF2,InpMaxBars);
| |||
g_s[2]=g_e1.Register(InpTF3,InpMaxBars);
| |||
g_s[3]=g_e1.Register(InpTF4,InpMaxBars);
| |||
g_e1.Refresh(); // build awal (sekali per TF)
| |||
| |||
for(int i=0;i<4;i++)
| |||
{
| |||
if(g_s[i]>=0)
| |||
{
| |||
g_lastBars[i]=g_e1.LastBars(g_s[i]);
| |||
g_histAtLast[i]=g_e1.HistoryCalls(g_s[i]);
| |||
}
| |||
CheckSlot(g_s[i],SlotName(i));
| |||
}
| |||
PrintFormat("AFTEST slot terdaftar: %d (max %d)",g_e1.SlotCount(),AF_E1_MAX_SLOTS);
| |||
g_initDone=true;
| |||
return(INIT_SUCCEEDED);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Tick |
| |||
//+------------------------------------------------------------------+
| |||
void OnTick()
| |||
{
| |||
if(!g_initDone) return;
| |||
g_e1.Refresh(); // internal: skip slot tanpa bar baru
| |||
for(int i=0;i<4;i++)
| |||
{
| |||
int s=g_s[i];
| |||
if(s<0 || !g_e1.IsReady(s)) continue;
| |||
long lb=g_e1.LastBars(s);
| |||
int hc=g_e1.HistoryCalls(s);
| |||
if(lb!=g_lastBars[i]) // ada bar baru di TF ini
| |||
{
| |||
if(g_histAtLast[i]>0 && hc!=g_histAtLast[i]+1)
| |||
Fail(SlotName(i)+": T5 jumlah panggilan History saat bar baru tidak tepat "
| |||
+"(hc="+IntegerToString(hc)+" prev="+IntegerToString(g_histAtLast[i])+")");
| |||
g_lastBars[i]=lb;
| |||
g_histAtLast[i]=hc;
| |||
CheckSlot(s,SlotName(i)); // verifikasi ulang T2-T4
| |||
}
| |||
else if(hc!=g_histAtLast[i]) // TANPA bar baru tapi ada baca History -> ANTI-FREEZE GAGAL
| |||
{
| |||
Fail(SlotName(i)+": T5 anti-freeze dilanggar - panggilan History tanpa bar baru");
| |||
g_histAtLast[i]=hc;
| |||
}
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Tester summary |
| |||
//+------------------------------------------------------------------+
| |||
double OnTester()
| |||
{
| |||
Print("AFTEST ============================================================");
| |||
PrintFormat("AFTEST RESULT: PASS=%d FAIL=%d",g_pass,g_fail);
| |||
Print("AFTEST RESULT: "+(g_fail==0 ? "SEMUA CEK LULUS" : "ADA CEK GAGAL"));
| |||
for(int i=0;i<4;i++) if(g_s[i]>=0 && g_e1.IsReady(g_s[i])) PrintFormat("AFTEST final [%s]: count=%d historyCalls=%d",SlotName(i),g_e1.Count(g_s[i]),g_e1.HistoryCalls(g_s[i]));
| |||
return (g_fail==0 ? 1000.0 : 0.0);
| |||
}
|