Особенности языка mql5, тонкости и приёмы работы - страница 30

 

Вот прям совсем не понял...

В индикаторе создаю хэндл стандартного АО, но с задаваемым таймфреймом. При получении данных от АО с таймфреймом, не совпадающим с текущим получаю ... ничего не получаю - ошибку 4806.

Вопрос: как правильно брать данные от стандартных индикаторов с таймфреймов, не совпадающих с текущим?

//+------------------------------------------------------------------+
//|                                                      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");
   }
}
//+------------------------------------------------------------------+
Файлы:
iMTF_AO.mq5  9 kb
 
Artyom Trishkin:

Вот прям совсем не понял...

В индикаторе создаю хэндл стандартного АО, но с задаваемым таймфреймом. При получении данных от АО с таймфреймом, не совпадающим с текущим получаю ... ничего не получаю - ошибку 4806.

Вопрос: как правильно брать данные от стандартных индикаторов с таймфреймов, не совпадающих с текущим?


Про получение значений ИНДИКАТОРА в ИНДИКАТОРЕ:

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Как в индикаторе получить данные с другого индикатора

Vladimir Karputov, 2016.12.27 08:41

Помня о том, что в индикаторах MQL5 по умолчанию бар с индексом "0" - это самый ЛЕВЫЙ бар на графике, попробуем получить в нашем индикаторе данные с двух других индикаторов - MA и Alligator (этот пример в индикаторе "IndicatorFromIndicators.mql5").

В индикаторе попытаемся получить данные с MA и Alligator на баре с индексом "0", "1" и "2":

  {
//---
   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);
  }

Прикрепим тестовый индикатор "IndicatorFromIndicators.mql5" на график и установим перекрестие на самый ПРАВЫЙ бар - то есть это не нулевой бар. Вот, что получилось:

IndicatorFromIndicators

Хотя перекрестие установлено на самый ПРАВЫЙ бар - то есть точно не на бар с индексом "0", при использовании CopyBuffer следует знать, что CopyBuffer будет копировать данные от настоящего к прошлому, то есть бар с индексом "0" означает текущий бар.


CopyBuffer: Отсчет элементов копируемых данных (индикаторный буфер с индексом buffer_num) от стартовой позиции ведется от настоящего к прошлому, то есть стартовая позиция, равная 0, означает текущий бар (значение индикатора для текущего бара).


То есть в индикаторе MQL5, если в нём используются операции CopyBuffer, нужно переворачивать массив (ArraySetAsSeries) так, чтобы самый правый бар на графике соответствовал индексу "0" в индикаторном буфере (сейчас в примере "iMTF_AO.mq5" самый правый бар на графике соответствует rates_total-1). 

 
Vladimir Karputov:


Про получение значений ИНДИКАТОРА в ИНДИКАТОРЕ:


CopyBuffer: Отсчет элементов копируемых данных (индикаторный буфер с индексом buffer_num) от стартовой позиции ведется от настоящего к прошлому, то есть стартовая позиция, равная 0, означает текущий бар (значение индикатора для текущего бара).


То есть в индикаторе MQL5, если в нём используются операции CopyBuffer, нужно переворачивать массив (ArraySetAsSeries) так, чтобы самый правый бар на графике соответствовал индексу "0" в индикаторном буфере (сейчас в примере "iMTF_AO.mq5" самый правый бар на графике соответствует rates_total-1). 

Получаю-то лишь один бар. И индикатор на "родном" таймфрейме нормально выводит данные. На "неродном" - пустое значение. Эмпирическим путём вывел то, что пока не подгрузится вся история по таймфрейму, с которого получаю данные от АО, так и будет возвращаться пустое значение.

//+------------------------------------------------------------------+
//|                                                      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");
   }
}
//+------------------------------------------------------------------+

Вопрос тогда будет звучать иначе: как не заходить в цикл, пока история по таймфрейму подгружается? Просто - это лишь тест, а вообще в индикаторе идут расчёты по истории заданного тф, и не нужно их пытаться выполнять пока истории нету.

Файлы:
iMTF_AO.mq5  12 kb
 
Artyom Trishkin:

Получаю-то лишь один бар. И индикатор на "родном" таймфрейме нормально выводит данные. На "неродном" - пустое значение. Эмпирическим путём вывел то, что пока не подгрузится вся история по таймфрейму, с которого получаю данные от АО, так и будет возвращаться пустое значение.

Вопрос тогда будет звучать иначе: как не заходить в цикл, пока история по таймфрейму подгружается? Просто - это лишь тест, а вообще в индикаторе идут расчёты по истории заданного тф, и не нужно их пытаться выполнять пока истории нету.

Пробовали синхронизацию? Также разработчики советуют поддерживать актуальность данных нужных ТФ/символа через таймер.
 
Вот здесь: 
      Buffer[i]=AO(i);

"i" равен не "0", а какому-то заоблачному значению. В итоге: допустим запустили пример на M15 - имеем на этом периоде 5000 баров. Запрашиваем данные с H4 - на нём всего 400 баров. Далее происходит попытка запроса "AO(4999)". 

То есть с периода H4 делается попытка запроса бара с индексом "4999" - а такого бара на H4 нет вообще, там всего-то 400 баров, хотя запрашивать нужно бар "0" и если в индикаторе используются операции CopyBuffer, нужно переворачивать массив (ArraySetAsSeries) так, чтобы самый правый бар на графике соответствовал индексу "0" в индикаторном буфере (сейчас в примере "iMTF_AO.mq5" самый правый бар на графике соответствует rates_total-1).

 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Ошибки, баги, вопросы

fxsaber, 2017.04.12 08:38

Небольшой лайфхак. Обход оператора присваивания
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);  
}

Результат

Struct.i = 1
StructCopy1.i = 5
StructCopy2.Value.i = 1
 
Vladimir Karputov:
Вот здесь: 

"i" равен не "0", а какому-то заоблачному значению. В итоге: допустим запустили пример на M15 - имеем на этом периоде 5000 баров. Запрашиваем данные с H4 - на нём всего 400 баров. Далее происходит попытка запроса "AO(4999)". 

То есть с периода H4 делается попытка запроса бара с индексом "4999" - а такого бара на H4 нет вообще, там всего-то 400 баров, хотя запрашивать нужно бар "0" и если в индикаторе используются операции CopyBuffer, нужно переворачивать массив (ArraySetAsSeries) так, чтобы самый правый бар на графике соответствовал индексу "0" в индикаторном буфере (сейчас в примере "iMTF_AO.mq5" самый правый бар на графике соответствует rates_total-1).

Не, ну я постарался конечно высчитать лимит:

   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;
      }                   

... но вижу, что напортачил второпях - это только для текущего тф подходит

 
Artyom Trishkin:

Не, ну я постарался конечно высчитать лимит:

... но вижу, что напортачил второпях - это только для текущего тф подходит


  1. Перевернуть буферный массив (ArraySetAsSeries) - это обязательно, если в индикаторе используется CopyBuffer
  2. Помнить о том, что CopyBuffer: Отсчет элементов копируемых данных (индикаторный буфер с индексом buffer_num) от стартовой позиции ведется от настоящего к прошлому, то есть стартовая позиция, равная 0, означает текущий бар (значение индикатора для текущего бара).
После этого в AO(запуливать "0" - означает самый ПРАВЫЙ БАР), а не какое-то заоблачное значение.
 
Vladimir Karputov:

  1. Перевернуть буферный массив (ArraySetAsSeries) - это обязательно, если в индикаторе используется CopyBuffer
  2. Помнить о том, что CopyBuffer: Отсчет элементов копируемых данных (индикаторный буфер с индексом buffer_num) от стартовой позиции ведется от настоящего к прошлому, то есть стартовая позиция, равная 0, означает текущий бар (значение индикатора для текущего бара).
После этого в AO(запуливать "0" - означает самый ПРАВЫЙ БАР), а не какое-то заоблачное значение.

Ты вообще код глядел, который показываю? А запускал его?

Я спрашивал не о том как заполнить буфер индикатора, а о том, почему если брать значения от АО не с текущего бара, то возвращаются пустые значения.
Это понял - нет истории - она подгружается, и пока она подгружается АО с неродного тф возвращает ошибку "нет данных".

Вопрос теперь стоит так: как узнать что история по нужному тф полностью загружена чтобы не заходить в цикл индикатора?

 
Комментарии, не относящиеся к этой теме, были перенесены в "Вопросы от начинающих MQL5 MT5 MetaTrader 5".
Причина обращения: