English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Как подготовить котировки MetaTrader 5 для других программ

Как подготовить котировки MetaTrader 5 для других программ

MetaTrader 5Примеры | 4 октября 2012, 17:45
9 036 6
Anatoli Kazharski
Anatoli Kazharski

Содержание

Введение
1. Рассматриваемые вопросы
2. Формат данных
3. Внешние параметры программы
4. Проверка введённых пользователем параметров
5. Глобальные переменные
6. Информационная панель
7. Основной блок программы
8. Создаём папки и записываем данные в файл
Заключение


Введение

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

Для работы в этих программах нужны исторические данные. Так как определённого стандарта в формате данных нет, их очень часто нужно было редактировать перед использованием (например, в Excel) под формат, который понимает та или иная программа. Даже если ты разобрался как, потом убеждаешься, что ручной работы всё же много. На разных форумах можно найти различные версии скриптов для копирования котировок из MetaTrader 4 в нужный формат. Если есть такой спрос, то создадим версию скрипта и на MQL5.


1. Рассматриваемые вопросы

В статье будут рассматриваться следующие вопросы:

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


2. Формат данных

Я приведу пример подготовки данных для использования в программе NeuroShell DayTrader Professional (далее NSDT). Я пробовал воспользоваться 5-ой и 6-ой версией NSDT и обнаружил, что у этих версий разные требования к формату данных. В 5-ой версии NSDT данные о дате и времени должны находиться в разных столбцах. Первая строка в файле должна выглядеть вот так:

"Date" "Time" "Open" "High" "Low" "Close" "Volume"

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

Date,Open,High,Low,Close,Volume

В MetaTrader 5 есть штатная возможность сохранить котировки в файл в формате *.csv. Данные в файле при этом выглядят вот так:

Рис. 1. Данные сохранённые терминалом MetaTrader 5

Рис. 1. Данные сохранённые терминалом MetaTrader 5


Но просто редактированием строки заголовков не обойтись, так как дата должна иметь другой формат. Для 5-ой версии NSDT:

dd.mm.yyyy,hh:mm,Open,High,Low,Close,Volume
Для 6-ой версии NSDT:
dd/mm/yyyy hh:mm,Open,High,Low,Close,Volume

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

  • Записать данные только текущего символа, на графике которого был запущен скрипт (ONLY CURRENT SYMBOL).
  • Записать в файлы данные тех символов, которые находятся в окне Обзор Рынка (MARKETWATCH SYMBOLS).
  • Записать данные всех символов, которые доступны на сервере (ALL LIST SYMBOLS).

Для создания таких списков в коде скрипта перед внешними параметрами введём вот такой код:

//_________________________________
// ПЕРЕЧИСЛЕНИЕ_ФОРМАТОВ_ЗАГОЛОВКОВ
enum FORMAT_HEADERS
  {
   NSDT_5 = 0, // "Date" "Time" "Open" "High" "Low" "Close" "Volume"
   NSDT_6 = 1  // Date,Open,High,Low,Close,Volume
  };
//---
//___________________________
// ПЕРЕЧИСЛЕНИЕ_ФОРМАТОВ_ДАТЫ
enum FORMAT_DATETIME
  {
   SEP_POINT1 = 0, // dd.mm.yyyy hh:mm
   SEP_POINT2 = 1, // dd.mm.yyyy, hh:mm
   SEP_SLASH1 = 2, // dd/mm/yyyy hh:mm
   SEP_SLASH2 = 3  // dd/mm/yyyy, hh:mm
  };
//---
//____________________________
// ПЕРЕЧИСЛЕНИЕ_РЕЖИМОВ_ЗАПИСИ
enum CURRENT_MARKETWATCH
  {
   CURRENT          = 0, // ONLY CURRENT SYMBOLS
   MARKETWATCH      = 1, // MARKETWATCH SYMBOLS
   ALL_LIST_SYMBOLS = 2  // ALL LIST SYMBOLS
  };

Подробнее о перечислениях можно узнать в Справочнике по MQL5.


3. Внешние параметры программы

Теперь можно сформировать весь список внешних параметров скрипта:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ВНЕШНИЕ_ПАРАМЕТРЫ                                                |
//+------------------------------------------------------------------+
input datetime            start_date     = D'01.01.2011'; // Start Date
input datetime            end_date       = D'18.09.2012'; // End Date
input FORMAT_HEADERS      format_headers = NSDT_5;     // Format Headers
input FORMAT_DATETIME     format_date    = SEP_POINT2; // Format Datetime
input CURRENT_MARKETWATCH curr_mwatch    = CURRENT;    // Mode Write Symbols
input bool                clear_mwatch   = true;        // Clear Market Watch
input bool                show_progress  = true;        // Show Progress (%)

Внешние параметры имеют следующее предназначение:

  • С помощью параметров Start Date (start_date) и End Date (end_date) пользователь может указать диапазон дат для записи.
  • В выпадающем списке Format Headers (format_headers) можно выбрать формат заголовков.
  • В выпадающем списке Format Datetime (format_date) можно выбрать формат даты и времени.
  • В выпадающем списке Mode Write Symbols (curr_mwatch) можно выбрать количество символов для записи.
  • Если параметр Clear Market Watch (clear_mwatch) равен true, это позволяет удалить из окна Обзор Рынка все символы в конце записи. Это касается только тех символов, чьи графики в текущий момент не открыты.
  • Параметр Show Progress (%) (show_progress) показывает в информационной панели ход процесса записи в файл. Если его отключить, то процесс записи будет проходить быстрее.

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

Рис. 2. Окно внешних параметров программы

Рис. 2. Окно внешних параметров программы


4. Проверка введённых пользователем параметров

Создадим функцию, которая будет проверять корректность параметров, введённых пользователем, ещё до начала основного кода. Например, начальная дата в параметре Start Date должна быть раньше, чем в End Date. А формат заголовков должен соответствовать формату даты и времени. Если пользователь ошибся при установке параметров, то выйдет соответствующее предупреждение и программа будет завершена.

Пример предупреждения:

Рис. 3. Пример предупреждения на некорректно введённые значения

Рис. 3. Пример предупреждения о некорректных значениях


Функция ValidationParameters():

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ПРОВЕРКА_КОРРЕКТНОСТИ_ПАРАМЕТРОВ                                 |
//+------------------------------------------------------------------+
bool ValidationParameters()
  {
   if(start_date>=end_date)
     {
      MessageBox("Начальная дата должна быть раньше конечной!\n\n"
                 "Программа не может продолжить работу. Попробуйте ещё раз.",
                 //---
                 "Ошибка в параметрах!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   if(format_headers==NSDT_5 && 
      (format_date==SEP_POINT1 || format_date==SEP_SLASH1))
     {
      MessageBox("Для заголовков формата:\n\n"
                 "\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\"\n\n"
                 "Формат даты/времени можно установить одним из двух:\n\n"
                 "dd.mm.yyyy, hh:mm\n"
                 "dd/mm/yyyy, hh:mm\n\n"
                 "Программа не может продолжить работу. Попробуйте ещё раз.",
                 //---
                 "Несоответствие форматов заголовков и даты/времени!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   if(format_headers==NSDT_6 && 
      (format_date==SEP_POINT2 || format_date==SEP_SLASH2))
     {
      MessageBox("Для заголовков формата:\n\n"
                 "Date,Open,High,Low,Close,Volume\n\n"
                 "Формат даты/времени можно установить одним из двух:\n\n"
                 "dd.mm.yyyy hh:mm\n"
                 "dd/mm/yyyy hh:mm\n\n"
                 "Программа не может продолжить работу. Попробуйте ещё раз.",
                 //---
                 "Несоответствие форматов заголовков и даты/времени!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   return(false);
  }


5. Глобальные переменные

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

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ГЛОБАЛЬНЫЕ_ПЕРЕМЕННЫЕ_И_МАССИВЫ                                  |
//+------------------------------------------------------------------+
MqlRates rates[]; // Массив для копирования данных
//---
string symbols[]; // Массив символов
//---
// Массив имён графических объектов
string arr_nmobj[22]=
  {
   "fon","hd01",
   "nm01","nm02","nm03","nm04","nm05","nm06","nm07","nm08","nm09","nm10",
   "nm11","nm12","nm13","nm14","nm15","nm16","nm17","nm18","nm19","nm20"
  };
//---
// Массив отображаемого текста графическими объектами
string arr_txtobj[21];
//---
string path="";         // Путь к файлу
int cnt_symb=0;         // Количество символов
int sz_arr_symb=0;      // Размер массива символов
int bars=0;             // Количество баров по указанному ТФ
int copied_bars=0;      // Количество скопированных баров для записи
double pgs_pcnt=0;      // Прогресс записи
int hFl=INVALID_HANDLE;  // Хэндл файла
//---
string   // Переменные для форматирования даты
sdt="",  // Строка с датой
dd="",   // Число
mm="",   // Месяц
yyyy="", // Год
tm="",   // Время
sep="";  // Разделитель
//---
int max_bars=0; // Максимальное количество баров в настройках терминала
//---
datetime
first_date=0,        // Первая доступная дата в указанном периоде
first_termnl_date=0, // Первая доступная дата в базе данных терминала
first_server_date=0, // Первая доступная дата в базе данных сервера
check_start_date=0;  // Проверенное корректное значение даты


6. Информационная панель

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

  • Самый простой и напрашивающийся в первую очередь – "Прямоугольная метка" (OBJ_RECTANGLE_LABEL).
  • Тем, кто хочет сделать свой интерфейс с уникальным внешним видом, подойдёт графический объект "Рисунок" (OBJ_BITMAP).
  • В качестве фона можно также использовать графический объект "Поле ввода" (OBJ_EDIT). Установив свойство "только чтение" можно исключить возможность ввода текста. На данный момент есть ещё одно преимущество при использовании объекта "Поле ввода". Если вы сделали информационную панель в эксперте и хотели бы видеть её в таком же виде во время теста в режиме визуализации, то для этого подходит пока только этот объект. Ни OBJ_RECTANGLE_LABEL, ни OBJ_BITMAP не отображаются во время теста в режиме визуализации.

Хоть в данном случае будет реализован скрипт, а не эксперт, в качестве примера всё равно сделаем фон с объектом OBJ_EDIT. На рисунке ниже показан итоговый результат:

Рис. 4. Внешний вид информационной панели

Рис. 4. Внешний вид информационной панели


Перечислим, что отображено в качестве информации на панели:

  • Symbol (current/total) – символ, данные которого загружаются/копируются/записываются в данный момент. В скобках слева показан номер текущего символа. Справа - общее количество символов, с которыми будет работать скрипт.
  • Path Symbol – путь к символу или категория символа, к которой он принадлежит. Если вызвать контекстное меню в окне Обзор Рынка (правой кнопкой мыши) и выбрать в нём "Символы…", то откроется окно, в котором можно увидеть список всех символов. Подробнее с этим можно ознакомиться в Справке терминала.
  • Timeframe – период (таймфрейм). Будет использоваться тот таймфрейм, на котором был запущен скрипт.
  • Input Start Date – дата начала данных, которую указал пользователь в параметрах скрипта.
  • First Date (H1) – самая первая доступная дата (бар) данных текущего таймфрейма.
  • First Terminal Date (M1) – самая первая доступная дата минутного таймфрейма в уже имеющихся данных терминала.
  • First Server Date (M1) – самая первая доступная дата минутного таймфрейма на сервере.
  • Max. Bars In Options Terminal – максимальное количество баров для отображения на графике, установленное в настройках терминала.
  • Copied Bars – количество скопированных баров для записи.
  • Progress Value Current Symbol – процент записанных данных текущего символа.

Ниже можно ознакомиться с кодом подобной информационной панели:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ИНФОРМАЦИОННАЯ_ПАНЕЛЬ                                            |
//|------------------------------------------------------------------+
void InfoTable(int s)
  {
   int fnt_sz=8;            // Размер шрифта
   string fnt="Calibri";     // Шрифт заголовков
   color clr=clrWhiteSmoke;  // Цвет
//---
   int xH=300;
   int height_pnl=0;
   int yV1=1,yV2=12,xV1=165,xV2=335,xV3=1;
//---
   string sf="",stf="",ssf="";
   bool flg_sf=false,flg_stf=false,flg_ssf=false;
//---
   if(show_progress) { height_pnl=138; } else { height_pnl=126; }
//---
   flg_sf=SeriesInfoInteger(symbols[s],_Period,SERIES_FIRSTDATE,first_date);
   flg_stf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_termnl_date);
   flg_ssf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date);
//---
   if(flg_sf) { sf=TSdm(first_date); } else { sf="?"; }
   if(flg_stf) { stf=TSdm(first_termnl_date); } else { stf="?"; }
   if(flg_ssf) { ssf=TSdm(first_server_date); } else { ssf="?"; }
//---
   if(cnt_symb==0) { cnt_symb=1; }
//---
   int anchor1=ANCHOR_LEFT_UPPER,anchor2=ANCHOR_RIGHT_UPPER,corner=CORNER_LEFT_UPPER;
//---
   string path_symbol=SymbolInfoString(symbols[s],SYMBOL_PATH);
   path_symbol=StringSubstr(path_symbol,0,StringLen(path_symbol)-StringLen(symbols[s]));
//---
   arr_txtobj[0]="INFO TABLE";
   arr_txtobj[1]="Symbol (current / total) : ";
   arr_txtobj[2]=""+symbols[s]+" ("+IS(s+1)+"/"+IS(cnt_symb)+")";
   arr_txtobj[3]="Path Symbol : ";
   arr_txtobj[4]=path_symbol;
   arr_txtobj[5]="Timeframe : ";
   arr_txtobj[6]=gStrTF(_Period);
   arr_txtobj[7]="Input Start Date : ";
   arr_txtobj[8]=TSdm(start_date);
   arr_txtobj[9]="First Date (H1) : ";
   arr_txtobj[10]=sf;
   arr_txtobj[11]="First Terminal Date (M1) : ";
   arr_txtobj[12]=stf;
   arr_txtobj[13]="First Server Date (M1) : ";
   arr_txtobj[14]=ssf;
   arr_txtobj[15]="Max. Bars In Options Terminal : ";
   arr_txtobj[16]=IS(max_bars);
   arr_txtobj[17]="Copied Bars : ";
   arr_txtobj[18]=IS(copied_bars);
   arr_txtobj[19]="Progress Value Current Symbol : ";
   arr_txtobj[20]=DS(pgs_pcnt,2)+"%";
//---
   Create_Edit(0,0,arr_nmobj[0],"",corner,fnt,fnt_sz,clrDimGray,clrDimGray,345,height_pnl,xV3,yV1,2,C'15,15,15');
//---
   Create_Edit(0,0,arr_nmobj[1],arr_txtobj[0],corner,fnt,8,clrWhite,C'64,0,0',345,12,xV3,yV1,2,clrFireBrick);
//---
   Create_Label(0,arr_nmobj[2],arr_txtobj[1],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2,0);
   Create_Label(0,arr_nmobj[3],arr_txtobj[2],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2,0);
//---
   Create_Label(0,arr_nmobj[4],arr_txtobj[3],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*2,0);
   Create_Label(0,arr_nmobj[5],arr_txtobj[4],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*2,0);
//---
   Create_Label(0,arr_nmobj[6],arr_txtobj[5],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*3,0);
   Create_Label(0,arr_nmobj[7],arr_txtobj[6],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*3,0);
//---
   Create_Label(0,arr_nmobj[8],arr_txtobj[7],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*4,0);
   Create_Label(0,arr_nmobj[9],arr_txtobj[8],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*4,0);
//---
   Create_Label(0,arr_nmobj[10],arr_txtobj[9],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*5,0);
   Create_Label(0,arr_nmobj[11],arr_txtobj[10],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*5,0);
//---
   Create_Label(0,arr_nmobj[12],arr_txtobj[11],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*6,0);
   Create_Label(0,arr_nmobj[13],arr_txtobj[12],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*6,0);
//---
   Create_Label(0,arr_nmobj[14],arr_txtobj[13],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*7,0);
   Create_Label(0,arr_nmobj[15],arr_txtobj[14],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*7,0);
//---
   Create_Label(0,arr_nmobj[16],arr_txtobj[15],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*8,0);
   Create_Label(0,arr_nmobj[17],arr_txtobj[16],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*8,0);
//---
   Create_Label(0,arr_nmobj[18],arr_txtobj[17],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*9,0);
   Create_Label(0,arr_nmobj[19],arr_txtobj[18],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*9,0);
//---
   if(show_progress)
     {
      Create_Label(0,arr_nmobj[20],arr_txtobj[19],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*10,0);
      Create_Label(0,arr_nmobj[21],arr_txtobj[20],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*10,0);
     }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| СОЗДАНИЕ_ОБЪЕКТА_LABEL                                           |
//+------------------------------------------------------------------+
void Create_Label(long   chrt_id,   // id графика
                  string lable_nm,  // имя объекта
                  string rename,    // отображаемое имя
                  long   anchor,    // точка привязки
                  long   corner,    // угол привязки
                  string font_bsc,  // шрифт
                  int    font_size, // размер шрифта
                  color  font_clr,  // цвет шрифта
                  int    x_dist,    // координата по шкале X
                  int    y_dist,    // координата по шкале Y
                  long   zorder)    // приоритет
  {
   if(ObjectCreate(chrt_id,lable_nm,OBJ_LABEL,0,0,0)) // создание объекта
     {
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,rename);          // установка имени
      ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc);        // установка шрифта
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr);      // установка цвета шрифта
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ANCHOR,anchor);       // установка точки привязки
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner);       // установка угола привязки
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size);  // установка размера шрифта
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist);    // установка координаты X
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist);    // установка координаты Y
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false);     // нельзя выделить объект, если FALSE
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder);       // Приоритет выше/ниже
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n");         // нет всплывающей подсказки, если "\n"
     }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| СОЗДАНИЕ_ОБЪЕКТА_EDIT                                            |
//+------------------------------------------------------------------+
void Create_Edit(long   chrt_id,       // id графика
                 int    nmb_win,       // номер окна (подокна)
                 string lable_nm,      // имя объекта
                 string text,          // отображаемый текст
                 long   corner,        // угол привязки
                 string font_bsc,      // шрифт
                 int    font_size,     // размер шрифта
                 color  font_clr,      // цвет шрифта
                 color  font_clr_brd,  // цвет шрифта
                 int    xsize,         // ширина
                 int    ysize,         // высота
                 int    x_dist,        // координата по шкале X
                 int    y_dist,        // координата по шкале Y
                 long   zorder,        // приоритет
                 color  clr)           // цвет фона
  {
   if(ObjectCreate(chrt_id,lable_nm,OBJ_EDIT,nmb_win,0,0)) // создание объекта
     {
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,text);                     // установка имени
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner);                // установка угла привязки
      ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc);                 // установка шрифта
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ALIGN,ALIGN_CENTER);         // выравнивание по центру
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size);           // установка размера шрифта
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr);               // цвет шрифта
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BORDER_COLOR,font_clr_brd);    // цвет фона
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BGCOLOR,clr);                  // цвет фона
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XSIZE,xsize);                  // ширина
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YSIZE,ysize);                  // высота
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist);             // установка координаты X
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist);             // установка координаты Y
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false);              // нельзя выделить объект, если FALSE
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder);                // Приоритет выше/ниже
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_READONLY,true);                // Только для чтения
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n");                  // нет всплывающей подсказки, если "\n"
     }
  }

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

//____________________________________________________________________
//+------------------------------------------------------------------+
//| УДАЛЯЕТ_ВСЕ_ГРАФИЧЕСКИЕ_ОБЪЕКТЫ_КОТОРЫЕ_СОЗДАЛ_СКРИПТ            |
//+------------------------------------------------------------------+
void DelAllScriptObjects()
  {
// Получим размер массива имён графических объектов
   int sz_arr1=ArraySize(arr_nmobj);
//---
// Удалим все объекты
   for(int i=0; i<sz_arr1; i++)
     { DelObjbyName(arr_nmobj[i]);  }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| УДАЛЯЕТ_ОБЪЕКТЫ_ПО_ИМЕНИ                                         |
//+------------------------------------------------------------------+
int DelObjbyName(string Name)
  {
   int nm_obj=0;
   bool res=false;
//---
   nm_obj=ObjectFind(ChartID(),Name);
//---
   if(nm_obj>=0)
     {
      res=ObjectDelete(ChartID(),Name);
      //---
      if(!res) { Print("Ошибка при удалении объекта: - "+ErrorDesc(Error())+""); return(false); }
     }
//---
   return(res);
  }


7. Основной блок программы

Основной функцией в скриптах является OnStart(). Именно в ней вызываются на исполнение все остальные функции. Основной ход программы подробно прокомментирован в коде:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| СКРИПТ >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
//+------------------------------------------------------------------+
void OnStart()
  {
// Если параметры введённые пользователем некорректны,
// выведем сообщение об ошибке и закроем программу
   if(ValidationParameters()) { return; }
//---
   max_bars=TerminalInfoInteger(TERMINAL_MAXBARS); // Получим доступное количество баров в окне
//---
   GetSymbolsToArray();           // Заполним массив символов именами
   sz_arr_symb=ArraySize(symbols); // Получим размер массива символов
//---
   SetSeparateForFormatDate();    // Определим разделитель для формата даты
//---
// Пройдём по всем символам и запишем их данные в файл
   for(int s=0; s<=sz_arr_symb-1; s++)
     {
      copied_bars=0; // Обнулим переменную скопированных баров для записи
      pgs_pcnt=0.0;  // Обнулим переменную прогресса записи данных символа
      //---
      InfoTable(s); ChartRedraw();
      //---
      // Получим данные текущего символа
      int res=GetDataCurrentSymbol(s);
      //---
      if(res==0) { BC } // Если ноль, то прерываем цикл либо переходим к следующей итерации
      //---
      if(res==2)        // Выполнение программы прервано пользователем
        {
         DelAllScriptObjects(); // Удаляем объекты с графика, которые создал скрипт
         //---
         Print("------\nПользователь удалил скрипт!"); break;
        }
      //---
      // Получим путь для создания файла и создадим директории для них
      // Если пустая строка, прерываем цикл либо переходим к следующей итерации
      if((path=CheckCreateGetPath(s))=="") { BC }
      //---
      WriteDataToFile(s); // Запишем данные в файл
     }
//---
// При необходимости удалить символы из окна Обзор Рынка
   DelSymbolsFromMarketWatch();
//---
// Удаляем объекты с графика, которые создал скрипт
   Sleep(1000); DelAllScriptObjects();
  }

Рассмотрим функции, в которых происходят главные действия.

В функции GetSymbolsToArray() происходит заполнение массива символов (symbols[]) именами символов. Размер массива и, соответственно, количество символов в нём зависит от того, какой вариант выбрал пользователь в параметре Mode Write Symbols (curr_mwatch).

Если пользователю нужны данные только от одного символа, то размер массива будет равен 1.

ArrayResize(symbols,1); // Установим размер массива равным 1
symbols[0]=_Symbol;     // Занесём имя текущего символа

Если пользователю нужны данные всех символов из окна Обзор рынка (MarketWatch) или вообще всех доступных символов, то размер массива будет определяться функцией:

int SymbolsTotal(
   bool selected   // true – только символы в MarketWatch
);

Чтобы не делать два блока для двух вариантов с почти одинаковым кодом, сделаем функцию-указатель MWatchOrAllList(), которая будет возвращать true или false. Это значение будет определять, откуда будет браться список символов. Только из окна Обзор Рынка (true) или из общего списка доступных символов (false).

//____________________________________________________________________
//+------------------------------------------------------------------+
//| УКАЗАТЕЛЬ_НА_ОКНО_ОБЗОР_РЫНКА_ИЛИ_НА_ОБЩИЙ_СПИСОК                |
//+------------------------------------------------------------------+
bool MWatchOrAllList()
  {
   if(curr_mwatch==MARKETWATCH) { return(true); }
   if(curr_mwatch==ALL_LIST_SYMBOLS) { return(false); }
//---
   return(true);
  }

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

int SymbolName(
   int pos,        // номер в списке
   bool selected   // true – только символы в MarketWatch
);

В функции SymbolName() для выбора списка символов также используем функцию-указатель MWatchOrAllList(). Полный код функции GetSymbolsToArray():

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ЗАПОЛНЯЕТ_МАССИВ_СИМВОЛОВ_ИМЕНАМИ                                |
//+------------------------------------------------------------------+
void GetSymbolsToArray()
  {
// Если нужны данные только текущего символа
   if(curr_mwatch==CURRENT)
     { ArrayResize(symbols,1); symbols[0]=_Symbol; }
//---
// Если нужны данные всех символов из окна "Обзор рынка (MarketWatch)" или
// всего списка символов
   if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS)
     {
      // Получим количество символов в окне "Обзор рынка (MarketWatch)"
      cnt_symb=SymbolsTotal(MWatchOrAllList());
      //---
      for(int i=0; i<=cnt_symb-1; i++)
        {
         string nm_symb="";
         //---
         ArrayResize(symbols,i+1); // Увеличим размер массива ещё на один
         //---
         // Получим имя символа из окна "Обзор рынка (MarketWatch)"
         nm_symb=SymbolName(i,MWatchOrAllList());
         symbols[i]=nm_symb; // Занесём имя символа в массив
        }
     }
  }

Функция SetSeparateForFormatDate() очень проста. С её помощью определяем, какой будет использоваться разделитель в дате в зависимости от выбора пользователя в выпадающем списке в параметре Format Date (format_date).

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ОПРЕДЕЛИМ_РАЗДЕЛИТЕЛЬ_ДЛЯ_ФОРМАТА_ДАТЫ                           |
//+------------------------------------------------------------------+
void SetSeparateForFormatDate()
  {
   switch(format_date)
     {
      case SEP_POINT1 : case SEP_POINT2 : sep="."; break; // Разделитель точка
      case SEP_SLASH1 : case SEP_SLASH2 : sep="/"; break; // Разделитель косая черта (слэш (slash))
     }
  }

Далее идёт основной цикл, в котором производятся различные проверки. После них, если всё проходит успешно, данные записываются в файл. Если же нет, то цикл останавливается, все объекты удаляются с графика и скрипт завершает свою работу (в случае с одним символом). Или же происходит переход к следующей итерации (если символов более одного). В цикле последовательно происходит обращение к каждому символу в массиве symbols[]. В каждую функцию, которая находится в этом цикле, передаётся номер (индекс). Таким образом, соблюдается точная последовательность во всех функциях.

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

Перед тем, как скопировать данные символа в массив rate[], производится проверка на доступность данных с помощью функции CheckLoadHistory(). Эта функция предоставлена разработчиками в качестве примера. Ее изначальную версию можно посмотреть в Справке по MQL5. Я лишь немного подкорректировал её под использование в этом скрипте. В справке дано довольно детальное описание (его желательно тоже изучить), поэтому здесь я не буду приводить свой вариант, который практически точно такой же. И к тому же он есть коде с очень подробными комментариями.

Единственное, что можно сейчас написать, так это то, что функция CheckLoadHistory() возвращает код ошибки или код успешного выполнения, по которому затем выводится соответствующее сообщение в журнал из блока оператора-переключателя switch. В зависимости от того, какой был получен код, функция GetDataCurrentSymbol() либо продолжает своё выполнение дальше, либо возвращает свой код.

Если всё прошло успешно, то далее происходит копирование исторических данных с помощью функции CopyRates(). В глобальную переменную сохраняется размер массива, затем осуществляется выход из функции с возвратом кода 1. Если же что-то пошло не так, то функция завершит своё выполнение ещё в операторе switch и вернёт код 0 или 2.

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ПОЛУЧИМ_ДАННЫЕ_СИМВОЛОВ                                          |
//+------------------------------------------------------------------+
int GetDataCurrentSymbol(int s)
  {
   Print("------\n№"+IS(s+1)+" >>>"); // Выведем в журнал номер символа
//---
// Проверяет и скачивает нужное количество запрошенных данных
   int res=CheckLoadHistory(s,_Period);
//---
   InfoTable(s); ChartRedraw(); // Обновим данные в информационной таблице
//---
   switch(res)
     {
      case -1 : Print("Неизвестный символ "+symbols[s]+" (code: -1)!");                         return(0);
      case -2 :
         Print("Запрошенных баров больше, чем можно отобразить на графике (code: -2)!...\n"
               "...Будет использовано для записи столько данных сколько доступно.");            break;
      //---
      case -3 : Print("Выполнение было прервано пользователем (code: -3)!");                    return(2);
      case -4 : Print("Загрузка окончилась неудачей (code: -4)!");                              return(0);
      case  0 : Print("Все данные символа загружены (code: 0).");                               break;
      case  1 : Print("Уже имеющихся данных в таймсерии достаточно (code: 1).");                break;
      case  2 : Print("Таймсерия построена из имеющихся данных терминала (code: 2).");          break;
      //---
      default : Print("Результат выполнения не определен!");
     }
//---
// Скопируем данные в массив
   if(CopyRates(symbols[s],_Period,check_start_date,end_date,rates)<=0)
     { Print("Ошибка копирования данных символа "+symbols[s]+" - ",ErrorDesc(Error())+""); return(0); }
   else
     {
      copied_bars=ArraySize(rates); // Получим размер массива
      //---
      Print("Symbol: ",symbols[s],"; Timeframe: ",gStrTF(_Period),"; Copied bars: ",copied_bars);
     }
//---
   return(1); // Вернём 1, если прошло успешно
  }

После этого программа снова в теле основного цикла в функции OnStart(). Код присваивается локальной переменной res, и по её значению проводится проверка. Если 0, то была ошибка, которая означает, что данные текущего в цикле символа записать невозможно. Пояснение ошибки вывелось в журнал, и принимается решение, прервать цикл (break) или перейти к следующей итерации (continue).

if(res==0) { BC } // Если ноль, то прерываем цикл либо переходим к следующей итерации

В строке кода выше видно, что этот выбор производится какими-то загадочными символами BC. Это макроподстановка. Подробнее об этом можно прочитать в Справке по MQL5. Здесь только отмечу, что целые выражения (в одну строку) можно вложить в короткую запись, как показано в примере выше (BC). В каких-то случаях этот метод может быть даже удобнее и компактнее, чем функция. В данном случае это выглядит так:

// Макроподстановка с выбором дальнейшего действия
#define BC if(curr_mwatch==CURRENT) { break; } if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS) { continue; }

Ещё примеры макроподстановок, которые используются в этом скрипте:

#define nmf __FUNCTION__+": " // Макроподстановка имени функции перед сообщением в журнал
//---
#define TRM_DP TerminalInfoString(TERMINAL_DATA_PATH) // Папка, в которой хранятся данные терминала

Если же функция GetDataCurrentSymbol() вернула 2, это значит, что программа была удалена пользователем. Для того чтобы определить это событие в MQL5 есть функция IsStopped(). Эту функцию рекомендуется использовать в циклах, чтобы вовремя успеть правильно закончить выполнение программы. Если функция возвращает true, то есть около трёх секунд, чтобы произвести все действия перед принудительным удалением программы. В нашем случае производится удаление всех графических объектов и выводится сообщение в журнал:

if(res==2) // Выполнение программы прервано пользователем
   {
    DelAllScriptObjects(); // Удаляем объекты с графика, которые создал скрипт
    //---
    Print("------\nПользователь удалил скрипт!"); break;
   }


8. Создаём папки и записываем данные в файл

Функция CheckCreateGetPath() проверяет существование корневой папки для данных. Назовем ее DATA_OHLC и находится она будет в директории C:\Metatrader 5\MQL5\Files. В ней будут находиться папки с названиями символов, в которых соответственно будут создаваться файлы для записи данных.

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

Код ниже усеян подробными комментариями и в нём будет несложно разобраться:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ПРОВЕРЯЕТ_ДИРЕКТОРИЮ_И_СОЗДАЁТ_НУЖНЫЕ_ПАПКИ_ДЛЯ_ДАННЫХ           |
//+------------------------------------------------------------------+
string CheckCreateGetPath(int s)
  {
   int i=1;
   long search=-1;
   string ffname="",lpath="";
   string file="*.csv",folder="*";
   string
   root="DATA_OHLC\\",         // Корневая папка данных
   fSmb=symbols[s]+"\\",     // Имя символа
   fTF=gStrTF(_Period)+"\\"; // Таймфрейм символа
//---
   bool flgROOT=false,flgSYMBOL=false;
//---
//+------------------------------------------------------------------+
//| ИЩЕМ_КОРНЕВУЮ_ПАПКУ_DATA_OHLC                                    |
//+------------------------------------------------------------------+
   lpath=folder;
   search=FileFindFirst(lpath,ffname); // Установим хэндл поиска в папке - Metatrader 5\MQL5\Files
//---
   Print("Директория: ",TRM_DP+"\\MQL5\\Files\\");
//---
// Если первая папка корневая, ставим флаг
   if(ffname==root)
     { flgROOT=true; Print("Корневая папка "+root+" существует"); }
//---
   if(search!=INVALID_HANDLE) // Если хэндл поиска получен
     {
      if(!flgROOT) // Если первая папка была не корневой
        {
         // Перебираем все файлы с целью поиска корневой папки
         while(FileFindNext(search,ffname))
           {
            if(IsStopped()) // Выполнение прервано пользователем
              {
               // Удаляем объекты с графика, которые создал скрипт
               DelAllScriptObjects();
               //---
               Print("------\nПользователь удалил скрипт!"); return("");
              }
            //---
            if(ffname==root) // Если находим, то ставим флаг
              { flgROOT=true; Print("Корневая папка "+root+" существует"); break; }
           }
        }
      //---
      FileFindClose(search); search=-1; // Закроем хэндл поиска корневой папки
     }
   else { Print("Ошибка при получении хэндла поиска либо директория "+TRM_DP+" пуста: ",ErrorDesc(Error())); }
//---
//+------------------------------------------------------------------+
//| ИЩЕМ_ПАПКУ_СИМВОЛА                                               |
//+------------------------------------------------------------------+
   lpath=root+folder;
//---
// Установим хэндл поиска в корневой папке ..\Files\DATA OHLC\
   search=FileFindFirst(lpath,ffname);
//---
// Если первая папка текущего символа, ставим флаг
   if(ffname==fSmb) { flgSYMBOL=true; Print("Папка символа "+fSmb+" существует"); }
//---
   if(search!=INVALID_HANDLE) // Если хэндл поиска получен
     {
      if(!flgSYMBOL) // Если первая папка была не текущего символа
        {
         // Перебираем все файлы в корневой папке с целью поиска папки символа
         while(FileFindNext(search,ffname))
           {
            if(IsStopped()) // Выполнение прервано пользователем
              {
               // Удаляем объекты с графика, которые создал скрипт
               DelAllScriptObjects();
               //---
               Print("------\nПользователь удалил скрипт!"); return("");
              }
            //---
            if(ffname==fSmb) // Если находим, то ставим флаг
              { flgSYMBOL=true; Print("Папка символа "+fSmb+" существует"); break; }
           }
        }
      //---
      FileFindClose(search); search=-1; // Закроем хэндл поиска папки символа
     }
   else { Print("Ошибка при получении хэндла поиска либо директория "+path+" пуста"); }
//---
//+------------------------------------------------------------------+
//| ПО_РЕЗУЛЬТАТАМ_ПРОВЕРКИ_СОЗДАДИМ_НУЖНЫЕ_КАТАЛОГИ                 |
//+------------------------------------------------------------------+
   if(!flgROOT) // Если нет корневой папки DATA_OHLC...
     {
      if(FolderCreate("DATA_OHLC")) // ...создадим её
        { Print("Создана корневая папка ..\DATA_OHLC\\"); }
      else
        { Print("Ошибка при создании корневой папки DATA_OHLC: ",ErrorDesc(Error())); return(""); }
     }
//---
   if(!flgSYMBOL) // Если нет папки символа, значения которого нужно получить...
     {
      if(FolderCreate(root+symbols[s])) // ...создадим её
        {
         Print("Создана папка символа ..\DATA_OHLC\\"+fSmb+"");
         //---
         return(root+symbols[s]+"\\"); // Вернём путь, в котором будет создан файл для записи
        }
      else
        { Print("Ошибка при создании папки символа ..\DATA_OHLC\\"+fSmb+"\: ",ErrorDesc(Error())); return(""); }
     }
//---
   if(flgROOT && flgSYMBOL)
     {
      return(root+symbols[s]+"\\"); // Вернём путь, в котором будет создан файл для записи
     }
//---
   return("");
  }

Если функция CheckCreateGetPath() вернула пустую строку, то прерываем цикл либо переходим к следующей итерации, используя уже знакомую макроподстановку (BC):

// Получим путь для создания файла и создадим директории для них
// Если пустая строка, прерываем цикл либо переходим к следующей итерации
if((path=CheckCreateGetPath(s))=="") { BC }

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

Для записи данных в файл создадим функцию WriteDataToFile(). В начале этой функции формируется [путь]+[имя файла]. Имя файла состоит из имени символа и текущего таймфрейма. Например, EURUSD_H1.csv. Если файл с таким именем уже есть, то он просто открывается для записи. Данные, которые были в него записаны в предыдущий раз, будут удалены. Вместо них будут записаны новые данные. Если файл создан/открыт успешно, функция FileOpen() возвращает хэндл, который будет использоваться для доступа к файлу.

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

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

Ниже показан весь код функции WriteDataToFile():

//____________________________________________________________________
//+------------------------------------------------------------------+
//| ЗАПИШЕМ_ДАННЫЕ_В_ФАЙЛ                                            |
//+------------------------------------------------------------------+
void WriteDataToFile(int s)
  {
// Количество знаков в цене символа после запятой
   int dgt=(int)SymbolInfoInteger(symbols[s],SYMBOL_DIGITS);
//---
   string nm_fl=path+symbols[s]+"_"+gStrTF(_Period)+".csv"; // Имя файла
//---
// Получим хэндл файла для записи
   hFl=FileOpen(nm_fl,FILE_WRITE|FILE_CSV|FILE_ANSI,',');
//---
   if(hFl>0) // Если хэндл получен
     {
      // Запишем заголовки
      if(format_headers==NSDT_5)
        { FileWrite(hFl,"\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\""); }
      //---
      if(format_headers==NSDT_6)
        { FileWrite(hFl,"Date","Open","High","Low","Close","Volume"); }
      //---
      // Запишем данные
      for(int i=0; i<=copied_bars-1; i++)
        {
         if(IsStopped()) // Если выполнение программы прервано пользователем
           {
            DelAllScriptObjects(); // Удаляем объекты с графика, которые создал скрипт
            //---
            Print("------\nПользователь удалил скрипт!"); break;
           }
         //---
         sdt=TSdm(rates[i].time); // Время открытия бара
         //---
         // Разъединим дату на год, месяц, число, время
         yyyy=StringSubstr(sdt,0,4);
         mm=StringSubstr(sdt,5,2);
         dd=StringSubstr(sdt,8,2);
         tm=StringSubstr(sdt,11);
         //---
         string sep_dt_tm=""; // Разделитель столбцов Date и Time
         //---
         // Соединим данные с разделителем и в нужном порядке
         if(format_date==SEP_POINT1 || format_date==SEP_SLASH1) { sep_dt_tm=" "; }
         if(format_date==SEP_POINT2 || format_date==SEP_SLASH2) { sep_dt_tm=","; }
         //---
         // Соединяем всё в одну строку
         StringConcatenate(sdt,dd,sep,mm,sep,yyyy,sep_dt_tm,tm);
         //---
         FileWrite(hFl,
                   sdt,// Дата-время
                   DS_dgt(rates[i].open,dgt),      // Цена открытия
                   DS_dgt(rates[i].high,dgt),      // Цена максимума
                   DS_dgt(rates[i].low,dgt),       // Цена минимума
                   DS_dgt(rates[i].close,dgt),     // Цена закрытия
                   IS((int)rates[i].tick_volume)); // Цена тикового объёма
         //---
         // Обновим значение прогресса записи для текущего символа
         pgs_pcnt=((double)(i+1)/copied_bars)*100;
         //---
         // Обновим данные в таблице
         InfoTable(s); if(show_progress) { ChartRedraw(); }
        }
      //---
      FileClose(hFl); // Закроем файл
     }
   else { Print("Ошибка при создании/открытии файла!"); }
  }

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

Пример того, как загружать данные в NeuroShell DayTrader Professional можно посмотреть у меня в блоге. Видео, демонстрирующее работу скрипта:



Заключение

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

Успехов в развитии!

Прикрепленные файлы |
writedatatofile.mq5 (68.76 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (6)
Anatoli Kazharski
Anatoli Kazharski | 5 окт. 2012 в 07:40
komposter:

Спасибо за статью, читать было приятно.

Вопрос по функциональности скрипта: а получить историю, длиннее "Макс. баров в окне", программно нельзя?

И пожелание для будущих версий: добавить другие форматы данных (для разных программ). А код выложить в базу и обновлять по мере доработок.

Еще раз спасибо! 

Спасибо. ))

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

Serhiy Dotsenko
Serhiy Dotsenko | 17 дек. 2014 в 22:49
как на счёт обратной задачи, с финама например вставить котировки (по лукойлу напрмер) в МТ?
Anatoli Kazharski
Anatoli Kazharski | 18 дек. 2014 в 10:02
thejobber:
как на счёт обратной задачи, с финама например вставить котировки (по лукойлу напрмер) в МТ?

В MetaTrader 5? Можно, если только в виде индикатора. Проще открыть счёт у брокера, где есть нужный инструмент.

Если брокер не предоставляет эту платформу, нужно почаще спрашивать "когда же Вы уже наконец дадите нам возможность торговать через MetaTrader 5". ))

Serhiy Dotsenko
Serhiy Dotsenko | 18 дек. 2014 в 11:44
tol64:

В MetaTrader 5? Можно, если только в виде индикатора. Проще открыть счёт у брокера, где есть нужный инструмент.

не вариант, мне это нужно, для того что бы всё привести к одной платформе, что бы не нужно было стоки в трейдматике или вэлслабе тестить, форекс в МТ.

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

и не нужно уже распылятся на QPILE, Lua (Quik), C# (ctrade, Tradematic, Wealth-Lab), S#(много чего), java (jforex) т.д...

Mikhail Khlestov
Mikhail Khlestov | 22 мар. 2019 в 09:14
Требуется вид <DATE>,<TIME>,<BID>,<OFFER>

20170102,0,1.07139,1.07149

20170102,500,1.07139,1.07159

20170102,1000,1.07139,1.07169

20170102,1500,1.07174,1.07194

20170102,2000,1.07197,1.07217

20170102,2500,1.07174,1.07194

Можете помочь реализовать такое?

Интервью с Рожериу Фигурелли (ATC 2012) Интервью с Рожериу Фигурелли (ATC 2012)
Сегодня мы поговорим о постоянном участнике из Бразилии Рожериу Фигурелли (figurelli), который с 2007 года не пропустил ни один Чемпионат. В этом году он также выставил своего конкурсного советника на продажу в Маркете наряду с другими своими продуктами. Рожериу считает, что сертификация платформы MetaTrader 5 на крупнейшей бразильской бирже BM&FBOVESPA приведет к появлению новых профессиональных разработчиков и трейдеров, которые пока не знают весь потенциал роботов-инвесторов.
Интервью с Ахмадом Хидаятом (ATC 2012) Интервью с Ахмадом Хидаятом (ATC 2012)
На всем протяжении Automated Trading Championship 2012 мы будем вести прямые трансляции с места событий - горячие репортажи и отчеты каждую неделю. Героем сегодняшнего репортажа стал участник из Индонезии по имени Ахмад Хидаят (achidayat). В первый день его советник закрепился в третьей десятке. Вполне неплохое начало. Ахмад заинтересовал нас своим активным участием в жизни MQL5 Market. На сегодняшний день он опубликовал уже более 20 продуктов.
Нейронные сети - от теории к практике Нейронные сети - от теории к практике
В наше время, наверное, каждый трейдер слышал о нейронных сетях и знает, как это круто. В представлении большинства те, которые в них разбираются, это какие-то чуть ли не сверхчеловеки. В этой статье я постараюсь рассказать, как устроена нейросеть, что с ней можно делать и покажу практические примеры её использования.
Изучаем классы торговых стратегий из Стандартной Библиотеки - Пользовательские стратегии Изучаем классы торговых стратегий из Стандартной Библиотеки - Пользовательские стратегии
В этой статье мы исследуем классы торговых стратегий из Стандартной Библиотеки и научимся добавлять пользовательские стратегии и фильтры/сигналы, следуя логике шаблонов и моделей Мастера MQL5. В конце вы сможете легко добавить свои собственные стратегии, используя стандартные индикаторы MetaTrader 5, а Мастер MQL5 создаст чистый код и полностью функциональный эксперт.