English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
preview
一からの取引エキスパートアドバイザーの開発(第30部):指標としてのCHART TRADE?

一からの取引エキスパートアドバイザーの開発(第30部):指標としてのCHART TRADE?

MetaTrader 5 | 9 12月 2022, 10:16
340 0
Daniel Jose
Daniel Jose

はじめに

前の「一からの取引エキスパートアドバイザーの開発(第29部)」稿では、EAからChart Tradeを削除しました。以前、EAのパフォーマンスと信頼性を向上させるために、Volume At PriceやTimes & Tradeなどを考察した他の場合にも同じことをおこないました。EAからChart Tradeを削除することにより、基本的な発注システムのみが残ります。一部のユーザーにはこれでは不十分に見えるかもしれませんが、EAは実際にすべての作業をおこなうことができます。ただし、市場で取引を開始および終了するのは好きであるが、それらを未決状態にしたり、取引を開始または終了するために価格が特定のレベルに達するのを待つのは好きでない人がいます。

取引している資産でMetaTrader 5プラットフォームを使用すると(本連載の第11部で説明した両建て注文システムを使用できるため、これについて言及します)、成行注文を出すボタンを使用してQUICK TRADINGにアクセスできます。それらは左上隅にあります。次のように表示されます。

これらのボタンは基本的なChart Tradeと同じように機能しますが、両建て注文システムではまったく表示されないため使用できません。この場合、Chart Tradeに戻る必要があります。ただし、EA内では使用されず、EAコードの一部にはなりません。これからは、Chart Tradeは単なる指標になります。

なぜChart Tradeでそうする必要があるのでしょうか。その理由は、EAは取引システムのみを担当するべきで、このシステムの一部ではないものはすべてEAコードから何らかの方法で削除する必要があるためです。これは今は無意味に思えるかもしれませんが、この記事の続きでこの現象の正確な理由の説明をすでに準備しているので、すぐに明らかになるでしょう.。

Chart Tradeをスクリプトとして使用することもできますが、これには、チャートの時間枠を変更するたびに、スクリプトが閉じてしまうため、手動で再度実行する必要があるという欠点があります。

指標として使用する場合は違います。その理由の1つは、Chart Tradeが指標の実行スレッドに影響を与えないため、EAが自由になり、そのコードと注文とポジションの管理に関係するものだけに集中するということです。

元のバージョンのすべてのコントロールとすべての情報が含まれているわけではありませんが、指標としてのこのChart Tradeははるかにシンプルであり、引き続き機能します。MetaTrader 5システムは高機能でありながらシンプルです。ただし、Chart Tradeにある特定の情報へのアクセスは提供しません。

それでは、始めましょう。このトピックは非常に興味深いものになるでしょう。


2.0.Chart Trade指標の開発

この指標を作成するには、かなり多くの変更を加える必要があります。Chart TradeをEAコードの一部にすることなくチャートに戻すために、これらの変更を実装します。

一見すると、Chart Tradeを少なくともコンパイルするには、以下に示すコードで十分であるように見えるかもしれませんが、違います。これは、オフにしなければならないいくつかのものに本質的に関連しています。いつかEA内に戻したいと思うかもしれないので、完全には削除しません。

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\SubWindow\C_TemplateChart.mqh>
//+------------------------------------------------------------------+
C_TemplateChart Chart;
C_Terminal                      Terminal;
//+------------------------------------------------------------------+
int OnInit()
{
        Chart.AddThese("IDE(,,170, 240)");

        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+

このコードをコンパイルしようとすると多くのエラーが発生しますが、指標として使用できるように適応させながら、コードをEAと互換性を保つために1つずつ修正します。

まずおこなう必要があるのは、サブウィンドウを作成または管理するシステムを分離することです。Chart Tradeはこれらのサブウィンドウを使用しないため、このコードを指標に埋め込む必要はありません。やり方はとても簡単です。C_Chart_IDE.mqhを次のように編集します。

#ifdef def_INTEGRATION_CHART_TRADER
        #include <NanoEA-SIMD\SubWindow\C_SubWindow.mqh>
        #include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh>
#else
        #include <NanoEA-SIMD\SubWindow\C_ChartFloating.mqh>
        #include <NanoEA-SIMD\Auxiliar\C_Terminal.mqh>
#endif  
//+------------------------------------------------------------------+
#ifdef def_INTEGRATION_CHART_TRADER
        class C_Chart_IDE : public C_SubWindow
#else 
        class C_Chart_IDE : public C_ChartFloating
#endif 

これで、システムをサブウィンドウから完全に分離し、Chart TradeとEAの注文表示システムの間の接続を回避します。これはdef_INTEGRATION_CHART_TRADER定義によって制御されますが、この定義はEAでのみ使用されるため、その中のものは指標にコンパイルされません。

指標はサブウィンドウを使用しないため、いくつかのことを回避する必要があります。それらの1つを以下に示します。

                bool Create(bool bFloat)
                        {
                                m_CountObject = 0;
                                if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
                                FileReadInteger(m_fp, SHORT_VALUE);
                                
                                for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
                                m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
                                m_szLine = "";
                                while (m_szLine != "</chart>")

// ... Rest of the code...

GetIdSubWinEA関数は、指標が配置されているウィンドウの番号を返します。この呼び出しは、複数の異なる時点で実行されます。この問題には2つの解決策があります。最初の解決策は、呼び出しが発生する各時点で上記の関数を次のように変更することです。

                bool Create(bool bFloat)
                        {
                                m_CountObject = 0;
                                if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
                                FileReadInteger(m_fp, SHORT_VALUE);
                                
                                for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
#ifdef def_INTEGRATION_CHART_TRADER
                                m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
#else 
                                m_SubWindow = 0;
#endif 

// ... The rest of the code...

これで問題は解決しますが、非常に多くの変更をおこなう必要があり、しばらくすると、これらの条件付きコンパイルディレクティブが多すぎるため、コードが理解しにくくなります。はるかに単純ですが、同様に効果的な解決策があります。定義を通じてこの呼び出しと他の呼び出しを「エミュレート」するために、次のスニペットをシステムコードに追加するだけです。

#ifndef def_INTEGRATION_CHART_TRADER
        #define GetIdSubWinEA() 0
        #define ExistSubWin() false
#endif 

このコードで呼び出しの問題が解決します。指標をコンパイル可能にするには、これで十分です(さらにいくつかの詳細が必要です)。

2番目に大きな問題は、マウス自体ではなく、C_Mouseクラスに関連するマウスイベントに関連する関数です。

理解しておくべきことは、Chart TradeがEAの一部であったとき、マウスの位置にアクセスすることは(とりわけ)可能でしたが、単にC_Mouseクラスを指標に追加すると、指標とEAの間で利益相反が発生するということです。競合がどのように解決されるかは、ここでは示しません。別の方向に進みますが、すべてが解決され、元のChart Tradeの機能が少なくともいくつか得られるまでの一時的なものです。

この目的のために、いくつかのものをC_Mouseクラスから新しい指標に移動する必要があります。小さなことなので心配しないでください。最初の変更はC_TemplateChartクラスで発生します。そのDispatchMessage関数で次の変更をおこないます。

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        datetime dt;
        double p;

        C_Chart_IDE::DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
#ifdef def_INTEGRATION_CHART_TRADER
                        Mouse.GetPositionDP(dt, p);
#else
                        {
                                int w;
                                ChartXYToTimePrice(Terminal.Get_ID(), (int)lparam, (int)dparam, w, dt, p);
                        }
#endif 
                        for (int c0 = 0; c0 < m_Counter; c0++)  if (m_Info[c0].szVLine != "")

// ... Rest of the code...

強調表示されたセクションを追加することで、C_Mouseクラスがまだ存在しているかのように機能するようになります。次の変更はC_ChartFloatingクラスで、以前と同様のものを作成します。ただし、コードは少し異なります。

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        int mx, my;
        datetime dt;
        double p;
        static int six = -1, siy = -1, sic = -1;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
#ifdef def_INTEGRATION_CHART_TRADER
                        Mouse.GetPositionXY(mx, my);
                        if ((Mouse.GetButtonStatus() & 0x01) == 1)
#else 
                        mx = (int)lparam;
                        my = (int)dparam;
                        if (((uint)sparam & 0x01) == 1)
#endif 
                        {
                                if (sic == -1)  for (int c0 = m_MaxCounter - 1; (sic < 0) && (c0 >= 0); c0--)

// ... Rest of the code...

これで指標をコンパイルして、以下のビデオに示す結果を得ることができます。



EAはまだ完全には機能していませんが、Chart TradeにEAと同じ機能を持たせるか、以前の機能と現在MetaTrader 5で提供されている機能の中間的な何かに縮小するかの.決定を下す必要があります。私の立場は非常に急進的であるため、EAにあった機能とほぼ同じ機能を備えたChart Tradeにします。お望みならば、ご自分で縮小してください。

この決定を下した後、Chart Tradeが指標になったので、次のトピックに進むことができます。


2.1.Chart Trade指標を機能させる方法

物事はより困難になります。注文を送信し、ポジションを決済し、取引の結果を報告できるように、この指標を機能させる方法を見てみましょう。実際、MetaTrader 5プラットフォームは従うべき道を提供するため、これは最小限の労力で実行できて、一見したほど難しくはありません。

ここでは、MetaTrader 5のいくつかの機能を使用して内部クライアントサーバーシステム(プラットフォーム内)を作成し、異なるプロセス間でデータを転送する第16部と同じことをおこないます。ここでも同様のことをおこないますが、双方向通信が必要であり、ユーザーには見えないようにする必要があるため、シミュレーションのみがわずかに異なります。

これから使用する手法は、可能な唯一なものではありません。DLLを使用してこの転送を有効にするなど、他の手法もあります。ただし、必要に応じてナビゲート、維持、変更するのが最も簡単であるため、MetaTrader 5変数を使用します。

この道を進むことにしたので、2番目の重要な決定があります。それは、誰がサーバーになり、誰がクライアントになるかということです。この決定は、システムが実際にどのように実装されるかに影響します。いずれにせよ、メッセージングプロトコルは既に用意されており、その実装を以下に示します。

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableLeverage      (_Symbol + "_Leverage")
#define def_GlobalVariableTake          (_Symbol + "_Take")
#define def_GlobalVariableStop          (_Symbol + "_Stop")
#define def_GlobalVariableResult        (_Symbol + "_Result")
#define def_GlobalVariableButton        (_Symbol + "_ButtonState")
//+------------------------------------------------------------------+
#define def_ButtonDTSelect              0x01
#define def_ButtonSWSelect              0x02
#define def_ButtonBuyMarket             0x04
#define def_ButtonSellMarket            0x08
#define def_ButtonClosePosition         0x10
//+------------------------------------------------------------------+

これですべてです。メッセージプロトコルは既に定義されているため、実際には、誰がクライアントになり、誰がサーバーになるかを決定する必要はありません。このプロトコルは、他のすべての開発を容易にするため、最初から定義する必要があります。

Chart Trade EAセットが存在する各資産に対して5つの変数を使用することに注意してください。変数名は、セットがリンクされている資産に依存するため、セットを同時に複数の資産に使用できます。

ここで、サーバーの作業をおこなうのは誰かという重要な問題が発生します。そのような変数の作成を担当します。個人的には、EAをサーバーとして使用し、Chart Tradeをクライアントとして使用する方が実用的だと思います。アイデアは、必要に応じて特定の時間にChart Tradeがそこにある間、常にチャートにEAを表示することです。したがって、EAは5つの変数のうち4つを作成する責任があります。これは、そのうちの1つがどのボタンが押されたかを通知する責任があるためです。したがって、これはChart Tradeの責任です。

これらすべてに基づいて、データフローは次のようになります。

  1. EAは、レバレッジ、テイクプロフィット、ストップロスを示す初期値を含むグローバル変数を作成します。最終的には、その日の結果を通知する変数も作成するため、Chart Tradeはそれをユーザーに表示でき、この情報を他の場所で取得する必要はありません。
  2. Chart Tradeは、ボタンの押下値を表す変数を作成します。この変数は、ポジションを決済する、市場での売買を実行するなど、何をすべきかをEAに伝えます。この変数はこの期間中にのみ存在し、EAによってリクエストが完了するとすぐに存在しなくなります。

フローは単純ですが、システムが注文を送信してポジションを決済する機能を含め、Chart TradeがEAとやり取りできることが保証されます。

EAが存在する場合にのみChart Tradeがチャート上に存在するようにするには、いくつかの確認を追加する必要があります。EAが注文を送信できない場合、チャート上にChart Tradeがあっても意味がありません。これらの確認には、次の2つの瞬間が含まれます。

  • 1つ目:指標が初期化されるとき
  •  2つ目:チャートでイベントが発生したとき

これをコードで見てみましょう。そうすれば、プロセスが理解しやすくなります。初期化中に、次のコードが実行されます。

#define def_SHORTNAME "CHART TRADE"
//+------------------------------------------------------------------+
int OnInit()
{
        long lparam = 0;
        double dparam = 0.0;
        string sparam = "";
        
        IndicatorSetString(INDICATOR_SHORTNAME, def_SHORTNAME);
        if(!GlobalVariableGet(def_GlobalVariableLeverage, dparam)) return INIT_FAILED;
        Terminal.Init();
        Chart.AddThese("IDE(,,170, 215)");
        Chart.InitilizeChartTrade(dparam * Terminal.GetVolumeMinimal(), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), true);
        OnChartEvent(CHARTEVENT_OBJECT_ENDEDIT, lparam, dparam, sparam);

        return INIT_SUCCEEDED;
}

強調表示された行は、グローバル変数の1つを評価します。この場合は、レバレッジレベルを示すものです。この変数がない場合、初期化は失敗します。Chart Tradeではなく、EAがこの変数を作成する責任があることを思い出してください。このようにして、指標はEAがチャート上に存在するかどうかを認識し、EAが作業を完了すると、MetaTrader 5からこの変数を削除します。Chart Tradeも強制的に削除されます。これは2番目の時点でおこなわれ、同じ条件(グローバルレバレッジ変数が利用可能かどうか)を確認します。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED);
        Chart.DispatchMessage(id, lparam, dparam, sparam);
}

上記の時点は、比較的高い頻度で実装されます。指標にOnTimeイベントシステムを配置することは魅力的ですが、これはお勧めできません。すべての指標が同じオペレーティングスレッドを使用し、OnTimeイベントを1つの指標に配置する場合、細心の注意を払っておこなわない限り、他のすべてに影響します。

上記の確認がプラットフォームの一般的なパフォーマンスを妨げると思われる場合は、OnTimeイベント内に配置できますが、その結果に注意してください。

誰がチャートからのChart Trade指標の削除をトリガーしたかに関係なく、それは同じ時点で発生します。これは以下で確認できます。

void OnDeinit(const int reason) 
{ 
        if (reason == REASON_INITFAILED)
        {
                Print("Unable to use Chart Trade. The EA is not on the chart of this asset...");
                if (!ChartIndicatorDelete(0, 0, def_SHORTNAME))
                        Print("Unable to delete Chart Trade from the chart.");
        }
}

選択した線はチャートから指標を削除します。失敗した場合は、関連するメッセージが表示されます。これらのメッセージは、次のようにツールボックスに表示されます。

このように、このウィンドウに表示される情報には常に注意する必要があります。

C_Chart_IDEクラスでおこなわれた変更の詳細な分析に進む前に、最後の関数が1つ不足しています。この関数は次のとおりです。

int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        double value;
        
        if (GlobalVariableGet(def_GlobalVariableResult, value))
        {
                GlobalVariableDel(def_GlobalVariableResult);
                Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, value, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eROOF_DIARY]);
        }
   
        return rates_total;
}

この関数がおこなうことは、グローバル変数を監視することです。したがって、時々、ポジションを決済するとき、EAはグローバル変数を作成し、その名前はdef_GlobalVariableResultで定義されます。この変数が作成され、その名前がChart Tradeによって観測されたものと一致すると、この変数の値が取得され、変数はすぐに削除されます。これは、処理が完了する前にEAが更新を送信してこの更新が失われるという状況を回避するためにおこなわれます。ただし、削除前に値を取得することで、メッセージを処理するChart Tradeクラスに送信できるため、EAによって渡された値ができるだけ早くChart Tradeに表示されます。

これで、Chart Trade指標を機能させる最初の部分が完了しました。2番目の部分はボタンに関するものです。それらも機能するようにする必要があります。これは、C_Chart_IDE関数の関数処理メッセージで簡単におこなうことができます。

// ... Previous code ...

case CHARTEVENT_OBJECT_CLICK:
        if (StringSubstr(sparam, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG)
	{
		Resize(-1);
		return;
	}
	sparam = StringSubstr(sparam, 9, StringLen(sparam));
	StringToUpper(sparam);
#ifdef def_INTEGRATION_CHART_TRADER
	if ((sparam == szMsgIDE[eBTN_SELL]) || (sparam == szMsgIDE[eBTN_BUY]))
		TradeView.ExecuteOrderInMarket(m_BaseFinance.Leverange, m_BaseFinance.FinanceTake, m_BaseFinance.FinanceStop, sparam == szMsgIDE[eBTN_BUY], m_BaseFinance.IsDayTrade);
	if (sparam == szMsgIDE[eBTN_CANCEL])
	{
		TradeView.CloseAllsPosition();
		ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
	}
#else 
	{
		union u00
		{
			double Value;
			ulong c;
		}u_local;
                                                        
		u_local.c = 0;
		if (sparam == szMsgIDE[eBTN_BUY]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonBuyMarket; else
		if (sparam == szMsgIDE[eBTN_SELL]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonSellMarket; else
		if (sparam == szMsgIDE[eBTN_CANCEL])
		{
			u_local.c = def_ButtonClosePosition;
			ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
		}
                if (u_local.Value > 0) GlobalVariableSet(def_GlobalVariableButton, u_local.Value);
	}
#endif 
	if (sparam == szMsgIDE[eCHECK_DAYTRADE]) InitilizeChartTrade(0, 0, 0, m_BaseFinance.IsDayTrade ? false : true);
	break;

//... Rest of the code...

フラグメントには2つのコードが含まれていることに注意してください。青のコードはChart TradeがEAに組み込まれている場合に使用されます。緑のものは、Chart Tradeが指標として存在する場合に使用されます。

興味があるのは緑のコードです。グローバル変数を作成し、ボタンの状態を設定します。したがって、トレーダーがポジションを決済すると、[close]ボタンに対応する値が変数に配置されます。しかし、成行注文を発注した場合、この値は異なり、他の2つの値の組み合わせになります。1つは注文が買いか売りかを示し、もう1つはデイトレードまたはそれ以上の注文をおこなうかどうかを示します。Chart TradeがEAに伝えるのはこれだけです。

重要:このシステムは自己排他的です。つまり、EAが何かをおこなう前に[Sell]ボタンをクリックしてから[Buy]ボタンをクリックすると、新しい買い値によって売りを示す値が失われるため、EAは実際に買います。さらに、すでにポジションがある状態で売りまたは買いのいずれかをリクエストし、EAが関連する取引をおこなう前にキャンセルを押すと、ポジションは決済されます。

これで、EAコードに移り、新しいモデルでどのように機能するかを確認できます。


2.2.Chart Tradeからメッセージを受信するようにEAコードを変更する

この部分は、いくつかの細かい部分を調整するだけなので、非常に簡単です。ただし、次の点に注意してください。EAとChart Tradeを読み込んだ後、EAに含まれるグローバル変数またはデータを変更しないでください。これまたはChart Trade自体に発注システムを使用しないと、問題が発生する可能性があります。解決策がある問題もあれば、ない問題もあります。念のため、利用可能なツールを使用する場合、物事を複雑にしようとしないでください。 

前回の第29回稿では、Chart Tradeの廃止を推進する中で、いくつかの変更を加えましたが、その一部を元に戻します。この質問に関して、他には何も変更する必要はありませんが、前述のように、修正可能なものとそうでないものがあるため、この記事の次のトピックでは、Chart Tradeとエキスパートアドバイザー(EA)の関係におけるいくつかの小さな問題を解消します。

最初に、EAとChart Tradeの間である程度の通信ができるように、EAで何を元に戻し、アクティブ化する必要があるかを見てみましょう。

まず、以下を変更しましょう。

input int       user20      = 1;        //Leverage
input double    user21      = 100;      //Take Profit
input double    user22      = 81.74;    //Stop Loss 
input bool      EA_user23   = true;     //Day Trade ?

EAがロングとショートのどちらの取引を開始することを好むかを示す値は変更されません。これは、Chart Tradeまたはチャートに配置する指値注文でおこなう必要があります。以前の記事でこれをおこなう方法を既に示したので、コーディング作業に取り掛かりましょう。OnInitイベントに次の変更を追加しましょう。

int OnInit()
{
        if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        {
                Sound.PlayAlert(C_Sounds::TRADE_ALLOWED);
                return INIT_FAILED;
        }
        
        Terminal.Init();

#ifdef def_INTEGRATION_TAPE_READING
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        EventSetTimer(1);
#endif 

        Mouse.Init(user50, user51, user52);
        
#ifdef def_INTEGRATION_CHART_TRADER
        static string   memSzUser01 = "";
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(EA_user20 * Terminal.GetVolumeMinimal(), EA_user21, EA_user22, EA_user23);
        TradeView.Initilize();
   OnTrade();
#else
        GlobalVariableTemp(def_GlobalVariableLeverage);
        GlobalVariableTemp(def_GlobalVariableTake);
        GlobalVariableTemp(def_GlobalVariableStop);
        GlobalVariableTemp(def_GlobalVariableResult);
        GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal());
        GlobalVariableSet(def_GlobalVariableTake, user21);
        GlobalVariableSet(def_GlobalVariableStop, user22);
        TradeView.Initilize();
        GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof());
#endif 
   
        return INIT_SUCCEEDED;
}

ご覧のとおり、通信に使用されるグローバル変数をここに追加します。すでに述べたように、EAは常にChart Tradeの前に開始する必要があります。そうしないと、指標を初期化できません。Chart Tradeで最初に使用される値は、EAで指定されていることに注意してください。以前の取引があった場合でも、初期化は完了します。蓄積された値もChart Tradeに再度転送されます。

作成された変数は一時的なタイプだという重要な詳細に注意してください。これは、これらの変数が特定の時間後に使用されなくなるため、EAデータダンプの場合にこれらの変数を保存したくないためです。何かが起こってプラットフォームがシャットダウンしても、これらの変数は存在しなくなっています。

実装される別の追加機能を以下に示します。

void OnDeinit(const int reason)
{
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        GlobalVariableDel(def_GlobalVariableLeverage);
        GlobalVariableDel(def_GlobalVariableTake);
        GlobalVariableDel(def_GlobalVariableStop);
        GlobalVariableDel(def_GlobalVariableResult);
        GlobalVariableDel(def_GlobalVariableButton);
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

変数は一時的なものですが、EAにそれらを強制的に削除するよう依頼します。これにより、Chart Tradeがチャートに残らなくなります。このOnDeinitイベントには小さな問題がありますが、次のトピックで取り上げます。次に、かなり興味深い別のポイントを見てみましょう。これは2つの異なる方法でおこなうことができますが、結果はほぼ同じになります。違いを生む可能性のある小さな違いがあるため、ほぼ同じと言います(しゃれでごめんなさい)。

Chart Tradeにはいくつかのボタンがあり、グローバル変数を通じてEAにメッセージを送信します。これらのクリックに適切に応答するEA内の関数は次のとおりです。

inline void ChartTrade_ClickButton(void)
{
        union u00
        {
                double Value;
                ulong c;
        }u_local;
        
        if (GlobalVariableGet(def_GlobalVariableButton, u_local.Value))
        {
                GlobalVariableDel(def_GlobalVariableButton);
                                
                if (u_local.c == def_ButtonClosePosition) TradeView.CloseAllsPosition(); else
                        TradeView.ExecuteOrderInMarket(GlobalVariableGet(def_GlobalVariableLeverage), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), ((u_local.c & def_ButtonBuyMarket) == def_ButtonBuyMarket), ((u_local.c & def_ButtonDTSelect) == def_ButtonDTSelect));
                TradeView.Initilize();
        }
}

関数はinlineとして宣言されます。つまり、宣言された位置にコンパイラによって配置される必要があります。これにより、可能な限り迅速に実行できます。

非常に重要な詳細:この関数をどこに配置するかを考えてみましょう。ここに確認があるので、常に実行されるわけではありません。したがって、2つの可能性があります。1つ目は関数をOnTickイベント内に配置する方法で、2つ目はOnTimeイベント内に配置する方法です。選択は特定のロジックに基づいている必要があります。そうしないと、問題が発生する可能性があります。

考えてみましょう。この関数をOnTickイベントに入れると、取引サーバーからプラットフォームに到着するすべての新しいティックで実行されます。関数は何度も実行されるのではなく、特定の時間にのみ実行されるため、これはいい解決策のようです。しかし、これは問題を引き起こします。取引している資産のボラティリティが非常に低い場合、OnTickイベントの頻度も非常に低くなります。これは、チャート取引のクリックによるイベントのトリガーに問題がある可能性があることを意味します。

これが2番目のオプションです。関数をOnTimeに配置すると、OnTimeイベントが特定の規則性でトリガーされるため、関数が確実に実行されます。ただし、これをおこなうと、グローバル変数を監視する以外に、OnTimeイベントを使用できなくなることを覚えておく必要があります。=

この段階では、EAは市場のみを監視するため、非常に良い選択をしました。次のいくつかの記事で、これがますます明らかになることがわかります。次に、OnTimeイベント内に関数を配置することをお勧めします。ただし、OnTimeイベントは毎秒しか発生しないという問題があります。

実際には、ほとんどの場合、1秒に1回起動しますが、EventSetMillisecondTimer関数を使用すればより短い期間を設定できます。したがって、1秒未満でイベントを発生させることができます。ほとんどの場合、500ミリ秒で十分だと思うので、EAのOnInitイベントに次の行を追加します。

#else
        GlobalVariableTemp(def_GlobalVariableLeverage);
        GlobalVariableTemp(def_GlobalVariableTake);
        GlobalVariableTemp(def_GlobalVariableStop);
        GlobalVariableTemp(def_GlobalVariableResult);
        GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal());
        GlobalVariableSet(def_GlobalVariableTake, user21);
        GlobalVariableSet(def_GlobalVariableStop, user22);
        TradeView.Initilize();
        GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof());
        EventSetMillisecondTimer(500);
#endif 

OnDeinit関数でこのイベントを閉じることを忘れないでください。これには、イベントに次の行を追加するだけです。

void OnDeinit(const int reason)
{
        EventKillTimer();

次に、OnTimeイベントがどのように見えるかを見てみましょう。これは非常に単純なコードで、強調表示された行だけが実際にコンパイルされます。

void OnTimer()
{
#ifndef def_INTEGRATION_CHART_TRADER
        ChartTrade_ClickButton();
#endif

#ifdef def_INTEGRATION_TAPE_READING
        VolumeAtPrice.Update();
        TimesAndTrade.Connect();
#endif 
}

それだけですか。いいえ、別の小さな問題があります。EAの初期化変数を変更し、新しい未決注文がチャートに配置されるたびにChart Tradeからデータを取得するために、C_IndicatorTradeViewクラスコードに小さな詳細を1つ追加する必要があります。以下に示します。

        case CHARTEVENT_MOUSE_MOVE:
                Mouse.GetPositionDP(dt, price);
                mKeys   = Mouse.GetButtonStatus();
                bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse button click
                bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT pressed
                bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
                if (bKeyBuy != bKeySell)
                {
                        if (!bMounting)
                        {
#ifdef def_INTEGRATION_CHART_TRADER
                                m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
#else 
                                m_Selection.vol = GlobalVariableGet(def_GlobalVariableLeverage) * Terminal.GetVolumeMinimal();
                                valueTp = GlobalVariableGet(def_GlobalVariableTake);
                                valueSl = GlobalVariableGet(def_GlobalVariableStop);
                                m_Selection.bIsDayTrade = EA_user23;
#endif 

強調表示されたコードは、グローバル変数にある値を取得するようになったため、Chart Tradeにあるものはすべて発注システムに入れられます。唯一の詳細は、未決注文はすべてEAによって指定された時間に従うということですが、これはチャート上で直接変更できます。詳細については、一からの取引エキスパートアドバイザーの開発(第27部)を参照してください。ここでは、Chart Tradeを経由せずに、未決注文をチャート上で直接変更する方法を示しました。

サウンドシステムにも小さな変更があります。C_Routerクラスにサウンドが追加され、Chart Tradeを介して作業している場合に、ポジションの開閉に関する通知が確実におこなわれます。サウンドシステムの何かを削除または変更したい場合は、考慮すべき点が他にもあることを覚えておく必要があります。

終わりにする前に、OnDeinitイベントには修正するべき問題があることを思い出してください。それでは、次のトピックに移り、これを修正しましょう。


2.3.Chart Tradeが時期尚早にチャートを終了するのを防ぐ

チャートの時間枠やEAパラメータ(何でも構いません)の変更など、何かをおこなうと、MetaTrader 5はOnDeinitイベントを生成します。このイベントのトリガーは、すべてが正常に機能し続けることを確認するために、物事を再分析する必要があることを意味します。

ほとんどの場合、このイベントをトリガーしても問題は発生しません。しかし、クライアントサーバーシステムを作成し、グローバル変数を使用してサーバー(EA)の実行が停止したかどうかを確認するため、特定の状況を回避する方法を理解する必要があります。

元のOnDeinitイベントを処理する関数は次のようになります。

void OnDeinit(const int reason)
{
        EventKillTimer();
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        GlobalVariableDel(def_GlobalVariableLeverage);
        GlobalVariableDel(def_GlobalVariableTake);
        GlobalVariableDel(def_GlobalVariableStop);
        GlobalVariableDel(def_GlobalVariableResult);
        GlobalVariableDel(def_GlobalVariableButton);
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

強調表示された行はグローバル変数を削除します。これは、EAがチャート上にあるかどうかを確認するためにChart Trade指標で使用するものとまったく同じです。この確認は、次のコードで実行されます。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED);
        Chart.DispatchMessage(id, lparam, dparam, sparam);
}

つまり、OnDeinitイベントをアクティブにする何かがEAで発生するたびに、この変数は削除されます。また、EAが変数を再作成する前に、指標がチャートから既に削除されている可能性があり、これは非常に奇妙なことです。いくつかの時点でそれが起こり、他の時点ではそうではありません。したがって、何らかの方法ですべてが期待どおりに機能することを確認する必要があります。

ドキュメントでこの問題の解決策を見つけました。これは「初期化解除の理由のコード」セクションで確認できます。これらのコードを見ると、EAがチャートから削除されるまで変数が削除されないように、OnDeinitイベント処理関数を構成できます。

したがって、解決策と新しい処理コードは次のようになります。

void OnDeinit(const int reason)
{
        EventKillTimer();
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        switch (reason)
        {
                case REASON_CHARTCHANGE:
                        break;
                default:                
                        GlobalVariableDel(def_GlobalVariableLeverage);
                        GlobalVariableDel(def_GlobalVariableTake);
                        GlobalVariableDel(def_GlobalVariableStop);
                        GlobalVariableDel(def_GlobalVariableResult);
                        GlobalVariableDel(def_GlobalVariableButton);
        };
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

ここで、チャートの時間枠またはプロット方法のみを変更すると、チャートからのChart Trade指標の消失に関連する不都合はなくなります。他の状況では、すべてがうまくいくという本当の保証がないため、チャートから削除される可能性があります.このため、EAとChart Tradeが読み込まれると、EAパラメータを変更する意味がなくなります。


結論

少しの創造性で何ができるかをご覧ください。解決できないと思われる問題を解決しなければならないこともありますが、ドキュメントを読むことで解決策を見つけることができるので、常にドキュメントを確認し、理解してアイデアを実行に移すことが不可欠です。


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

添付されたファイル |
EA_-_p_Parte_30_u.zip (14532.23 KB)
ニューラルネットワークが簡単に(第28部):方策勾配アルゴリズム ニューラルネットワークが簡単に(第28部):方策勾配アルゴリズム
強化学習法の研究を続けます。前回は、Deep Q-Learning手法に触れました。この手法では、特定の状況下でとった行動に応じて、これから得られる報酬を予測するようにモデルを訓練します。そして、方策と期待される報酬に応じた行動がとられます。ただし、Q関数を近似的に求めることは必ずしも可能ではありません。その近似が望ましい結果を生み出さないこともあります。このような場合、効用関数ではなく、行動の直接的な方針(戦略)に対して、近似的な手法が適用されます。その1つが方策勾配です。
一からの取引エキスパートアドバイザーの開発(第29部):おしゃべりプラットフォーム 一からの取引エキスパートアドバイザーの開発(第29部):おしゃべりプラットフォーム
この記事では、MetaTrader 5プラットフォームをしゃべらせる方法を学びます。EAをもっと楽しくしたらどうでしょうか。金融市場の取引は退屈で単調すぎることがよくありますが、私たちはこの仕事の疲れを軽減することができます。依存症などの問題を経験している方にとってはこのプロジェクトは危険な場合があるのでご注意ください。ただし、一般的には、それは退屈を軽減するだけです。
デマーカーによる取引システムの設計方法を学ぶ デマーカーによる取引システムの設計方法を学ぶ
最も人気のあるテクニカル指標によって取引システムを設計する方法についての連載の新しい記事へようこそ。今回は、デマーカー(DeMarker)指標による取引システムの作り方を紹介します。
母集団最適化アルゴリズム 母集団最適化アルゴリズム
最適化アルゴリズム(OA)の分類についての入門記事です。この記事では、OAを比較するためのテストスタンド(関数群)を作成し、広く知られたアルゴリズムの中から最も普遍的なものを特定することを試みています。