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

Alexander Fedosov | 5 июня, 2020

Содержание

Введение

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


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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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