Получение времени или цены в заданных точках линий

Множество графических объектов включает в свой состав одну или несколько прямых линий. MQL5 позволяет выполнять интерполяцию и экстраполяцию точек на этих линиях и получать по одной координате другую, например, цену по времени или время по цене.

Интерполяция доступна всегда: она работает "внутри" объекта, то есть между точками привязки. Экстраполяция за пределы объекта возможна только в том случае, если для него включено свойство "луча" в соответствующем направлении (см. Свойства лучей для объектов с прямыми линиями).

Функция ObjectGetValueByTime возвращает значение цены для указанного времени. Функция ObjectGetTimeByValue возвращает значение времени для указанной цены.

double ObjectGetValueByTime(long chartId, const string name, datetime time, int line)

datetime ObjectGetTimeByValue(long chartId, const string name, double value, int line)

Вычисления производятся для объекта с именем name на графике с идентификатором chartId. Параметры time и value задают известную координату, для которой следует рассчитать неизвестную. Так как объект может иметь несколько линий, одной координате будет соответствовать несколько значений, в связи с чем необходимо указать номер линии в параметре line.

Функция возвращает значение цены или времени для проекции точки с указанной исходной координатой относительно линии.

В случае ошибки будет получен 0, а код ошибки записан в _LastError. Например, попытка экстраполировать значение линии при отключенном свойстве луча генерирует ошибку OBJECT_GETVALUE_FAILED (4205).

Функции применимы для следующих объектов:

  • Трендовая линия (OBJ_TREND)
  • Трендовая линия по углу (OBJ_TRENDBYANGLE)
  • Линия Ганна (OBJ_GANNLINE)
  • Равноудаленный канал (OBJ_CHANNEL) — 2 линии
  • Канал на линейной регрессии (OBJ_REGRESSION) — 3 линии
  • Канал стандартного отклонения (OBJ_STDDEVCHANNEL) — 3 линии
  • Линия со стрелкой (OBJ_ARROWED_LINE)

Проверим работу функции с помощью безбуферного индикатора ObjectChannels.mq5. Он создает два объекта с каналами стандартного отклонения и линейной регрессии, после чего запрашивает и выводит в комментарий цену верхней и нижней линий на будущих барах. Для канала стандартного отклонения свойство OBJPROP_RAY_RIGHT включено, а для канала регрессии — нет (намеренно). В связи с этим значения от второго канала получены не будут, и на экране для него всегда выводятся нули.

По мере формирования новых баров каналы будут автоматически перемещаться вправо. Длина каналов задается во входном параметре WorkPeriod (10 баров по умолчанию).

input int WorkPeriod = 10;
   
const string Prefix = "ObjChnl-";
const string ObjStdDev = Prefix + "StdDev";
const string ObjRegr = Prefix + "Regr";
   
void OnInit()
{
   CreateObjects();
   UpdateObjects();
}

Функция CreateObjects создает 2 канала и делает начальные настройки для них.

void CreateObjects()
{
   ObjectCreate(0ObjStdDevOBJ_STDDEVCHANNEL000);
   ObjectCreate(0ObjRegrOBJ_REGRESSION000);
   ObjectSetInteger(0ObjStdDevOBJPROP_COLORclrBlue);
   ObjectSetInteger(0ObjStdDevOBJPROP_RAY_RIGHTtrue);
   ObjectSetInteger(0ObjRegrOBJPROP_COLORclrRed);
   // NB: луч не включен у канала регрессии (намеренно)
}

Функция UpdateObjects перемещает каналы на последние WorkPeriod баров.

void UpdateObjects()
{
   const datetime t0 = iTime(NULL0WorkPeriod);
   const datetime t1 = iTime(NULL00);
   
   // мы не используем ObjectMove, потому что каналы работают
   // только с координатой времени (цена рассчитывается автоматически)
   ObjectSetInteger(0ObjStdDevOBJPROP_TIME0t0);
   ObjectSetInteger(0ObjStdDevOBJPROP_TIME1t1);
   ObjectSetInteger(0ObjRegrOBJPROP_TIME0t0);
   ObjectSetInteger(0ObjRegrOBJPROP_TIME1t1);
}

В обработчике OnCalculate обновляем позицию каналов на новых барах, а на каждом тике вызываем DisplayObjectData, чтобы получить экстраполяцию цен и вывести в комментарий.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
   static datetime now = 0;
   if(now != iTime(NULL00))
   {
      UpdateObjects();
      now = iTime(NULL00);
   }
   
   DisplayObjectData();
   
   return rates_total;
}

В функции DisplayObjectData узнаем цены в точках привязки на средней линии (OBJPROP_PRICE), а также запрашиваем с помощью ObjectGetValueByTime значения цен для верхней и нижней линий каналов через WorkPeriod баров в будущем.

void DisplayObjectData()
{
   const double p0 = ObjectGetDouble(0ObjStdDevOBJPROP_PRICE0);
   const double p1 = ObjectGetDouble(0ObjStdDevOBJPROP_PRICE1);
   
   // следующие равенства всегда верны вследствие алгоритма расчета каналов:
   // - средние линии обоих каналов совпадают,
   // - точки привязки всегда лежат на средней линии,
   // ObjectGetValueByTime(0, ObjStdDev, iTime(NULL, 0, 0), 0) == p1
   // ObjectGetValueByTime(0, ObjRegr, iTime(NULL, 0, 0), 0) == p1
   
   // пытаемся экстраполировать будущие цены по верхней и нижней линии
   const double d1 = ObjectGetValueByTime(0ObjStdDeviTime(NULL00)
      + WorkPeriod * PeriodSeconds(), 1);
   const double d2 = ObjectGetValueByTime(0ObjStdDeviTime(NULL00)
      + WorkPeriod * PeriodSeconds(), 2);
   
   const double r1 = ObjectGetValueByTime(0ObjRegriTime(NULL00)
      + WorkPeriod * PeriodSeconds(), 1);
   const double r2 = ObjectGetValueByTime(0ObjRegriTime(NULL00)
      + WorkPeriod * PeriodSeconds(), 2);
   
   // выводим все полученные цены в комментарий
   Comment(StringFormat("%.*f %.*f\ndev: up=%.*f dn=%.*f\nreg: up=%.*f dn=%.*f",
      _Digitsp0_Digitsp1,
      _Digitsd1_Digitsd2,
      _Digitsr1_Digitsr2));
}

Важно отметить, что из-за того, что свойство луча не включено для канала регрессии, он всегда дает нули в будущем (хотя при запросе цен внутри временного отрезка канала мы получили бы корректные значения).

Каналы и значения цен в точках их линий

Каналы и значения цен в точках их линий

Здесь для каналов длиной 10 баров экстраполяция также делается на 10 баров вперед, что дает будущие значения, показанные в строке с "dev:", примерно соответствующие правой границе окна.