English 中文 Español Deutsch 日本語 Português
preview
Интеграция ML-моделей с тестером стратегий (Часть 3): Управление файлами CSV(II)

Интеграция ML-моделей с тестером стратегий (Часть 3): Управление файлами CSV(II)

MetaTrader 5Машинное обучение | 14 июня 2023, 10:52
853 0
Jonathan Pereira
Jonathan Pereira

Введение

В данной статье мы сосредоточимся на третьей части интеграции тестера стратегии с Python. Мы покажем создание класса CFileCSV для эффективного управления CSV-файлами, включая примеры и код так, чтобы читатели поняли, как этот класс может быть реализован на практике.

Может вы себя спросите: а что такое CSV?

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

Формат CSV появился в начале 1970-х и впервые был использован в системах мэйнфреймов. Для CSV не существует конкретного создателя, поскольку это широко применяемый вид файлов.

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

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


Мотивация

Необходимость интегрировать среду тестера стратегий Meta Trade 5 с Python привела к созданию класса CFileCSV. Разрабатывая торговые стратегии с использованием моделей машинного обучения, мы столкнулись с трудностью использования моделей, созданных на языке Python. Нам либо пришлось бы создавать библиотеку машинного обучения на MQL5, что выходило за рамки основной цели, либо создавать советника полностью на Python.

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

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

Предложение заключалось в создании системы обмена сообщениями, в которой Meta Trader и Python могли бы оперативно взаимодействовать друг с другом. Это позволит управлять инициализацией и передачей данных из Meta Trader 5 в Python и отправкой прогнозов из Python в Meta Trader 5. Класс CFileCSV был разработан для облегчения данного взаимодействия, позволяя эффективно хранить и загружать данные.


Введение в класс CFileCSV

Класс CFileCSV - это класс работы с CSV-файлами (Comma Separated Values), производный от CFile, который обеспечивает специфическую функциональность для работы с CSV-файлами. Цель этого класса - упростить чтение и запись CSV-файлов, облегчая работу с различными типами данных.

Одним из больших преимуществ использования CSV-файлов является простота обмена и импорта/экспорта данных. Их можно легко открывать и редактировать в таких программах, как Excel или Google Sheets, а также считывать их различными языками программирования. Более того, поскольку у них нет определенного формата, их можно читать и писать в соответствии с различными потребностями.

Класс CFileCSV имеет четыре основных публичных метода: Open, WriteHeader, WriteLine и Read. Кроме того, у него есть два вспомогательных частных метода, используемых для преобразования массивов или матриц в строки и для записи этих значений в файл.

class CFileCSV : public CFile
  {
private:
   template<typename T>
   string            ToString(const int, const T &[][]);
   template<typename T>
   string            ToString(const T &[]);
   short             m_delimiter;

public:
                     CFileCSV(void);
                    ~CFileCSV(void);
   //--- methods for working with files
   int               Open(const string,const int, const short);
   template<typename T>
   uint              WriteHeader(const T &values[]);
   template<typename T>
   uint              WriteLine(const T &values[][]);
   string            Read(void);
  };  

При использовании данного класса помните, что он был разработан для работы с определенными CSV-файлами. Если данные в файле отформатированы неправильно, результаты могут стать неожиданными. Кроме того, очень важно убедиться в том, чтобы файл был открыт перед попыткой записи в него, и что у него есть разрешение на запись.

Примером использования класса CFileCSV может быть создание CSV-файла из массива данных. Сначала мы создадим экземпляр класса и откроем файл методом Open, определив имя файла и флаги открытия. Затем мы используем метод WriteHeader для записи заголовка в файл, а метод WriteLine для записи строк данных из матрицы.  Далее мы проиллюстрируем эти шаги на примере функции:

#include "FileCSV.mqh"

void CreateCSVFile(string fileName, string &headers[], string &data[][])
  {
   // Создает объект класса CFileCSV
   CFileCSV csvFile;

   // Проверяет, можно ли открывать файл для записи в формате ANSI
   if(csvFile.Open(fileName, FILE_WRITE|FILE_ANSI))
     {
        int rows = ArrayRange(data, 0);
        int cols = ArrayRange(data, 1);
        int headerSize = ArraySize(headers);
        //Проверяет, равно ли количество столбцов в массиве данных количеству элементов в массиве заголовков и больше ли нуля количество строк в массиве данных
        if(cols != headerSize || rows == 0)
        {
            Print("Error: Invalid number of columns or rows. Data array must have the same number of columns as the headers array and at least one row.");
            return;
        }
      // Записывает заголовок в файл
      csvFile.WriteHeader(headers);
      // Записывает строки кода данных в файл
      csvFile.WriteLine(data);
      // Закрывает файл
      csvFile.Close();
     }
   else
     {
      // Показывает сообщение об ошибке если файл невозможно открывать
      Print("Error opening file!");
     }
  }

Цель данного метода - создать CSV-файл из массива заголовков и массива данных. Начнем с создания объекта класса CFileCSV, а затем проверим, можно ли открывать файл для записи в формате ANSI. Если файл можно открыть, проверим, что количество столбцов в матрице данных равно количеству элементов в матрице заголовков и что количество строк в матрице данных больше нуля. Если эти условия выполняются, метод записывает заголовок в файл с помощью метода WriteHeader(), а затем записывает строки данных с помощью метода WriteLine(). В конце метод закрывает файл. Если файл не удается открыть, выводится сообщение об ошибке.

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

Класс CFileCSV предоставляет простой и практичный способ работы с CSV-файлами, облегчая чтение и запись данных в CSV-файлы. Однако при его использовании необходимо убедиться в том, чтобы файлы имели ожидаемый формат, и проверить возвраты методов, чтобы убедиться, что они были выполнены правильно.


Реализация

Как упоминалось выше, у класса CFileCSV есть четыре основных публичных метода: Open, WriteHeader, WriteLine и Read. Кроме того, у него есть два частных вспомогательных метода, оба перегружены под названием ToString.

  • Метод Open(const string file_name,const int open_flags, const short delimiter=';') используется для открытия CSV-файла. Этот метод принимает в качестве параметров имя файла, флаги открытия (например, FILE_WRITE или FILE_READ) и разделитель для использования в файле (по умолчанию ';'). Он вызывает метод Open базового класса CFile и сохраняет указанный разделитель в частной переменной. Также возвращает целочисленное значение, указывающее на успех или неудачу операции.

int CFileCSV::Open(const string file_name,const int open_flags, const short delimiter=';')
  {
   m_delimiter=delimiter;
   return(CFile::Open(file_name,open_flags|FILE_CSV|delimiter));
  }
  • Метод WriteHeader(const T &values[]) используется для записи заголовка в открытый CSV-файл. В качестве параметра он получает массив значений, представляющих заголовки столбцов файла. Он использует метод ToString для преобразования массива в строку и записывает данную строку в файл с помощью метода FileWrite базового класса CFile. К тому же возвращает целое число, указывающее на количество байтов, записанных в файле.
template<typename T>
uint CFileCSV::WriteHeader(const T &values[])
  {
   string header=ToString(values);
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(::FileWrite(m_handle,header));
//--- failure
   return(0);
  }
  • Метод WriteLine(const T &values[][]) используется для записи строк данных в открытый CSV-файл. Этот метод принимает в качестве параметра массив значений, представляющих строки данных в файле. Он перебирает по каждой строке массива, используя метод ToString для преобразования каждой строки в строку и конкатенации этих строк в одну строку. Потом записывает эту строку в файл с помощью метода FileWrite базового класса CFile. К тому же возвращает целое число, указывающее на количество байтов, записанных в файле.

template<typename T>
uint CFileCSV::WriteLine(const T &values[][])
  {
   int len=ArrayRange(values, 0);

   if(len<1)
      return 0;

   string lines="";
   for(int i=0; i<len; i++)
      if(i<len-1)
         lines += ToString(i, values)  + "\n";
      else
         lines += ToString(i, values);

   if(m_handle!=INVALID_HANDLE)
      return(::FileWrite(m_handle, lines));
   return 0;
  }
  • Метод Read(void) используется для чтения содержимого открытого CSV-файла. Используйте метод FileReadString базового класса CFile для чтения содержимого файла построчно и сохранения его в одной строке. Возвращает строку с содержимым файла.

string CFileCSV::Read(void)
  {
   string res="";
   if(m_handle!=INVALID_HANDLE)
      res = FileReadString(m_handle);

   return res;

Методы ToString являются частными вспомогательными методами класса CFileCSV и используются для преобразования массивов в строки и для записи этих значений в файл.

  • Метод ToString(const int row, const T &values[][]) используется для того, чтобы преобразовать массив в строку. Он принимает как параметр строку преобразуемой матрицы и саму матрицу. Метод перебирает каждый элемент строки в массиве, добавляя его к полученной строке. Разделитель добавляется в конце каждого элемента, за исключением последнего элемента в строке.

template<typename T>
string CFileCSV::ToString(const int row, const T &values[][])
  {
   string res="";
   int cols=ArrayRange(values, 1);

   for(int x=0; x<cols; x++)
      if(x<cols-1)
         res+=values[row][x] + ShortToString(m_delimiter);
      else
         res+=values[row][x];

   return res;
  }
  • Метод ToString(const T &values[]) преобразует массив в строку. Он перебирает каждый элемент массива и добавляет его к полученной строке. Разделитель добавляется в конце каждого элемента, за исключением последнего элемента в массиве.

template<typename T>
string CFileCSV::ToString(const T &values[])
  {
   string res="";
   int len=ArraySize(values);

   if(len<1)
      return res;

   for(int i=0; i<len; i++)
      if(i<len-1)
         res+=values[i] + ShortToString(m_delimiter);
      else
         res+=values[i];

   return res;
  }

Данные методы используются методами WriteHeader и WriteLine для преобразования значений, переданных в качестве параметров, в строки и для записи этих строк в открытый файл. Они используются для того, чтобы значения записывались в файл в соответствующем формате, и были отделены указанным разделителем. Они необходимы для того, чтобы обеспечить правильную и упорядоченную запись данных в CSV-файл. 

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

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


Пример использования метода ToString(const int row, const T &values[][]):

int data[2][3] = {{1, 2, 3}, {4, 5, 6}};
string str = csvFile.ToString(1, data);
//str -> "4;5;6"

В этом примере передаем в метод ToString вторую строку матрицы данных. Метод перебирает каждый элемент в строке, добавляя его к полученной строке и вставляя разделитель в конец каждого элемента, кроме последнего элемента строки. Полученной строкой будет '4;5;6'.

Пример использования метода ToString(const T &values[]):

string headers[] = {"Name", "Age", "Gender"};
string str = csvFile.ToString(headers);
//str -> "Name;Age;Gender"

В этом примере, массив 'headers' передается в метод ToString. Метод перебирает каждый элемент массива, добавляя его к полученной строке и вставляя разделитель в конец каждого элемента, кроме последнего элемента массива. Полученной строкой будет 'Name;Age;Gender'.

Это лишь примеры использования методов ToString и ToString. Их можно применить к любому виду данных, который можно преобразовать в строку, но важно отметить, что они доступны только внутри класса CFileCSV, поскольку объявлены как private.


Алгоритмическая сложность 

Как мы можем измерить сложность алгоритмов и использовать данную информацию для оптимизации работы алгоритмов и систем?

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

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

Концепция Big O основана на том, что для алгоритма время выполнения растет в соответствии с определенной математической функцией, обычно полиномиальной. Для выражения данной функции используется обозначение Big O, которое можно представить как O(f(n)), где f(n) - функция, представляющая сложность алгоритма.

Давайте теперь рассмотрим несколько примеров использования нотации Big O:

  • O(1), которая представляет собой алгоритм с постоянным временем, т.е. оно не меняется в зависимости от размера данных.
  • O(n), которая символизирует алгоритм линейного времени, где время выполнения увеличивается пропорционально размеру данных.
  • O(n^2), которая соответствует алгоритму квадратичного времени, где время выполнения растет как квадрат размера данных.
  • O(log n), которая обозначает алгоритм с логарифмическим временем, где время выполнения растет как функция логарифма размера данных.

Big O - это фундаментальный инструмент для понимания и сравнения эффективности различных алгоритмов. Она помогает принимать обоснованные решения о том, какой алгоритм выбрать для решения конкретной задачи, и также оптимизировать работу систем.



Временная сложность каждого метода класса CFileCSV меняется в зависимости от размера данных, предоставляемых в качестве параметра.

  • Метод Open имеет сложность O(1), так как он выполняет одну операцию для открытия файла, независимо от размера данных.
  • Метод Read имеет сложность O(n), где n - размер файла. Считывает всё содержимое файла и сохраняет его в строке.
  • Метод WriteHeader также имеет сложность O(n), где n - размер массива, предоставляемого в качестве параметра. Преобразует массив в строку и записывает ее в файл.
  • Метод WriteLine имеет сложность O(mn), где m - количество строк в массиве, а n является количеством элементов в каждой строке. Он перебирает каждую строку массива, преобразует ее в строку и записывает в файл.

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

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

 


Пример использования

//+------------------------------------------------------------------+
//|                                                    exemplo_2.mq5 |
//|                                     Copyright 2022, Lethan Corp. |
//|                           https://www.mql5.com/pt/users/14134597 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Lethan Corp."
#property link      "https://www.mql5.com/pt/users/14134597"
#property version   "1.00"
#include "FileCSV.mqh"

CFileCSV csvFile;
string fileName = "dados.csv";
string headers[] = {"Timestamp", "Close", "Last"};
string data[1][3];

//Функция OnInit
int OnStart(void)
  {
//Заполняем массив 'data' со значениями timestamp, Bid, Ask, Indicador1 и Indicador2
   data[0][0] = TimeToString(TimeCurrent());
   data[0][1] = DoubleToString(iClose(Symbol(), PERIOD_CURRENT, 0), 2);
   data[0][2] = DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_LAST), 2);

//Открываем CSV-файл
   if(csvFile.Open(fileName, FILE_WRITE|FILE_ANSI))
     {
      //Записываем заголовок
      csvFile.WriteHeader(headers);
      //Записываем строки со значениями
      csvFile.WriteLine(data);
      //Закрываем файл
      csvFile.Close();
     }
   else
     {
      Print("Error opening file!");
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

Данный код представляет собой реализацию класса CFileCSV в MQL5. Он охватывает следующие функциональности:

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


Заключение

В целом, класс CFileCSV предоставляет практичное и эффективное средство для работы с CSV-файлами с помощью методов для открытия, записи заголовков и строк, а также для чтения CSV-файлов. Методы Open, WriteHeader, WriteLine и Read имеют ключевое значение для обеспечения правильной работы с CSV-файлами, гарантируя, что данные записываются в организованном и читабельном виде. Спасибо за внимание! В следующем материале мы рассмотрим, как использовать модели машинного обучения через обмен файлами с помощью класса CFileCSV, который был представлен в этой статье.

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

Прикрепленные файлы |
MQL5 — Вы тоже можете стать мастером этого языка MQL5 — Вы тоже можете стать мастером этого языка
В этой статье я проведу нечто вроде интервью с самим собой и расскажу, как я делал свои первые шаги в языке MQL5. С помощью данного руководства я хочу помочь вам стать выдающимся программистом на MQL5, поэтому мы рассмотрим необходимые основы, чтобы достичь этого. Всё, что вам нужно иметь при себе - это искреннее желание учиться.
Оценка ONNX-моделей при помощи регрессионных метрик Оценка ONNX-моделей при помощи регрессионных метрик
Регрессия – это задача предсказания вещественной величины по непомеченному примеру. Для оценки точности предсказаний регрессионных моделей предназначены так называемые метрики регрессии.
Разработка системы репликации — моделирование рынка (Часть 01): Первые эксперименты (I) Разработка системы репликации — моделирование рынка (Часть 01): Первые эксперименты (I)
Что вы думаете: создавать системы для изучения рынка, когда он закрыт, или создать систему, которая позволит моделировать рыночные ситуации? Здесь мы начнем новую серию статей, посвященных этому вопросу.
Нейросети — это просто (Часть 45): Обучение навыков исследования состояний Нейросети — это просто (Часть 45): Обучение навыков исследования состояний
Обучение полезных навыков без явной функции вознаграждения является одной из основных задач в иерархическом обучении с подкреплением. Ранее мы уже познакомились с 2 алгоритмами решения данной задачи. Но вопрос полноты исследования окружающей среды остается открытым. В данной статье демонстрируется иной подход к обучению навыком. Использование которых напрямую зависит от текущего состояния системы.