English Русский 中文 Español Deutsch Português
MQLベースのエキスパートアドバイザとデータベースの統合 (SQL server、.NET、および C#)

MQLベースのエキスパートアドバイザとデータベースの統合 (SQL server、.NET、および C#)

MetaTrader 5 | 24 10月 2018, 09:17
2 101 0
Сергей Ткаченко
Сергей Ткаченко

イントロダクション. MQL ベースのエキスパートとデータベース

MQL5コミュニティでは、たびたびEAにデータベースとタスクを統合する方法についての質問が浮上します。 このトピックへの関心は驚くべきことではありません。 データベースは、データを保存する手段として非常に優れています。 ターミナルログとは異なり、データはデータベースから消えません。 必要なものだけを選び、選別し、フィルターし易いという特徴があります。 データベースを使用すると、特定のコマンドなど、必要な情報をエキスパートに渡すことができます。 そして最も重要なことに、得られたデータは、異なる観点から分析し、統計的に処理することができます。 たとえば、1行のクエリを作成するだけで、各通貨ペアの指定した時間の平均と総利益を調べることができます。 そして手動でトレードターミナルのアカウント履歴を計算する場合、かかる時間を想像してみてください。

残念ながら、メタトレーダーは、データベースサーバーと通信するための組み込みツールを提供していません。 この問題は、DLL ファイルから関数をインポートすることによってのみ解決できます。 このタスクはシンプルではありませんが、実行可能です。

この操作を複数回を行ったことがあるので、この記事で経験を共有することにしました。 例としては、Microsoft SQL server データベースサーバーとの MQL5 エキスパートの相互作用を構成する方法です。 エキスパートがデータベースを操作するための関数をインポートするための DLL ファイルを作成するには、Microsoft.NET プラットフォームと C# 言語が使用されていました。 この記事では、DLL ファイルを作成して準備するプロセスと、その関数を MQL5 で記述されたエキスパートにインポートする方法について説明します。 例として提供されるエキスパートコードは非常にシンプルです。 MQL4 でコンパイルするためには、少しばかりの変更が必要です。

タスクの準備

このタスクには次のことが必要になります。

  1. アクティブなトレーディングアカウントを持つインストールされたMetaTrader5ターミナル。 リアルアカウント同様にデモ口座を使用することができます。資産に対するリスクを抑えることができます。
  2. Microsoft SQL server データベースサーバーのインストール済みインスタンス。 別のコンピュータ上のデータベースを使用して、ネットワーク経由で接続することができます。 無料の Express 版は、マイクロソフトのサイトからダウンロードすることができますが、その制限はほとんどのユーザーにとって重要ではありません。 こちらでダウンロードできます。: https://www.microsoft.com/en-us/sql-server/sql-server-editions-express. マイクロソフトでは、サイトのリンクを変更することがあります。 したがって、直リンクが動作しない場合は、任意の検索エンジンで "SQL server Express のダウンロード " のようなフレーズを入力してください。 SQL server を初めてインストールする場合は、インストールに問題がある可能性があります。 特に、古いバージョンの OS では、追加のコンポーネント (具体的には PowerShell と .NET 4.5) をインストールする必要があります。 また、 SQL Server and VS C++ 2017 はC++の保存に対して障害が発生するかもしれません。 "コントロールパネル "、 "プログラム "、 "プログラムと関数 "、 "VS C++ 2017 "、 "変更 "、 "修復 " を通じて行うことができます。 この問題は常に発生することとは限りませんが、発生したとしてもかんたんに解決することができます。
  3. .NET と C# を使用した統合開発環境。 個人的には (無料版を使っています) マイクロソフトの Visual Studio を使用しています。したがって、例もそれように作っています。 別の開発環境と他のプログラミング言語を使用することもできます。 しかし、環境と選ばれた言語で与えられた例を実装する方法を考える必要があるでしょう。
  4. .NETで記述された DLL ファイルからアンマネージコードに関数をエクスポートするためのツールです。 MQL ベースのエキスパートは、マネージド.NET コードでは動作しません。 したがって、DLL ファイルは、関数をエクスポートする関数を提供し、準備する必要があります。 これを達成するための様々なやり方が、ウェブ上で公開されています。 今回は、ロバートギーゼッケによって作成された "UnmanagedExports " パッケージを使用しました。 Microsoft Visual Studio version 2012 以降を使用する場合は、IDE メニューから直接プロジェクトに追加できます。 やり方については、後で説明します。

必要なプログラムをインストールする以外にも、別の準備タスクを行う必要があります。 これらの理由から、 "UnmanagedExports " は、コンピュータの言語設定で非 Unicode プログラムの言語として "ロシア語 (ロシア) " が選択されている場合には機能しません。 "英語 (US) " 以外の言語でも問題が発生することがあります。 インストールするには、コントロールパネルを開きます。 "ローカルと言語のオプション " タブに移動し、 "詳細 " タブに移動します。 [Unicode 対応でないプログラムの言語] タブで、 "システムロケールの変更... " を押します。 "英語 (US) " が設定されている場合は、問題ありません。 他の言語の場合は、 "英語 (米国) " に変更し、コンピュータを再起動します。

これを行わないと、 "UnmanagedExports " スクリプトの実行段階でプロジェクトをコンパイルするときに、 ".il " ファイルに構文エラーが発生します。 今のところこれを修正することはできません。 プロジェクトが非常にシンプルで、C# コードにエラーがない場合でも、 "il " ファイルでエラーが発生しても、プロジェクトからアンマネージコードに関数をエクスポートすることはできません。

これは、64ビットアプリケーションにのみ適用されます。 32ビットアプリケーションは、システムロケールの変更を必要としない他の方法で処理できます。 例えば、DllExporter.exeプログラムを使用することができ、こちらでダウンロードできます。 https://www.codeproject.com/Articles/37675/Simple-Method-of-DLL-Export-without-C-CLI.

システムロケールを変更すると、一部のアプリケーションが動作しなくなります。 残念ながら、ある程度の不便は妥協する必要があります。 ロケールを変更する必要があるのは、プロジェクトをコンパイルするときだけです。 コンパイルが成功すると、システムロケールを切り替えることができます。

DLL ファイルの作成

Visual Studio を開き、新しい visual C# プロジェクトを作成し、その型として "クラスライブラリ " を選択します。 MqlSqlDemo と名前を付けてください。 プロジェクトのプロパティで、 "Build " セクションで、 "プラットフォームターゲット " を構成する必要があります。 デバッグ構成とリリース構成の両方で、 "Any CPU " を "x64 " に変更する必要があります。 これは、アンマネージコードへの関数のエクスポートの特殊性によるもので、プロセッサの種類を指定することは必須です。

.NEt framework バージョン4.5 をインストールします。 通常はデフォルトで選択されます。

プロジェクトを作成すると、 "Class1.cs " クラスを含む "" ファイルがプロジェクトに自動的に追加されます。 ファイルとクラスの両方を "MqlSqlDemo.cs " および "MqlSqlDemo " に変更します。 DLL ファイルからエクスポートされる関数は静的である場合があります (アンマネージコードへのエクスポートには、必要です)。

厳密に言えば、非静的関数をエクスポートすることもできます。 しかし、これを行うには、この記事では考慮されていない C++/CLI ツールを参照する必要があります。

クラス内のすべての関数は静的である必要があるため、クラス自体を静的にするということは論理的です。 この場合、一部の関数に対して "static " 修飾子がない場合は、プロジェクトをコンパイルするときにすぐに検出されます。 次のクラス定義を得ます。

public static class MqlSqlDemo
{
    //...
}

プロジェクトの依存関係を構成する必要があります ( "ソリューションエクスプローラ " の "参照 " セクション)。 すべての冗長オプションを削除し、 "System " と "System.Data" のみを残します。

次に、 "UnmanagedExports " パッケージを追加します。

このパッケージの詳細は著者のサイトで利用可能です。 https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports.

これを追加する最も便利な方法は、NuGet パッケージマネージャーを使用することです。 追加のインストラクションはNuGetサイトにあります。: https://www.nuget.org/packages/UnmanagedExports

その1つだけが今回必要です:

インストール-パッケージ UnmanagedExports-バージョン1.2.7

Visual Studio のメニューで、 "Tools " セクション、 "NuGet パッケージマネージャ "、および "パッケージマネージャコンソール " を選択します。 コマンドラインが下部に表示されます。 コピーされた "インストールパッケージ UnmanagedExports-バージョン 1.2.7 " オーダーを挿入し、 "Enter " を押します。 パッケージマネージャは、しばらくの間、インターネットに接続し、パッケージをダウンロードし、プロジェクトに追加されると、次のように出力されます:

PM> Install-Package UnmanagedExports -Version 1.2.7
Installing 'UnmanagedExports 1.2.7'.
Successfully installed 'UnmanagedExports 1.2.7'.
Adding 'UnmanagedExports 1.2.7' to MqlSqlDemo.
Successfully added 'UnmanagedExports 1.2.7' to MqlSqlDemo.

PM> 

これは、パッケージが正常に追加されたことを意味します。

その後、直接クラス定義ファイル MqlSqlDemo.cs のコードの記述に進むことができます。

使用する名前空間を構成します。

  • Visual Studio は、余分なもの多くを追加します。 "using " セクションから、 "システムを使用する以外のすべてのものを削除します。".
  • その後、 "using System.Data "を追加し、データベースを操作するためのクラスを取得します。
  • using System.Data.SqlClient; "を追加します。 ": ここでは、SQL server データベースを操作するためのクラスを具体的に示します。
  • using System.Runtime.InteropServices; "を追加します。 ": アンマネージコードと通信するための属性を次に示します。
  • "using RGiesecke.DllExport; を追加": エクスポートされた関数をマークするための属性は、ここから取得されます。
次に、結果セットを示します。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;

必要な変数を追加します。 静的クラスの変数は、静的だけにすることもできます。 データベースを操作するためのオブジェクト (接続オブジェクトと コマンドオブジェクト) が必要です。

private static SqlConnection conn = null;
private static SqlCommand com = null;

また、関数の実行結果に関する詳細なメッセージを送信するために使用する文字列も必要です。

private static string sMessage = string.Empty;

値0と値1を持つ2つの定数を宣言します。これはほとんどの関数の戻り値として機能します。 関数が正常に実行された場合は、0を返します (それ以外の場合は 1)。 これより、コードがよりわかりやすくなります。

public const int iResSuccess = 0;
public const int iResError = 1;

そして関数に対して

MQL5 で使用するためにエクスポートされる関数には制限があります。

  1. 前述のように、関数は静的である必要があります。
  2. テンプレートコレクションクラス (System.Collections.Generic名前空間) は禁止されています。 これを含むコードは正常にコンパイルされますが、実行時に予期エラーが発生することがあります。 このクラスは、インポートされない他の関数で使用することもできますが、なしで行うことをお勧めします。 通常の配列を使用できます。 プロジェクトは情報提供の目的のためだけに書かれているので、そのようなクラス (だけでなく、配列) が含まれていません。

このデモプロジェクトは、シンプルなデータ型 (数値または文字列) のみを使用して動作します。 理論的には、内部的に整数として表されるBoolean値も渡すことができます。 しかし、数値の値は、異なるシステム (MQL と .NET) によって異なる解釈をすることができます。 これは、エラーにつながります。 したがって、 intstring 、およびdoubleの3種類のデータに制限します。 必要に応じて、Bool値を int として渡す必要があります。

実際のプロジェクトでは、複雑なデータ構造を渡すことは可能ですが、SQL server を使用してタスクを整理することはできません。

データベースを操作するには、まず接続を確立する必要があります。 これは、 CreateConnection関数によって行われます。 この関数は、SQL server データベースに接続するためのパラメータを持つ1つのパラメータ (文字列) を取ります。 接続が成功したかどうかを示す整数を返します。 接続が成功した場合は、 iResSuccessが返されます (つまり 0)。 失敗した場合- iResErrorです。すなわち1。 より詳細な情報は、メッセージ文字列- sMessageに入れられます。

ここで、結果です:

[DllExport("CreateConnection", CallingConvention = CallingConvention.StdCall)]
    public static int CreateConnection(
            [MarshalAs(UnmanagedType.LPWStr)] string sConnStr)
    {
        //メッセージ文字列をクリア
        sMessage = string.Empty;
        // 接続がある場合、閉じて変更
        // コネクションストリングを新しくする、それ以外の場合
        //接続とコマンドオブジェクトを再作成します。
        if (conn != null)
        {
                conn.Close();
                conn.ConnectionString = sConnStr;
        }
        else
        {
                conn = new SqlConnection(sConnStr);
                com = new SqlCommand();
                com.Connection = conn;
        }
        //接続を開始
        try
        {
                conn.Open();
        }
        catch (Exception ex)
        {
                //何らかの理由で接続が開かれませんでした。
                //エラー情報をメッセージ文字列に書き込みます。
                sMessage = ex.Message;
                //リソースをエントリーし、オブジェクトをリセットします。
                com.Dispose();
                conn.Dispose();
                conn = null;
                com = null;
                //エラー:
                return iResError;
        }
        //すべてがうまくいったら、接続が開いている:
        return iResSuccess;
}

エクスポートされる各関数は、関数定義の前にDllExport属性によってマークされます。 RGiesecke.DllExport メタデータアセンブリからインポートされた RGiesecke.DllExport.Metadataにあります。 NuGet マネージャーが UnmanagedExports パッケージをインストールすると、アセンブリがプロジェクトに自動的に追加されます。 この属性には2つのパラメータを渡す必要があります。

  • エクスポートされる関数名。 この名前は、DLL から呼び出す外部プログラム (MetaTrader5を含む) によって使用されます。 エクスポートされた関数の名前は、コードCreateConnection内の関数名と同じにすることができます;
  • 2番目のパラメータは、どの関数呼び出しメカニズムが使用するかを示します。 CallingConvention.StdCallは、すべての関数に適します。

属性 [MarshalAs(UnmanagedType.LPWStr)]. に注意を払ってください。 関数によって取得された接続文字列パラメータ ConnStringIn の前にいます。 この属性は、文字列の送信メソッドをあらわします。 この記事を書いている時点で、MetaTrader5とMetaTrader4は Unicode 文字列を使用しています— UnmanagedType.LPWStr.

関数呼び出しの時点で、以前の接続試行でエラーを説明するテキストがメッセージ文字列に残ることがあるため、文字列は関数の先頭でクリアされます。 また、この関数は、以前の接続がまだ閉じていないときに呼び出すことができます。 したがって、最初に接続とコマンドオブジェクトが存在するかどうかを確認します。 この場合、接続を閉じて、オブジェクトを再利用できます。 そうでない場合は、新しいオブジェクトを作成する必要があります。

接続に使用するOpenは結果を返しません。 したがって、接続が成功したかどうかを調べるには、例外をキャッチするしかありません。 エラーが発生した場合は、リソースを開放し、オブジェクトをゼロにして、メッセージ文字列に情報を書き込み、iResError を返します。 すべてが正常であれば-iResSuccess を返します。

接続が開かない場合、障害の原因を調べるために、sMessage 文字列に含まれるメッセージを読み取る必要があります。 これを行うには、 GetLastMessage関数を追加します。 メッセージの文字列を返します。

[DllExport("GetLastMessage", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static string GetLastMessage()
{
        return sMessage;
}

接続を確立するための関数と同様に、この関数は DllExport export 属性でもマークされています。 [return: MarshalAs (UnmanagedType LPWStr)]属性は、返された結果を渡すメソッドを示します。 結果は文字列であるため、MetaTrader5にも Unicode で渡す必要があります。 したがって、 UnmanagedType.LPWStrもここで使用します。

接続を開くと、データベースの操作を開始できます。 データベースにクエリを実行する関数を追加してみましょう。 これは、ExecuteSql 関数: によって行われます。

[DllExport("ExecuteSql", CallingConvention = CallingConvention.StdCall)]
public static int ExecuteSql(
        [MarshalAs(UnmanagedType.LPWStr)] string sSql)
{
        //メッセージ文字列をクリア
        sMessage = string.Empty;
        //まず、接続が確立されているかどうかを確認
        if (conn == null)
        {
                //接続はまだオープンではありません。
                //エラーを報告し、エラーフラグを返します。
                sMessage = "Connection is null, call CreateConnection first.";
                return iResError;
        }
        //コマンドを実行しようとする準備が整いました。
        try
        {
                com.CommandText = sSql;
                com.ExecuteNonQuery();
        }
        catch (Exception ex)
        {
                //コマンドの実行中にエラーが発生しました。
                //エラー情報をメッセージ文字列に書き込みます。
                sMessage = ex.Message;
                //エラーフラグを返します。
                return iResError;
        }
        //すべてがうまくいった-成功した実行のフラグを返します。
        return iResSuccess;
}

クエリテキストは、パラメータによって関数に渡されます。 クエリを実行する前に、接続が開いているかどうかを確認してください。 接続を開くための関数と同様に、この関数は成功した場合は iResSuccess を返し、エラーが発生した場合は iResError を返します。 エラーの原因に関する詳細情報を取得するには、GetLastMessage 関数を使用する必要があります。 ExecuteSql 関数を使用すると、データの書き込み、削除、変更などのクエリを実行できます。 また、データベース構造を使用することもできます。 残念ながら、データの読み取りは許可されませんが、関数は結果を返さず、読み取りデータをどこにも格納しません。 クエリは実行されますが、読み取られた内容を確認することはできません。 したがって、データを読み取るための2つの関数を追加します。

最初の関数は、データベーステーブルから1つの整数を読み取るように設計されています。

[DllExport("ReadInt", CallingConvention = CallingConvention.StdCall)]
public static int ReadInt(
        [MarshalAs(UnmanagedType.LPWStr)] string sSql)
{
        //メッセージ文字列をクリア
        sMessage = string.Empty;
        //まず、接続が確立されているかどうかを確認
        if (conn == null)
        {
                //接続はまだオープンではありません。
                //エラーを報告し、エラーフラグを返します。
                sMessage = "Connection is null, call CreateConnection first.";
                return iResError;
        }
        //返された結果を受け取る変数:
        int iResult = 0;
        //コマンドを実行しようとする準備が整いました。
        try
        {
                com.CommandText = sSql;
                iResult = (int)com.ExecuteScalar();
        }
        catch (Exception ex)
        {
                //コマンドの実行中にエラーが発生しました。
                //エラー情報をメッセージ文字列に書き込みます。
                sMessage = ex.Message;
        }
        //取得した結果を返します。
        return iResult;
}

データの読み取りは、コマンドのシンプルな実行よりもはるかに実装が困難です。 この関数は大幅に簡略化され、SqlCommandクラスのExecuteScalar関数を利用します。 このメソッドは、クエリによって返される最初の行の最初の列の値を返します。 したがって、パラメータによって渡される SQL クエリは、返されたデータセットに行があり、最初の列に整数が含まれるように形成する必要があります。 また、この関数は何とかして読み取り数を返す必要があります。 したがって、その結果は、もはや実行の成功のメッセージになりません。 クエリが成功したかどうかを理解し、データを読み取るには、GetLastMessage を呼び出すことによって、どのような場合でも最後の分析を行う必要があります。 最後のメッセージが空の場合、エラーはなく、データが読み取られました。 何かが書き込まれている場合は、エラーが発生し、データを読み取ることができなかったことを意味します。

2番目の関数は、データベースから1つの値を読み取りますが、別の型 (整数ではなく文字列) を読み込みます。 文字列は、数字と同じように読み取ることができます。差は、返される結果の型にのみあります。 この関数は文字列を返すため、[return: MarshalAs(UnmanagedType.LPWStr)]でマークする必要があります。 この関数のコードを次に示します。

[DllExport("ReadString", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static string ReadString(
        [MarshalAs(UnmanagedType.LPWStr)] string sSql)
{
        //メッセージ文字列をクリア
        sMessage = string.Empty;
        //まず、接続が確立されているかどうかを確認
        if (conn == null)
        {
                //接続はまだオープンではありません。
                //エラーを報告し、エラーフラグを返します。
                sMessage = "Connection is null, call CreateConnection first.";
                return string.Empty;
        }
        //返された結果を受け取る変数:
        string sResult = string.Empty;
        //コマンドを実行しようとする準備が整いました。
        try
        {
                com.CommandText = sSql;
                sResult = com.ExecuteScalar().ToString();
        }
        catch (Exception ex)
        {
                //コマンドの実行中にエラーが発生しました。
                //エラー情報をメッセージ文字列に書き込みます。
                sMessage = ex.Message;
        }
        //取得した結果を返します。
        return sResult;
}

このようなデータ読み取り関数は、デモプロジェクトで十分です。 実際のEAにとっては、まったく不要である可能性があります。つまり、より詳細な分析のため、データベースにデータを書き込むことがEAにとって重要です。 しかしデータを読み取る必要がある場合は、関数を使用することができます。これは、非常に今回のタスクに適します。 ただし、複数の列を含むテーブルから多くの行を読み取る必要がある場合もあります。

2つの方法で行うことができます。 関数から複雑なデータ構造を返すことができます (このパスは MQL4 には適していません)。 また、クラスのデータセットクラスの静的変数を宣言することもできます。 読み取り時には、データベースからデータをデータセットに読み込み、その他の関数を使用して、1つの関数呼び出しに対して1つのセルからデータを読み取る必要があります。 このアプローチは、以下のHerdOfRobotsプロジェクトで実装されています。 プロジェクトコードで詳細に検討することができます。 記事内容を膨らませないために、複数行からのデータ読み取りはここでは考慮しません。

データベースの処理が完了したら、接続を閉じて、使用しているリソースをエントリーする必要があります。 CloseConnection関数によって行われます。

[DllExport("CloseConnection", CallingConvention = CallingConvention.StdCall)]
public static void CloseConnection()
{
        //まず、接続が確立されているかどうかを確認
        if (conn == null)
                //接続はまだ始値レートではありません-どちらかを閉じる必要はありません:
                return;
        //接続がオープン-終了する必要があります:
        com.Dispose();
        com = null;
        conn.Close();
        conn.Dispose();
        conn = null;
}

このシンプルな関数は、パラメータを取得せず、結果を返しません。

必要な関数のすべて準備が整いました。 プロジェクトをコンパイルします。

この関数は、他の .NET アプリケーションからではなく、メタトレーダー (.NET を使用しない) から使用するため、コンパイルは2段階で行われます。 最初の段階では、すべての .NET プロジェクトと同じ方法ですべてが実行されます。 通常のビルドが作成され、後で UnmanagedExports パッケージによって処理されます。 このパッケージは、ビルドのコンパイル後にタスクを開始します。 最初に、ILデコンパイラが起動し、結果のビルドがILコードに解析されます。 IL コードが変更されると、DllExport 属性への参照が削除され、この属性でマークされた関数をエクスポートするため追加されます。 その後、IL コードを含むファイルが再コンパイルされて、元の DLL を上書きします。

このアクションはすべて自動的に実行されます。 しかし、前述のように、オペレーティングシステムの設定で非 Unicode プログラムに対してロシア語が選択されている場合、変更された IL コードを使用してファイルをコンパイルしようとすると、UnmanagedExports がエラーを与え、何もできなくなる可能性があります。

コンパイル中にエラーメッセージが受信されなかった場合は、すべてがうまくいき、取得した DLL をエキスパートで使用できます。 さらに、UnmanagedExports が DLL を正常に処理すると、拡張子 ". exp " および ".lib " (この場合は "MqlSqlDemo " および "MqlSqlDemo ") を含む2つのファイルが追加されます。 その存在は UnmanagedExports が正常に完了したことを示すことができます。

デモプロジェクトは非常に重要な制限があることに注意する必要があります。1つのメタトレーダーのターミナル上でデータベースを操作するEAは一つだけにする必要があります。 すべてのこのEAは、読み込まれた DLL の1つのインスタンスを使用します。 クラスは静的に作られているので、すべての実行中のEAの唯一のものになります。 変数もコモンになります。 複数のEAを実行する場合は、すべて同じ接続と同じコマンドオブジェクトを使用します。 複数のEAがオブジェクトに同時に対処しようとすると、問題が発生する可能性があります。

しかし、このようなプロジェクトは、操作の原則を説明し、データベースへの接続をテストするのに十分です。 さて、この機能を持ったDLL 関数があります。 MQL5 のエキスパートの作成に進むことができます。

MQL5 でEAを作成する

MQL5 で簡単なエキスパートを作成しましょう。 そのコードは、 "mq5 " から "mq4 " に拡張子を変更することによって、MQL4 エディタでも編集することができます。 このエキスパートは、データベースの正常なタスクをデモンストレーションするためにのみ使用するため、トレード操作は実行されません。

メタエディタ を実行し、 "New " ボタンを押します。 "EA (テンプレート) " を選択し、 "Next " を押します。 名前 "MqlSqlDemo " を指定してください。 また、型 "string " の1つのパラメータ ( "ConnectionString ") を追加します。 データベースサーバーへの接続方法を示す接続文字列になります。 たとえば、パラメータにこの初期値を設定できます。

Server=localhost;Database=master;Integrated Security=True

この接続文字列を使用すると、メタトレーダーターミナルが実行されている同じコンピュータにインストールされている名前のない ( "デフォルトのインスタンス ") データベースサーバーに接続できます。 ログインとパスワードを指定する必要はありません— Windows アカウントによる承認が使われます。

SQL Server Express をダウンロードして、パラメータを変更せずにコンピュータにインストールした場合、SQL server は "名前付きインスタンス " になります。 名前 "SQLEXPRESS " が表示されます。 別の接続文字列があります。

Server=localhost\\SQLEXPRESS;Database=master;Integrated Security=True

エキスパートアドバイザテンプレートに文字列パラメータを追加する場合は、文字列サイズに制限があります。 長い接続文字列 (たとえば、 "SQLEXPRESS ") が収まらない場合があります。 しかし、これは問題ではありません。パラメータ値は、この段階で空白のままにすることができます。 エキスパートコードを編集するときは、後で任意の値に変更できます。 また、エキスパートを起動するときに必要な接続文字列を指定することもできます。

[次へ] をクリックします。 これ以上機能を追加する必要はないので、次の画面のチェックボックスをすべてオフにしておきます。 もう一度 "Next " を押すと、エキスパートに対して生成された初期コードが届きます。

このEAの目的は、データベースへの接続をデモンストレーションすることです。 これを行うには、初期化関数のみを使用します-。 その他の関数 (OnDeinit および OnTick) の下書きは、すぐに削除できます。

その結果、次のものが得られます。

//+------------------------------------------------------------------+ //|                                                   MqlSqlDemo.mq5 | //|                        Copyright 2018, MetaQuotes Software Corp. | //|                                             https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link      "https://www.mql5.com" #property version   "1.00" #property strict //--- input parameters input string   ConnectionString = "Server=localhost\\SQLEXPRESS;Database=master;Integrated Security=True"; //+------------------------------------------------------------------+ //| Expert initialization function                                   | //+------------------------------------------------------------------+ int OnInit()   { //---    //---    return(INIT_SUCCEEDED);   }

注意: 名前付きインスタンスに接続する場合 (この場合は "SQLEXPRESS ")、 "localhostSQLEXPRESS" という文字を2回繰り返す必要があります。 エキスパートテンプレートおよびコードにパラメータを追加するときに必要です。 文字が1回だけ指定されている場合、コンパイラは、文字列でエスケープシーケンス (特殊文字) "S" が指定されているかのように処理し、コンパイル時に認識されなかったことを報告します。

ただし、コンパイルされたロボットをチャートにアタッチする場合、そのパラメータの2つがコードで指定されているにもかかわらず、そのパラメータには "\" 文字しかありません。 文字列内のすべてのエスケープシーケンスがコンパイル時に対応する文字に変換されるために発生します。 シーケンス "\\" は、1つの "\" 文字に変換され、ユーザー (コードを操作する必要はありません) は通常の文字列を参照します。 したがって、コードにない接続文字列を指定し、エキスパートアドバイザを起動する場合は、接続文字列に "\" という文字を1つだけ指定する必要があります。

Server=localhost\SQLEXPRESS;Database=master;Integrated Security=True

ここでは、ドラフトエキスパートに関数を追加してみましょう。 まず、作成した DLL からデータベースを操作するための関数をインポートする必要があります。 関数の前にインポートセクションを追加します。 インポートした関数は、C# コードで宣言されているのとほぼ同じ方法で記述されています。 すべての修飾子と属性を削除する必要があります:

//インポートされた関数の説明。
#import "MqlSqlDemo.dll"

//接続を開くための関数:
int CreateConnection(string sConnStr);
//最後のメッセージを読むための関数:
string GetLastMessage();
//SQL コマンドを実行するための関数:
int ExecuteSql(string sSql);
//整数を読み取るための関数:
int ReadInt(string sSql);
//文字列を読み取るための関数:
string ReadString(string sSql);
//接続を閉じるための関数:
void CloseConnection();

//インポートの終了:
#import

コードをより明確にするために、関数の実行結果の定数を宣言します。 DLL と同様に、正常に実行された場合は0、エラーの場合は1になります。

//関数の正常な実行:
#define iResSuccess  0
//関数の実行中にエラーが発生しました:
#define iResError 1

データベースを操作する関数の呼び出しを、初期化関数に追加できます。 ここではこのようになります:

int OnInit()
  {
   //接続をしてみてください:
   if (CreateConnection(ConnectionString) != iResSuccess)
   {
      //接続を確立できませんでした。
      //メッセージをPrintして終了します。
      Print("Error when opening connection. ", GetLastMessage());
      return(INIT_FAILED);
   }
   Print("Connected to database.");
   //接続は正常に確立されました。
   //クエリを実行します。
   //テーブルを作成し、データを書き込む:
   if (ExecuteSql(
      "create table DemoTest(DemoInt int, DemoString nvarchar(10));")
      == iResSuccess)
      Print("Created table in database.");
   else
      Print("Failed to create table. ", GetLastMessage());
   if (ExecuteSql(
      "insert into DemoTest(DemoInt, DemoString) values(1, N'Test');")
      == iResSuccess)
      Print("Data written to table.");
   else
      Print("Failed to write data to table. ", GetLastMessage());
   //データの読み取りに進みます。 データベースから整数を読み取ります。
   int iTestInt = ReadInt("select top 1 DemoInt from DemoTest;");
   string sMessage = GetLastMessage();
   if (StringLen(sMessage) == 0)
      Print("Number read from database: ", iTestInt);
   else //番号を読み取れませんでした。
      Print( "データベースから番号を読み取れませんでした。 ", GetLastMessage());
   //文字列を読む:
   string sTestString = ReadString("select top 1 DemoString from DemoTest;");
   sMessage = GetLastMessage();
   if (StringLen(sMessage) == 0)
      Print("String read from database: ", sTestString);
   else //文字列の読み取りに失敗しました。
      Print( "データベースから文字列を読み取れませんでした。 ", GetLastMessage());
   //テーブルは不要になりました-削除することができます。
   if (ExecuteSql("drop table DemoTest;") != iResSuccess)
      Print( "テーブルの削除に失敗しました。 ", GetLastMessage());
   //接続を完了しました:
   CloseConnection();
   //完全な初期化:
   return(INIT_SUCCEEDED);
  }

エキスパートをコンパイルします。 テストのEAの準備が整いました。 これで実行することができます。 エキスパートを実行する前に、使用するメタトレーダープロファイルの [ライブラリ] フォルダに DLL を追加する必要があります。 メタトレーダーを起動し、 "ファイル " メニューで、[データフォルダを開く] を選択します。 "MQL5 " フォルダ (MetaTrader4、 "MQL4 " フォルダ)、 "ライブラリ " フォルダを開きます。 作成した dll ファイル (MqlSqlDemo.dll) をこのフォルダに配置します。 EAはすでにコンパイルされ、この時点で使用する準備が整いました。 当然のことながら、EAを実行し、DLL から関数をインポートするには、MetaTrader5 の設定で許可する必要があります。

EAを起動し、接続文字列の値をデータベース・サーバのアクセス・パラメータに変更します。 すべてが正しく行われている場合、エキスパートは次をログに出力します。

2018.07.10 20:36:21.428    MqlSqlDemo (EURUSD,H1)    Connected to database.
2018.07.10 20:36:22.187    MqlSqlDemo (EURUSD,H1)    Created table in database.
2018.07.10 20:36:22.427    MqlSqlDemo (EURUSD,H1)    Data written to table.
2018.07.10 20:36:22.569    MqlSqlDemo (EURUSD,H1)    Number read from database: 1
2018.07.10 20:36:22.586    MqlSqlDemo (EURUSD,H1)    String read from database: Test

データベースへの接続、SQL コマンドの実行、データの書き込みと読み取り-すべてが正常に実行されました。

結論

Visual Studio の完全なソリューション-必要なすべてのファイルを含むアーカイブは、名前 "MqlSqlDemo.zip" に添付されています。 "UnmanagedExports " パッケージは既にインストールされています。 MQL4 のテストエキスパートMqlSqlDemo.mq5とその亜種は、 "MQL" サブフォルダにあります。

この記事で説明した方法は完全に動作します。 上記の原則に基づいて、アプリケーションは何千ものEAの処理を同時に行うことができます。 すべてが繰り返しテストされており、現在までに動作しています。

この記事で作成した DLL ファイルとエキスパートは両方とも、教育および評価の目的でのみ使用しています。 もちろん、実際のプロジェクトでも、DLL を使用することができます。 しかし、すぐにアンフィッティング状態になる可能性があります。 EAにデータベースを使用する関数を追加する場合は、もっとさまざまな機能が必要になります。 その場合は、この記事を例として使用して、コードを自分で追加する必要があります。 何か問題がある場合は、記事へコメントを書いてください。Skypeで私に連絡しても (cansee378) または私のサイトの連絡先でも結構です。: http://life-warrior.org/contact

C# コードをする時間やモチベーションがない場合は、完成したプロジェクトをダウンロードできます。 HerdOfRobots という名前の無料のオープンソースプログラムは、この記事に記載されているのと同じ原則を使用して実装されます。 インストールパックには、MetaTrader5とMetaTrader4の両方にインポートされた関数を持つ完全なファイルが含まれています。 このライブラリには、強力な関数があります。 たとえば、1つのターミナルで最大63のエキスパート (異なるデータベースに接続可能) を実行し、テーブルからデータを行ごとに読み取り、日付/時刻値をデータベースに書き込むことができます。

HerdOfRobots プログラムは、データベースに接続するエキスパートを制御するための便利な関数と、データ分析を提供します。 このパッケージには、詳細にタスクのすべての側面を説明するマニュアルがあります。 アーカイブには、プログラムのインストーラ-SetupHerdOfRobots.zip-も記事に添付されています。 MqlToSql64 プロジェクト (MetaTrader4 用 MqlToSql) のデータベースへの接続に使用するプログラムのコードを確認したい場合は、後でプロジェクトに記載された高度な関数を使用するために、リポジトリからコードを自由にダウンロードすることができます。:

https://bitbucket.org/CanSeeThePain/herdofrobots

https://bitbucket.org/CanSeeThePain/mqltosql64

https://bitbucket.org/CanSeeThePain/mqltosql

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2895

添付されたファイル |
MqlSqlDemo.zip (565.98 KB)
SetupHerdOfRobots.zip (6741.3 KB)
デルタインジケータの例によるボリュームコントロールを特徴とする株式インジケータの開発 デルタインジケータの例によるボリュームコントロールを特徴とする株式インジケータの開発
この記事では、CopyTicks() および CopyTicksRange() 関数を使用して、実際のボリュームに基づいた株価インジケータを開発するアルゴリズムを扱います。 このようなインジケータの開発については、リアルタイムでの操作とストラテジーテスターにおける細かい側面も説明されています。
同時に2方向で機能するためのユニバーサル RSI インジケータ 同時に2方向で機能するためのユニバーサル RSI インジケータ
トレーディングアルゴリズムを開発するとき、しばしばある問題に遭遇します。その一つが、トレンド/レンジの始まりと終点を決定する方法です。 この記事では、さまざまな種類のシグナルを結合するユニバーサルインジケータを作成します。 今回はEAのトレードシグナルを取得するプロセスをできるだけ簡素化します。 1つのインジケータを組み合わせた例を挙げます。
データ配列間の相関を解析するためのCGraphicに基づくPairPlot グラフ (時系列) データ配列間の相関を解析するためのCGraphicに基づくPairPlot グラフ (時系列)
テクニカル分析に複数の時系列を比較することは、適切なツールを必要としますが一般的なタスクです。 この記事では、グラフィカル解析のツールを開発し、2つ以上の時系列間の相関関係を検出します。
トレード履歴のカスタム表示とレポート図の作成 トレード履歴のカスタム表示とレポート図の作成
この記事では、トレード履歴を評価するためのカスタム・メソッドについて説明します。 2つのクラスが、ヒストリーを分析するために書かれ、ダウンロード可能です。 最初のトレード履歴を収集し、要約表として表します。 2番目は、統計情報を扱います。: 変数を計算し、トレード結果のより効率的な評価チャートを構築します。