Набор инструментов для ручной разметки графиков и торговли (Часть I). Подготовка - описание структуры и класс вспомогательных функций

5 мая 2020, 10:46
Oleh Fedorov
6
3 531

Введение


Я  — "ручник". В смысле  — анализирую графики не с помощью сложных формул и индикаторов, а "вручную", графически. Это даёт мне возможность быть более гибким в торговле, замечать некоторые вещи, которые формализовать довольно сложно или не целесообразно, легко переключаться между таймфреймами, если я вижу, что движение усиливается или замедляется, знать вероятное поведение цены задолго до того, как буду входить в сделку...

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

Видеопрезентация. Как это работает



Общая концепция. Постановка задачи


Итак, необходим инструмент, который помогает с помощью сочетаний клавиш выполнять наиболее частые операции, которые я выполняю.

Что это за задачи? Мне хочется, например:

  • чтобы простая горизонтальная линия рисовалась при нажатии клавиши "H" (от английского "Horizontal"  — горизонталь), а вертикальная  — по нажатию клавиши "i" ([ай], которая у меня ассоциируется по внешнему виду);
  • рисовать горизонтальные уровни определённой длины (НЕ бесконечные) от любой точки графика;
  • рисовать вертикальные уровни на определённом (произвольном) расстоянии от начальной точки;
  • переключать клавишами таймфрейм и порядок расположения слоёв на графике;
  • веер Фибоначчи с заданными мною уровнями по ближайшим экстремумам;
  • трендовые линии по ближайшим экстремумам  — их длина должна быть кратной расстоянию между экстремумами, хотя в некоторых случаях линия может быть и лучом;
  • вилы Эндрюса разных видов (обычные, Шиффа, "обратные вилы Шиффа"  — для быстрых трендов) (см. видео);
  • для экстремумов вил, прямых и вееров должна быть возможность настраивать "порядок" экстремумов (количество баров  — отдельно справа и отдельно слева);
  • графический интерфейс, позволяющий настраивать параметры нужных линий и экстремумов, не открывая главное окно настроек;
  • набор функций для сопровождения ордера: открытие ордеров в процентах от депозита, автоматический стоп при открытии ордера по рынку или срабатывании отложки без стопа, частичное закрытие позиций по уровням, для некоторых стратегий  — трал...

Для большинства этих задач можно, конечно, создать скрипты. У меня есть несколько, и я ими поделюсь во вложении. Но мне ближе другой подход.

В любом советнике или индикаторе можно создать метод OnChartEvent, в котором и будет описана реакция на любое событие: нажатие клавиш, движение мыши, создание графического объекта  — и его удаление...

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

И, конечно, я хочу, чтобы класс получился кросс-платформенным и работал похожим образом как в MQL4, так и в MQL5.

Структура программы

Список файлов

Библиотека содержит пять взаимосвязанных файлов. Все файлы располагаются в одной папке "Shortcuts" в каталоге Include. Их имена видны на рисунке: GlobalVariables.mqh, Graphics.mqh, Mouse.mqh, Shortcuts.mqh, Utilites.mqh.

Главный файл библиотеки(Shortcuts.mqh)

Главный файл программы  — "Shortcuts.mqh". В нём будет прописана логика реагирования на клавиши. И именно этот файл будет подключен к эксперту. В него также будут подключены все вспомогательные файлы.

//+------------------------------------------------------------------+
//|                                                    Shortcuts.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru/articles/7468"

#include "GlobalVariables.mqh"
#include "Mouse.mqh"
#include "Utilites.mqh"
#include "Graphics.mqh"

//+------------------------------------------------------------------+
//| Основной управляющий класс программы. Именно его нужно подключать|
//| в советник.                                                      |
//+------------------------------------------------------------------+
class CShortcuts
  {
private:
   CGraphics         m_graphics;   // Object for drawing m_graphics
public:
                     CShortcuts();
   void              OnChartEvent(const int id,
                                  const long &lparam,
                                  const double &dparam,
                                  const string &sparam);
  };
//+------------------------------------------------------------------+
//| Конструктор по умолчанию                                         |
//+------------------------------------------------------------------+
CShortcuts::CShortcuts(void)
  {
   ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
  }

//+------------------------------------------------------------------+
//| Функция обработки событий                                        |
//+------------------------------------------------------------------+
void CShortcuts::OnChartEvent(
   const int id,
   const long &lparam,
   const double &dparam,
   const string &sparam
)
  {
//---

   // Здесь будет описание событий нажатия кнопок клавиатуры и движений
   // мыши

   //  ...

  }
}

CShortcuts shortcuts;

Файл содержит описание класса CShortcuts.

В начала файла подключаются все вспомогательные классы.

В классе всего два метода: первый  — обработчик событий OnChartEvent, в котором, собственно, и будет происходить обработка событий клавиш и движения мыши, второй  — конструктор по умолчанию, в котором разрешается обрабатывать движения мыши.

После описания класса создаётся переменная shortcuts,которую необходимо использовать в методе OnChartEvent основного эксперта при подключении библиотеки.

Само подключение требует две строки:

//+------------------------------------------------------------------+
//| Основной эксперт (файл "Shortcuts-Main-Expert.mq5")              |
//+------------------------------------------------------------------+
#include <Shortcuts\Shortcuts.mqh>


// ...

//+------------------------------------------------------------------+
//| Функция ChartEvent                                               |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   shortcuts.OnChartEvent(id,lparam,dparam,sparam);
  }

Первая строка подключает файл класса, вторая  — передаёт классу управление обработкой событий.

После этого советник будет готов к работе, его можно будет скомпилировать и сразу начать рисовать линии...

Класс обработки движений мыши

Класс, который будет хранить все основные параметры текущего положения курсора: координаты X, Y  — в пикселях и ценах/времени, номер бара, на котором находится указатель, ну, и так далее, хранится в файле "Mouse.mqh"...

//+------------------------------------------------------------------+
//|                                                        Mouse.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru/articles/7468"
//+------------------------------------------------------------------+
//| Класс обработки движений мыши                                    |
//+------------------------------------------------------------------+
class CMouse
  {
   //--- Members
private:
   static int        m_x;           // X
   static int        m_y;           // Y
   static int        m_barNumber;   // Номер бара
   static bool       m_below;       // Признак курсора над ценой
   static bool       m_above;       // Признак курсора под ценой

   static datetime   m_currentTime; // Текущее время
   static double     m_currentPrice;// Текущая цена
   //--- Methods
public:

   //--- Запоминает основные поараметры курсора мыши
   static void       SetCurrentParameters(
      const int id,
      const long &lparam,
      const double &dparam,
      const string &sparam
   );
   //--- Возвращает координату X (в пикселах) текущего положения курсора
   static int        X(void) {return m_x;}
   //--- Возвращает координату Y (в пикселах) текущего положения курсора
   static int        Y(void) {return m_y;}
   //--- Возвращает цену текущего положения курсора
   static double     Price(void) {return m_currentPrice;}
   //--- Возвращает время текущего положения курсора
   static datetime   Time(void) {return m_currentTime;}
   //--- Возвращает номер бара текущего положения курсора
   static int        Bar(void) {return m_barNumber;}
   //--- Возвращает флаг, показывающий, что цена находится ниже Low текущего бара
   static bool       Below(void) {return m_below;}
   //--- Возвращает признак того, что цена находится выше High текущего бара
   static bool       Above(void) {return m_above;}
  };
//---

int CMouse::m_x=0;
int CMouse::m_y=0;
int CMouse::m_barNumber=0;
bool CMouse::m_below=false;
bool CMouse::m_above=false;
datetime CMouse::m_currentTime=0;
double CMouse::m_currentPrice=0;


//+------------------------------------------------------------------+
//| Запоминает основные параметры при движении мыши: координаты      |
//| курсора в пикселах и в ценах/времени, находится ли курсор над    |
//| или под ценами...                                                |
//|+-----------------------------------------------------------------+
static void CMouse::SetCurrentParameters(
   const int id,
   const long &lparam,
   const double &dparam,
   const string &sparam
)
  {
//---
   int window = 0;
//---
   ChartXYToTimePrice(
      0,
      (int)lparam,
      (int)dparam,
      window,
      m_currentTime,
      m_currentPrice
   );
   m_x=(int)lparam;
   m_y=(int)dparam;
   m_barNumber=iBarShift(
                  Symbol(),
                  PERIOD_CURRENT,
                  m_currentTime
               );
   m_below=m_currentPrice<iLow(Symbol(),PERIOD_CURRENT,m_barNumber);
   m_above=m_currentPrice>iHigh(Symbol(),PERIOD_CURRENT,m_barNumber);
  }

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

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

Описание блока настроек эксперта. Файл "GlobalVariables.mqh"


Все настройки, доступные пользователю, хранятся в файле "GlobalVariables.mqh".

В следующей статье будут описаны гораздо больше настроек, так как появится больше объектов для рисования.

А сейчас  — код того, что используется в текущей версии:

//+------------------------------------------------------------------+
//|                                              GlobalVariables.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru/articles/7468"
//+------------------------------------------------------------------+
//| Файл описания параметров, доступных пользователю                 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Настройки клавиш                                                 |
//+------------------------------------------------------------------+
input string   Keys="=== Настройки клавиш ===";
input string   Up_Key="U";            // Переключить таймфрейм вверх
input string   Down_Key="D";          // Переключить таймфрейм вниз
input string   Trend_Line_Key="T";              // Трендовая линия
input string   Switch_Trend_Ray_Key="R";        // Признак луча трендовой
input string   Z_Index_Key="Z";                 // Признак графика сверху

//+------------------------------------------------------------------+
//| Настройки размеров                                               |
//+------------------------------------------------------------------+
input string   Dimensions="=== Настройки размеров ===";
input int      Trend_Line_Width=2;        // Ширина трендовой линии

//+------------------------------------------------------------------+
//| Стили отображения                                                |
//+------------------------------------------------------------------+
input string   Styles="=== Стили отображения ===";
input ENUM_LINE_STYLE      Trend_Line_Style=STYLE_SOLID;       // Стиль Трендовая

//+------------------------------------------------------------------+
//| Остальные параметры                                              |
//+------------------------------------------------------------------+
input string               Others="=== Остальные параметры ===";

input bool                 Is_Trend_Ray=false;                      // Трендовая - луч
input bool                 Is_Change_Timeframe_On_Create = true;    // Скрывать объекты на старших таймфреймах?
                                                                    //   (true - скрывать, false - отображать)
input bool                 Is_Select_On_Create=true;                // Выделение при создании
input bool                 Is_Different_Colors=true;                // Менять ли цвета для таймов

// Количество баров слева и спарава
// для экстремумов веера и трендовой
input int                  Fractal_Size_Left=1;                     // Размер фрактала слева
input int                  Fractal_Size_Right=1;                    // Размер фрактала справа

//+------------------------------------------------------------------+
//| Префиксы имён рисуемых фигур (меняются только в коде,            |
//| в параметрах советника не видны)                                 |
//+------------------------------------------------------------------+
// string   Prefixes="=== Prefixes ===";

string   Trend_Line_Prefix="Trend_";                     // Префикс трендовой

//+------------------------------------------------------------------+
//| Цвета объектов одного таймфрейма (меняются только в коде,        |
//| в параметрах советника не видны)                                 |
//+------------------------------------------------------------------+
// string TimeframeColors="=== Time frame colors ===";
color mn1_color=clrCrimson;
color w1_color=clrDarkOrange;
color d1_color=clrGoldenrod;
color h4_color=clrLimeGreen;
color h1_color=clrLime;
color m30_color=clrDeepSkyBlue;
color m15_color=clrBlue;
color m5_color=clrViolet;
color m1_color=clrDarkViolet;
color common_color=clrGray;

//--- Вспомогательная константа для вывода сообщаений об ошибках
#define DEBUG_MESSAGE_PREFIX "=== ",__FUNCTION__," === "

//--- Константы для описания основных таймфреймов при рисовании
#define PERIOD_LOWER_M5 OBJ_PERIOD_M1|OBJ_PERIOD_M5
#define PERIOD_LOWER_M15 PERIOD_LOWER_M5|OBJ_PERIOD_M15
#define PERIOD_LOWER_M30 PERIOD_LOWER_M15|OBJ_PERIOD_M30
#define PERIOD_LOWER_H1 PERIOD_LOWER_M30|OBJ_PERIOD_H1
#define PERIOD_LOWER_H4 PERIOD_LOWER_H1|OBJ_PERIOD_H4
#define PERIOD_LOWER_D1 PERIOD_LOWER_H4|OBJ_PERIOD_D1
#define PERIOD_LOWER_W1 PERIOD_LOWER_D1|OBJ_PERIOD_W1
//+------------------------------------------------------------------+

Вспомогателтьные функции

В программе есть множество функций, которые непосредственно к рисованию отношения не имеют, но помогают найти экстремумы, переключить таймфреймы  — и так далее. Все они вынесены в файл "Utilites.mqh".

Заголовок файла Utilites.mqh

//+------------------------------------------------------------------+
//|                                                     Utilites.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru/articles/7468"

//+------------------------------------------------------------------+
//| Класс для описания вспомогательных функций                       |
//+------------------------------------------------------------------+
class CUtilites
  {
public:
   //--- Изменяет тайм фрейм на следующий в панели инструментьв
   static void       ChangeTimeframes(bool isUp);
   //--- Преобразует строковые константы команд в коды клавиш
   static int        GetCurrentOperationChar(string keyString);
   //--- Переключает слои в графиках (график поверх всех объектов)
   static void       ChangeChartZIndex(void);
   //--- Возвращает номер ближайшего экстремального бара
   static int        GetNearestExtremumBarNumber(
      int starting_number=0,
      bool is_search_right=false,
      bool is_up=false,
      int left_side_bars=1,
      int right_side_bars=1,
      string symbol=NULL,
      ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT
   );

   //--- Возвращает цвет для текущего таймфрейма
   static color      GetTimeFrameColor(long allDownPeriodsValue);
   //--- Возвращает список всех таймфреймов, лежащих ниже текущего (включая текущий)
   static long       GetAllLowerTimeframes(int NeededTimeframe=PERIOD_CURRENT);
   //--- Координаты прямой. Заносит в точки p1 и p2 номера экстремалных баров
   static void       SetExtremumsBarsNumbers(bool _is_up,int &p1, int &p2);
   //--- Преобразование строки в массив чисел с плавающей точкой
   static void       StringToDoubleArray(
      string _haystack,
      double &_result[],
      const string _delimiter=","
   );
   //--- Определяет имя текущего объекта
   static string            GetCurrentObjectName(
      const string _prefix,
      const ENUM_OBJECT _type=OBJ_TREND,
      int _number = -1
   );
   //--- Получает номер следующего объекта
   static int               GetNextObjectNumber(
      const string _prefix,
      const ENUM_OBJECT _object_type,
      bool true
   );
   //--- Возвращает расстояние в экранных пикселях между соседними барами
   static int               GetBarsPixelDistance(void);
   //--- Конвертация числового значения таймфрейма в его строковое имя
   static string            GetTimeframeSymbolName(
      ENUM_TIMEFRAMES _timeframe=PERIOD_CURRENT  // Нужный таймфрейм
   );
  };

Функция, последовательно изменяющая период графика

Первые функции в этом файле тривиальны. Например, функция, изменяющая периоды текущего графига последовательно, друг за другом, выглядит следующим образом:

//+------------------------------------------------------------------+
//| Последовательно изменяет период текущего графика                 |
//|                                                                  |
//| В данной реализации используются только таймфреймы, выведенные   |
//| в стандартную панель.                                            |
//|                                                                  |
//| Параметры:                                                       |
//|    _isUp - направление смены таймфрейма: вверх (true)            |
//|            или вниз (false)                                      |
//+------------------------------------------------------------------+
static void CUtilites::ChangeTimeframes(bool _isUp)
  {
   ENUM_TIMEFRAMES timeframes[] =
     {
      PERIOD_CURRENT,
      PERIOD_M1,
      PERIOD_M5,
      PERIOD_M15,
      PERIOD_M30,
      PERIOD_H1,
      PERIOD_H4,
      PERIOD_D1,
      PERIOD_W1,
      PERIOD_MN1
     };
   int period = Period();
   int shift = ArrayBsearch(timeframes,period);
   if(_isUp && shift < ArraySize(timeframes)-1)
     {
      ChartSetSymbolPeriod(0,NULL,timeframes[++shift]);
     }
   else
      if(!_isUp && shift > 1)
        {
         ChartSetSymbolPeriod(0,NULL,timeframes[--shift]);
        }
  } 

Для начала в этой функции описывается массив всех таймфреймов, указанных в стандартной панели управления по умолчанию. Если лично вам необходимо переключаться между всеми доступными в MetaTrader 5 временными периодами, нужно просто дописать соответствующие константы в нужные места массива. Правда, в этом случае может потеряться обратная совместимость, то есть библиотека может перестать работать с MQL4.

Дальше используем стандартные функции, чтобы получить текущий период и найти его в списке.

А потом для переключения  временных периодов графика используется стандартная функция ChartSetSymbolPeriod, в которую передаётся период, следующий за текущим.

По остальным функциям особо комментировать вне кода не вижу смысла. Просто код...

Несколько простых функций

//+------------------------------------------------------------------+
//| Преобразует строковые константы команд в коды клавиш             |
//+------------------------------------------------------------------+
static int CUtilites::GetCurrentOperationChar(string keyString)
  {
   string keyValue = keyString;
   StringToUpper(keyValue);
   return(StringGetCharacter(keyValue,0));
  }
//+------------------------------------------------------------------+
//| Переключает расположение графика поверх других объектов          |
//+------------------------------------------------------------------+
static void CUtilites::ChangeChartZIndex(void)
  {
   ChartSetInteger(
      0,
      CHART_FOREGROUND,
      !(bool)ChartGetInteger(0,CHART_FOREGROUND)
   );
   ChartRedraw(0);
  } 
//+------------------------------------------------------------------+
//| Возвращает строковое имя для любого стандартного таймфрейма      |
//| Параметры:                                                       |
//|    _timeframe - числовое значение ENUM_TIMEFRAMES, для которого  |
//|                 нужно подобрать строковое название               |
//| Возвращаемое значение:                                           |
//|   строковое название нужного таймфрейма                          |
//+------------------------------------------------------------------+
static string CUtilites::GetTimeframeSymbolName(
   ENUM_TIMEFRAMES _timeframe=PERIOD_CURRENT  // Нужный таймфрейм
)
  {
   ENUM_TIMEFRAMES current_timeframe; // текущий таймфрейм
   string result = "";
//---
   if(_timeframe == PERIOD_CURRENT)
     {
      current_timeframe = Period();
     }
   else
     {
      current_timeframe = _timeframe;
     }
//---
   switch(current_timeframe)
     {
      case PERIOD_M1:
         return "M1";
      case PERIOD_M2:
         return "M2";
      case PERIOD_M3:
         return "M3";
      case PERIOD_M4:
         return "M4";
      case PERIOD_M5:
         return "M5";
      case PERIOD_M6:
         return "M6";
      case PERIOD_M10:
         return "M10";
      case PERIOD_M12:
         return "M12";
      case PERIOD_M15:
         return "M15";
      case PERIOD_M20:
         return "M20";
      case PERIOD_M30:
         return "M30";
      case PERIOD_H1:
         return "H1";
      case PERIOD_H2:
         return "M1";
      case PERIOD_H3:
         return "H3";
      case PERIOD_H4:
         return "H4";
      case PERIOD_H6:
         return "H6";
      case PERIOD_H8:
         return "H8";
      case PERIOD_D1:
         return "D1";
      case PERIOD_W1:
         return "W1";
      case PERIOD_MN1:
         return "MN1";
      default:
         return "Unknown";
     }
  }
//+------------------------------------------------------------------+
//| Возвращает стандартный цвет для текущего таймфрейма              |
//+------------------------------------------------------------------+
static color CUtilites::GetTimeFrameColor(long _all_down_periods_value)
  {
   if(Is_Different_Colors)
     {
      switch((int)_all_down_periods_value)
        {
         case OBJ_PERIOD_M1:
            return (m1_color);
         case PERIOD_LOWER_M5:
            return (m5_color);
         case PERIOD_LOWER_M15:
            return (m15_color);
         case PERIOD_LOWER_M30:
            return (m30_color);
         case PERIOD_LOWER_H1:
            return (h1_color);
         case PERIOD_LOWER_H4:
            return (h4_color);
         case PERIOD_LOWER_D1:
            return (d1_color);
         case PERIOD_LOWER_W1:
            return (w1_color);
         case OBJ_ALL_PERIODS:
            return (mn1_color);
         default:
            return (common_color);
        }
     }
   else
     {
      return (common_color);
     }
  }

Функция поиска экстремумов и её применение

Следующая функция помогает искать экстремумы. В зависимости от параметров экстремумы могут быть найдены как справа, так и слева от указателя мыши, и можно задавать, сколько баров должно быть слева от экстремума, и сколько — справа.

//+------------------------------------------------------------------+
//| Возвращает номер бара ближайшего "фрактала" в выбранном          |
//|   направлении                                                    |
//| Параметры:                                                       |
//|   starting_number - номер бара, с  которого стратует поиск       |
//|   is_search_right - Ищем вправо (true) или влево(false)?         |
//|   is_up - если "true" - поиск по уровням High, иначе - Low       |
//|   left_side_bars - количество баров слева                        |
//|   right_side_bars - количество баров справа                      |
//|   symbol - имя символа для поиска                                |
//|   timeframe - временной интервал                                 |
//| Возвращаемое значение:                                           |
//|   номер ближайшего к starting_number экстремума,                 |
//|   соответствующего заданным параметрам                           |                 |
//+------------------------------------------------------------------+
static int CUtilites::GetNearestExtremumBarNumber(
   int starting_number=0,              // Номер начального бара
   const bool is_search_right=false,   // Направление вправо
   const bool is_up=false,             // Искать по "верхам" (High)
   const int left_side_bars=1,         // Количество баров слева
   const int right_side_bars=1,        // Количество баров справа
   const string symbol=NULL,           // Нужный символ
   const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT // Нужный таймфрейм
)
  {
//---
   int   i,
         nextExtremum,
         sign = is_search_right ? -1 : 1;

//--- Если стартовый бар указан неверно
//--- (лежит за границами текущего графика)
//--- и поиск - в сторону границы...
   if((starting_number-right_side_bars<0
       && is_search_right)
      || (starting_number+left_side_bars>iBars(symbol,timeframe)
          && !is_search_right)
     )
     { //--- ...Необходимо вывести сообщение об ошибке
      if(Print_Warning_Messages)
        {
         Print(DEBUG_MESSAGE_PREFIX,
               "Не могу найти экстремум: ",
               "неверное направление");
         Print("left_side_bars = ",left_side_bars,"; ",
               "right_side_bars = ",right_side_bars);
        }
      return (-2);
     }
   else
     {
      //--- иначе - направление позволяет выбрать правильный бар.
      //---   бежим по всем барам в нужном направлении,
      //---   пока находимся за границами известного графика
      while((starting_number-right_side_bars<0
             && !is_search_right)
            || (starting_number+left_side_bars>iBars(symbol,timeframe)
                && is_search_right)
           )
        {
         starting_number +=sign;
        }
     }
//---
   i=starting_number;
 
   //--- Подготовка закончена. Приступаем к поиску
   while(i-right_side_bars>=0
         && i+left_side_bars<iBars(symbol,timeframe)
        )
     {
      //--- В зависимости от уровня проверяем нужный экстремум
      if(is_up)
        { //--- ...либо верхний...
         nextExtremum = iHighest(
                           Symbol(),
                           Period(),
                           MODE_HIGH,
                           left_side_bars+right_side_bars+1,
                           i-right_side_bars
                        );
        }
      else
        {  //--- ...либо нижний...
         nextExtremum = iLowest(
                           Symbol(),
                           Period(),
                           MODE_LOW,
                           left_side_bars+right_side_bars+1,
                           i-right_side_bars
                        );
        }
      if(nextExtremum == i) // Если текущий бар - экстремум...
        {
         return nextExtremum;  // ...то задача решена
        }
      else // Иначе - продолжаем смещать счётчик проверяемой свечи в нужную сторону
         if(is_search_right)
           {
            if(nextExtremum<i)
              {
               i=nextExtremum;
              }
            else
              {
               i--;
              }
           }
         else
           {
            if(nextExtremum>i)
              {
               i=nextExtremum;
              }
            else
              {
               i++;
              }
           }
     }
//--- Если добежали до края и никаких экстремумов не нашли...
   if(Print_Warning_Messages)
     {
      //--- ...Нужно сообщить об ошибке.
      Print(DEBUG_MESSAGE_PREFIX,
            "Не могу найти экстремум: ",
            "ошибочно выбрана началная точка либо граничные условия.");
      Print("left_side_bars = ",left_side_bars,"; ",
            "right_side_bars = ",right_side_bars);
     }
   return (-1);
  } 

Для рисования трендовых линий нужна функция, которая находит два ближайших экстремума справа от мышки. Эта функция может использовать предыдущую:

//+------------------------------------------------------------------+
//| Находит 2 ближайших экстремума справа от текущей позиции         |
//| указателя мыши                                                   |
//| Параметры:                                                       |
//|    _is_up - поиск по High (true) или по Low (false)              |
//|    int &_p1 - номер бара первой точки                            |
//|    int &_p2 - номер бара второй точки                            |
//+------------------------------------------------------------------+
static void CUtilites::SetExtremumsBarsNumbers(
   bool _is_up, // поиск по High (true) или по Low (false)
   int &_p1,    // номер бара первой точки
   int &_p2     // номер бара второй точки
)
  {
   int dropped_bar_number=CMouse::Bar();
//---
   _p1=CUtilites::GetNearestExtremumBarNumber(
          dropped_bar_number,
          true,
          _is_up,
          Fractal_Size_Left,
          Fractal_Size_Right
       );
   _p2=CUtilites::GetNearestExtremumBarNumber(
          _p1-1, // Бар левее предыдущего найденного экстремума
          true,
          _is_up,
          Fractal_Size_Left,
          Fractal_Size_Right
       );
   if(_p2<0)
     {
      _p2=0;
     }
  } 

Генерация имён объектов

Для того, чтобы рисовать одинаковые объекты сериями, им надо давать уникальные имена. Для этого проще всего взять какой-нибудь префикс, соответствующий данному типу объектов, и добавить к нему уникальный номер. Префиксы для разных типов объектов перечислены в файле "GlobalVariables.mqh".

Номера же генерируются соответствующей функцией.

//+------------------------------------------------------------------+
//| Возвращает номер следующего объекта в серии                      |
//| Параметры:                                                       |
//|   prefix - префикс имени для данной группы объектов.             |
//|   object_type - тип тех объектов, которые нужно искать.          |
//|   only_prefixed - если "false", то поиск - по всем объектам      |
//|                      данного типа, "true" - только по объектам   |
//|                      с указанным префиксом                       |
//| Возвращаемое значение:                                           |              |
//|   номер следующего объекта серии. Если ищем по префиксам,        |
//|      и существующая нумерация имеет "пробел", следующий номер    |
//|      будет в начале этого "пробела".                             |
//+------------------------------------------------------------------+
int               CUtilites::GetNextObjectNumber(
   const string prefix,
   const ENUM_OBJECT object_type,
   bool true
)
  {
   int count = ObjectsTotal(0,0,object_type),
       i,
       current_element_number,
       total_elements = 0;
   string current_element_name = "",
          comment_text = "";
//---
   if(only_prefixed)
     {
      for(i=0; i<count; i++)
        {
         current_element_name=ObjectName(0,i,0,object_type);
         if(StringSubstr(current_element_name,0,StringLen(prefix))==prefix)
           {
            current_element_number=
               (int)StringToInteger(
                  StringSubstr(current_element_name,
                               StringLen(prefix),
                               -1)
               );
            if(current_element_number!=total_elements)
              {
               break;
              }
            total_elements++;
           }
        }
     }
   else
     {
      total_elements = ObjectsTotal(0,-1,object_type);
      do
        {
         current_element_name = GetCurrentObjectName(
                                   prefix,
                                   object_type,
                                   total_elements
                                );
         if(ObjectFind(0,current_element_name)>=0)
           {
            total_elements++;
           }
        }
      while(ObjectFind(0,current_element_name)>=0);
     }
//---
   return(total_elements);
  } 

В коде реализовано два алгоритма поиска. Первый (основной для данной библиотеки)  — перебирает все объекты, соответствующие типу и имеющие указанный префикс. Как только находит свободный номер, возвращет его пользователю. Это позволяет закрывать "дыры" в нумерации.

Однако такой алгоритм не очень подходит для случая, когда могут быть несколько объектов с одинаковым номером, но разными суффиксами. Я в ранних версиях, когда ещё пользовался для рисования скриптами, так именовал комплекты вил. 

Поэтому в библиотеке реализован и второй способ поиска. Берётся общее число объектов данного типа, а затем проверяется, есть ли имя, которое начинается на тот же префикс и имеет тот же номер. Если да, номер увеличивается на 1 до тех пор, пока не найдётся свободное значение.

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

//+------------------------------------------------------------------+
//| Генерирует имя текущего элемента                                 |
//| Параметры:                                                       |
//|    _prefix - префикс имени для данной группы объектов.           |
//|    _type - тип тех объектов, которые нужно искать.               |
//|    _number - номер текущего объекта, если он уже готов.          |
//| Возвращаемое значение:                                           |
//|    строка имени текущего объекта.                                |
//+------------------------------------------------------------------+
string            CUtilites::GetCurrentObjectName(
   string _prefix,
   ENUM_OBJECT _type=OBJ_TREND,
   int _number = -1
)
  {
   int Current_Line_Number;

//--- Дополнение к префиксу - текущий таймфрейм
   string Current_Line_Name=IntegerToString(PeriodSeconds()/60)+"_"+_prefix;

//--- Получаем номер элемента
   if(_number<0)
     {
      Current_Line_Number = GetNextObjectNumber(Current_Line_Name,_type);
     }
   else
     {
      Current_Line_Number = _number;
     }

//--- Генерируем имя
   Current_Line_Name +=IntegerToString(Current_Line_Number,4,StringGetCharacter("0",0));

//---
   return (Current_Line_Name);
  } 

Дистанция между соседними барами (в пикселах)

Иногда бывает нужно вычислить расстояние до определённой точки в будущем. Один из самых надёжных способов этого достичь d  — рассчитать расстояние в пикселах между двумя соседними барами и затем умножить его на нужный коэффициент (сколько баров хотим отступить).

Дистанцию между соседними барами можно вычислить с помощью следующей функции:

//+------------------------------------------------------------------+
//| Вычисляет расстояние в пикселах между двумя соседними барами     |
//+------------------------------------------------------------------+
int        CUtilites::GetBarsPixelDistance(void)
  {
   double price; // произвольная цена на графике (нужна для
                 // стандартных функций вычисления коогдинат
   datetime time1,time2;  // время текущей и соседней свечей
   int x1,x2,y1,y2;       // экранные координаты двух точек
                          //   на соседних свечах
   int deltha;            // результат - искомое расстояние

//--- Начальные установки
   price = iHigh(Symbol(),PERIOD_CURRENT,CMouse::Bar());
   time1 = CMouse::Time();

//--- Получение времени соседней свечи
   if(CMouse::Bar()<Bars(Symbol(),PERIOD_CURRENT)){ // если в середине графика...
      time2 = time1+PeriodSeconds();                // ...берём свечу слева
   }
   else {                                           // ...иначе...
      time2 = time1;
      time1 = time1-PeriodSeconds();                // ...берём свечу справа
   }

//--- Преобразование координат из значений цена/время в экранные пикселы
   ChartTimePriceToXY(0,0,time1,price,x1,y1);
   ChartTimePriceToXY(0,0,time2,price,x2,y2);

//--- Вычисление расстояния
   deltha = MathAbs(x2-x1);
//---
   return (deltha);
  } 

Функция, преобразующая строку в массив чисел double

Для настроек уровней Фибоначчи с помощью параметров советника проще всего использовать строки, которые состоят из значений, разделённых запятой. Однако MQL  требует, чтобы для задания уровня использовались числа double.

Вытащить числа из строки поможет следующая функция.

//+------------------------------------------------------------------+
//| Преобразует строку с разделителями в массив двоичных (double)    |
//|    чисел                                                         |
//| Параметры:                                                       |
//|    _haystack - исходная строка для конвертации                            |
//|    _result[] - результирующий массв                              |
//|    _delimiter - символ-разделитель                               |
//+------------------------------------------------------------------+
static void CUtilites::StringToDoubleArray(
   string _haystack,             // исходная строка
   double &_result[],            // массив результатов
   const string _delimiter=","   // символ-разделитель
)
  {
//---
   string haystack_pieces[]; // Массив фрагментов строки
   int pieces_count,         // Количество фрагментов
       i;                    // Счётчик
   string current_number=""; // Текущий фрагмент строки (предполагаемое число)

//--- Разбиение строки на фрагменты
   pieces_count=StringSplit(_haystack,StringGetCharacter(_delimiter,0),haystack_pieces);
//--- Конвертация
   if(pieces_count>0)
     {
      ArrayResize(_result,pieces_count);
      for(i=0; i<pieces_count; i++)
        {
         StringTrimLeft(haystack_pieces[i]);
         StringTrimRight(haystack_pieces[i]);
         _result[i]=StringToDouble(haystack_pieces[i]);
        }
     }
   else
     {
      ArrayResize(_result,1);
      _result[0]=0;
     }
  } 

Класс рисования: пример использования функций-утилит

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

Заголовок файла рисования графики Graphics.mqh

//+------------------------------------------------------------------+
//|                                                     Graphics.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/ru/articles/7468"

//+------------------------------------------------------------------+
//| Класс для рисования графических объектов                         |
//+------------------------------------------------------------------+
class CGraphics
  {
   //--- Поля
private:
   bool              m_Is_Trend_Ray;
   bool              m_Is_Change_Timeframe_On_Create;
   //--- Методы
private:
   //--- Устанавливает общие параметры для любого вновь созданного объекта
   void              CurrentObjectDecorate(
      const string _name,
      const color _color=clrNONE,
      const int _width = 1,
      const ENUM_LINE_STYLE _style = STYLE_SOLID
   );
public:
   //--- Конструктор по умолчанию
                     CGraphics();
   //--- Универсальная функция для создания трендовых линий с заданными параметрами
   bool              TrendCreate(
      const long            chart_ID=0,        // ID графика
      const string          name="TrendLine",  // имя линии
      const int             sub_window=0,      // номер подокна
      datetime              time1=0,           // время первой точки
      double                price1=0,          // цена первой точки
      datetime              time2=0,           // время второй точки
      double                price2=0,          // цена второй точки
      const color           clr=clrRed,        // цвет линии
      const ENUM_LINE_STYLE style=STYLE_SOLID, // стиль линии
      const int             width=1,           // толщина линии
      const bool            back=false,        // на заднем плане
      const bool            selection=true,    // выбрана ли линия
      const bool            ray_right=false,   // луч вправо
      const bool            hidden=true,       // скрывать в списке объектов
      const long            z_order=0          // приоретет щелчка мыши (Z-Index)
   );
   //--- Отрисовывает трендовую линию по двум ближайшим (справа от мыши) экстремумам
   void              DrawTrendLine(void);
   //--- Проверяет, является ли прямая лучом
   bool              IsRay() {return m_Is_Trend_Ray;}
   //--- Устанавливает признак луча (продлевать ли прямую вправо)
   void              IsRay(bool _is_ray) {m_Is_Trend_Ray = _is_ray;}
   //--- Проверяет, менять ли отображение создаваемых программой объектов на старших таймфреймах
   bool              IsChangeTimeframe(void) {return m_Is_Change_Timeframe_On_Create;}
   //--- Устанавливает признак отображения создаваемых программой объектов на старших таймфреймах
   void              IsChangeTimeframe(bool _is_tf_change) {m_Is_Change_Timeframe_On_Create = _is_tf_change;}
  };
  

Функция, устанавливающая общие параметры для любого вновь созданного графического объекта

//+------------------------------------------------------------------+
//| Устанавливает общие параметры для всех новых обхектов            |
//| Параметры:                                                       |
//|    _name - имя изменяемого объекта                               |
//|    _color - цвет изменяемого объекта. Если не задан,             |
//|             используется "стандартный" цвет текущего периода     |
//|    _width - ширина линии объекта                                 |
//|    _style - тип линии объекта                                    |
//+------------------------------------------------------------------+
void CGraphics::CurrentObjectDecorate(
   const string _name,            // имя изменяемого объекта
   const color _color=clrNONE,    // цвет изменяемого объекта
   const int _width = 1,          // ширина линии объекта
   const ENUM_LINE_STYLE _style = STYLE_SOLID  // тип линии объекта
)
  {
   long timeframes;         // таймфреймы, на которых будет отображён объект
   color currentColor;      // цвет объекта
//--- Уточнение таймфреймов, на которых будет отображен объект
   if(Is_Change_Timeframe_On_Create)
     {
      timeframes = CUtilites::GetAllLowerTimeframes();
     }
   else
     {
      timeframes = OBJ_ALL_PERIODS;
     }
//--- Уточнение цвета объекта
   if(_color != clrNONE)
     {
      currentColor = _color;
     }
   else
     {
      currentColor = CUtilites::GetTimeFrameColor(timeframes);
     }
//--- Задание атрибутов
   ObjectSetInteger(0,_name,OBJPROP_COLOR,currentColor);            // Цвет
   ObjectSetInteger(0,_name,OBJPROP_TIMEFRAMES,timeframes);         // Периоды
   ObjectSetInteger(0,_name,OBJPROP_HIDDEN,true);                   // Скрыавть в списке объектов
   ObjectSetInteger(0,_name,OBJPROP_SELECTABLE,true);               // Возможность выделять
   ObjectSetInteger(0,_name,OBJPROP_SELECTED,Is_Select_On_Create);  // Оставлять ли выделенным после создания
   ObjectSetInteger(0,_name,OBJPROP_WIDTH,_width);                  // Толщина линии
   ObjectSetInteger(0,_name,OBJPROP_STYLE,_style);                  // Стиль линии
  } 

Функции рисования прямой

//+------------------------------------------------------------------+
//| Универсальная функция для создания трендовых линий с заданными   |
//|    параметрами                                                   |
//| Параметры:                                                       |
//|    chart_ID - ID графика                                         |
//|    name - имя линии                                              |
//|    sub_window - номер подокна                                    |
//|    time1 - время первой точки                                    |
//|    price1 - цена первой точки                                    |
//|    time2 - время второй точки                                    |
//|    price2 - цена второй точки                                    |
//|    clr - цвет линии                                              |
//|    style - стиль линии                                           |
//|    width - толщина линии                                         |
//|    back - на заднем плане                                        |
//|    selection - выбрана ли линия                                  |
//|    ray_right - луч вправо                                        |
//|    hidden - скрывать в списке объектов                           |
//|    z_order - приоретет щелчка мыши (Z-Index)                     |
//| Возвращаемое значение:                                           |
//|    признак удачного завершения. Если нарисовать линию не вышло,  |
//|    вернёт false, иначе - true                                    |
//+------------------------------------------------------------------+
bool              CGraphics::TrendCreate(
      const long            chart_ID=0,        // ID графика
      const string          name="TrendLine",  // имя линии
      const int             sub_window=0,      // номер подокна
      datetime              time1=0,           // время первой точки
      double                price1=0,          // цена первой точки
      datetime              time2=0,           // время второй точки
      double                price2=0,          // цена второй точки
      const color           clr=clrRed,        // цвет линии
      const ENUM_LINE_STYLE style=STYLE_SOLID, // стиль линии
      const int             width=1,           // толщина линии
      const bool            back=false,        // на заднем плане
      const bool            selection=true,    // выбрана ли линия
      const bool            ray_right=false,   // луч вправо
      const bool            hidden=true,       // скрывать в списке объектов
      const long            z_order=0          // приоретет щелчка мыши (Z-Index)
)
  {

//--- Сбросить последнее сообщение об ошибке
   ResetLastError();
//--- Создаём линию
   if(!ObjectCreate(chart_ID,name,OBJ_TREND,sub_window,time1,price1,time2,price2))
     {
      if(Print_Warning_Messages) // Если не получилось, сообщаем об ошибке
        {
         Print(__FUNCTION__,
               ": Can't create trend line! Error code = ",GetLastError());
        }
      return(false);
     }

//--- Задаём дополнительные атрибуты
   CurrentObjectDecorate(name,clr,width,style);

//--- линия на переднем (false) или на заднем (true) плане?
   ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
//--- луч вправо (true) или чёткие границы (false)?
   ObjectSetInteger(chart_ID,name,OBJPROP_RAY_RIGHT,ray_right);
//--- приоретет кликов мыши (Z-index)
   ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
//--- Обновим картинку графика
   ChartRedraw(0);
//--- Всё в порядке. Линия успешно нарисована.
   return(true);
  }

И на основе этой общей функции создадим другую функцию, которая рисует прямую по двум соседним экстремумам.

//+------------------------------------------------------------------+
//| Рисует трендовую линию по ближайшим справа экстремумам           |
//+------------------------------------------------------------------+
void              CGraphics::DrawTrendLine(void)
  {
   int dropped_bar_number=CMouse::Bar(); // номер свечи под мышью
   int p1=0,p2=0;                        // номера первой и второй точек
   string trend_name =                   // имя линии тренда
      CUtilites::GetCurrentObjectName(Trend_Line_Prefix,OBJ_TREND);
   double
      price1=0,   // цена первой точки
      price2=0,   // цена второй точки
      tmp_price;  // переменная для временного хранения цены
   datetime
      time1=0,    // Время первой точки
      time2=0,    // Время второй точки
      tmp_time;
   int x1,x2,y1,y2;   // Координаты точек
   int window=0;      // Номер подокна

//--- Установка начальных параметров
   if(CMouse::Below()) // Если мышь под Low свечи
     {
      //--- Находим два экстремума снизу
      CUtilites::SetExtremumsBarsNumbers(false,p1,p2);

      //--- Определяем координаты точек
      time1=iTime(Symbol(),PERIOD_CURRENT,p1);
      price1=iLow(Symbol(),PERIOD_CURRENT,p1);
      time2=iTime(Symbol(),PERIOD_CURRENT,p2);
      price2=iLow(Symbol(),PERIOD_CURRENT,p2);
     }
   else // иначе
      if(CMouse::Above()) // если мышь над High свечи
        {
         //--- Находим два экстремума сверху
         CUtilites::SetExtremumsBarsNumbers(true,p1,p2);
        
         //--- Определяем координаты точек
         time1=iTime(Symbol(),PERIOD_CURRENT,p1);
         price1=iHigh(Symbol(),PERIOD_CURRENT,p1);
         time2=iTime(Symbol(),PERIOD_CURRENT,p2);
         price2=iHigh(Symbol(),PERIOD_CURRENT,p2);

        }
//--- Отрисовка линии
   TrendCreate(0,trend_name,0,
               time1,price1,time2,price2,
               CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()),
               0,Trend_Line_Width,false,true,m_Is_Trend_Ray
              );

//--- Перерисовка графика
   ChartRedraw(0);
  } 

Хочу ещё раз обратить ваше внимание на вызов функции CUtilites::SetExtremumsBarsNumbers, которая получает номера баров для точек 1 и 2. Её код был описан выше. Остальное мне кажется очевидным и не требующим длинного описания.

Итоговая функция рисует простую прямую по двум точкам. В зависимости от глобального параметра Is_Trend_Ray, (описан в файле "GlobalVariables.mqh") линия будет либо лучом, продлённым вправо, либо просто коротким отрезком между двумя экстремумами.

 Is_Trend_Ray = true  Is_Trend_Ray = false

Возможность переключать продление линии тоже выведем на клавиатуру.


Создание блока управления: настройка метода OnChartEvent

Теперь, когда основные функции написаны, можно настроить сочетания клавиш.

В "Shortcuts.mqh" прописываем метод CShortcuts::OnChartEvent.

//+------------------------------------------------------------------+
//| Функция обработки событий                                        |
//+------------------------------------------------------------------+
void CShortcuts::OnChartEvent(
   const int id,
   const long &lparam,
   const double &dparam,
   const string &sparam
)
  {
//---
   switch(id)
     {
      //--- Сохранить координаты курсора мыши
      case CHARTEVENT_MOUSE_MOVE:
         CMouse::SetCurrentParameters(id,lparam,dparam,sparam);
         break;

      //--- Обработка нажатий клавиш
      case CHARTEVENT_KEYDOWN:
         //--- Изменить таймфрейм на 1 уровень вверх
         if(CUtilites::GetCurrentOperationChar(Up_Key) == lparam)
           {
            CUtilites::ChangeTimeframes(true);
           };
         //--- Изменить таймфрейм на 1 уровень вниз
         if(CUtilites::GetCurrentOperationChar(Down_Key) == lparam)
           {
            CUtilites::ChangeTimeframes(false);
           };
         //--- Изменить Z-Index графика (график поверх всех объектов)
         if(CUtilites::GetCurrentOperationChar(Z_Index_Key) == lparam)
           {
            CUtilites::ChangeChartZIndex();
           }
         //--- Нарисовать трендовую линию
         if(CUtilites::GetCurrentOperationChar(Trend_Line_Key) == lparam)
           {
            m_graphics.DrawTrendLine();
           }
         //--- Переключить параметр Is_Trend_Ray
         if(CUtilites::GetCurrentOperationChar(Switch_Trend_Ray_Key) == lparam)
           {
            m_graphics.IsRay(!m_graphics.IsRay());
           }

         break;
         //---

     }
  } 

Клавиши, используемые в текущей реализации библиотеки

Действие
 Клавиша От английского слова
 Перейти на таймфрейм вверх по основным периодам (из панели периодов) U  Up
 Перейти на таймфрейм вниз  D  Down
 Смена Z-уровня графика (график сверху или снизу объектов)  Z  Z-order
 Рисование наклонной трендовой линии по двум ближайшим к мыши однонаправленным экстремумам  T  Trend line
 Переключение режима луча для новых прямых
 R  Ray

Заключение

В прикреплённом файле архив текущей версии библиотеки. Также в файле лежит три скрипта.

  • Первый называется Del-All-Graphics. Он удаляет все графические объекты в текущем окне. В своём терминале я назначил ему сочетание клавиш Ctrl+A (All).
  • Второй — Del-All-Prefixed. С его помощью можно удалить все объекты по префиксу имени (например, все трендовые линии, или все объекты, начинающиеся на H1). Я его вызываю с помощью Alt+R (Remove).
  • И, наконец, третий скрипт (DeselectAllObjects) позволяет снять все выделения в текущем окне. Моё сочетание для него Ctrl+D (Deselect, как в Фотошопе).

Библиотеку лучше подключать к эксперту, а не к индикатору, так как если подключить к индикатору и попытаться использовать этот индикатор вместе с каким-нибудь другим советником, то будут жуткие тормоза. Во всяком случае, у меня было так... Может, я не умею его готовить?

Ну и напоследок  — о планах.

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

Третья версия будет содержать графический интерфейс для настройки параметров графических инструментов.

Четвёртая  — если дойдут руки  — будет уже полноценным советником-сопроводилкой и позволит облегчить ручную торговлю. Вот тут вопрос к сообществу. Я не уверен, что буду применять какие-то новые решения по сравнению с существующими аналогами... Интерфейс, конечно, буду рисовать сам, и могу выслушать пожелания, но торговля руками есть торговля руками :-).  Соответственно... Оно кому-нибудь надо?

Прикрепленные файлы |
Oleh Fedorov
Oleh Fedorov | 14 май 2020 в 14:46
aJannaDream:
Как все интересно и непонятно...
А чего непонятно-то? Вторая часть на подходе, там будут картинки ;-) Надеюсь, до выходных выложу. Не факт, конечно, но... Очень хочу.
Алексей Мокрушин
Алексей Мокрушин | 15 май 2020 в 20:26
Да не, все очень понятно. Я читаю каждую часть, и по окончании перечитываю сначала. Все сразу встает на свои места. Кстати, стырил часть кода)) Спасибо!!!!
Oleh Fedorov
Oleh Fedorov | 16 май 2020 в 15:06
Алексей Мокрушин:
Да не, все очень понятно. Я читаю каждую часть, и по окончании перечитываю сначала. Все сразу встает на свои места. Кстати, стырил часть кода)) Спасибо!!!!
На здоровье :-) Для того и пишу...
MrVIS
MrVIS | 19 май 2020 в 10:13
Спасибо, Oleh, за статью, которая мне пришлась очень кстати. Уверен, что Ваша информация облегчит жизнь многим, кто также как и Вы, торгует руками.
Oleh Fedorov
Oleh Fedorov | 27 май 2020 в 11:32
MrVIS:
Спасибо, Oleh, за статью, которая мне пришлась очень кстати. Уверен, что Ваша информация облегчит жизнь многим, кто также как и Вы, торгует руками.
На здоровье! Рад, что помогло. Продолжение на подходе...
Работа с таймсериями в библиотеке DoEasy (Часть 44): Класс-коллекция объектов индикаторных буферов Работа с таймсериями в библиотеке DoEasy (Часть 44): Класс-коллекция объектов индикаторных буферов

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

Мультивалютный мониторинг торговых сигналов (Часть 5): Составные сигналы Мультивалютный мониторинг торговых сигналов (Часть 5): Составные сигналы

В пятой части разработки приложения мониторинга торговых сигналов введем в систему понятие составного сигнала и реализуем необходимый для этого функционал. Ранее в приложении использовались простые сигналы, такие как RSI, WPR, CCI, а также введенная возможность использования собственного индикатора.

О методах поиска зон перекупленности/перепроданности. Часть I О методах поиска зон перекупленности/перепроданности. Часть I

Зоны перекупленности/перепроданности характеризуют определённое состояние рынка, отличающееся ослаблением динамики цен финансовых инструментов. Причём такое негативное изменение динамики наиболее выражено в заключительной стадии развития тренда любого масштаба. А так как величина прибыли при трейдинге напрямую зависит от возможности охвата максимальной амплитуды тренда, то точность выявления таких зон является важнейшей задачей при торговле любым финансовым инструментом.

Инструментарий для быстрой ручной торговли: Базовый функционал Инструментарий для быстрой ручной торговли: Базовый функционал

На текущий момент всё больше трейдеров переходят на автоматические системы торговли, которые либо требуют начальную настройку, либо часть из них уже полностью автоматизированы. Тем не менее остается немалая часть трейдеров, которые торгуют руками по старинке. В данной статье создадим набор инструментов для быстрой ручной торговли с помощью горячих клавиш и выполнения типичных торговых действий в один клик.