English 中文 Español Deutsch 日本語 Português
preview
Разметка данных в анализе временных рядов (Часть 1):Создаем набор данных с маркерами тренда с помощью графика советника

Разметка данных в анализе временных рядов (Часть 1):Создаем набор данных с маркерами тренда с помощью графика советника

MetaTrader 5Эксперты | 11 января 2024, 12:00
717 1
Yuqiang Pan
Yuqiang Pan

Введение

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

 

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

Содержание:

  1. Определение формата данных метки
  2. Инициализация графиков и файлов
  3. Логика работы
  4. Организация данных и запись в файл
  5. Приложение: полный пример кода советника


Определение формата данных метки

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

Time Open High Low Close Tick_volume
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264

Выше показано, как выглядят данные пяти временных рядов. Значения Close и Open связаны друг с другом от начала до конца, и связь очень сильна. Мы исходим из того, что первые два ряда — это восходящий тренд, а остальные — нисходящий (данные выше приведены лишь в качестве примера). Общепринятый метод разметки разделит данные на две части:

Time

Open High   Low   Close Tick_volume
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210

Time Open High   Low Close Tick_volume
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264

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

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

Time Open High Low Close Tick_volume Trend_group
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173 0
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210 0
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212 1
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392 1
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264 1

Но если мы хотим реализовать в модели анализ развития тренда, в частности, насколько развилась текущая тенденция (например, волновая теория говорит нам, что общий тренд обычно включает в себя стадию тренда и стадию корректировки, стадия тренда имеет 5 стадий волн, а стадия корректировки имеет 3 волновые корректировки и т. д.), нам нужно дополнительно разметить данные. Мы можем сделать это, добавив еще один столбец, который представляет развитие тенденции в данных (при условии, что первые 2 из следующих 10 данных — восходящий тренд, а последние 5 — восходящий тренд, остальные в середине — нисходящий тренд), вот так:

Time Open High Low Close Tick_volume Trend_group Trend_index
2021-12-10 03:15:00 1776.38 1777.94 1775.47 1777.71 565 0 0
2021-12-10 03:30:00 1777.75 1778.93 1777.68 1778.61 406 0 1
2021-12-10 03:45:00 1778.58 1778.78 1777.65 1778.16 388 1 0
2021-12-10 04:00:00 1778.14 1779.42 1778.06 1779.14 393 1 1
2021-12-10 04:15:00 1779.16 1779.49 1778.42 1779.31 451 1 2
2021-12-10 04:30:00 1779.22 1779.42 1778.36 1778.37 306 0 0
2021-12-10 04:45:00 1778.42 1778.51 1777.60 1777.78 411 0 1
2021-12-10 05:00:00 1777.81 1778.68 1777.61 1778.57 372 0 2
2021-12-10 05:15:00 1778.54 1779.29 1778.42 1779.02 413 0 3
2021-12-10 05:30:00 1778.97 1779.49 1778.48 1778.50 278 0 4

Примечания:

1. Trend_group, определяющая восходящий тренд, равна 0

2. Trend_group, определяющая нисходящий тренд, равна 1.

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


Инициализация графиков и файлов

    Инициализация графика

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

           ChartSetInteger (0, CHART_AUTOSCROLL, false);
          
            ChartSetInteger (0, CHART_SHIFT, true);
          
            ChartSetInteger (0, CHART_MOUSE_SCROLL ,1);
          Примечание: Зеленая часть кода предназначена для управления графиком с помощью колеса мыши
            Инициализация файла

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

                 do
                     {
                       //---Find if there are files that match the chart
                       if (StringFind(name, Symbol())!=-1 && StringFind(name,".csv")!=-1)
                         reName=name;
                     }
                
                   while (FileFindNext(hd,name));
                Примечание: Мы используем цикл do - while, который отличается от цикла while тем, что сначала выполняет оператор, а затем оценивает выражение. Главная проблема здесь - это инициализация имени
                int hd= FileFindFirst("*",name,0);


                  Если существует исходный размеченный файл, откроем его и получим последнее время, отмеченное функцией read_csv():

                    read_csv(file_handle,a);
                    Затем прокрутим график до последнего отмеченного времени:
                    shift = - iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                    ChartNavigate(0, CHART_END ,shift);


                    Создадим файл истории, если его нет:

                    file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t), FILE_WRITE | FILE_CSV | FILE_READ);
                    Затем прокрутим диаграмму до позиции, указанной глобальной переменной start_t.
                     shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                      ChartNavigate(0,CHART_END,shift);
                    Добавьте вертикальную красную линию, чтобы отметить начальный столбец:
                     ObjectCreate (0,"Start",OBJ_VLINE,0,(datetime)start_t,0)
                    Логика этой части организована следующим образом:
                     if (FileIsExist(reName))
                         {
                          file_handle = FileOpen(reName, FILE_WRITE | FILE_CSV | FILE_READ );
                           string a[];
                           int i= 0 ;
                          read_csv(file_handle,a);
                          i = ArraySize (a);
                          shift = -iBarShift(Symbol(), PERIOD_CURRENT,(datetime)a[i-8]);
                           ChartNavigate(0,CHART_END,shift);
                         }
                       else
                         {
                          file_handle = FileOpen (StringFormat ("%s%d-%d.csv", Symbol(), Period(),start_t), FILE_WRITE | FILE_CSV | FILE_READ );
                           Print ("There is no history file,create file:" , StringFormat ( "%s%d-%d",Symbol(), Period(),start_t));
                           shift = - iBarShift (Symbol(), PERIOD_CURRENT ,(datetime)start_t);
                           ChartNavigate (0, CHART_END ,shift);
                           ObjectCreate (0,"Start", OBJ_VLINE,0,(datetime)start_t,0);
                         }
                    Внимание: Поскольку мы хотим переместить график влево, перед функцией iBarShift() необходимо добавить "-".
                    shift = -iBarShift(Symbol(), PERIOD_CURRENT ,(datetime)start_t);
                    Конечно, логику также можно реализовать в функции ChartNavigate(), например:
                    ChartNavigate(0,CHART_END,-shift);
                    Однако код в этой статье реализован по первому способу.
                      Эти действия по инициализации будут реализованы в нашем OnInit(), включая определение необходимых нам переменных. Самое главное — уточнить, куда мы хотим сместить график, и начать разметку. В основном это контролируется переменными shift и start_t. Конечный код будет выглядеть так:
                        int OnInit()
                          {
                        //---initial
                           string name;
                           string reName="1";
                           int hd=FileFindFirst("*",name,0);
                           int shift;
                        
                           ChartSetInteger(0,CHART_AUTOSCROLL,false);
                           ChartSetInteger(0,CHART_SHIFT,false);
                           ChartSetInteger(0,CHART_MOUSE_SCROLL,1);
                        
                        
                           do
                             {
                              //---check File
                              if(StringFind(name,Symbol())!=-1 && StringFind(name,".csv")!=-1)
                                 reName=name;
                             }
                           while(FileFindNext(hd,name));
                        
                           if(FileIsExist(reName))
                             {
                              file_handle = FileOpen(reName,FILE_WRITE|FILE_CSV|FILE_READ);
                              string a[];
                              int i=0;
                              read_csv(file_handle,a);
                              i = ArraySize(a);
                              shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                              ChartNavigate(0,CHART_END,shift);
                             }
                           else
                             {
                              file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t),FILE_WRITE|FILE_CSV|FILE_READ);
                              Print(FileTell(file_handle));
                              Print("No history file,create file:",StringFormat("%s%d-%d",Symbol(),Period(),start_t));
                              shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                              ChartNavigate(0,CHART_END,shift);
                              ObjectCreate(0,"Start",OBJ_VLINE,0,(datetime)start_t,0);
                             }
                           return(INIT_SUCCEEDED);
                          }

                          Примечания:

                          1. start_t variable - таймфрейм для запуска;

                          2. shift variable - количество столбцов, которые будут смещены, в примере кода показано количество столбцов, которые будут смещены путем преобразования указанного времени;

                          3. Функция read_csv () будет определена позже.

                            Определение функции read_csv():
                               void read_csv(int hd,
                                             string &arry[])
                                {
                                 int i= 0;
                                 while(!FileIsEnding(hd))
                                   {
                                    ArrayResize(arry,i+1);
                                    arry[i]= FileReadString(hd);
                                    i++;
                                   }
                                }

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


                              Логика работы

                                Управление графиками: Этот раздел можно легко найти в клиентской справке.
                                  • Home — перейти к последнему бару графика;
                                  • End — перейти к первому бару графика;
                                  • Page Up — переместить график назад на одно окно;
                                  • Page Down — переместить график вперед на одно окно;
                                  • Ctrl+I — открыть окно со списком индикаторов;
                                  • Ctrl+B — открыть окно со списком объектов;
                                  • Alt+1 — график отображается в виде серии баров;
                                  • Alt+2 — график отображается в виде последовательности японских свечей;
                                  • Alt+3 — график отображается в виде линии, соединяющей цены закрытия;
                                  • Ctrl+G — показать/скрыть сетку в окне графика;
                                  • "+" — увеличить масштаб графика;
                                  • "-" — уменьшить масштаб графика;
                                  • F12 — пошаговая прокрутка графика (бар за баром);
                                  • F8 — открыть окно свойств;
                                  • Backspace — удалить с графика последний добавленный объект;
                                  • Delete — удалить все выбранные объекты;
                                  • Ctrl+Z — отменить удаление последнего объекта.
                                    Логика управления:
                                      1. Нажмите клавишу, чтобы сообщить советнику, какого рода трендом будут данные, отмеченные следующими.
                                        Определим клавиши b и s. Определяется виртуальным кодом клавиши:
                                           #define KEY_B     66
                                           #define KEY_S     83
                                            Нажмите b, а затем s для восходящего тренда либо s, а затем b для нисходящего. В качестве примера возьмем восходящий тренд:
                                              1) Нажмем b для восходящего тренда. Устанавливаем переменную typ на 0, переменную tp - на start, цвет стрелки - на clrBlue, а количество меток Num увеличиваем на 1. Нам нужно только увеличить переменную в начале сегмента данных и указать, что повторное нажатие кнопки выполнит "конечную" часть отмеченного сегмента данных путем инвертирования;
                                              b_press
                                              2) Нажмем s, чтобы отметить конец восходящего тренда, переменная typ по-прежнему равна 0, переменная tp установлена в значение end, цвет стрелки по-прежнему clrBlue, а количество меток Num остается неизменным. Нам нужно увеличивать переменную только в начале сегмента данных, а инверсия first используется для указания того, что повторное нажатие кнопки приведет к выполнению "стартовой" части отмеченного сегмента данных. s_press
                                              3) После выполнения оператора switch вызовем функцию ChartRedraw(), чтобы перерисовать график.
                                              if(id==CHARTEVENT_KEYDOWN)
                                                   {
                                                    switch(lparam)
                                                      {
                                                       case KEY_B:
                                                          if(first)
                                                            {
                                                             col=clrBlue ;
                                                             typ =0;
                                                             Num+=1;
                                                             tp = "start";
                                                            }
                                                          else
                                                            {
                                                             col=clrRed ;
                                                             typ = 1;
                                                             tp = "end";
                                                            }
                                                          ob =OBJ_ARROW_BUY;
                                                          first = !first;
                                                          Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                          break;
                                                       case KEY_S:
                                                          if(first)
                                                            {
                                                             col=clrRed ;
                                                             typ =1;
                                                             Num+=1;
                                                             tp = "start";
                                                            }
                                                          else
                                                            {
                                                             col=clrBlue ;
                                                             typ = 0;
                                                             tp = "end";
                                                            }
                                                          ob =OBJ_ARROW_SELL;
                                                          first = !first;
                                                          Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                          break;
                                              
                                                       default:
                                                          Print("You pressed:"+lparam+" key, do nothing!");
                                                      }
                                                    ChartRedraw(0);
                                                   }

                                              Примечания:

                                              1. переменная type — 0 означает восходящий тренд, 1 — нисходящий;

                                              2. переменная "Num" - количество отметок, будет интуитивно отображаться на графике;

                                              3. переменная first контролирует, чтобы наши метки всегда шли парами, гарантируя, что каждая группа представляет собой b и s или s и b, избегая путаницы;

                                              4. переменная tp используется для определения начала или конца сегмента данных.

                                              2. Щелкните левой кнопкой мыши на графике, чтобы определить положение отметки

                                              if(id==CHARTEVENT_CLICK)
                                                   {
                                                    //--- definition
                                                    int x=(int)lparam;
                                                    int y=(int)dparam;
                                                    datetime dt    =0;
                                                    double   price =0;
                                                    int      window=0;
                                                    if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                      {
                                                       ObjectCreate(0,Name,ob,window,dt,price);
                                                       ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                       //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                       if(tp=="start")
                                                          Start=dt;
                                                       else
                                                         {
                                                          if(file_handle)
                                                             file_write(Start,dt);
                                                         }
                                                       ChartRedraw(0);
                                                      }
                                                    else
                                                       Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                   }
                                              //--- object delete
                                                 if(id==CHARTEVENT_OBJECT_DELETE)
                                                   {
                                                    Print("The object with name ",sparam," has been deleted");
                                                   }
                                              //--- object create
                                                 if(id==CHARTEVENT_OBJECT_CREATE)
                                                   {
                                                    Print("The object with name ",sparam," has been created!");
                                                   }

                                              Примечания:

                                              1. Функция ChartXYToTimePrice() в основном используется для получения свойств графика на месте щелчка мыши, включая текущее время и цену. Глобальная переменная dt нужна для получения текущего времени;

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

                                              3. Необходимые действия


                                                Если вы хотите отметить восходящий тренд, сначала нажмите клавишу b, щелкните левой кнопкой мыши по столбцу, который начинает обозначаться на графике, затем нажмите клавишу s, а затем щелкните левой кнопкой мыши в конце столбца на значке, чтобы завершить разметку. На графике появится пара синих стрелок, как показано на изображении ниже:

                                                  вверх


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

                                                        вниз


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

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


                                                            Организация данных и запись в файл

                                                              Определим переменные Start и MqlRatesrates[], чтобы сохранить время начала и ряд данных тренда:
                                                                datetime Start;
                                                                MqlRates rates[];
                                                                ArraySetAsSeries(rates, false);
                                                                  Примечание: 1. Здесь нам не нужно определять время окончания, поскольку последнее время, полученное из графика, является временем окончания; 2. Флаг в функции ArraySetAsSeries(rates,false) равен false, чтобы гарантировать последовательное соединение периодов времени.
                                                                    При tp = "end" мы записываем сегмент данных в файл (зеленая часть кода):
                                                                         if(id==CHARTEVENT_CLICK)
                                                                           {
                                                                            //--- definition
                                                                            int x=(int)lparam;
                                                                            int y=(int)dparam;
                                                                            datetime dt    =0;
                                                                            double   price =0;
                                                                            int      window=0;
                                                                            if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                                              {
                                                                               ObjectCreate(0,Name,ob,window,dt,price);
                                                                               ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                                               //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                                               if(tp=="start")
                                                                                  Start=dt;
                                                                               else
                                                                                 {
                                                                                  if(file_handle)
                                                                                     file_write(Start,dt);
                                                                                 }
                                                                               ChartRedraw(0);
                                                                              }
                                                                            else
                                                                               Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                                           }

                                                                        Получим данные сегмента с помощью функции CopyRates() и добавим столбцы trend_group и trend_index, просматривая каждый фрагмент данных, содержащийся в rates[]. Нам нужно реализовать эти функции в функции file_write():
                                                                          void file_write(datetime start,
                                                                                          datetime end)
                                                                            {
                                                                             MqlRates rates[];
                                                                             ArraySetAsSeries(rates,false);
                                                                             int n_cp=CopyRates(Symbol(),PERIOD_CURRENT,start,end,rates);
                                                                             if(n_cp>0)
                                                                               {
                                                                                if(FileTell(file_handle)==2)
                                                                                  {
                                                                                   FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                                   for(int i=0; i<n_cp; i++)
                                                                                     {
                                                                                      FileWrite(file_handle,
                                                                                                rates[i].time,
                                                                                                rates[i].open,
                                                                                                rates[i].high,
                                                                                                rates[i].low,
                                                                                                rates[i].close,
                                                                                                rates[i].tick_volume,
                                                                                                typ,
                                                                                                i);
                                                                                     }
                                                                                  }
                                                                                else
                                                                                  {
                                                                                   for(int i=0; i<n_cp; i++)
                                                                                     {
                                                                                      FileWrite(file_handle,
                                                                                                rates[i].time,
                                                                                                rates[i].open,
                                                                                                rates[i].high,
                                                                                                rates[i].low,
                                                                                                rates[i].close,
                                                                                                rates[i].tick_volume,
                                                                                                typ,
                                                                                                i);
                                                                                     }
                                                                                  }
                                                                               }
                                                                             else
                                                                                Print("No data copied!");
                                                                             FileFlush(file_handle);
                                                                             typ=3;
                                                                            }



                                                                          Примечания:

                                                                          1. Нам нужно записать заголовок индекса при первой записи файла;

                                                                          2. Trend_group на самом деле является глобальной переменной typ;

                                                                          3. Мы не вызывали здесь функцию FileClose(), поскольку наша разметка еще не завершена. Мы собираемся вызвать эту функцию в функции OnDeinit(), чтобы записать окончательный результат в файл.

                                                                          4. Особое внимание следует обратить на желтую часть кода.

                                                                          if(FileTell(file_handle)==2)
                                                                          При определении наличия данных (конечно, можно использовать и другие методы, например, добавление переменной для присвоения ей значения при инициализации), если данных в файле нет, нужно добавить заголовок так:
                                                                          FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                          Если в файле есть данные, заголовок добавлять не нужно, иначе данные будут обрезаны, это очень важно!
                                                                            Пример записанного файла:
                                                                              data_0


                                                                                Давайте проверим согласованность между различными сегментами данных:

                                                                                  data_1


                                                                                  Приложение: полный пример кода советника

                                                                                  1. Определение глобальных переменных и констант. Параметр start_t можно определить по данным в секунду с 01.01.1970. Конечно, его также можно определить с помощью стандартного datetime или входной переменной input int start_t=1403037112; чтобы его можно было изменить в любой момент при дальнейшем запуске советника:
                                                                                  #define KEY_B     66
                                                                                  #define KEY_S     83
                                                                                  
                                                                                  
                                                                                  int Num= 0;
                                                                                  int typ= 3;
                                                                                  string Name;
                                                                                  string tp;
                                                                                  color col;
                                                                                  bool first= true;
                                                                                  ENUM_OBJECT ob;
                                                                                  int file_handle=0;
                                                                                  int start_t=1403037112;
                                                                                  datetime Start;

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

                                                                                  input int KEY_B=66;
                                                                                  input int KEY_S=83;

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


                                                                                  2. Функция OnInit(), в которой мы инициализируем наши приготовления:

                                                                                  int OnInit()
                                                                                    {
                                                                                  //---initial
                                                                                     string name;
                                                                                     string reName="1";
                                                                                     int hd=FileFindFirst("*",name,0);
                                                                                     int shift;
                                                                                  
                                                                                     ChartSetInteger(0,CHART_AUTOSCROLL,false);
                                                                                     ChartSetInteger(0,CHART_SHIFT,false);
                                                                                     ChartSetInteger(0,CHART_MOUSE_SCROLL,1);
                                                                                  
                                                                                  
                                                                                     do
                                                                                       {
                                                                                        //---check File
                                                                                        if(StringFind(name,Symbol())!=-1 && StringFind(name,".csv")!=-1)
                                                                                           reName=name;
                                                                                       }
                                                                                     while(FileFindNext(hd,name));
                                                                                  
                                                                                     if(FileIsExist(reName))
                                                                                       {
                                                                                        file_handle = FileOpen(reName,FILE_WRITE|FILE_CSV|FILE_READ);
                                                                                        string a[];
                                                                                        int i=0;
                                                                                        read_csv(file_handle,a);
                                                                                        i = ArraySize(a);
                                                                                        shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                                                                                        ChartNavigate(0,CHART_END,shift);
                                                                                       }
                                                                                     else
                                                                                       {
                                                                                        file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t),FILE_WRITE|FILE_CSV|FILE_READ);
                                                                                        Print(FileTell(file_handle));
                                                                                        Print("No history file,create file:",StringFormat("%s%d-%d",Symbol(),Period(),start_t));
                                                                                        shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                                                                                        ChartNavigate(0,CHART_END,shift);
                                                                                        ObjectCreate(0,"Start",OBJ_VLINE,0,(datetime)start_t,0);
                                                                                       }
                                                                                  //---
                                                                                     Print("EA:",MQL5InfoString(MQL5_PROGRAM_NAME),"Working!");
                                                                                  //---
                                                                                     ChartSetInteger(ChartID(),CHART_EVENT_OBJECT_CREATE,true);
                                                                                  //---
                                                                                     ChartSetInteger(ChartID(),CHART_EVENT_OBJECT_DELETE,true);
                                                                                  //---
                                                                                     ChartRedraw(0);
                                                                                  //---
                                                                                     return(INIT_SUCCEEDED);
                                                                                    }


                                                                                  3. Поскольку все наши операции с клавиатурой и мышью на графике завершены, мы поместили основные логические функции в функцию OnChartEvent():

                                                                                  void OnChartEvent(const int id,
                                                                                                    const long &lparam,
                                                                                                    const double &dparam,
                                                                                                    const string &sparam)
                                                                                    {
                                                                                  //Comment(__FUNCTION__,": id=",id," lparam=",lparam," dparam=",dparam," sparam=",sparam);
                                                                                     if(id==CHARTEVENT_KEYDOWN)
                                                                                       {
                                                                                        switch(lparam)
                                                                                          {
                                                                                           case KEY_B:
                                                                                              if(first)
                                                                                                {
                                                                                                 col=clrBlue ;
                                                                                                 typ =0;
                                                                                                 Num+=1;
                                                                                                 tp = "start";
                                                                                                }
                                                                                              else
                                                                                                {
                                                                                                 col=clrRed ;
                                                                                                 typ = 1;
                                                                                                 tp = "end";
                                                                                                }
                                                                                              ob =OBJ_ARROW_BUY;
                                                                                              first = !first;
                                                                                              Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                                                              break;
                                                                                           case KEY_S:
                                                                                              if(first)
                                                                                                {
                                                                                                 col=clrRed ;
                                                                                                 typ =1;
                                                                                                 Num+=1;
                                                                                                 tp = "start";
                                                                                                }
                                                                                              else
                                                                                                {
                                                                                                 col=clrBlue ;
                                                                                                 typ = 0;
                                                                                                 tp = "end";
                                                                                                }
                                                                                              ob =OBJ_ARROW_SELL;
                                                                                              first = !first;
                                                                                              Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                                                              break;
                                                                                  
                                                                                           default:
                                                                                              Print("You pressed:"+lparam+" key, do nothing!");
                                                                                          }
                                                                                        ChartRedraw(0);
                                                                                       }
                                                                                  //---
                                                                                     if(id==CHARTEVENT_CLICK&&(typ!=3))
                                                                                       {
                                                                                        //--- definition
                                                                                        int x=(int)lparam;
                                                                                        int y=(int)dparam;
                                                                                        datetime dt    =0;
                                                                                        double   price =0;
                                                                                        int      window=0;
                                                                                        if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                                                          {
                                                                                           ObjectCreate(0,Name,ob,window,dt,price);
                                                                                           ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                                                           //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                                                           if(tp=="start")
                                                                                              Start=dt;
                                                                                           else
                                                                                             {
                                                                                              if(file_handle)
                                                                                                 file_write(Start,dt);
                                                                                             }
                                                                                           ChartRedraw(0);
                                                                                          }
                                                                                        else
                                                                                           Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                                                       }
                                                                                  //--- object delete
                                                                                     if(id==CHARTEVENT_OBJECT_DELETE)
                                                                                       {
                                                                                        Print("The object with name ",sparam," has been deleted");
                                                                                       }
                                                                                  //--- object create
                                                                                     if(id==CHARTEVENT_OBJECT_CREATE)
                                                                                       {
                                                                                        Print("The object with name ",sparam," has been created!");
                                                                                       }
                                                                                  
                                                                                    }

                                                                                  Примечание: Мы изменили код выше при реализации этой функции

                                                                                   if (id==CHARTEVENT_CLICK&&(typ!=3))

                                                                                  Причина, по которой мы это делаем, очень проста: мы избегаем неправильных операций, вызванных случайными щелчками мыши, и используем переменную typ, чтобы контролировать допустимость действия мыши. Когда мы отмечаем тренд, мы выполняем функцию file_write(). Мы добавляем следующую строку в конец функции

                                                                                  typ=3;

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


                                                                                  4. Реализация функции записи данных - file_write():

                                                                                  void file_write(datetime start,
                                                                                                  datetime end)
                                                                                    {
                                                                                     MqlRates rates[];
                                                                                     ArraySetAsSeries(rates,false);
                                                                                     int n_cp=CopyRates(Symbol(),PERIOD_CURRENT,start,end,rates);
                                                                                     if(n_cp>0)
                                                                                       {
                                                                                        if(FileTell(file_handle)==2)
                                                                                          {
                                                                                           FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                                           for(int i=0; i<n_cp; i++)
                                                                                             {
                                                                                              FileWrite(file_handle,
                                                                                                        rates[i].time,
                                                                                                        rates[i].open,
                                                                                                        rates[i].high,
                                                                                                        rates[i].low,
                                                                                                        rates[i].close,
                                                                                                        rates[i].tick_volume,
                                                                                                        typ,
                                                                                                        i);
                                                                                             }
                                                                                          }
                                                                                        else
                                                                                          {
                                                                                           for(int i=0; i<n_cp; i++)
                                                                                             {
                                                                                              FileWrite(file_handle,
                                                                                                        rates[i].time,
                                                                                                        rates[i].open,
                                                                                                        rates[i].high,
                                                                                                        rates[i].low,
                                                                                                        rates[i].close,
                                                                                                        rates[i].tick_volume,
                                                                                                        typ,
                                                                                                        i);
                                                                                             }
                                                                                          }
                                                                                       }
                                                                                     else
                                                                                        Print("No data copied!");
                                                                                     FileFlush(file_handle);
                                                                                     typ=3;
                                                                                    }

                                                                                  5. Реализация функции чтения файла - read_csv():
                                                                                  void read_csv(int hd,
                                                                                                string &arry[])
                                                                                    {
                                                                                     int i=0;
                                                                                     while(!FileIsEnding(hd))
                                                                                       {
                                                                                        ArrayResize(arry,i+1);
                                                                                        arry[i]=FileReadString(hd);
                                                                                        i++;
                                                                                       }
                                                                                    }

                                                                                  6. Есть еще важная проблема, которая здесь не рассмотрена, хэндл файла file_handle, открытый при инициализации советника, не освобождается. Мы освобождаем хэндл в последней функции OnDeinit(). При вызове функции FileClose(file_handle) все данные будут записаны в csv-файл, поэтому особенно важно не пытаться открыть csv-файл во время работы советника:
                                                                                  void OnDeinit(const int reason)
                                                                                    {
                                                                                     FileClose(file_handle);
                                                                                     Print("Write data!");
                                                                                    }


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

                                                                                  Спасибо за внимание!

                                                                                  Перевод с английского произведен MetaQuotes Ltd.
                                                                                  Оригинальная статья: https://www.mql5.com/en/articles/13225

                                                                                  Прикрепленные файлы |
                                                                                  Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
                                                                                  Maxim Kuznetsov
                                                                                  Maxim Kuznetsov | 11 янв. 2024 в 12:58

                                                                                  честно говоря не понял зачем статья.

                                                                                  По заголовку и введению вроде про интерактивную разметку данных, а внутри про файловые операции MQL

                                                                                  и очень много ошибок в коде. 

                                                                                  Создаем алгоритм маркет-мейкинга на MQL5 Создаем алгоритм маркет-мейкинга на MQL5
                                                                                  Как работают маркет-мейкеры на рынке? Рассмотрим этот вопрос и создадим примитивный алгоритм маркет-мейкинга.
                                                                                  Популяционные алгоритмы оптимизации: Гибридный алгоритм оптимизации бактериального поиска с генетическим алгоритмом (Bacterial Foraging Optimization - Genetic Algorithm, BFO-GA) Популяционные алгоритмы оптимизации: Гибридный алгоритм оптимизации бактериального поиска с генетическим алгоритмом (Bacterial Foraging Optimization - Genetic Algorithm, BFO-GA)
                                                                                  В статье представлен новый подход к решению оптимизационных задач, путём объединения идей алгоритмов оптимизации бактериального поиска пищи (BFO) и приёмов, используемых в генетическом алгоритме (GA), в гибридный алгоритм BFO-GA. Он использует роение бактерий для глобального поиска оптимального решения и генетические операторы для уточнения локальных оптимумов. В отличие от оригинального BFO бактерии теперь могут мутировать и наследовать гены.
                                                                                  Нейросети — это просто (Часть 72): Прогнозирование траекторий в условиях наличия шума Нейросети — это просто (Часть 72): Прогнозирование траекторий в условиях наличия шума
                                                                                  Качество прогнозирование будущих состояний играет важную роль в метода Goal-Conditioned Predictive Coding, с которым мы познакомились в предыдущей статье. В данной статье я хочу познакомить Вас с алгоритмом, способным значительно повысить качество прогнозирования в стохастических средах, к которым можно отнести и финансовые рынки.
                                                                                  Нейросети — это просто (Часть 71): Прогнозирование будущих состояний с учетом поставленных целей (GCPC) Нейросети — это просто (Часть 71): Прогнозирование будущих состояний с учетом поставленных целей (GCPC)
                                                                                  В предыдущих работах мы познакомились с методом Decision Transformer и несколькими производными от него алгоритмами. Мы экспериментировали с различными методами постановки цели. В процессе экспериментов мы работали с различными способами постановки целей, однако изучение моделью уже пройденной траектории всегда оставалось вне нашего внимания. В данной статье я хочу познакомить Вас с методом, который заполняет этот пробел.