sequential-countdown/tests/mql5/Experts/RepaintProbe/RepaintProbe.mq5
96 lines
3.9 KiB
MQL5
//+------------------------------------------------------------------+
//| RepaintProbe.mq5 |
//| Copyright 2026, Allan Niñal |
//| https://www.allanninal.dev/mql5 |
//+------------------------------------------------------------------+
//| Test tool for the Strategy Tester (never trades). On every new |
//| bar it records the indicator's buffers on the bar that just |
//| closed, as they were at that moment. At the end it reads the |
//| same bars again and counts every value that changed afterwards |
//| (= repainting). It also counts signals that were ever shown on |
//| the live bar. Writes Common\Files\repaint_<tag>.csv |
//+------------------------------------------------------------------+
// SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE)
#property copyright "Copyright 2026, Allan Niñal <https://www.allanninal.dev/mql5>"
#property version "1.00"
#include <IndTest\Params.mqh>
input string InpIndicator = "UTBotAlerts\\UTBotAlerts"; // indicator path under MQL5\Indicators
input string InpParams = ""; // parameters (empty = defaults)
input string InpBuffers = "2,3"; // buffers to check (signal arrows)
input string InpLiveBufs = "2,3"; // buffers that must stay empty on the live bar
input string InpTag = "probe"; // file name suffix
int g_ind=INVALID_HANDLE;
int g_bufs[],g_live[];
datetime g_last=0;
datetime g_t[];
double g_v[]; // [bar*nb + j]
long g_live_hits=0,g_ticks=0;
int OnInit()
{
ParseInts(InpBuffers,g_bufs);
ParseInts(InpLiveBufs,g_live);
g_ind=MakeHandle(_Symbol,_Period,InpIndicator,InpParams);
return(g_ind==INVALID_HANDLE ? INIT_FAILED : INIT_SUCCEEDED);
}
void OnTick()
{
g_ticks++;
double x[];
for(int j=0; j<ArraySize(g_live); j++) // a signal on the forming bar would be a repaint risk
if(CopyBuffer(g_ind,g_live[j],0,1,x)==1 && x[0]!=EMPTY_VALUE && x[0]!=0)
g_live_hits++;
datetime t0=iTime(_Symbol,_Period,0);
if(t0==g_last)
return;
g_last=t0;
datetime t1=iTime(_Symbol,_Period,1);
if(t1==0)
return;
int nb=ArraySize(g_bufs),k=ArraySize(g_t);
ArrayResize(g_t,k+1,100000);
ArrayResize(g_v,(k+1)*nb,100000*nb);
g_t[k]=t1;
for(int j=0; j<nb; j++)
g_v[k*nb+j]=(CopyBuffer(g_ind,g_bufs[j],1,1,x)==1 ? x[0] : -1);
}
void OnDeinit(const int reason)
{
int nb=ArraySize(g_bufs),n=ArraySize(g_t),changed=0,signals=0,checked=0;
int f=FileOpen("repaint_"+InpTag+".csv",FILE_WRITE|FILE_TXT|FILE_ANSI|FILE_COMMON);
string rows="";
for(int k=0; k<n; k++)
{
int sh=iBarShift(_Symbol,_Period,g_t[k],true);
if(sh<1)
continue;
checked++;
double x[];
for(int j=0; j<nb; j++)
{
double then=g_v[k*nb+j],now=(CopyBuffer(g_ind,g_bufs[j],sh,1,x)==1 ? x[0] : -1);
if(then!=EMPTY_VALUE && then!=0)
signals++;
bool same=(then==now) || (MathAbs(then-now)<=1e-9*MathMax(1.0,MathAbs(now)));
if(!same)
{
changed++;
if(changed<=20)
rows+=StringFormat("%s,buffer %d,then %.10g,now %.10g\n",TimeToString(g_t[k]),g_bufs[j],then,now);
}
}
}
if(f!=INVALID_HANDLE)
{
FileWriteString(f,StringFormat("indicator,%s\nparams,%s\nsymbol,%s\nperiod,%s\nticks,%I64d\nbars_checked,%d\nsignals,%d\nchanged_after_close,%d\nlive_bar_signals,%I64d\n",
InpIndicator,InpParams,_Symbol,EnumToString(_Period),g_ticks,checked,signals,changed,g_live_hits));
FileWriteString(f,rows);
FileWriteString(f,"DONE\n");
FileClose(f);
}
}
//+------------------------------------------------------------------+