Features of the mql5 language, subtleties and tricks - page 30

 

I don't get it at all...

I create a handle of a standard AO in the indicator, but with a set timeframe. When I receive data from AO with a timeframe that does not match the current one, I get ... I get nothing - the error 4806.

Question: What is the correct way to get data from standard indicators with timeframes that do not coincide with the current one?

//+------------------------------------------------------------------+
//|                                                      iMTF_AO.mq5 |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                       https://login.mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://login.mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot Label1
#property  indicator_label1  "AO"
#property  indicator_type1   DRAW_LINE
#property  indicator_color1  clrRed
#property  indicator_style1  STYLE_SOLID
#property  indicator_width1  1
//--- input parameters
sinput   ENUM_TIMEFRAMES   PeriodForWork  =  PERIOD_H4;  // Таймфрейм, с которого берём данные AO
//--- indicator buffers
double         Buffer[];
int   handle, error=ERR_SUCCESS;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,Buffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,Digits());
   handle=iAO(NULL,PeriodForWork);
   if(handle==INVALID_HANDLE) return(INIT_FAILED);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(Buffer,true);
   if(rates_total<1) return(0);
   int limit=rates_total-prev_calculated;
   if(limit>1) {
      limit=rates_total-1;
      }
   //---
   static string txt="";
   for(int i=limit; i>=0; i--) {
      Buffer[i]=AO(i);
      if(i<11 && i>0) {
         string ao=(Buffer[i]==EMPTY_VALUE?"EMPTY_VALUE":DoubleToString(AO(i),Digits()));
         txt+="\nAO("+(string)i+")="+ao;
         }
      }
   Comment("handle AO: ",handle,", TIMEFRAME AO: ",GetNameTF(PeriodForWork),", error: ",error,"\n-----------",txt,"\n-----------");
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
double AO(int shift){
   double array[1];
   error=ERR_SUCCESS;
   ResetLastError();
   if(CopyBuffer(handle,0,shift,1,array)==1) return(array[0]);
   else error=GetLastError();
   return(EMPTY_VALUE);
}
//+------------------------------------------------------------------+
string GetNameTF(int timeframe=PERIOD_CURRENT) {
   if(timeframe==PERIOD_CURRENT) timeframe=Period();
   switch(timeframe) {
      //--- MQL4
      case 1: return("M1");
      case 5: return("M5");
      case 15: return("M15");
      case 30: return("M30");
      case 60: return("H1");
      case 240: return("H4");
      case 1440: return("D1");
      case 10080: return("W1");
      case 43200: return("MN");
      //--- MQL5
      case 2: return("M2");
      case 3: return("M3");
      case 4: return("M4");      
      case 6: return("M6");
      case 10: return("M10");
      case 12: return("M12");
      case 16385: return("H1");
      case 16386: return("H2");
      case 16387: return("H3");
      case 16388: return("H4");
      case 16390: return("H6");
      case 16392: return("H8");
      case 16396: return("H12");
      case 16408: return("D1");
      case 32769: return("W1");
      case 49153: return("MN");      
      default: return("UnknownPeriod");
   }
}
//+------------------------------------------------------------------+
Files:
iMTF_AO.mq5  9 kb
 
Artyom Trishkin:

I don't get it at all...

I create a handle of a standard AO in the indicator, but with a set timeframe. When I receive data from AO with a timeframe that does not match the current one, I get ... I get nothing - the error 4806.

My question is: what is the correct way to get the data from standard indicators with timeframes that do not coincide with the current one?


About getting the INDICATOR values in the INDICATOR:

Forum on trading, automated trading systems and strategy testing

How to take data from another Indicator in an Indicator

Vladimir Karputov, 2016.12.27 08:41

Keeping in mind that in MQL5 indicators the bar with index "0" is by default the LEFT bar on the chart, let's try to get data in our indicator from two other indicators - MA and Alligator(this example in indicator "IndicatorFromIndicators.mql5").

Let's try to get data from MA and Alligator on the bar with index "0", "1" and "2" in our indicator:

  {
//---
   Comment("Проверка: time[0]=",time[0],"\n",
           "rates_total-1: ",rates_total,"\n",
           "BarsCalculated(iMA): ",BarsCalculated(handle_iMA),"\n",
           "BarsCalculated(iAlligator): ",BarsCalculated(handle_iAlligator),"\n",
           "MA[",0,"]=",StringFormat("%."+IntegerToString(Digits()+1)+"f",iMAGet(0)),"\n",
           "MA[",1,"]=",StringFormat("%."+IntegerToString(Digits()+1)+"f",iMAGet(1)),"\n",
           "MA[",2,"]=",StringFormat("%."+IntegerToString(Digits()+1)+"f",iMAGet(2)),"\n",
           "Jaws[",0,"]=",StringFormat("%."+IntegerToString(Digits())+"f",iAlligatorGet(GATORJAW_LINE,0)),"\n",
           "Jaws[",1,"]=",StringFormat("%."+IntegerToString(Digits())+"f",iAlligatorGet(GATORJAW_LINE,1)),"\n",
           "Jaws[",2,"]=",StringFormat("%."+IntegerToString(Digits())+"f",iAlligatorGet(GATORJAW_LINE,2)));
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Let's attach the test indicator"IndicatorFromIndicators.mql5" to the chart and set the crosshair to the RIGHTEST bar, i.e. it is not zero bar. This is what has come out:

IndicatorFromIndicators

Although the crosshair is set to the RIGHTEST bar - that is definitely not a bar with index "0", when usingCopyBuffer you should know thatCopyBuffer will copy data from present to past, i.e. a bar with index "0" means the current bar.


CopyBuffer: The data elements copied (indicator buffer with an index buffer_num) are counted from the starting position from the present to the past, that is, the starting position equal to 0 means the current bar (the indicator value for the current bar).


That is, in the MQL5 indicator, if you use the operation CopyBuffer, you should flip the array (ArraySetAsSeries), so that the rightmost bar in the chart corresponds to index "0" in the indicator buffer (now in the example "iMTF_AO.mq5" the rightmost bar in the chart corresponds to rates_total-1).

 
Vladimir Karputov:


About getting INDICATOR values in the INDICATOR:


CopyBuffer: The counting of elements of copied data (indicator buffer with an index buffer_num) from the starting position is made from the present to the past, that is, the starting position equal to 0 means the current bar (the indicator value for the current bar).


That is, in the MQL5 indicator, if it uses the operation CopyBuffer, you need to flip the array (ArraySetAsSeries), so that the rightmost bar in the chart corresponds to the index "0" in the indicator buffer (now in the example "iMTF_AO.mq5", the rightmost bar in the chart corresponds to rates_total-1).

I get only one bar. And the indicator normally displays data on the "native" timeframe. On the "non-native" - blank value. I figured out empirically that an empty value will be returned until the entire history is loaded for the timeframe from which I receive data from AO.

//+------------------------------------------------------------------+
//|                                                      iMTF_AO.mq5 |
//|              Copyright 2017, Artem A. Trishkin, Skype artmedia70 |
//|                       https://login.mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70"
#property link      "https://login.mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot Label1
#property  indicator_label1  "AO"
#property  indicator_type1   DRAW_LINE
#property  indicator_color1  clrRed
#property  indicator_style1  STYLE_SOLID
#property  indicator_width1  1
//--- input parameters
sinput   ENUM_TIMEFRAMES   PeriodForWork  =  PERIOD_H4;  // Таймфрейм, с которого берём данные AO
//--- indicator buffers
double         Buffer[];
int   handle, error=ERR_SUCCESS;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,Buffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,Digits());
   handle=iAO(NULL,PeriodForWork);
   if(handle==INVALID_HANDLE) return(INIT_FAILED);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(Buffer,true);
   int bars=Bars(NULL,PeriodForWork);
   datetime time_limit=GetTime(Symbol(),PeriodForWork,bars-1);
   int limit_p=GetBarShift(Symbol(),Period(),time_limit);
   if(rates_total<1) return(0);
   int limit=(PeriodForWork==Period()?rates_total-prev_calculated:limit_p);
   if(limit>1) {
      limit=rates_total-1;
      }
   //---
   static string txt="";
   for(int i=limit; i>=0; i--) {
      Buffer[i]=AO(i);
      if(i<6 && i>0) {
         string ao=(Buffer[i]==EMPTY_VALUE?"EMPTY_VALUE":DoubleToString(AO(i),Digits()));
         txt+="\nAO("+(string)i+")="+ao;
         }
      }
   Comment("handle AO: ",handle,", TIMEFRAME AO: ",GetNameTF(PeriodForWork),", error: ",error,"\n-----------",(error>0?"\nLoading history":txt),
           "\n-----------",
           "\nAO(1)=",DoubleToString(AO(1),Digits()),
           "\nAO(",limit_p,")=",DoubleToString(AO(limit_p),Digits())
          );
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
double AO(int shift){
   double array[];
   ArrayResize(array,1);
   ArrayInitialize(array,EMPTY_VALUE);
   error=ERR_SUCCESS;
   ResetLastError();
   if(CopyBuffer(handle,0,shift,1,array)==1) {
      ArraySetAsSeries(array,false);
      return(array[0]);
      }
   else error=GetLastError();
   return(EMPTY_VALUE);
}
//+------------------------------------------------------------------+
datetime GetTime(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const int shift) {
   datetime array[1];
   ResetLastError();
   if(CopyTime(symbol_name,timeframe,shift,1,array)==1) return(array[0]);
   Print(__FUNCTION__," > Ошибка получения времени бара ",shift,": ",GetLastError());
   return(0);
}
//+------------------------------------------------------------------+
int GetBarShift(const string symbol_name, const ENUM_TIMEFRAMES timeframe, const datetime time) {
   int res=WRONG_VALUE;
   datetime last_bar;
   if(SeriesInfoInteger(symbol_name,timeframe,SERIES_LASTBAR_DATE,last_bar)) {
      if(time>last_bar) res=0;
      else {
         const int shift=Bars(symbol_name,timeframe,time,last_bar);
         if(shift>0) res=shift-1;
         }
      }
   return(res);
}
//+------------------------------------------------------------------+
string GetNameTF(int timeframe=PERIOD_CURRENT) {
   if(timeframe==PERIOD_CURRENT) timeframe=Period();
   switch(timeframe) {
      //--- MQL4
      case 1: return("M1");
      case 5: return("M5");
      case 15: return("M15");
      case 30: return("M30");
      case 60: return("H1");
      case 240: return("H4");
      case 1440: return("D1");
      case 10080: return("W1");
      case 43200: return("MN");
      //--- MQL5
      case 2: return("M2");
      case 3: return("M3");
      case 4: return("M4");      
      case 6: return("M6");
      case 10: return("M10");
      case 12: return("M12");
      case 16385: return("H1");
      case 16386: return("H2");
      case 16387: return("H3");
      case 16388: return("H4");
      case 16390: return("H6");
      case 16392: return("H8");
      case 16396: return("H12");
      case 16408: return("D1");
      case 32769: return("W1");
      case 49153: return("MN");      
      default: return("UnknownPeriod");
   }
}
//+------------------------------------------------------------------+

The question would then be: How to avoid looping while history is being loaded for a timeframe? In general, the indicator performs calculations according to the history of the specified timeframe and there's no need to try to perform them until there is no history.

Files:
iMTF_AO.mq5  12 kb
 
Artyom Trishkin:

I only get one bar. And the indicator on the "native" timeframe displays the data normally. On the "non-native" one it shows a blank value. The empirical result is that until all of the history is loaded for the timeframe, from which I receive data from AO, the empty value will be returned.

Then the question will sound differently: how not to enter the loop, while the history of the timeframe is being loaded? It's just a test. In general, the indicator performs calculations based on the history of a given timeframe, and there is no need to try to perform them until there is no history.

Have you tried synchronization? Also, the developers advise to keep the data of the required timeframe/symbol up to date through the timer.
 
Right here:
      Buffer[i]=AO(i);

The "i" is not "0", but some exorbitant value. Bottom line: let's say we run the example on M15 - we have 5000 bars on this period. We request data from H4 - there are only 400 bars on it. Then we try to request "AO(4999)".

That is, from the period H4 we try to request the bar with the index "4999" - but there's no such bar on H4 at all, there are only 400 bars there, though we request the bar "0" and if the "CopyBuffer" operation is used in the indicator, we should rotate the array (ArraySetAsSeries), so that the rightmost bar in the chart corresponds to the index "0" in the indicator buffer (now in the "iMTF_AO.mq5" example the rightmost bar on the chart corresponds to rates_total-1).

 

Forum on trading, automated trading systems and trading strategies testing

Bugs, bugs, questions

fxsaber, 2017.04.12 08:38

A little tiphook. Bypassing the assignment operator
template <typename T>
struct STRUCT_COPY
{
  T Value;
  
  STRUCT_COPY( const T& tValue)
  {
    this = (STRUCT_COPY)tValue;
  }  
};

struct STRUCT
{
  int i;
  
  void operator =( const STRUCT& )
  {
    this.i = 5;
  }
};

#define  PRINT(A) ::Print(#A + " = " + (string)(A));

void OnStart()
{
  STRUCT Struct;  
  Struct.i = 1;  
  PRINT(Struct.i);
  
  STRUCT StructCopy1 = Struct;
  PRINT(StructCopy1.i);
  
  // Обходим void STRUCT::operator=(const STRUCT&)
  STRUCT_COPY<STRUCT> StructCopy2(Struct);
  PRINT(StructCopy2.Value.i);  
}

Result

Struct.i = 1
StructCopy1.i = 5
StructCopy2.Value.i = 1
 
Vladimir Karputov:
Here:

The "i" is not "0", but some exorbitant value. Bottom line: let's say we run the example on M15 - we have 5000 bars on this period. We request data from H4 - there are only 400 bars on it. Then we try to request "AO(4999)".

That is, from the period H4 we try to request the bar with the index "4999" - but there's no such bar on H4 at all, there are only 400 bars there, but we request the bar "0" and if the indicator uses the operation "CopyBuffer", we should rotate the array (ArraySetAsSeries), so that the rightmost bar in the chart corresponds to the index "0" in the indicator buffer (now in the example "iMTF_AO.mq5" the rightmost bar in the chart corresponds to rates_total-1).

Well, of course I tried to calculate the limit:

   int bars=Bars(NULL,PeriodForWork);                         
   datetime time_limit=GetTime(Symbol(),PeriodForWork,bars-1);
   int limit_p=GetBarShift(Symbol(),Period(),time_limit);     
   if(rates_total<1) return(0);
   int limit=(PeriodForWork==Period()?rates_total-prev_calculated:limit_p);
   if(limit>1) {          
      limit=rates_total-1;
      }                   

... But I see that I screwed up too quickly - this is correct only for the current timeframe

 
Artyom Trishkin:

No, of course I tried to calculate the limit:

... But I see that I screwed up in a hurry - it only fits for the current timeframe


  1. Reverse the buffer array (ArraySetAsSeries) - this is mandatory, if the indicator uses the CopyBuffer
  2. Keep in mind that the CopyBuffer: elements of the copied data(indicator buffer with an index buffer_num) is counted from the starting position from the present to the past, that is, the starting position equal to 0 means the current bar (the indicator value for the current bar).
After that in AO(start "0" means the RIGHTEST bar), not some exorbitant value.
 
Vladimir Karputov:

  1. Reverse the buffer array (ArraySetAsSeries) - this is mandatory if the indicator uses the CopyBuffer
  2. Keep in mind that the CopyBuffer: elements of the copied data(indicator buffer with an index buffer_num) is counted from the starting position from the present to the past, that is, the starting position equal to 0 means the current bar (the indicator value for the current bar).
After that in AO(start "0" means the RIGHTEST bar), not some exorbitant value.

Have you even looked at the code I'm showing you? Did you run it?

I didn't ask how to fill the indicator buffer, but why if I take values from AO not from the current bar, they return empty values.
I got it - there is no history, it is being loaded and while it is being loaded AO from a non-native timeframe returns the error "no data".

Now the question is this: How do I know that the history for the desired timeframe is fully loaded, so as not to enter the cycle of the indicator?

 
Comments not related to this topic were moved to"Questions from Beginners in MQL5 MT5 MetaTrader 5".