English Русский 中文 Español Deutsch Português
preview
MLモデルとストラテジーテスターの統合(第3回):CSVファイルの管理(II)

MLモデルとストラテジーテスターの統合(第3回):CSVファイルの管理(II)

MetaTrader 5機械学習 | 20 7月 2023, 10:15
226 0
Jonathan Pereira
Jonathan Pereira

はじめに

この記事では、ストラテジーテスターとPythonの統合の3番目の部分に焦点を当てます。ここでは、CSVファイルを効率的に管理するためのCFileCSVクラスの作成について説明します。このクラスが実際にどのように実装できるかを読者がよりよく理解できるように、いくつかの例とコードを検証します。

では、CSVとは何でしょうか。

CSV (Comma Separated Values)は、データを保存・交換するためのシンプルで広く使われているファイル形式です。これは、各行がデータのセットを表し、各列がそのデータのフィールドを表す表に似ています。値は区切り文字で区切られ、異なるツールやプログラミング言語でも読み書きが容易になります。

CSVフォーマットは1970年代初頭に登場し、当初はメインフレームシステムで使われていました。CSVは広く使われているファイルタイプなので、特定の作成者を特定することはできません。

スプレッドシート、データベース、データ分析プログラムなど、さまざまなアプリケーションでデータのインポートやエクスポートによく使用されるCSVの人気の理由は、使いやすさと理解しやすさ、そして多くのシステムやツールとの互換性にあります。これは、あるシステムから別のシステムへ情報を転送するなど、異なるアプリケーション間でデータを共有する必要がある場合に特に便利です。

つまり、CSVを使用する主な利点は、使いやすさと互換性です。しかし、複雑なデータ型をサポートしていなかったり、非常に大量のデータを扱う能力が低かったりといった制限もあります。また、CSVフォーマットには普遍的な標準がないため、異なるアプリケーション間で互換性の問題が生じる可能性があります。さらに、このフォーマットでは検証をおこなわないため、誤ってデータを紛失したり修正したりする可能性があります。一般的に、CSVはデータを保存共有するための多用途で使いやすいオプションです。とはいえ、その限界を知り、十分に理解し、データの正確性を確保するための措置を講じることが重要です。


動機

CFileCSVクラスは、MetaTrader 5ストラテジーテスター環境をPythonと統合する必要性から作成されました。機械学習(ML)モデルを使った取引戦略を開発する中で、私はPythonで作成したモデルを使う難しさに直面しました。MQL5で機械学習ライブラリを作るか、エキスパートアドバイザーを完全にPythonで作るか、どちらかしかありませんでした。

MQL5言語にはMLライブラリを作成するためのリソースが用意されていますが、主な目的がデータを分析し、高速かつ効率的な方法でモデルを構築することだったので、その開発に時間と労力を費やしたくありませんでした。

そこで、中間的な解決策を見つけることが課題となりました。Pythonで作られたMLモデルを利用したかっただけでなく、MQL5を使った仕事に直接適用できるようにしたかったので、この制限を克服し、この2つの環境を統合するソリューションを見つける方法を探し始めました。

アイデアは、MetaTrader 5とPythonがタイムリーに通信できるメッセージングシステムを作ることでした。これにより、MetaTrader 5からPythonへのデータの初期化と転送、およびPythonからMetaTrader 5への予測の送信を制御できるようになります。CFileCSVクラスは、効率的なデータの保存と読み込みを可能にすることで、この相互作用を促進するために設計されました。


CFileCSVクラスの紹介

CFileCSVはCSV (Comma Separated Values)ファイルを扱うためのクラスです。このクラスはCFileから派生しており、CSVファイルを扱うための特別な機能を提供しています。このクラスの目的は、さまざまなデータ型を扱いやすくすることで、CSVファイルの読み書きを容易にすることです。

CSVファイルを使う大きなメリットのひとつは、共有が簡単で、データのインポート/エクスポートに便利なことです。このようなファイルは、ExcelやGoogle Sheetsのようなプログラムで簡単に開いて編集することができ、さまざまなプログラミング言語で読むことができます。また、特定の書式がないため、さまざまなニーズに応じて読み書きができます。

CFileCSVクラスには、Open、WriteHeader、WriteLine、Readの4つの主要なpublicメソッドがあります。さらに、配列や行列を文字列に変換し、その値をファイルに書き出す2つのprivateヘルパーメソッドがあります。

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メソッドを使ってファイルを開きます。このメソッドでは、ファイル名とOpenフラグを指定します。次に、WriteHeaderメソッドを使ってヘッダーをファイルに書き込み、WriteLineメソッドを使って行列からデータ行を書き込みます。関数の例でこれらの手順を説明します。

#include "FileCSV.mqh"

void CreateCSVFile(string fileName, string &headers[], string &data[][])
  {
   // Creates an object of the CFileCSV class
   CFileCSV csvFile;

   // Checks if the file can be opened for writing in the ANSI format
   if(csvFile.Open(fileName, FILE_WRITE|FILE_ANSI))
     {
        int rows = ArrayRange(data, 0);
        int cols = ArrayRange(data, 1);
        int headerSize = ArraySize(headers);
        //Checks if the number of columns in the data matrix is equal to the number if elements in the header array and if the number of rows in the data matrix is greater than zero
        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;
        }
      // Writes header to file
      csvFile.WriteHeader(headers);
      // Writes data rows to file
      csvFile.WriteLine(data);
      // Closes the file
      csvFile.Close();
     }
   else
     {
      // Shows an error message if the file cannot be opened
      Print("Error opening file!");
     }
  }

このメソッドの目的は、ヘッダーの配列とデータの配列からCSVファイルを作成することです。まず、CFileCSVクラスのオブジェクトを作ることから始めます。次に、そのファイルがANSIフォーマットで書き込み用に開くことができるかどうかを確認します。ファイルを開くことができる場合は、データ行列の列数がヘッダー行列の要素数と等しく、データ行列の行数がゼロより大きいことを確認します。これらの条件が満たされると、このメソッドはWriteHeader()メソッドを使用してヘッダーをファイルに書き込み、 WriteLine()メソッドを使用してデータ行を書き込みます。最後に、このメソッドはファイルを閉じます。ファイルを開くことができない場合は、エラーメッセージが表示されます。

このメソッドについては、すぐに例を挙げて説明します。その実装は、他のタスクを実行するために拡張することができます。たとえば、ファイルを開こうとする前にファイルが存在するかどうかを確認したり、どの区切り文字を使うかを選択するオプションを追加したりといったようにです。

CFileCSVクラスは、CSVファイルを扱うためのシンプルで実用的な方法を提供し、CSVファイルへのデータの読み書きを容易にします。ファイルが期待されたフォーマットであることを確認し、メソッドが正常に実行されたことを確認します。


実装

上述したように、CFileCSVクラスには、Open、WriteHeader、WriteLine、Readの4つの主要なpublicメソッドがあります。また、ToStringの名前のオーバーロードを持つ2つのprivateヘルパーメソッドもあります。

  • メソッドOpen(const string file_name,const int open_flags, const short delimiter=';')は、CSV ファイルを開くために使用されます。このメソッドは、ファイル名、オープンフラグ(例えば、FILE_WRITEやFILE_READ)、ファイル内で使用される区切り文字(デフォルトは「;」)のパラメータを受け取ります。CFile基本クラスのOpenメソッドを呼び出し、指定された区切り文字をprivate変数に保存します。また、操作の成否を示す整数を返します。

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メソッドを使って配列を文字列に変換し、CFile基本クラスのFileWriteメソッドを使ってこの文字列をファイルに書き込みます。また、ファイルに書き込まれたバイト数を示す整数を返します。
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メソッドを使用して各行を文字列に変換し、それらの文字列を1つの文字列に連結します。そして、CFile基本クラスのFileWriteメソッドを使って、この文字列をファイルに書き込みます。また、ファイルに書き込まれたバイト数を示す整数を返します。

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ファイルの内容を読み込むために使われます。これは、CFile基本クラスのFileReadStringメソッドを使用して、ファイルの内容を1行ずつ読み込み、1つの文字列に保存します。ファイルの内容を含む文字列を返します。

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

   return res;

ToStringメソッドは、CFileCSVクラスのprivateヘルパーメソッドで、行列や配列を文字列に変換し、その値をファイルに書き込むために使用されます。

  • メソッド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;
  }

これらのメソッドは、WriteHeaderWrite Lineによって使用され、パラメータとして渡された値を文字列に変換し、その文字列をオープンファイルに書き込みます。これらの区切り文字は、値が期待される形式でファイルに書き込まれ、指定された区切り文字で区切られていることを確認するために使用されます。これらは、データが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"

この例では、データ行列の2行目を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メソッドの使用例に過ぎません。文字列に変換できるデータ型であれば、どのようなものにも適用できます。ただし、これらはprivateとして宣言されているため、CFileCSVクラスの内部でしか利用できないことにご注意ください。


アルゴリズムの複雑さ 

アルゴリズムの複雑さを測定し、アルゴリズムやシステムのパフォーマンスを最適化するためにこの情報をどのように利用できるでしょうか。

ビッグオー表記法は、アルゴリズムを分析するための重要なツールであり、コンピュータサイエンスの黎明期から認識されてきました。ビッグオーの概念は1960年代に正式に定義され、現在でも広く使われています。これにより、プログラマーは入力とその実行に必要な操作に基づいて、アルゴリズムの複雑さをおおまかに見積もることができます。このツールを使うことで、異なるアルゴリズムを比較し、特定のタスクに対してより良いパフォーマンスを提供するアルゴリズムを定義することができます。

データ量と解決すべき問題の複雑さは指数関数的に増大します。だからこそ、ビッグオーという表記が重要です。日々、より多くのデータが生成される一方で、このデータを処理するために、より効率的なアルゴリズムが必要です。

ビッグオーの概念は、あるアルゴリズムにおいて、実行時間がある数学的関数(通常は多項式)に従って成長するという考えに基づいています。この関数はビッグオー表記で表され、O(f(n))と表すことができます。

では、ビッグオー記法の使用例をいくつか見てみましょう。

  • O(1):定時間アルゴリズム、実行時間はデータサイズによって変化しない
  • O(n):線形時間アルゴリズム、実行時間はデータサイズに比例して増加
  • O(n^2):2次時間アルゴリズム、実行時間はデータサイズの2乗として増加
  • O(log n):対数時間アルゴリズム、実行時間がデータサイズの対数の関数として増加

ビッグオーは、特定の問題を解決するためにどのアルゴリズムを選択すべきかを決定し、またシステムのパフォーマンスを最適化するのに役立ちます。



CFileCSVクラスの各メソッドの時間の複雑さは、パラメータとして提供されるデータのサイズによって異なります。

  • Openメソッドの複雑さはO(1)です。これは、データサイズに関係なく、ファイルを開くために1つの操作を実行するからです。
  • Readメソッドの複雑さはO(n)で、nはファイルのサイズです。ファイルの内容全体を読み込み、文字列に保存します。
  • WriteHeaderメソッドの複雑さもO(n)であり、nはパラメータとして提供される配列のサイズです。配列を文字列に変換してファイルに書き出します。
  • WriteLineメソッドの計算量はO(mn)であり、mは行列の行数、nは各行の要素数です。各行を繰り返し、文字列に変換し、ファイルに書き出します。

これらの複雑さは、ファイルの書き込みバッファのサイズやファイルシステムなど、他の要因に影響される可能性があるため、推定値であることに注意してください。さらに、ビッグオー表記は最悪のシナリオを推定します。メソッドに提供するデータが多すぎると、複雑さが増します。

一般的に、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];

//The OnInit function
int OnStart(void)
  {
//Fill the 'data' array with values timestamp, Bid, Ask, Indicador1 and 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);

//Open the CSV file
   if(csvFile.Open(fileName, FILE_WRITE|FILE_ANSI))
     {
      //Write the header
      csvFile.WriteHeader(headers);
      //Write data rows
      csvFile.WriteLine(data);
      //Close the file
      csvFile.Close();
     }
   else
     {
      Print("File opening error!");
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

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

このコードはMQL5のCFileCSVクラスの実装で、以下の機能をカバーしています。

  • ファイルをオープンできない場合のエラー処理を提供
  • 指定された名前と適切な書き込み権限でCSVファイルを開く
  • 文字列の配列として定義されたヘッダーのファイルへの書き込みを許可
  • ファイルへの書き込み(文字列の配列として定義)
  • 書き込みが完了したらファイルを閉じる


結論

CFileCSVクラスは、CSVファイルを扱うための実用的で効率的なメソッドを提供します。ヘッダーや文字列を開いたり、書き込んだり、CSVファイルを読み込んだりするメソッドが含まれています。Open、WriteHeader、WriteLine、Readの各メソッドは、CSVファイルの正しい操作を保証し、データが読みやすい方法で書き込まれ、整理されるようにします。お時間をありがとうございました。次回は、今回紹介したCFileCSVクラスを使って、ファイル共有でMLモデルを利用する方法を紹介します。

MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/12069

添付されたファイル |
知っておくべきMQL5ウィザードのテクニック(第06回):フーリエ変換 知っておくべきMQL5ウィザードのテクニック(第06回):フーリエ変換
ジョセフ・フーリエによって導入されたフーリエ変換は、複雑なデータの波動点を単純な構成波に分解する手段です。この記事では、トレーダーにとって有益なこの機能を見ていきます。
MQL5における行列とベクトル:活性化関数 MQL5における行列とベクトル:活性化関数
ここでは、機械学習の一側面である活性化関数についてのみ説明します。人工ニューラルネットワークでは、ニューロンの活性化関数は、入力シグナルまたは入力シグナルのセットの値に基づいて出力シグナル値を計算します。その内幕に迫ります。
モスクワ取引所(MOEX)におけるストップ注文を利用した取引所グリッド取引の自動化 モスクワ取引所(MOEX)におけるストップ注文を利用した取引所グリッド取引の自動化
本稿では、MQL5エキスパートアドバイザー(EA)に実装されたストップ指値注文に基づくグリッド取引についてモスクワ取引所(MOEX)で考察します。市場で取引する場合、最も単純な戦略の1つは、市場価格を「キャッチ」するように設計された注文のグリッドです。
Rebuyのアルゴリズム:効率を上げるための数学モデル Rebuyのアルゴリズム:効率を上げるための数学モデル
この記事では、取引システムの効率をより深く理解するためにRebuyアルゴリズムを使用し、数学と論理を使用して取引効率を向上させる一般的な原則に着手し、どのような取引システムでも制約なく使用するという観点から、最も非標準的な、効率を高める方法を適用します。