English 中文 Español Deutsch 日本語 Português
Инструментарий для быстрой ручной торговли: Работа с открытыми и отложенными ордерами

Инструментарий для быстрой ручной торговли: Работа с открытыми и отложенными ордерами

MetaTrader 5Трейдинг | 5 июня 2020, 13:53
6 137 8
Alexander Fedosov
Alexander Fedosov

Содержание

Введение

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


Постановка задачи

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

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

Теперь рассмотрим более наглядно каждый из этапов в отдельности. 


Переработка структуры приложения

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

Рис.1 Основные блоки инструментария

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

Рис.2 Новая структура приложения

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

Рис.3 Вкладка Контроль рыночных позиций

Итак, на рис.3 вкладка Контроль рыночных позиций. Включает в себя следующие элементы:

  • Таблица рыночных позиций. Отображает информацию по всем открытым данным приложением позициям. Похожа на таблицу, которая находится во вкладке Торговля в терминале MetaTrader 5. 
  • Три поля ввода. Соответствуют и привязаны к столбцам таблицы, слева направо: Объем, Стоп Лосс, Тейк профит.
  • Две кнопки. По нажатию на строку в таблице в полях ввода отобразятся параметры позиции, которые можно редактировать. Так при нажатии на Изменить можно поменять Стоп Лосс и Тейк Профит выбранной в таблице позиции, или же удалить их вовсе. Кнопка Закрыть закрывает позицию по текущей цене. Обращу внимание на то, что при закрытии проверяется поле ввода Объем. То есть можно выбрать меньший, чем у текущей позиции лот и закрыть ее частично. 

Теперь на рис.4 рассмотрим вкладку Контроль отложенных ордеров.

Рис.4 Контроль отложенных ордеров.

Здесь почти всё тоже самое:

  • Таблица отложенных ордеров. Содержит в себе список отложенных ордеров, открытых через данный инструментарий.
  • Три поля ввода. Здесь при выборе в таблице отложенного ордера можно изменить его текущую цену исполнения, а также поменять или удалить Стоп Лосс или Тейк Профит.
  • Две кнопки. Кнопка Изменить, в отличие от таблицы рыночных позиций, обращается ко всем трем полям ввода для редактирования выбранного  отложенного ордера. А кнопка Закрыть удаляет ордер.

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

Рис.5 Расширение функционала создания закрытия рыночных позиций.

Как мы видим на рис.5 у нас появились 4 новые кнопки: Закрыть BUY прибыльные, Закрыть SELL прибыльные, Закрыть BUY убыточные, Закрыть SELL убыточные. Суть их применения очевидна, но помимо этого, справа от них добавлены переключатели, их следует рассмотреть более подробно. Переключатели все одинаковы, поэтому описания меток на них буду справедливы ко всем.

  • All. Стоит по умолчанию. Применимо к кнопкам не накладывает каких либо условий и закрывает все выбранные.
  • >Point. Закрывает все позиции выбранного типа у которых прибыль/убыток выше заданного количества пунктов.
  • >Currency. Закрывает все позиции выбранного типа у которых прибыль/убыток больше заданного числа в валюте депозита.
  • Sum>Points. Закрывает все позиции выбранного типа сумма прибыли/убытка больше заданного количества пунктов.
  • Sum>Currency. Закрывает все позиции выбранного типа сумма прибыли/убытка больше заданного числа в валюте депозита.
Важное замечание: При выборе последних двух пунктов суммы рассчитываются только для позиций открытых данным приложением с заданным магическим номер. Также проверка суммы идет по всем позициям, удовлетворяющим двум критериям: тип позиции и магический номер.

Для ясности понимания приведем пример, согласно настройкам на рис.5: Закрыть Все Убыточные и опция sum>currency. В данном случае инструментарий найдет все открытые им позиции, суммирует их прибыль и если она будет более 10 единиц в валюте депозита, то закроет их все.


Реализация дополнений инструментария

За основу возьмем предыдущий проект из архива в конце статьи Инструментарий для быстрой ручной торговли: Базовый функционал. Первым делом необходимо перестроить структуру главного окна согласно рис.2 и для этого добавим элемент интерфейса Вкладка с помощью создания в базовом классе CProgram метода CreateTabs() и реализуем его в файле MainWindow.mqh.

//+------------------------------------------------------------------+
//| Создаёт группу с вкладками                                       |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTabs(const int x_gap,const int y_gap)
{
//--- Сохраним указатель на главный элемент
   m_tab.MainPointer(m_main_window);
//--- Свойства
   m_tab.Font(m_base_font);
   m_tab.FontSize(m_base_font_size);
   m_tab.LabelColor(clrWhite);
   m_tab.LabelColorHover(clrWhite);
   m_tab.IsCenterText(true);
   m_tab.AutoXResizeMode(true);
   m_tab.AutoYResizeMode(true);
   m_tab.AutoXResizeRightOffset(5);
   m_tab.AutoYResizeBottomOffset(5);
   m_tab.TabsYSize(27);
   m_tab.GetButtonsGroupPointer().Font(m_base_font);
   m_tab.GetButtonsGroupPointer().FontSize(m_base_font_size);
//--- Добавим вкладки с указанными свойствами
   string tabs_names[3];
   tabs_names[0]=TRADING;
   tabs_names[1]=CAPTION_M_CONTROL_NAME;
   tabs_names[2]=CAPTION_P_CONTROL_NAME;
   for(int i=0; i<3; i++)
      m_tab.AddTab(tabs_names[i],180);
//--- Создадим элемент управления
   if(!m_tab.CreateTabs(x_gap,y_gap))
      return(false);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,m_tab);
   return(true);
}

В теле созданного метода введены три новые макроподстановки, служащие заголовками вкладок, поэтому добавим их на двух языках в файл Defines.mqh:

#define TRADING                        (m_language==RUSSIAN ? "Трейдинг" : "Trading")
#define CAPTION_M_CONTROL_NAME         (m_language==RUSSIAN ? "Контроль рыночных позиций" : "Market Control")
#define CAPTION_P_CONTROL_NAME         (m_language==RUSSIAN ? "Контроль отложенных ордеров" : "Pending Control")

Прежде чем применить только что созданный метод необходимо перестроить методы создания кнопок и назначить их первой вкладке. Для этого найдем общий создающий их метод CreateButton() и изменим его:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateButton(CButton &button,string text,color baseclr,int x_gap,int y_gap)
{
//--- Сохраним указатель на окно
   button.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(0,button);
//--- Установим свойства перед созданием
   button.XSize(180);
   button.YSize(40);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Создадим элемент управления
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

Теперь применим текущие изменения в методе создания главного окна.

//+------------------------------------------------------------------+
//| Создаёт форму для ордеров                                        |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMainWindow(void)
{
//--- Добавим указатель окна в массив окон
   CWndContainer::AddWindow(m_main_window);
//--- Свойства
   m_main_window.XSize(600);
   m_main_window.YSize(375);
//--- Координаты
   int x=5;
   int y=20;
   m_main_window.CaptionHeight(22);
   m_main_window.IsMovable(true);
   m_main_window.CaptionColor(m_caption_color);
   m_main_window.CaptionColorLocked(m_caption_color);
   m_main_window.CaptionColorHover(m_caption_color);
   m_main_window.BackColor(m_background_color);
   m_main_window.FontSize(m_base_font_size);
   m_main_window.Font(m_base_font);
//--- Создание формы
   if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y))
      return(false);
//--- Вкладки
   if(!CreateTabs(5,22+27))
      return(false);
//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10))
      return(false);
   if(!CreateButton(m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60))
      return(false);
   if(!CreateButton(m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110))
      return(false);
   if(!CreateButton(m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60))
      return(false);
//---
   return(true);
}

Как видно здесь мы добавили сначала вкладки, потом обновили вызовы методов создающих кнопки, а также поменяли размеры главного окна. После компиляции проекта получим результат в виде переноса всех кнопок в первую из трех созданных вкладок:

Рис.6 Создание вкладок и перенос кнопок в первую из них

Согласно намёткам из рис.5 создадим дополнительные кнопки и поля ввода под реализацию заявленного выше функционала. Если для больших кнопок мы будем применять обновленный метод CreateButton(), то для создания полей ввода и переключателей необходимо ввести дополнительные методы: CreateModeButton() — кнопка-переключатель режимов, CreateModeEdit() — поле ввода значений. Их полная реализация:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModeButton(CButton &button,string text,int x_gap,int y_gap)
{
//--- Сохраним указатель на окно
   button.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(0,button);
   color baseclr=clrDarkViolet;
//--- Установим свойства перед созданием
   button.XSize(80);
   button.YSize(20);
   button.Font(m_base_font);
   button.FontSize(9);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Создадим элемент управления
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModeEdit(CTextEdit &text_edit,int x_gap,int y_gap)
{
//--- Сохраним указатель на окно
   text_edit.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(0,text_edit);
//--- Свойства
   text_edit.XSize(80);
   text_edit.YSize(20);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.SetDigits(2);
   text_edit.MaxValue(99999);
   text_edit.StepValue(0.01);
   text_edit.MinValue(0.01);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Создадим элемент управления
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0.01));
   text_edit.IsLocked(true);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

Теперь метод создания главного окна с применением выше созданных методов и добавлением кнопок из рис.5 будет таким:

//+------------------------------------------------------------------+
//| Создаёт форму для ордеров                                        |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMainWindow(void)
{
//--- Добавим указатель окна в массив окон
   CWndContainer::AddWindow(m_main_window);
//--- Свойства
   m_main_window.XSize(600);
   m_main_window.YSize(375);
//--- Координаты
   int x=5;
   int y=20;
   m_main_window.CaptionHeight(22);
   m_main_window.IsMovable(true);
   m_main_window.CaptionColor(m_caption_color);
   m_main_window.CaptionColorLocked(m_caption_color);
   m_main_window.CaptionColorHover(m_caption_color);
   m_main_window.BackColor(m_background_color);
   m_main_window.FontSize(m_base_font_size);
   m_main_window.Font(m_base_font);
//--- Создание формы
   if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y))
      return(false);
//--- Вкладки
   if(!CreateTabs(5,22+27))
      return(false);
//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10))
      return(false);
   if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT,C'87,128,255',10,10+45*2))
      return(false);
   if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT,C'87,128,255',10,10+45*3))
      return(false);
   if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4))
      return(false);
   if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS,C'87,128,255',10,10+45*5))
      return(false);
   if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS,C'87,128,255',10,10+45*6))
      return(false);
//---
   for(int i=0; i<6; i++)
   {
      if(!CreateModeButton(m_mode_button[i],"all",205-10,10+45*(i+1)))
         return(false);
      if(!CreateModeEdit(m_mode_edit[i],204-10,30+45*(i+1)))
         return(false);
   }
//---
   return(true);
}

Тут также введены новые макроподстановки, поэтому их значения на обоих языках добавим в Defines.mqh:

#define CLOSE_BUY_PROFIT               (m_language==RUSSIAN ? "Закрыть BUY прибыльные" : "Close BUY Profit")
#define CLOSE_SELL_PROFIT              (m_language==RUSSIAN ? "Закрыть SELL прибыльные" : "Close SELL Profit")
#define CLOSE_ALL_PROFIT               (m_language==RUSSIAN ? "Закрыть ВСЕ прибыльные" : "Close ALL Profit")
#define CLOSE_BUY_LOSS                 (m_language==RUSSIAN ? "Закрыть BUY убыточные" : "Close BUY Losing")
#define CLOSE_SELL_LOSS                (m_language==RUSSIAN ? "Закрыть SELL убыточные" : "Close SELL Losing")
#define CLOSE_ALL_LOSS                 (m_language==RUSSIAN ? "Закрыть ВСЕ убыточные" : "Close ALL Losing")

Компилируем проект и получаем следующий промежуточный результат:

Рис.7 Добавление кнопок и переключателей режимов

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

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ModeButtonSwitch(CButton &button,long lparam,string &states[])
{
   if(lparam==button.Id())
   {
      int size=ArraySize(states);
      for(int i=0; i<size; i++)
      {
         if(button.LabelText()==states[i])
         {
            if(i==size-1)
            {
               SetButtonParam(button,states[0]);
               break;
            }
            else
            {
               SetButtonParam(button,states[i+1]);
               break;
            }
         }
      }
   }
}

А второй новый метод ModeEditSwitch() необходим для того, чтобы настройки полей ввода соответствовали выбранного кнопкой режиму. Например пункты это целые числа, а при вводе значения в валюте депозита необходимо числа с разрядностью 2.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ModeEditSwitch(long lparam,string &states[])
{
   for(int i=0; i<6; i++)
   {
      if(lparam==m_mode_button[i].Id())
      {
         if(m_mode_button[i].LabelText()==states[1])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(0);
            m_mode_edit[i].StepValue(1);
            m_mode_edit[i].MaxValue(9999);
            m_mode_edit[i].MinValue(1);
            m_mode_edit[i].SetValue(string(20));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=1;
         }
         else if(m_mode_button[i].LabelText()==states[2])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(2);
            m_mode_edit[i].StepValue(0.01);
            m_mode_edit[i].MaxValue(99999);
            m_mode_edit[i].MinValue(0.01);
            m_mode_edit[i].SetValue(string(0.1));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=2;
         }
         else if(m_mode_button[i].LabelText()==states[3])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(0);
            m_mode_edit[i].StepValue(1);
            m_mode_edit[i].MaxValue(9999);
            m_mode_edit[i].MinValue(1);
            m_mode_edit[i].SetValue(string(20));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=3;
         }
         else if(m_mode_button[i].LabelText()==states[4])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(2);
            m_mode_edit[i].StepValue(0.01);
            m_mode_edit[i].MaxValue(99999);
            m_mode_edit[i].MinValue(0.01);
            m_mode_edit[i].SetValue(string(0.1));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=4;
         }
         else
         {
            m_mode_edit[i].IsLocked(true);
            m_current_mode[i]=0;
         }
      }
   }
}

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

      //---
      string states[5]= {"all",">points",">currency","sum>points","sum>currency"};
      for(int i=0; i<6; i++)
         ModeButtonSwitch(m_mode_button[i],lparam,states);
      //---
      ModeEditSwitch(lparam,states);

Компилируем проект. Теперь видно, что при переключении режима, это видно на рис.8, меняется и свойства полей ввода.


Рис.8 Переключение режима закрытия рыночных позиций

Следующим шагом необходимо реализовать логику действий согласно описанию на кнопках, при этом их связать с теми режимами, которые были введены. Два действия у нас уже есть, это "Закрыть Все Прибыльные" и "Закрыть Все убыточные". Однако с учетом введения режимов закрытия необходимо их дополнить. Этим действиям соответствуют методы CloseAllMarketProfit() и CloseAllMarketLoss().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[2].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_C))
   {
      //--- объявление запроса и результата
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // количество открытых позиций
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Подсчет прибыли для режима 4 и 5
      if(m_current_mode[0]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- параметры ордера
            ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            int profit_pp;
            if(type==POSITION_TYPE_BUY)
               profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
            else
               profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
               {
                  switch(m_current_mode[0])
                  {
                  case  3:
                     sum_pp+=profit_pp;
                     break;
                  case  4:
                     sum_cur+=profit_cur+swap;
                     break;
                  }
               }
         }
      }
      //--- перебор всех открытых позиций
      for(int i=total-1; i>=0; i--)
      {
         //--- параметры ордера
         ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // количество знаков после запятой
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // объем позиции
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         int profit_pp;
         //---
         if(type==POSITION_TYPE_BUY)
            profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
         else
            profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
         //--- если MagicNumber совпадает
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               if(   (m_current_mode[0]==0 && profit_cur+swap>0) ||                                   // Закрыть все прибыльные
                     (m_current_mode[0]==1 && profit_pp>int(m_mode_edit[0].GetValue())) ||            // Закрыть все прибыльные выше N пунктов
                     (m_current_mode[0]==2 && profit_cur+swap>double(m_mode_edit[0].GetValue())) ||   // Закрыть все прибыльные выше N валюты депозита
                     (m_current_mode[0]==3 && sum_pp>int(m_mode_edit[0].GetValue())) ||               // Закрыть все, сумма которых выше N пунктов
                     (m_current_mode[0]==4 && sum_cur>double(m_mode_edit[0].GetValue()))              // Закрыть все, сумма которых выше N валюты депозита
                 )
               {
                  //--- обнуление значений запроса и результата
                  ZeroMemory(request);
                  ZeroMemory(result);
                  //--- установка параметров операции
                  request.action   =TRADE_ACTION_DEAL;        // тип торговой операции
                  request.position =position_ticket;          // тикет позиции
                  request.symbol   =position_symbol;          // символ
                  request.volume   =volume;                   // объем позиции
                  request.deviation=5;                        // допустимое отклонение от цены
                  request.magic    =m_magic_number;           // MagicNumber позиции
                  //--- установка цены и типа ордера в зависимости от типа позиции
                  if(type==POSITION_TYPE_BUY)
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                  }
                  else
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                  }
                  //--- отправка запроса
                  bool res=true;
                  for(int j=0; j<5; j++)
                  {
                     res=OrderSend(request,result);
                     if(res && result.retcode==TRADE_RETCODE_DONE)
                        break;
                     else
                        PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
                  }
               }
            }
      }
   }
}

Это модификация метода Закрытия всех рыночных позиций. Как мы помним, ранее был введен статический массив m_current_mode, отслеживающий выбор режима для каждого из действий на кнопке. Поэтому для режимов 4 и 5, что соответствуют закрытию всех позиции по общей прибыли в пунктах или в валюте депозита, производится ее расчет. Далее снова происходит выборка, принадлежащих нашему инструментарию и в зависимости от выбранного режима закрытия проверяются условия, при удовлетворении которых происходит закрытие всех рыночных позиции, созданных инструментарием.

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

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[6].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_D))
   {
      //--- объявление запроса и результата
      MqlTradeRequest request;
      MqlTradeResult  result;
      ZeroMemory(request);
      ZeroMemory(result);
      int total=PositionsTotal(); // количество открытых позиций
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Подсчет убытков
      if(m_current_mode[3]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- параметры ордера
            ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
            double loss_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            int loss_pp;
            //---
            if(type==POSITION_TYPE_BUY)
               loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
            else
               loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
               {
                  switch(m_current_mode[3])
                  {
                  case  3:
                     sum_pp+=loss_pp;
                     break;
                  case  4:
                     sum_cur+=loss_cur+swap;
                     break;
                  }
               }
         }
      }
      //--- перебор всех открытых позиций
      for(int i=total-1; i>=0; i--)
      {
         //--- параметры ордера
         ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // количество знаков после запятой
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // объем позиции
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
         double loss_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         int loss_pp;
         if(type==POSITION_TYPE_BUY)
            loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
         else
            loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
         //--- если MagicNumber совпадает
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               if(   (m_current_mode[3]==0 && loss_cur+swap<0) ||
                     (m_current_mode[3]==1 && loss_pp<-int(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==2 && loss_cur+swap<-double(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==3 && sum_pp<-int(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==4 && sum_cur<-double(m_mode_edit[3].GetValue()))
                 )
               {
                  //--- обнуление значений запроса и результата
                  ZeroMemory(request);
                  ZeroMemory(result);
                  //--- установка параметров операции
                  request.action=TRADE_ACTION_DEAL;         // тип торговой операции
                  request.position=position_ticket;         // тикет позиции
                  request.symbol=position_symbol;           // символ
                  request.volume=volume;                    // объем позиции
                  request.deviation=5;                      // допустимое отклонение от цены
                  request.magic=m_magic_number;             // MagicNumber позиции
                  //--- установка цены и типа ордера в зависимости от типа позиции
                  if(type==POSITION_TYPE_BUY)
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                  }
                  else
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                  }
                  //--- отправка запроса
                  bool res=true;
                  for(int j=0; j<5; j++)
                  {
                     res=OrderSend(request,result);
                     if(res && result.retcode==TRADE_RETCODE_DONE)
                        break;
                     else
                        PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
                  }
               }
            }
      }
   }
}

Также для режимов проверки суммы убытка всех открытых позиций в пунктах или валюте депозита производится расчет и далее в зависимости от режима закрытия, проверяются условия, по которым закрываются всe созданные приложением рыночные позиции.

Теперь перейдем к новым действиям с открытыми позициями, это Закрытие Buy/Sell позиций либо прибыльных, либо убыточных. По сути это частные случаи двух методов, которые описаны выше. В текущих же будет всё тоже самое, разве что будет добавлена фильтрация по типу. Для начала создадим методы, роль которых будет в выполнении заданных действий:

  • CloseBuyMarketProfit() — закрывает все прибыльные Buy-позиции.
  • CloseSellMarketProfit() — закрывает все прибыльные Sell-позиции.
  • CloseBuyMarketLoss() — закрывает все убыточные Buy-позиции.  
  • CloseSellMarketLoss() — закрывает все убыточные Sell-позиции.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseBuyMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_U))
   {
      //--- объявление запроса и результата
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // количество открытых позиций
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Подсчет прибыли для режима 4 и 5
      if(m_current_mode[1]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- параметры ордера
            ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_BUY)
                  {
                     int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- перебор всех открытых позиций
      for(int i=total-1; i>=0; i--)
      {
         //--- параметры ордера
         ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // количество знаков после запятой
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // объем позиции
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_BUY)
               {
                  int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                  if(   (m_current_mode[1]==0 && profit_cur+swap>0) ||                                   // Закрыть все прибыльные
                        (m_current_mode[1]==1 && profit_pp>int(m_mode_edit[1].GetValue())) ||            // Закрыть все прибыльные выше N пунктов
                        (m_current_mode[1]==2 && profit_cur+swap>double(m_mode_edit[1].GetValue())) ||   // Закрыть все прибыльные выше N валюты депозита
                        (m_current_mode[1]==3 && sum_pp>int(m_mode_edit[1].GetValue())) ||               // Закрыть все, сумма которых выше N пунктов
                        (m_current_mode[1]==4 && sum_cur>double(m_mode_edit[1].GetValue()))              // Закрыть все, сумма которых выше N валюты депозита
                    )
                  {
                     //--- обнуление значений запроса и результата
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- установка параметров операции
                     request.action   =TRADE_ACTION_DEAL;        // тип торговой операции
                     request.position =position_ticket;          // тикет позиции
                     request.symbol   =position_symbol;          // символ
                     request.volume   =volume;                   // объем позиции
                     request.deviation=5;                        // допустимое отклонение от цены
                     request.magic    =m_magic_number;           // MagicNumber позиции
                     //--- установка цены и типа ордера в зависимости от типа позиции
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                     //--- отправка запроса
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseBuyMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[7].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_H))
   {
      //--- объявление запроса и результата
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // количество открытых позиций
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Подсчет прибыли для режима 4 и 5
      if(m_current_mode[4]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- параметры ордера
            ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_BUY)
                  {
                     int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- перебор всех открытых позиций
      for(int i=total-1; i>=0; i--)
      {
         //--- параметры ордера
         ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // количество знаков после запятой
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // объем позиции
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_BUY)
               {
                  int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                  if(   (m_current_mode[4]==0 && profit_cur+swap<0) ||                                   // Закрыть все прибыльные
                        (m_current_mode[4]==1 && profit_pp<-int(m_mode_edit[4].GetValue())) ||            // Закрыть все прибыльные выше N пунктов
                        (m_current_mode[4]==2 && profit_cur+swap<-double(m_mode_edit[4].GetValue())) ||   // Закрыть все прибыльные выше N валюты депозита
                        (m_current_mode[4]==3 && sum_pp<-int(m_mode_edit[4].GetValue())) ||               // Закрыть все, сумма которых выше N пунктов
                        (m_current_mode[4]==4 && sum_cur<-double(m_mode_edit[4].GetValue()))              // Закрыть все, сумма которых выше N валюты депозита
                    )
                  {
                     //--- обнуление значений запроса и результата
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- установка параметров операции
                     request.action   =TRADE_ACTION_DEAL;        // тип торговой операции
                     request.position =position_ticket;          // тикет позиции
                     request.symbol   =position_symbol;          // символ
                     request.volume   =volume;                   // объем позиции
                     request.deviation=5;                        // допустимое отклонение от цены
                     request.magic    =m_magic_number;           // MagicNumber позиции
                     //--- установка цены и типа ордера в зависимости от типа позиции
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                     //--- отправка запроса
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseSellMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[5].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_J))
   {
      //--- объявление запроса и результата
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // количество открытых позиций
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Подсчет прибыли для режима 4 и 5
      if(m_current_mode[2]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- параметры ордера
            ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_SELL)
                  {
                     int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                     switch(m_current_mode[2])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- перебор всех открытых позиций
      for(int i=total-1; i>=0; i--)
      {
         //--- параметры ордера
         ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // количество знаков после запятой
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // объем позиции
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_SELL)
               {
                  int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                  if(   (m_current_mode[2]==0 && profit_cur+swap>0) ||                                   // Закрыть все прибыльные
                        (m_current_mode[2]==1 && profit_pp>int(m_mode_edit[2].GetValue())) ||            // Закрыть все прибыльные выше N пунктов
                        (m_current_mode[2]==2 && profit_cur+swap>double(m_mode_edit[2].GetValue())) ||   // Закрыть все прибыльные выше N валюты депозита
                        (m_current_mode[2]==3 && sum_pp>int(m_mode_edit[2].GetValue())) ||               // Закрыть все, сумма которых выше N пунктов
                        (m_current_mode[2]==4 && sum_cur>double(m_mode_edit[2].GetValue()))              // Закрыть все, сумма которых выше N валюты депозита
                    )
                  {
                     //--- обнуление значений запроса и результата
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- установка параметров операции
                     request.action   =TRADE_ACTION_DEAL;        // тип торговой операции
                     request.position =position_ticket;          // тикет позиции
                     request.symbol   =position_symbol;          // символ
                     request.volume   =volume;                   // объем позиции
                     request.deviation=5;                        // допустимое отклонение от цены
                     request.magic    =m_magic_number;           // MagicNumber позиции
                     //--- установка цены и типа ордера в зависимости от типа позиции
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                     //--- отправка запроса
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseSellMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[8].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_L))
   {
      //--- объявление запроса и результата
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // количество открытых позиций
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Подсчет прибыли для режима 4 и 5
      if(m_current_mode[5]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- параметры ордера
            ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_SELL)
                  {
                     int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- перебор всех открытых позиций
      for(int i=total-1; i>=0; i--)
      {
         //--- параметры ордера
         ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // количество знаков после запятой
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // объем позиции
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_SELL)
               {
                  int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                  if(   (m_current_mode[5]==0 && profit_cur+swap<0) ||                                   // Закрыть все прибыльные
                        (m_current_mode[5]==1 && profit_pp<-int(m_mode_edit[5].GetValue())) ||            // Закрыть все прибыльные выше N пунктов
                        (m_current_mode[5]==2 && profit_cur+swap<-double(m_mode_edit[5].GetValue())) ||   // Закрыть все прибыльные выше N валюты депозита
                        (m_current_mode[5]==3 && sum_pp<-int(m_mode_edit[5].GetValue())) ||               // Закрыть все, сумма которых выше N пунктов
                        (m_current_mode[5]==4 && sum_cur<-double(m_mode_edit[5].GetValue()))              // Закрыть все, сумма которых выше N валюты депозита
                    )
                  {
                     //--- обнуление значений запроса и результата
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- установка параметров операции
                     request.action   =TRADE_ACTION_DEAL;        // тип торговой операции
                     request.position =position_ticket;          // тикет позиции
                     request.symbol   =position_symbol;          // символ
                     request.volume   =volume;                   // объем позиции
                     request.deviation=5;                        // допустимое отклонение от цены
                     request.magic    =m_magic_number;           // MagicNumber позиции
                     //--- установка цены и типа ордера в зависимости от типа позиции
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                     //--- отправка запроса
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
                     }
                  }
               }
      }
   }
}

После создания и реализации вызовем их в теле Обработчика событий OnEvent(), вне каких либо секций.

//---
   CloseBuyMarketProfit(id,lparam);
   CloseSellMarketProfit(id,lparam);
   CloseBuyMarketLoss(id,lparam);
   CloseSellMarketLoss(id,lparam);

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

//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10))
      return(false);
   if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT+"(U)",C'87,128,255',10,10+45*2))
      return(false);
   if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT+"(J)",C'87,128,255',10,10+45*3))
      return(false);
   if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4))
      return(false);
   if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS+"(H)",C'87,128,255',10,10+45*5))
      return(false);
   if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS+"(L)",C'87,128,255',10,10+45*6))
      return(false);

В результате получатся следующие назначения:

Рис.9 Назначение горячих клавиш добавленным действиям

На этом функционал вкладки Трейдинг закончен. Теперь перейдем к следующему этапу, это создание таблицы открытых инструментарием позиций и возможности ими управлять во вкладке Контроль рыночных позиций. На рис.3 в начале статьи представлена визуальная схема создания элементов интерфейса, который состоит из трех полей ввода, двух кнопок и самой таблицы. Приступим к их созданию. В начале создадим три поля ввода для редактирования лота, стоп лосса и тейк профита открытых позиций. Это будут методы CreateLotControl(),CreateStopLossControl(),CreateTakeProfitControl().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateLotControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Сохраним указатель на главный элемент
   text_edit.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(tab,text_edit);
//--- Свойства
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Создадим элемент управления
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTakeProfitControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Сохраним указатель на главный элемент
   text_edit.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(tab,text_edit);
//--- Свойства
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Создадим элемент управления
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateStopLossControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Сохраним указатель на главный элемент
   text_edit.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(tab,text_edit);
//--- Свойства
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.MinValue(0);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Создадим элемент управления
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

И вызовем новые методы в теле CreateMainWindow().

//--- Поля ввода редактирования открытых позиций
   if(!CreateLotControl(m_lot_edit[6],375,3,1))
      return(false);
   if(!CreateStopLossControl(m_sl_edit[6],375+80,3,1))
      return(false);
   if(!CreateTakeProfitControl(m_tp_edit[6],375+83*2,3,1))
      return(false);

Для кнопок Изменить и Закрыть мы также создадим два новых, реализующих их, метода — CreateModifyButton(), CreateCloseButton().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModifyButton(CButton &button,string text,int x_gap,int y_gap,int tab)
{
//--- Сохраним указатель на окно
   button.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(tab,button);
   color baseclr=clrDarkOrange;
   color pressclr=clrOrange;
//--- Установим свойства перед созданием
   button.XSize(80);
   button.YSize(24);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Создадим элемент управления
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateCloseButton(CButton &button,string text,int x_gap,int y_gap,int tab)
{
//--- Сохраним указатель на окно
   button.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(tab,button);
   color baseclr=clrCrimson;
   color pressclr=clrFireBrick;
//--- Установим свойства перед созданием
   button.XSize(80);
   button.YSize(24);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Создадим элемент управления
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

И таким же образом добавляем их в метод создания главного окна:

//--- Кнопки изменения/закрытия позиции
   if(!CreateModifyButton(m_small_button[0],MODIFY,622,3,1))
      return(false);
   if(!CreateCloseButton(m_small_button[1],CLOSE,622+80,3,1))
      return(false);

Тут новые две макроподстановки для локализации интерфейса, поэтому переходим в Defines.mqh и прописываем их значения там:

#define MODIFY                         (m_language==RUSSIAN ? "Изменить" : "Modify")
#define CLOSE                          (m_language==RUSSIAN ? "Закрыть" : "Close")

И теперь перейдем к самой таблице. Для этого создадим метод CreatePositionsTable(), реализуем его и тоже добавим в метод главного окна приложения.

//+------------------------------------------------------------------+
//| Создаёт таблицу позиций                                          |
//+------------------------------------------------------------------+
bool CFastTrading::CreatePositionsTable(CTable &table,const int x_gap,const int y_gap)
{
#define COLUMNS2_TOTAL 9
//--- Сохраним указатель на главный элемент
   table.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(1,table);
//--- Массив ширины столбцов
   int width[COLUMNS2_TOTAL];
   ::ArrayInitialize(width,80);
   width[0]=100;
   width[1]=110;
   width[2]=100;
   width[3]=60;
   width[6]=90;
//--- Массив выравнивания текста в столбцах
   ENUM_ALIGN_MODE align[COLUMNS2_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
//--- Массив отступа текста в столбцах по оси X
   int text_x_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Массив отступа картинок в столбцах по оси X
   int image_x_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(image_x_offset,3);
//--- Массив отступа картинок в столбцах по оси Y
   int image_y_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(image_y_offset,2);
//--- Свойства
   table.Font(m_base_font);
   table.FontSize(m_base_font_size);
   table.XSize(782);
   table.CellYSize(24);
   table.TableSize(COLUMNS2_TOTAL,1);
   table.TextAlign(align);
   table.ColumnsWidth(width);
   table.TextXOffset(text_x_offset);
   table.ImageXOffset(image_x_offset);
   table.ImageYOffset(image_y_offset);
   table.ShowHeaders(true);
   table.HeadersColor(C'87,128,255');
   table.HeadersColorHover(clrCornflowerBlue);
   table.HeadersTextColor(clrWhite);
   table.IsSortMode(false);
   table.LightsHover(true);
   table.SelectableRow(true);
   table.IsZebraFormatRows(clrWhiteSmoke);
   table.DataType(0,TYPE_LONG);
   table.AutoYResizeMode(true);
   table.AutoYResizeBottomOffset(5);
//--- Создадим элемент управления
   if(!table.CreateTable(x_gap,y_gap))
      return(false);
//--- Установим названия заголовков
   table.SetHeaderText(0,TICKET);
   table.SetHeaderText(1,SYMBOL);
   table.SetHeaderText(2,TYPE_POS);
   table.SetHeaderText(3,PRICE);
   table.SetHeaderText(4,VOLUME);
   table.SetHeaderText(5,SL);
   table.SetHeaderText(6,TP);
   table.SetHeaderText(7,SWAP);
   table.SetHeaderText(8,PROFIT);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,table);
   return(true);
}

Таблица будет иметь 9 столбцов. Также настроим в ней ширину столбцов так как названия столбцов различаются по длине. И установим заголовки столбцов с применением локализации, то есть для каждого столбца создадим свою макроподстановку в файле Defines.mqh.

#define SYMBOL                         (m_language==RUSSIAN ? "Символ" : "Symbol")
#define VOLUME                         (m_language==RUSSIAN ? "Объем" : "Volume")
#define TYPE_POS                       (m_language==RUSSIAN ? "Тип позиции" : "Position Type")
#define SWAP                           (m_language==RUSSIAN ? "Своп" : "Swap")
#define PROFIT                         (m_language==RUSSIAN ? "Прибыль" : "Profit")

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

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::WindowResize(int x_size,int y_size)
{
   m_main_window.GetCloseButtonPointer().Hide();
   m_main_window.ChangeWindowWidth(x_size);
   m_main_window.ChangeWindowHeight(y_size);
   m_main_window.GetCloseButtonPointer().Show();
}

И в обработчике событий создадим новое событие по клику на вкладку. В нем мы пропишем ширину главного окна для каждой вкладки.

//--- Событие переключение вкладки
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_TAB)
   {
      if(m_tab.SelectedTab()==0)
         WindowResize(600,m_main_window.YSize());
      if(m_tab.SelectedTab()==1)
         WindowResize(782+10,m_main_window.YSize());
      if(m_tab.SelectedTab()==2)
         WindowResize(682+10,m_main_window.YSize());
   }

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

  • Инициализация. Здесь мы определяем число удовлетворяющих условию принадлежности нашему инструментарию и перестраиваем таблицу под эти данные. 
  • Добавление данных. В подготовленную таблицу вносим параметры каждой открытой позиции согласно описанию в заголовках столбцов.
  • Обновляем таблицу с заполненной информацией.

Для инициализации создадим функцию InitializePositionsTable() и в ней делаем выборку из всех открытых позиций по текущем символу и магическому номеру. Получаем число позиций, удовлетворяющих нашим условиям. Добавляем в таблицу такое же число строк.

//+------------------------------------------------------------------+
//| Инициализация таблицы позиций                                    |
//+------------------------------------------------------------------+
void CFastTrading::InitializePositionsTable(void)
{
//--- Получим символы открытых позиций
   int total=PositionsTotal(); // количество открытых позиций
   int cnt=0;
//--- Удалить все строки
   m_table_positions.DeleteAllRows();
//--- Установим количество строк по количеству позиций
   for(int i=0; i<total; i++)
   {
      //--- параметры ордера
      ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
      //--- если MagicNumber совпадает
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.AddRow(cnt);
            cnt++;
         }
   }
//--- Если есть позиции
   if(cnt>0)
   {
      //--- Установим значения в таблицу
      SetValuesToPositionsTable();
      //--- Обновить таблицу
      UpdatePositionsTable();
   }
}

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

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetValuesToPositionsTable(void)
{
//---
   int cnt=0;
   for(int i=PositionsTotal()-1; i>=0; i--)
   {
      //--- параметры ордера
      ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
      int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // количество знаков после запятой
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
      double volume=PositionGetDouble(POSITION_VOLUME);                                 // объем позиции
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
      double stoploss=PositionGetDouble(POSITION_SL);
      double takeprofit=PositionGetDouble(POSITION_TP);
      double swap=PositionGetDouble(POSITION_SWAP);
      double profit=PositionGetDouble(POSITION_PROFIT);
      double openprice=PositionGetDouble(POSITION_PRICE_OPEN);
      string pos=(type==POSITION_TYPE_BUY)?"BUY":"SELL";
      profit+=swap;
      //--- если MagicNumber совпадает
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.SetValue(0,i,string(position_ticket));
            m_table_positions.SetValue(1,i,string(position_symbol));
            m_table_positions.SetValue(2,i,pos);
            m_table_positions.SetValue(3,i,string(openprice));
            m_table_positions.SetValue(4,i,string(volume));
            m_table_positions.SetValue(5,i,string(stoploss));
            m_table_positions.SetValue(6,i,string(takeprofit));
            m_table_positions.SetValue(7,i,string(swap));
            m_table_positions.SetValue(8,i,DoubleToString(profit,2));
            //---
            m_table_positions.TextColor(2,i,(pos=="BUY")? clrForestGreen : clrCrimson);
            m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver);
            cnt++;
         }
   }
}

И после заполнения данных, обновляем таблицу с помощью UpdatePositionsTable():

//+------------------------------------------------------------------+
//| Обновляет таблицу позиций                                        |
//+------------------------------------------------------------------+
void CFastTrading::UpdatePositionsTable(void)
{
//--- Обновить таблицу
   m_table_positions.Update(true);
   m_table_positions.GetScrollVPointer().Update(true);
}

Чтоб внесенные в проект изменения заработали необходимо их правильно настроить. Для этого перейдем в файл SimpleTrading.mq5 и в функции OnInit()добавим вызов метода инициализации класса приложения:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   tick_counter=GetTickCount();
//--- Инициализация переменных класса
   program.FontName("Trebuchet MS");
   program.FontSize(Inp_BaseFont);
   program.BackgroundColor(Background);
   program.CaptionColor(Caption);
   program.SetLanguage(Language);
   program.SetMagicNumber(MagicNumber);
//--- Установим торговую панель
   if(!program.CreateGUI())
   {
      Print(__FUNCTION__," > Не удалось создать графический интерфейс!");
      return(INIT_FAILED);
   }
   program.OnInitEvent();
//---
   return(INIT_SUCCEEDED);
}

Сделать это нужно строго после метода создания интерфейса приложения CreateGUI(). Теперь перейдем в тело OnInitEvent() и там вызовем инициализацию нашей таблицы. 

//+------------------------------------------------------------------+
//| Инициализация                                                    |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
   InitializePositionsTable();
}

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

Для этого создадим в публичной секции базового класса метода OnTradeEvent() и в его теле будем вызывать инициализацию таблицы. 

//+------------------------------------------------------------------+
//| Событие торговой операции                                        |
//+------------------------------------------------------------------+
void CFastTrading::OnTradeEvent(void)
{
//--- Если новая сделка
      InitializePositionsTable();
}

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

//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade(void)
{
   program.OnTradeEvent();
}

Совершенными действиями мы настроили актуальность отображения открытых позиций. Для обновления прибыли в публичной секции класса CFastTrading создадим метод UpdatePositionProfit():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UpdatePositionProfit(void)
{
//---
   int cnt=0;
   for(int i=PositionsTotal()-1; i>=0; i--)
   {
      //--- параметры ордера
      ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
      double swap=PositionGetDouble(POSITION_SWAP);
      double profit=PositionGetDouble(POSITION_PROFIT);
      profit+=swap;
      //--- если MagicNumber совпадает
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.SetValue(8,i,DoubleToString(profit,2));
            //---
            m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver);
            cnt++;
         }
   }
//---
   if(cnt>0)
      UpdatePositionsTable();
}

И вызовем его в OnTick():

void OnTick()
{
   program.UpdatePositionProfit();
}

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

//--- Событие клик по строке в таблице
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Проверка идентификатора элемента
      if(lparam==m_table_positions.Id())
      {
         //---
         int row=m_table_positions.SelectedItem();
         m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row)));
         m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row)));
         m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row)));
         m_lot_edit[6].GetTextBoxPointer().Update(true);
         m_sl_edit[6].GetTextBoxPointer().Update(true);
         m_tp_edit[6].GetTextBoxPointer().Update(true);
      }
   }

Компилируем проект и получаем, как показано на рис.10, значения из выбранной рыночной позиции в поля ввода.

Рис.10 Выбор открытой позиции для дальнейшего редактирования.

Далее создадим два метода ModifyPosition() и ClosePosition(), которые по нажатию на кнопки Изменить и Закрыть будут применять соответствующее действие к выбранной открытой позиции. 

//+------------------------------------------------------------------+
//| Изменение выбранной открытой позиции                             |
//+------------------------------------------------------------------+
bool CFastTrading::ModifyPosition(long lparam)
{
//--- Проверка идентификатора элемента
   if(lparam==m_small_button[0].Id())
   {
//--- Получим индекс и символ
      if(m_table_positions.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_positions.SelectedItem();
      ulong ticket=(ulong)m_table_positions.GetValue(0,row);
//---
      if(PositionSelectByTicket(ticket))
      {
         //--- объявление запроса и результата
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- вычисление и округление значений Stop Loss и Take Profit
         double sl=NormalizeDouble((double)m_sl_edit[6].GetValue(),_Digits);
         double tp=NormalizeDouble((double)m_tp_edit[6].GetValue(),_Digits);
         //--- обнуление значений запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- установка параметров операции
         request.action  =TRADE_ACTION_SLTP; // тип торговой операции
         request.position=ticket;            // тикет позиции
         request.symbol=Symbol();            // символ
         request.sl      =sl;                // Stop Loss позиции
         request.tp      =tp;                // Take Profit позиции
         request.magic=m_magic_number;       // MagicNumber позиции
         //--- отправка запроса
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
         }
      }
   }
//---
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::ClosePosition(long lparam)
{
//--- Проверка идентификатора элемента
   if(lparam==m_small_button[1].Id())
   {
      //--- Получим индекс и символ
      if(m_table_positions.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_positions.SelectedItem();
      ulong ticket=(ulong)m_table_positions.GetValue(0,row);
//---
      if(PositionSelectByTicket(ticket))
      {
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // тип позиции
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ
         //--- объявление запроса и результата
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- обнуление значений запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- установка цены и типа ордера в зависимости от типа позиции
         if(type==POSITION_TYPE_BUY)
         {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
            request.type =ORDER_TYPE_SELL;
         }
         else if(type==POSITION_TYPE_SELL)
         {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
            request.type =ORDER_TYPE_BUY;
         }
         //--- check volume
         double position_volume=PositionGetDouble(POSITION_VOLUME);
         double closing_volume=(double)m_lot_edit[6].GetValue();
         if(closing_volume>position_volume)
            closing_volume=position_volume;
         //--- setting request
         request.action   =TRADE_ACTION_DEAL;
         request.position =ticket;
         request.symbol   =Symbol();
         request.volume   =NormalizeLot(Symbol(),closing_volume);
         request.magic    =m_magic_number;
         request.deviation=5;
         //--- отправка запроса
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
         }
      }
   }
//---
   return(false);
}

На этом реализация задач во вкладке Контроль рыночных позиций закончена. Перейдем к разработке вкладки Контроль отложенных ордеров. Для этого в конце тела метода главного окна CreateMainWindow() добавим код, который добавит тот же функционал что и в предыдущей вкладке, а именно: таблицу учета отложенных ордеров, кнопки и поля ввода для их редактирования.

//--- Создаем таблицу отложенных ордеров
   if(!CreateOrdersTable(m_table_orders,0,22+5))
      return(false);
//--- Поля ввода редактирования отложенных ордеров
   if(!CreateLotControl(m_pr_edit[4],360,3,2))
      return(false);
   if(!CreateStopLossControl(m_sl_edit[7],360+80,3,2))
      return(false);
   if(!CreateTakeProfitControl(m_tp_edit[7],360+80*2,3,2))
      return(false);
//--- Кнопки изменения/удаления отложенных ордеров
   if(!CreateModifyButton(m_small_button[2],MODIFY,361+80*3,3,2))
      return(false);
   if(!CreateCloseButton(m_small_button[3],REMOVE,361+80*3,3+24,2))
      return(false);

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

//+------------------------------------------------------------------+
//| Создаёт таблицу учета отложенных ордеров                         |
//+------------------------------------------------------------------+
//---
bool CFastTrading::CreateOrdersTable(CTable &table,const int x_gap,const int y_gap)
{
#define COLUMNS1_TOTAL 7
//--- Сохраним указатель на главный элемент
   table.MainPointer(m_tab);
//--- Закрепить за вкладкой
   m_tab.AddToElementsArray(2,table);
//--- Массив ширины столбцов
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,80);
   width[0]=100;
   width[2]=100;
//--- Массив отступа текста в столбцах по оси X
   int text_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Массив отступа картинок в столбцах по оси X
   int image_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_x_offset,3);
//--- Массив отступа картинок в столбцах по оси Y
   int image_y_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_y_offset,2);
//--- Массив выравнивания текста в столбцах
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[6]=ALIGN_LEFT;
//--- Свойства
   table.Font(m_base_font);
   table.FontSize(m_base_font_size);
   table.XSize(602);
   table.CellYSize(24);
   table.TableSize(COLUMNS1_TOTAL,1);
   table.TextAlign(align);
   table.ColumnsWidth(width);
   table.TextXOffset(text_x_offset);
   table.ImageXOffset(image_x_offset);
   table.ImageYOffset(image_x_offset);
   table.ShowHeaders(true);
   table.HeadersColor(C'87,128,255');
   table.HeadersColorHover(clrCornflowerBlue);
   table.HeadersTextColor(clrWhite);
   table.IsSortMode(false);
   table.LightsHover(true);
   table.SelectableRow(true);
   table.IsZebraFormatRows(clrWhiteSmoke);
   table.AutoYResizeMode(true);
   table.AutoYResizeBottomOffset(5);
   table.DataType(0,TYPE_LONG);
   table.DataType(1,TYPE_STRING);
   table.DataType(2,TYPE_STRING);
   table.DataType(3,TYPE_DOUBLE);
   table.DataType(4,TYPE_DOUBLE);
   table.DataType(5,TYPE_DOUBLE);
//--- Создадим элемент управления
   if(!table.CreateTable(x_gap,y_gap))
      return(false);
//--- Установим названия заголовков
   table.SetHeaderText(0,TICKET);
   table.SetHeaderText(1,SYMBOL);
   table.SetHeaderText(2,TYPE_POS);
   table.SetHeaderText(3,VOLUME);
   table.SetHeaderText(4,PRICE);
   table.SetHeaderText(5,SL);
   table.SetHeaderText(6,TP);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,table);
   return(true);
}

Здесь число столбцов и их порядок меняется. Потому как при редактировании открытых рыночных позиций изменять можно лот и  значения тейк профита и стоп лосса. В случае же в отложенными ордерами, здесь правке подлежат цена открытия вместо лота. Собираем проект и получаем такую визуализацию на вкладке Контроль отложенных ордеров:

Рис.11 Интерфейс работы с отложенными ордерами.

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

//+------------------------------------------------------------------+
//| Инициализация таблицы позиций                                    |
//+------------------------------------------------------------------+
void CFastTrading::InitializeOrdersTable(void)
{
//---
   int total=OrdersTotal();
   int cnt=0;
//--- Удалить все строки
   m_table_orders.DeleteAllRows();
//--- Установим количество строк по количеству позиций
   for(int i=0; i<total; i++)
   {
      //--- параметры ордера
      ulong  order_ticket=OrderGetTicket(i);                                   // тикет позиции
      string order_symbol=OrderGetString(ORDER_SYMBOL);                        // символ
      ulong  magic=OrderGetInteger(ORDER_MAGIC);                               // MagicNumber позиции
      //--- если MagicNumber совпадает
      if(magic==m_magic_number)
         if(order_symbol==Symbol())
         {
            m_table_orders.AddRow(cnt);
            cnt++;
         }
   }
//--- Если есть позиции
   if(cnt>0)
   {
      //--- Установим значения в таблицу
      SetValuesToOrderTable();
      //--- Обновить таблицу
      UpdateOrdersTable();
   }
}

Если отложенные ордера найдены, то вносим информацию о них в таблицу через метод SetValuesToOrderTable():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetValuesToOrderTable(void)
{
//---
   int cnt=0;
   ulong    ticket;
   for(int i=0; i<OrdersTotal(); i++)
   {
      //--- параметры ордера
      if((ticket=OrderGetTicket(i))>0)
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // символ
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // MagicNumber позиции
         double volume=OrderGetDouble(ORDER_VOLUME_INITIAL);                           // объем позиции
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // тип позиции
         double price=OrderGetDouble(ORDER_PRICE_OPEN);
         double stoploss=OrderGetDouble(ORDER_SL);
         double takeprofit=OrderGetDouble(ORDER_TP);
         string pos="";
         if(type==ORDER_TYPE_BUY_LIMIT)
            pos="Buy Limit";
         else if(type==ORDER_TYPE_SELL_LIMIT)
            pos="Sell Limit";
         else if(type==ORDER_TYPE_BUY_STOP)
            pos="Buy Stop";
         else if(type==ORDER_TYPE_SELL_STOP)
            pos="Sell Stop";
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               m_table_orders.SetValue(0,i,string(ticket));
               m_table_orders.SetValue(1,i,string(position_symbol));
               m_table_orders.SetValue(2,i,pos);
               m_table_orders.SetValue(3,i,string(volume));
               m_table_orders.SetValue(4,i,string(price));
               m_table_orders.SetValue(5,i,string(stoploss));
               m_table_orders.SetValue(6,i,string(takeprofit));
               cnt++;
            }
      }
   }
}

И обновляем установленные данные посредством метода UpdateOrdersTable():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UpdateOrdersTable(void)
{
//--- Обновить таблицу
   m_table_orders.Update(true);
   m_table_orders.GetScrollVPointer().Update(true);
}

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

//+------------------------------------------------------------------+
//| Инициализация                                                    |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
   InitializeOrdersTable();
   InitializePositionsTable();
}

И повторяем тоже самое в обработчике торговых событий:

//+------------------------------------------------------------------+
//| Событие торговой операции                                        |
//+------------------------------------------------------------------+
void CFastTrading::OnTradeEvent(void)
{
//--- 
   InitializePositionsTable();
   InitializeOrdersTable();
}

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

//--- Событие клик по строке в таблице
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Проверка идентификатора элемента
      if(lparam==m_table_positions.Id())
      {
         //---
         int row=m_table_positions.SelectedItem();
         m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row)));
         m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row)));
         m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row)));
         m_lot_edit[6].GetTextBoxPointer().Update(true);
         m_sl_edit[6].GetTextBoxPointer().Update(true);
         m_tp_edit[6].GetTextBoxPointer().Update(true);
      }
      //--- Проверка идентификатора элемента
      if(lparam==m_table_orders.Id())
      {
         //---
         int row=m_table_orders.SelectedItem();
         m_pr_edit[4].SetValue(string(m_table_orders.GetValue(4,row)));
         m_sl_edit[7].SetValue(string(m_table_orders.GetValue(5,row)));
         m_tp_edit[7].SetValue(string(m_table_orders.GetValue(6,row)));
         m_pr_edit[4].GetTextBoxPointer().Update(true);
         m_sl_edit[7].GetTextBoxPointer().Update(true);
         m_tp_edit[7].GetTextBoxPointer().Update(true);
      }
   }

И последним что нам остается сделать, это назначить кнопкам Изменить и Удалить необходимые дейтсвия. Для этого создадим методы ModifyOrder() и RemoveOrder()

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::ModifyOrder(long lparam)
{
//--- Проверка идентификатора элемента
   if(lparam==m_small_button[2].Id())
   {
      //--- Получим индекс и символ
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // символ
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // MagicNumber позиции
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // тип позиции
         //--- вычисление и округление значений Stop Loss и Take Profit
         double sl=NormalizeDouble((double)m_sl_edit[7].GetValue(),_Digits);
         double tp=NormalizeDouble((double)m_tp_edit[7].GetValue(),_Digits);
         double price=NormalizeDouble((double)m_pr_edit[4].GetValue(),_Digits);
         //--- объявление запроса и результата
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- обнуление значений запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- установка параметров операции
         request.action=TRADE_ACTION_MODIFY; // тип торговой операции
         request.order = ticket;             // тикет ордера
         request.symbol=Symbol();            // символ
         request.sl      =sl;                // Stop Loss позиции
         request.tp      =tp;                // Take Profit позиции
         request.price=price;                // новая цена
         request.magic=m_magic_number;       // MagicNumber позиции
         //--- отправка запроса
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
         }
      }
   }
//---
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::RemoveOrder(long lparam)
{
//--- Проверка идентификатора элемента
   if(lparam==m_small_button[3].Id())
   {
      //--- Получим индекс и символ
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // символ
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // MagicNumber позиции
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // тип позиции
         //--- объявление запроса и результата
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- обнуление значений запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- установка параметров операции
         request.action=TRADE_ACTION_REMOVE;             // тип торговой операции
         request.order = ticket;                         // тикет ордера
         //--- отправка запроса
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, вывести код ошибки
         }
      }
   }
//---
   return(false);
}

И вызовем их в обработчике событий вне каких-либо секций:

      //---
      if(ModifyOrder(lparam))
         return;
      if(RemoveOrder(lparam))
         return;

И в конце статьи хотелось бы добавить еще одно удобство для работы с отложенными ордерами. Оно состоит в возможности щелчком мыши по графику выставлять цену открытия предварительно выбранного отложенного ордера. Для более ясного понимания это действие изображено на рис.12 ниже:

Рис.12 Установка цены открытия отложенного ордера.

Порядок действия такой: 

  • Щелчком мыши по полю ввода активируем его редактирование.
  • После переводим указатель мыши в любое место графика и ориентируемся по оси ординат. Для удобства можно нажать на колесико мыши и выбирать нужную цену через перекрестие.
  • Остановив указатель мыши на нужной цене кликаем по графику и она переносится в поле ввода.

Таким способом можно очень быстро и удобно устанавливать нужное значение цены отложенного ордера, если это нужно делать на глаз. Для более точной установки само собой вводим значение цены в самом поле ввола с клавиатуры. Реализация данного действия довольно проста. В обработчике событий базового класса создаем секцию с событие по клику на графике и в нем пишем такой код:

//--- Событие клик по графику
   if(id==CHARTEVENT_CLICK)
   {
      for(int i=0; i<4; i++)
      {
         if(m_pr_edit[i].GetTextBoxPointer().TextEditState())
         {
            m_last_index=i;
            break;
         }
         else
         {
            if(m_last_index>=0)
            {
               //---
               datetime dt    =0;
               int      window=0;
               //--- преобразуем координаты X и Y  в терминах дата/время
               if(ChartXYToTimePrice(0,(int)lparam,(int)dparam,window,dt,m_xy_price))
               {
                  m_pr_edit[m_last_index].SetValue(DoubleToString(m_xy_price));
                  m_pr_edit[m_last_index].GetTextBoxPointer().Update(true);
                  m_last_index=-1;
               }
            }
         }
      }
   }

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




Заключение

В конце статьи приложен архив со всеми перечисленными файлами, отсортированными по папкам. Поэтому для корректной работы достаточно положить папку MQL5 в корень терминала. Для того чтобы найти корень терминала, в котором находится папка MQL5, нужно в MetaTrader 5 нажать комбинацию клавиш   Ctrl+Shift+D или воспользоваться контекстным меню, как показано на рис.13 ниже.


Рис.13 Поиск папки MQL5 в корне терминала MetaTrader 5

Прикрепленные файлы |
MQL5.zip (6160.94 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (8)
Alexander Fedosov
Alexander Fedosov | 8 июн. 2020 в 09:22
leonerd:

Вы сами-то этой поделкой пробовали пользоваться? Это же просто ужас!

Sell Limit так и не смог поставить. Sell Stop выставляет вместо. Ладно, тест окончен.

Ужас так ужас, а ошибку исправим.

Vitalii Vilnyi
Vitalii Vilnyi | 16 июн. 2020 в 15:01

Интересная статья в познавательных целях а доработать можно

хотелось бы такуюже иметь и для МТ4

Alexander Fedosov
Alexander Fedosov | 17 июн. 2020 в 11:43
Vitalii Vilnyi:

Интересная статья в познавательных целях а доработать можно

хотелось бы такую же иметь и для МТ4

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

А под МТ4 такую же каждый может заказать мне во фрилансе например. 

Gladiator WXT
Gladiator WXT | 28 авг. 2020 в 22:02
Хотелось бы увидеть реализацию на Easy&FastGUI - интегрировать еще один данный гнрафический самопал уж нет сил.
bankova.elizabet
bankova.elizabet | 8 сент. 2020 в 16:43
В одном можно согласиться - без умения торговать руками никуда..
Вычисление математических выражений (Часть 2). Парсеры Пратта и сортировочной станции Вычисление математических выражений (Часть 2). Парсеры Пратта и сортировочной станции
В статье рассматриваются принципы разбора и вычисления математических выражений с помощью парсеров, основывающихся на старшинстве операторов, реализованы парсеры Пратта и сортировочной станции, генерация байт-кода и вычисления по нему, продемонстрировано использование индикаторов в качестве функций в выражениях и настройка с помощью них торговых сигналов в экспертах.
Вычисление математических выражений (Часть 1). Парсеры рекурсивного спуска Вычисление математических выражений (Часть 1). Парсеры рекурсивного спуска
В статье рассматриваются базовые принципы разбора и вычисления математических выражений, реализованы парсеры рекурсивного спуска, работающие в режимах интерпретатора и быстрых расчетов на основе предварительно построенного синтаксического дерева.
Проекты позволяют создавать прибыльных торговых роботов! Но это не точно Проекты позволяют создавать прибыльных торговых роботов! Но это не точно
Большая программа начинается с маленького файла, который затем начинает расти в размерах, наполняться множеством функций и объектов. Большинство разработчиков роботов справляется с этой проблемой с помощью включаемых файлов. Но лучше сразу же начинать писать любую программу для трейдинга в проекте — это выгодно во всех отношениях.
Практическое применение нейросетей в трейдинге. Переходим к практике Практическое применение нейросетей в трейдинге. Переходим к практике
В статье даны описание и инструкция по практическому применению нейросетевых модулей на платформе Matlab. Также затронуты основные аспекты построения системы торговли с использованием НСМ. Для ознакомления с комплексом в рамках сжатого изложения для данной статьи мне пришлось его несколько модернизировать таким образом, чтобы в одной программе совместить несколько функций НСМ.