MQL5: CopyHigh() gives strange results in Tester

 

Below is a simple test script. It uses CopyHigh() to grab the high of the week so that I can create an envelope around the daily highs.

Thus, it runs on PERIOD_D1 chart and pulls PERIOD_W1 values.

It works as I would expect on the chart (picture 1).

picture 1

But in the Visualization window of the Tester, I get all sorts of strange values (picture 2).

picture 2

Here are the settings I used:

settings 1

So here's an interesting tidbit: it only gives the strange values within the specified Custom period of the Tester, year 2017. The preceding data appears as I would expect. That is, all data from:

2018.09.17 15:03:57.237    USDCAD,Weekly: history begins from 2014.12.28 00:00

to the start of the Custom period.

Any thoughts?

#property description    "CopyHigh() Test"
#property strict

//--- indicator settings
#property indicator_chart_window

#property indicator_buffers     1
#property indicator_plots       1

#property indicator_type1        DRAW_LINE
#property indicator_color1       Red
#property indicator_width1       2
#property indicator_style1       STYLE_SOLID

//---- constants
const int                        DEBUG = 0;

//---- indicator buffers
double    ExtNextTfHigh[];

//---- globals
ENUM_TIMEFRAMES    g_chartTimeFrame = PERIOD_D1;
ENUM_TIMEFRAMES    g_nextTimeFrame = PERIOD_W1;


int OnInit()
{
    Comment("");

    string shortName = "TEST";

    // indicator buffers mappings
    SetIndexBuffer(0,ExtNextTfHigh,INDICATOR_DATA);      // weekly high

    PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,5);
    PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);            // Set as an empty value 0
    PlotIndexSetString(0,PLOT_LABEL,"WK-HI");

    IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
    IndicatorSetString(INDICATOR_SHORTNAME,shortName);

    // initialization done
    return(INIT_SUCCEEDED);
}
//---------------------------------------------------------------------
//    Need for indicator recalculation event handler:
//---------------------------------------------------------------------
int OnCalculate (const int rates_total,      // size of input time series
                 const int prev_calculated,  // bars handled in previous call
                 const datetime& time[],     // Time
                 const double& open[],       // Open
                 const double& high[],       // High
                 const double& low[],        // Low
                 const double& close[],      // Close
                 const long& tick_volume[],  // Tick Volume
                 const long& volume[],       // Real Volume
                 const int& spread[]         // Spread
   )
{

    // correct position, when it's first iteration
    int pos = prev_calculated - 1;
    if ( pos <= 0 )
    {
        pos = 1;
    }

    //--- the main loop of calculations
    for(int i=pos;i<rates_total && !IsStopped();i++)
    {
        ExtNextTfHigh[i] = 0.;

        double NXTF_High = pWrapCopyHigh(i, time[i]);
        if ( NXTF_High == NULL )
        {
            return ( i - 1 );
        }

        ExtNextTfHigh[i] = NXTF_High;
    }

    return(rates_total);
}

//+------------------------------------------------------------------+

double pWrapCopyHigh(const int i, const datetime bartime)
{
    double tmpHighArray[1] = {0.};    // Intermediate array for the time of the bar.

    if ( DEBUG )
    {
        PrintFormat("%s(): i [%d] [%s] Bars [%d] TF [%s]",
                    __FUNCTION__,
                    i,
                    TimeToString(bartime),
                    Bars(NULL,g_nextTimeFrame),
                    EnumToString(g_nextTimeFrame));
    }

    ResetLastError();
    int copiedCount = CopyHigh(NULL,g_nextTimeFrame,bartime,1,tmpHighArray);    // Requesting only 1 bar!
    int lastError = GetLastError();
    if ( copiedCount == -1 )
    {
        PrintFormat("%s(): copiedCount is -1. i [%d] TF [%s] bartime [%s]. Error [%d]",
                    __FUNCTION__,
                    i,
                    EnumToString(g_nextTimeFrame),
                    TimeToString(bartime),
                    lastError);

        // This is OK. I'll just let OnCalculate() know, and it will run that bar again.
        return NULL;
    }

    if ( DEBUG )
    {
        PrintFormat("i [%d] [%s] copied count [%d] TF [%s] value [%.5f]",
                    i,
                    TimeToString(bartime),
                    copiedCount,
                    EnumToString(g_nextTimeFrame),
                    tmpHighArray[0]);
    }

    return tmpHighArray[0];
}

 
Fernando Carreiro:

Yes, I have seen that post. I modeled my own code after it. Of course, I took the Sleep()'s out.

That's not to say I don't have a bug somewhere.
 
Anthony Garot: Yes, I have seen that post. I modeled my own code after it. Of course, I took the Sleep()'s out.

Is it your intention to use the High of the current week or the prior week?

Your code is currently looking at the current week so it is "repainting" of sorts as the current weekly high can change as the days go by!

EDIT: If you really intend to use the current week, then you would have to recalculate the few previous bars so as to "repaint" the correct updated high for those previous days of the current week.

 
Fernando Carreiro:

Is it your intention to use the High of the current week or the prior week?

Your code is currently looking at the current week so it is "repainting" of sorts!

Ah! Interesting. I think you are on to something . . . .

I only want to calculate when I have a full week. Yup, I think I am repainting, which looks fine on the chart, but the Tester chokes on it.
 
Anthony Garot:Ah! Interesting. I think you are on to something . . . .
I edited my post, so read it again please!
 
Fernando Carreiro:
I edited my post, so read it again please!

I only want to calculate when I have a full week. Yup, I think I am repainting, which looks fine on the chart, but the Tester chokes on it.

I'll chase this down, but I am pretty sure you nailed it.

Thanks man!

 
Anthony Garot:

I only want to calculate when I have a full week. Yup, I think I am repainting, which looks fine on the chart, but the Tester chokes on it.

I'll chase this down, but I am pretty sure you nailed it.

Thanks man!

You are welcome!
 

Here is my modified test script that shows the W1 high on D1 bars. It may be useful to someone else who is trying to write a multi-timeframe indicator.

This test indicator:

1. Pulls W1 data when needed.

2. Works in the Tester. That is, it doesn't repaint because it uses a lookback when a new week is formed.


Thanks again to @Fernando Carreiro who pointed me in the right direction.

Files: