Калькулятор сигналов

Vladimir Karputov | 13 апреля, 2016

Оглавление


Введение

Наиболее часто возникающий вопрос у подписчиков: "Смогу ли я подписаться на сигнал NNN, и какой объём позиции будет копироваться на мой торговый счёт?". Данная статья поможет создать калькулятор сигналов  — помощника для желающих подписаться на сигналы. Также в данной статье приводится краткое руководство по использованию калькулятора сигналов.

Сам калькулятор — это панель на базе класса CDialog. В этой панели использованы следующие элементы:

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

Представленный в данной статье калькулятор можно бесплатно загрузить из Маркета как для MetaTrader 5, так и для MetaTrader 4:


1. Ограничение на использование

Следует чётко понимать, что в терминале во вкладке "Сигналы" показываются только те сигналы, которые максимально совместимы с торговым счётом подписчика. Другими словами, в терминале Вы не увидите всех сигналов, которые доступны в витрине сигналов на сайте. Кроме того, переключаясь в терминале с одного торгового сервера (торгового счёта) на другой, Вы каждый раз будете видеть, что список сигналов в терминале меняется.


2. Руководство по работе с калькулятором сигналов

2.1. Начало работы

Для работы калькулятору сигналов нужна актуальная база торговых сигналов в терминале. Для этого нужно сделать активной вкладку "Сигналы" в окне "Инструменты" (кликнуть на вкладку "Сигналы"):

Активация вкладки "Сигналы"

Рис. 1. Активация вкладки "Сигналы" 

Если в базе сигналов есть изменения, то они будут подкачаны в течении трёх-пяти секунд.

Вкладку "Сигналы" нужно делать активной после подключения к торговому счёту, а также в случае подключения к другому торговому счёту.


2.2. Пользовательский интерфейс

Интерфейс калькулятора сигналов включает следующие элементы:

Интерфейс калькулятора сигналов

Рис. 2. Интерфейс калькулятора сигналов 

2.3. Получение коэффициента копирования

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

При желании можно менять настройки: "Баланс торгового счёта", "Валюта торгового счёта", "Плечо торгового счёта" или "Нагрузка на депозит при копировании сигнала". Изменение любой из этих настроек приводит к пересчёту коэффициента копирования и обновлению таблицы, и не факт, что торговые сигналы останутся на своих местах в таблице после сортировки по убыванию по столбцу "Мин. депозит*". Таким образом, можно в реальном режиме времени увидеть, как изменится коэффициент копирования торгового сигнала при разных настройках торгового счёта.

2.4. Детализация расчёта коэффициента копирования

Чтобы получить детальный расчёт коэффициента копирования для конкретного сигнала, нужно кликнуть в таблице сигналов в строке интересующего Вас сигнала (действие 1). После этого под таблицей сигналов появится подробный расчёт коэффициента копирования для выбранного сигнала (действие 2):

  Детализация расчёта коэффициента копирования

Рис. 3. Детализация расчёта коэффициента копирования  

2.5. Невозможность маппинга

После выбора другой валюты в выпадающем списке "Валюта торгового счёта", для расчёта коэффициента копирования калькулятор сигналов будет стараться найти в окне "Обзор рынка" символ, который содержит в своём названии одновременно и валюту Вашего торгового счёта (или валюту, выбранную в выпадающем списке "Валюта торгового счёта"). Например, валюта Вашего торгового счёта — "USD", а валюта торгового счёта провайдера сигнала — "EUR". В таком случае калькулятор будет пытаться найти в окне "Обзор рынка" символ "USDEUR" и "EURUSD".  Если символ не удастся найти, во вкладку терминала "Эксперты" будет выведено сообщение об ошибке.

Пример сообщения об ошибке после выбора в выпадающем списке "Валюта торгового счёта" валюты "SGD":

Сalculator for signals (EURUSD,M5)      Error find symbols: (Account currency SGD, Signal currency RUB)
Сalculator for signals (EURUSD,M5)      Error find symbols: (Account currency SGD, Signal currency EUR)
Сalculator for signals (EURUSD,M5)      Error find symbols: (Account currency SGD, Signal currency EUR)

Это сообщение означает, что в окне "Обзор рынка" нет символов "SGDRUB", "SGDEUR", "RUBSGD", "EURSGD". Проверим, так ли это: в окне "Обзор рынка" попробуем найти любой символ, в названии которого есть "SGD". Для этого в окне "Обзор рынка" нужно кликнуть на "+ добавить":

Команда "добавить" в окне "Обзор рынка"

Рис. 4. Команда "добавить" в окне "Обзор рынка"   

и ввести в открывшееся поле "SGD":

Список доступных символов в окне "Обзор рынка" , в названии которых есть "SGD"

Рис. 5. Список доступных символов в окне "Обзор рынка", в названии которых есть "SGD"   

Как видите, в окне "Обзор рынка" уже есть один символ "USDSGD" и ещё один — "SGDJPY" можно добавить, а вот символов "SGDRUB", "SGDEUR", "RUBSGD", "EURSGD" нет. 

Если маппинг невозможен, в таблице сигналов в столбце "Коэф. копирования" будет указано "n/d", что означает "нет данных".

Небольшое видео о калькуляторе сигналов:



3. Разработка калькулятора сигналов

3.1. Проектируем интерфейс

Элементы управления в калькуляторе сигналов расположены следующим образом:

Расположение элементов управления  

Рис. 6. Расположение элементов управления 

За расположение, размер и за создание элементов управления отвечает включаемый файл "Сalculator for signals Dialog.mqh". Основные размеры элементов управления и отступов задаются при помощи блока макроподстановок:

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- indents and gaps
#define INDENT_LEFT                         (11)      // indent from left (with allowance for border width)
#define INDENT_TOP                          (11)      // indent from top (with allowance for border width)
#define CONTROLS_GAP_Y                      (5)       // gap by Y coordinate
//--- for combo boxes
#define COMBOBOX_WIDTH                      (60)      // size by X coordinate
#define COMBOBOX_HEIGHT                     (20)      // size by Y coordinate
//--- for list view
#define LIST_HEIGHT                         (102)     // size by Y coordinate
//--- for buttons
#define BUTTON_WIDTH                        (72)      // size by X coordinate
#define BUTTON_HEIGHT                       (20)      // size by Y coordinate
//--- for the indication area
#define EDIT_WIDTH                          (60)      // size by X coordinate
#define EDIT_HEIGHT                         (20)      // size by Y coordinate

Схематично объекты управления расположены на панели в пять строк:

  1. первая строка — Label1, Edit1, ComboBox1, Label2, ComboBox2, Label3, ComboBox2
  2. вторая строка — Label 4
  3. третья строка — кнопки с Button1 по Button8 включительно
  4. четвёртая строка — новый элемент управления — таблица TableListView1
  5. пятая строка — объект BmpButton1, которому в качестве bmp-файла для отображения элемента в состоянии ON назначен графический ресурс на базе CCanvas.
Главное — помнить, что все объекты управления расположены на панели диалога (главная панель, в свою очередь, создаётся здесь):

//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CoSDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//---
   m_error=true;
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//---

Это значит, что каждый объект управления после создания нужно в обязательном порядке добавить в клиентскую область при помощи метода Add класса CDialog. Вот как это выглядит на примере создания объекта Label4:

//+------------------------------------------------------------------+
//| Create the "Signals" Label                                       |
//+------------------------------------------------------------------+
bool CoSDialog::CreateLabel4(void)
  {
//--- coordinates
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+COMBOBOX_HEIGHT+CONTROLS_GAP_Y;
   int x2=x1+100;
   int y2=y1+COMBOBOX_HEIGHT;
//--- create
   if(!m_label4.Create(m_chart_id,m_name+"Label4",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_label4.Text(m_languages.GetText(3)))
      return(false);
   if(!Add(m_label4))
      return(false);
//--- succeed
   return(true);
  }

Сначала создаём объект Label4:

   if(!m_label4.Create(m_chart_id,m_name+"Label4",m_subwin,x1,y1,x2,y2))
      return(false);

и перед выходом из функции, обязательно добавляем вновь созданный объект Label4 в клиентскую область при помощи метода Add:

   if(!Add(m_label4))
      return(false);

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

Схема создания объектов панели

Рис. 7. Схема создания объектов панели 

Подведу итог. В общем случае создание панели идёт по следующему плану:

3.2. Создаём канвас на панели

Создание канваса происходит в CoSDialog::CreateBmpButton1.

Сначала кратко, в несколько шагов:

Шаг 1: создаём графический ресурс без привязки к объекту чарта (используем метод Create класса CCanvas)

Шаг 2: создаём элемент управления m_bmp_button1 класса CBmpButton (класс простого элемента управления на основе объекта 'Графическая метка')

Шаг 3: для элемента управления m_bmp_button1, в качестве bmp-файла в состоянии ON (метод BmpOnName), устанавливаем наш канвас

Шаг 4: добавляем элемент управления m_bmp_button1 к панели (метод Add класса CDialog)

Теперь подробнее о каждом шаге.

Шаг 1

//--- create canvas
   if(!m_canvas1.Create("Canvas1",x2,y2,COLOR_FORMAT_XRGB_NOALPHA))
     {
      Print("Error creating canvas: ",GetLastError());
      return(false);
     }
   m_canvas1.FontSet("Trebuchet MS",-100,FW_THIN);
   m_canvas1.Erase(ColorToARGB(C'0xF7,0xF7,0xF7',255));
   m_canvas1.Update(true);

Обратите внимание, что при создании канваса (m_canvas1.Create) указываются ширина x2 и высота y2 — по сути это размеры картинки, которые не будут меняться:

Размеры канваса  

Рис. 8. Размеры канваса 

Далее задаётся шрифт для канваса (m_canvas1.FontSet), канвас заполняется цветом внутренней области панелей (m_canvas1.Erase) и, ОБЯЗАТЕЛЬНО, отображение изменений на экран (m_canvas1.Update).

Шаг 2.

//--- create
   if(!m_bmp_button1.Create(m_chart_id,m_name+"BmpButton1",m_subwin,x1,y1,x1+10,y1+10))
      return(false);

Создание элемента управления m_bmp_button1 размером 10*10 пикселей. Небольшой размер элемента управления не имеет значения. Здесь главное — сами координаты x1 и y1. Это точка привязки. То есть, когда размер элемента управления будет подстраиваться под размеры картинки, угол с координатами (x1; y1) останется на месте.

Шаг 3.

//--- sets the name of bmp files of the control CBmpButton
   if(!m_bmp_button1.BmpOnName(m_canvas1.ResourceName()))
      return(false);

Получаем имя ресурса (m_canvas1.ResourceName()) и назначаем в качестве bmp-файла в состоянии ON (m_bmp_button1.BmpOnName). Именно на этом этапе элемент управления m_bmp_button1 растягивается под размеры канваса.

Шаг 4.

После создания элемента управления, не забываем добавить этот элемент управления к панели: 

   if(!Add(m_bmp_button1))
      return(false);

3.3. Двумерный массив. Сортировка

Работа с сортировкой производится в файле "Сalculator for signals Dialog.mqh" в методе CoSDialog::InitStructurs. 

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

Name Copy ratio
TestUSD 15.0
TradeLargeVolumes 120.0
Zeus PRO 15.0
CS Trading Co Beta Free Provider 1510.0
Mint blueberry ice cream 8.0
MT5Hedging 7.0
Forex Leos Trading 160.0
Hedge 1.0
Siksikawa 8770.0
Week H4 15.0
WGT Live Signal 45.0
Atrader 30.0
Trajecta Advisor FX491 30.0
MOsg style 6.0 

Для сортировки было применено решение, подсказанное пользователем Vasiliy Sokolov: каждую строку нужно представить как некий объект CObject, а всю таблицу — как объект CArrayObj. Такая таблица была названа линейной, класс виртуальной линейной таблицы (виртуальной, так как таблица не имеет визуального интерфейса) был размещён в файле "LineTable.mqh".

Ниже представлен результат сортировки в линейной таблице (пример приведён для нескольких первых вставок линий в таблицу). Сначала показываются параметры, которые вставляются в линейную таблицу (Insert : ), ниже следует перебор по всем элементам линейной таблицы (row #):

name rate  min_deposit
 Insert : MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #0: MyCorrelation EURUSD XAUUSD  7  133134.7143
 Insert : EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 row #0: MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #1: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 Insert : EURUSD Daytrade  170  5482.017647
 row #0: EURUSD Daytrade  170  5482.017647
 row #1: MyCorrelation EURUSD XAUUSD  7   133134.7143
 row #2: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 Insert : Exp TickSniper PRO FULL MT5  50  18638.86
 row #0: EURUSD Daytrade  170  5482.017647
 row #1: Exp TickSniper PRO FULL MT5  50  18638.86
 row #2: MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #3: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 Insert : Example1  3  310647.6667
 row #0: EURUSD Daytrade  170  5482.017647
 row #1: Exp TickSniper PRO FULL MT5  50  18638.86
 row #2: MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #3: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 row #4: Example1  3  310647.6667
 Insert : Big sale  80  11649.2875
 row #0: EURUSD Daytrade  170  5482.017647
 row #1: Big sale  80  11649.2875
 row #2: Exp TickSniper PRO FULL MT5  50  18638.86
 row #3: MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #4: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 row #5: Example1  3  310647.6667

На рисунке хорошо видно, что после добавления в линейную таблицу происходит сортировка по полю "rate" — это именно то поведение, которое нам нужно. В дальнейшем, зная, что все элементы в линейной таблице отсортированы, можно копировать эти значения в новый элемент управления, таблицу CTableListView, и быть уверенным, что данные будут представлены в отсортированном виде.

Для распечатки пошаговой сортировки и получения такой же таблицы просто раскомментируйте строки в файле "Сalculator for signals Dialog.mqh" в методе CoSDialog::InitStructurs: 

         else
           {
            min_deposit=AccInfo.balance/rate*100.0;
           }
         //Print("Insert : ",name,"; ",rate,"; ",min_deposit); 
         m_table.InsertSort(new CLineTable(name,rate,min_deposit));
         //for(int m=0;m<m_table.Total();m++)
         //  {
         //   CLineTable *line=m_table.At(m);
         //   Print("row #",m,": ",line.Text(),"; ",line.Number(),"; ",line.Number1());
         //  }
        }
      else PrintFormat("Error in call of SignalBaseSelect. Error code=%d",GetLastError());
     }
//---
   return(true);
  }

3.4. Новый элемент управления - таблица CTableListView

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

Элемент управления таблица CTableListView имеет две части — видимую, которую пользователь видит и физически контактирует с ней, и невидимую, в которой хранятся данные ячеек таблицы.

Видимая часть таблицы обусловлена размерами самого элемента управления. Он ограничен прямоугольником с координатами (x1; y1) и (x2; y2) и количеством колонок columns, которые задаются при создании элемента в методе Create:

   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,
                            const int y1,const int x2,const int y2,const uchar columns,const ushort &columns_size[]);

Видимая часть состоит из элементов управления CEdit

Построение видимой части таблицы CTableListView

Рис. 9. Построение видимой части таблицы CTableListView

Элементы управления CEdit видимой части создаются и управляются с помощью динамического массива указателей m_arr_rows  — объекта класса CArrayObj:

   CArrayObj         m_arr_rows;             // array of pointer to objects-rows (CEdit) 

(статья о применении указателей Когда нужно использовать указатели в MQL5)

Массив указателей m_arr_rows представляет собой матрёшку из двух уровней. На первом уровне хранятся указатели на объекты-строки row, в свою очередь объекты-строки row хранят указатели на объекты-ячейки (элементы CEdit) таблицы:

Указатели на видимые объекты

Рис. 10. Указатели на видимые объекты 

Невидимая часть также реализуется при помощи класса CArrayObj. За невидимую часть отвечают два динамических массива указателей m_arr_rows_str и m_arr_rows_val ,

   CArrayObj         m_arr_rows_str;         // array of pointer to objects-rows (CArrayString) 
   CArrayObj         m_arr_rows_val;         // array of pointer to objects-rows (CArrayLong) 

в которых хранятся текст ячеек и значения соответственно.

Структура массивов указателей m_arr_rows_str и m_arr_rows_val схожа со структурой массива указателей m_arr_rows, только в качестве строк выступают массивы классов CArrayString и CArrayLong соответственно.

3.4.1. Пример работы с динамическим массивом указателей

Рассмотрим работу с динамическим массивом указателей на примере создания объекта - таблицы:

//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CTableListView::Create(const long chart,const string name,const int subwin,const int x1,
                            const int y1,const int x2,const int y2,const uchar columns,const ushort &columns_size[])
  {
   m_columns=columns;
   ArrayResize(m_columns_size,m_columns);
   if(ArraySize(columns_size)!=m_columns)
      return(false);
   ArrayCopy(m_columns_size,columns_size,0,0,WHOLE_ARRAY);
   m_columns_size[0]-=1;
   m_columns_size[m_columns-1]-=1;
   int y=y2;
//--- if the number of visible rows is previously determined, adjust the vertical size
   if(!TotalView((y2-y1)/m_item_height))
      y=m_item_height+y1+2*CONTROLS_BORDER_WIDTH;
//--- check the number of visible rows
   if(m_total_view<1)
      return(false);
//--- call method of the parent class
   if(!CWndClient::Create(chart,name,subwin,x1,y1,x2,y))
      return(false);
//--- set up
   if(!m_background.ColorBackground(CONTROLS_LIST_COLOR_BG))
      return(false);
   if(!m_background.ColorBorder(CONTROLS_LIST_COLOR_BORDER))
      return(false);
//--- create dependent controls
   CArrayObj *m_arr_cells;
   for(int i=0;i<m_total_view;i++)
     {
      m_arr_cells=new CArrayObj;
      if(CheckPointer(m_arr_cells)==POINTER_INVALID)
         return(false);
      for(int j=0;j<m_columns;j++)
        {
         CEdit *m_cell;
         m_cell=new CEdit;
         if(CheckPointer(m_cell)==POINTER_INVALID)
            return(false);
         m_arr_cells.Add(m_cell);
        }
      m_arr_rows.Add(m_arr_cells);
     }
//---
   for(int i=0;i<m_total_view;i++)
     {
      if(!CreateRow(i))
         return(false);
      if(m_height_variable && i>0)
        {
         // m_rows[i].Hide(); ///
         CArrayObj *m_arr_cells_i=m_arr_rows.At(i);
         if(CheckPointer(m_arr_cells_i)==POINTER_INVALID)
            return(false);
         for(int j=0;j<m_arr_cells_i.Total();j++)
           {
            CEdit *m_cell=m_arr_cells_i.At(j);
            if(CheckPointer(m_cell)==POINTER_INVALID)
               return(false);
            if(!m_cell.Hide())
               return(false);
           }
        }
     }
//--- succeed
   return(true);
  }

В блоке кода

//--- if the number of visible rows is previously determined, adjust the vertical size
   if(!TotalView((y2-y1)/m_item_height))
      y=m_item_height+y1+2*CONTROLS_BORDER_WIDTH;

происходит вызов TotalView, где рассчитывается количество видимых строк. Это количество сохраняется в переменной m_total_view.

Дальше в цикле for(int i=0;i<m_total_view;i++) создаются строки m_arr_cells, и в цикле for(int j=0;j<m_columns;j++) строки заполняются ячейками m_cell:

//--- create dependent controls
   CArrayObj *m_arr_cells;
   for(int i=0;i<m_total_view;i++)
     {
      m_arr_cells=new CArrayObj;
      if(CheckPointer(m_arr_cells)==POINTER_INVALID)
         return(false);
      for(int j=0;j<m_columns;j++)
        {
         CEdit *m_cell;
         m_cell=new CEdit;
         if(CheckPointer(m_cell)==POINTER_INVALID)
            return(false);
         m_arr_cells.Add(m_cell);
        }
      m_arr_rows.Add(m_arr_cells);
     }
//---

После полного обхода цикла for(int j=0;j<m_columns;j++) каждая заполненная строка добавляется в главный массив m_arr_rows:

      m_arr_rows.Add(m_arr_cells);

Таким образом, после полного обхода цикла for(int i=0;i<m_total_view;i++) мы имеем заполненный массив указателей m_arr_rows, который по своей структуре соответствует видимой части таблицы (см. рис. 9).

После заполнения массива указателей во втором цикле for(int i=0;i<m_total_view;i++) происходит визуализация таблицы (создание видимой части таблицы) путём вызова CreateRow:

//---
   for(int i=0;i<m_total_view;i++)
     {
      if(!CreateRow(i))
         return(false);
      .
      .
      .
     }
//--- succeed
   return(true);

Метод CreateRow:

//+------------------------------------------------------------------+
//| Create "row"                                                     |
//+------------------------------------------------------------------+
bool CTableListView::CreateRow(const int index)
  {
   .
   .
   .
//--- create
   CArrayObj *m_arr_cells=m_arr_rows.At(index);
   if(CheckPointer(m_arr_cells)==POINTER_INVALID)
      return(false);
   for(int i=0;i<m_arr_cells.Total();i++)
     {
      CEdit *m_cell=m_arr_cells.At(i);
      if(CheckPointer(m_cell)==POINTER_INVALID)
         return(false);
      x1+=x2;
      x2=m_columns_size[i];
      if(!m_cell.Create(m_chart_id,m_name+"_"+IntegerToString(index)+"_"+IntegerToString(i),
         m_subwin,x1,y1,x1+x2,y2))
         return(false);
      if(!m_cell.Text(""))
         return(false);
      if(!m_cell.ReadOnly(true))
         return(false);
      if(!Add(m_cell))
         return(false);
     }
   .
   .
   .
   return(true);
  }

В блоке кода получаем из массива указателей m_arr_rows указатель на элемент m_arr_cells (элемент m_arr_cells  имеет тип CArrayObj), находящийся на позиции index. Элемент m_arr_cells - это и есть строка таблицы row (см. рис. 10). Используем метод At класса CArrayObj:

   .
//--- create
   CArrayObj *m_arr_cells=m_arr_rows.At(index);
   if(CheckPointer(m_arr_cells)==POINTER_INVALID)
      return(false);
   for(int i=0;i<m_arr_cells.Total();i++)

Дальше в цикле for(int i=0;i<m_arr_cells.Total();i++) работаем уже с элементом m_arr_cells (массивом указателей): получаем из массива указателей m_arr_cells указатель на элемент m_cell (элемент m_cell имеет тип CEdit),  находящийся на позиции i:

      CEdit *m_cell=m_arr_cells.At(i);
      if(CheckPointer(m_cell)==POINTER_INVALID)
         return(false);

Затем создаём ячейки (элементы управления класса CEdit) с уникальным именем:

      if(!m_cell.Create(m_chart_id,m_name+"_"+IntegerToString(index)+"_"+IntegerToString(i),
         m_subwin,x1,y1,x1+x2,y2))
         return(false);

меняем некоторые свойства созданных элементов управления (стираем весь текст элемента управления и делаем элемент управления нередактируемым):

      if(!m_cell.Text(""))
         return(false);
      if(!m_cell.ReadOnly(true))
         return(false);

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

      if(!Add(m_cell))
         return(false);
     }

 

Заключение

Я надеюсь, что Вам пригодится калькулятор сигналов при выборе сигнала, а главное калькулятор поможет Вам видеть, как будет изменяться коэффициент копирования, если изменять (в калькуляторе) размер депозита и/или кредитное плечо Вашего торгового счёта.