Разметка данных в анализе временных рядов (Часть 1):Создаем набор данных с маркерами тренда с помощью графика советника
Введение
Когда мы разрабатываем модели искусственного интеллекта, нам часто необходимо сначала подготовить данные. Хорошее качество данных позволит нам получить вдвое лучший результат, затратив вдвое меньше усилий на обучение и проверку модели. Но данные о валюте или акциях особенные. Они содержат сложную рыночную информацию и информацию о времени, а разметка данных затруднена, но мы можем легко проанализировать тренд на исторических данных, доступных на графике.
В этом разделе представлен метод создания наборов данных с отметками тренда с помощью графиков советника. Вы можете интуитивно управлять данными, реализуя свои идеи. Конечно, вы также можете использовать этот метод для расширения и настройки собственных наборов данных!
Содержание:
- Определение формата данных метки
- Инициализация графиков и файлов
- Логика работы
- Организация данных и запись в файл
- Приложение: полный пример кода советника
Определение формата данных метки
Когда мы получаем от клиента данные о валюте или акциях (в этой статье не обсуждаются внешние данные, считанные из файлов или загруженные с других веб-сайтов), общая ситуация такова:
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);Однако код в этой статье реализован по первому способу.
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 () будет определена позже.
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 — отменить удаление последнего объекта.
#define KEY_B 66 #define KEY_S 83
2) Нажмем s, чтобы отметить конец восходящего тренда, переменная typ по-прежнему равна 0, переменная tp установлена в значение end, цвет стрелки по-прежнему clrBlue, а количество меток Num остается неизменным. Нам нужно увеличивать переменную только в начале сегмента данных, а инверсия first используется для указания того, что повторное нажатие кнопки приведет к выполнению "стартовой" части отмеченного сегмента данных.
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, а затем щелкните левой кнопкой мыши в конце столбца на графике. После завершения разметки появится пара красных стрелок, как показано на изображении ниже:
В столбце вывода разметки в любое время будет отображаться действие разметки, что очень удобно для отслеживания процесса, как показано на рисунке:
Примечание: Эту часть на самом деле можно лучше оптимизировать, например, добавив функцию отмены последнего действия, тогда вы сможете в любой момент отрегулировать положение метки, а также избежать неправильных операций, но я человек ленивый... (^о^)
Организация данных и запись в файл
datetime Start; MqlRates rates[]; ArraySetAsSeries(rates, false);
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()); }
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");Если в файле есть данные, заголовок добавлять не нужно, иначе данные будут обрезаны, это очень важно!
Давайте проверим согласованность между различными сегментами данных:
Приложение: полный пример кода советника
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
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
честно говоря не понял зачем статья.
По заголовку и введению вроде про интерактивную разметку данных, а внутри про файловые операции MQL
и очень много ошибок в коде.