English Русский 中文 Español Deutsch Português
preview
リプレイシステムの開発 - 市場シミュレーション(第8回):指標のロック

リプレイシステムの開発 - 市場シミュレーション(第8回):指標のロック

MetaTrader 5 | 21 11月 2023, 09:04
211 0
Daniel Jose
Daniel Jose

はじめに

前回の記事、リプレイシステムの開発 - 市場シミュレーション(第7回):最初の改善点(II)では、いくつかの修正と調整をおこないました。しかし、その記事に添付されたビデオにあるように、まだエラーがありました。

この記事では、このエラーを修正する方法を見ていきます。これは一見簡単なことのように思えますが、いくつかのステップを踏む必要があります。その過程は興味深く、興味深いものになるでしょう。ここでの目標は、指標を特定のチャートと銘柄だけに適用させることです。ユーザーが試みたとしても、指標を別のチャートに適用したり、1セッションで複数回開いたりすることはできません。

とても役に立つ内容なので、ぜひ読み進めてください。


特定の銘柄に指標をロックします。

最初のステップは、市場リプレイに使用する銘柄にコントロール指標をリンクすることです。このステップは、簡単なように見えますが、私たちの主な作業を発展させるために必要なものです。このような場合、指標のコードはどのようになるか見てみましょう。

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <Market Replay\C_Controls.mqh>
//+------------------------------------------------------------------+
C_Controls      Control;
//+------------------------------------------------------------------+
int OnInit()
{
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if (_Symbol != def_SymbolReplay)
        {
                ChartIndicatorDelete(ChartID(), 0, def_ShortName);
                return INIT_FAILED;
        }
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Control.DispatchMessage(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        switch (reason)
        {
                case REASON_REMOVE:
                case REASON_CHARTCLOSE:
                        if (_Symbol != def_SymbolReplay) break;
                        GlobalVariableDel(def_GlobalVariableReplay);
                        ChartClose(ChartID());
                        break;
        }
}
//+------------------------------------------------------------------+


まず、問題の銘柄が市場リプレイに使用されているものかどうかを確認します。そうでない場合、指標は自動的に閉じます。指標名を知っておくことが重要であることに注意してください。初期化中に実行される最初の関数は指標を呼び出します。これにより、複雑にすることなく指標を削除できます。

ここで重要な点が1つあります。チャートから削除すると、MetaTrader 5はDeInitイベントを生成します。このイベントは、チャートからの指標の削除を示すREASON_REMOVEイベントを条件として OnDeInit関数をトリガーします。これは、指標が使用するように設計された銘柄とは異なるからです。再びチェックしてコードが実行されないようにしなければ、銘柄チャートは閉じてしまいます。しかし、私たちのチェックのおかげで、開いたままです。

指標のコードが前回の記事で紹介したコードと異なっていても驚かないでください。前回の記事では、その他の改善や修正に焦点を当てていました。しかし、記事とコードを書き、この記事に付随するビデオを録画した後、私は問題の1つは修正されたものの、もう1つは検出されないまま残っていることに気づきました。よってコードを変えなければならなかったのです。

変更点はありますが、ここではすべての変更点の詳細は省きます。ここで取り上げたロックには効果がないため、かなりの部分を取り除かなければなりませんでした。したがって、上記のコードは以前のものとは大きく異なっています。しかし、前回の記事で紹介した知識は、いずれ誰かの役に立つかもしれないと信じています。私たちは誰でも間違いを犯すことがありますが、それでも物事を正しくおこなうよう努力すべきだということを示すために、その記事を保存しておきました。

このように、コントロール指標が市場リプレイ銘柄のチャートにのみ存在することを確認することで、最初のロックステップを確立しました。ただし、この措置では、同じチャートに複数の指標を追加したり、異なるチャートに複数の指標を追加したりすることはできないので、調整する必要があります。


同じチャートで複数の指標を使うのは避けるべきです。

問題を1つ解決したので、次は別の問題に取り組みましょう。ここで紹介する解決策は、私たちが本当に何を望んでいるか、何をする気があるかによってさまざまです。個人的には、この問題に理想的で最終的な解決策があるとは思えません。しかし、読者が慣れ親しみ、理解できるようなアプローチを提示しようと思います。最も重要なことは、解決策がMQL5のみに基づくということです。外部コーディングの可能性も考えましたが、純粋なMQL5を使うことにしました。外部コーディングに頼ったり、ロックにDLLを使ったりするアイデアも魅力的ですが、それでは簡単すぎます。

MQL5言語が埋められないギャップを埋めるために外部DLLに頼る前に、MQL5で学ぶべきことはまだたくさんあると思います。これは、外部コードを使用する際に「よりクリーン」に見える解決策を提供します。しかし、これではMQL5をより深く理解する助けにはなりません。さらに、MetaTrader 5が限定的なプラットフォームであるという誤解が強まる可能性もあります。プラットフォームに対する誤解と活用不足が、この誤解を助長しています。

ここで提案する解決策を適用するには、いくつかの変更を加え、他の変更を元に戻さなければなりません。最初のステップは、InterProcess.mqhヘッダーファイルを以下のような構造体に変更することです。

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableReplay        "Replay Infos"
#define def_GlobalVariableIdGraphics    "Replay ID"
#define def_SymbolReplay                "RePlay"
#define def_MaxPosSlider                400
#define def_ShortName                   "Market Replay"
//+------------------------------------------------------------------+
union u_Interprocess
{
        union u_0
        {
                double  df_Value;
                long    IdGraphic;
        }u_Value;
        struct st_0
        {
                bool    isPlay;
                int     iPosShift;
        }s_Infos;
};
//+------------------------------------------------------------------+

プログラミングに馴染みのない多くの人にとっては少し奇妙に思えるかもしれませんが、驚くべきことに、上記の構造体は8バイトのメモリしか使っていません。前回の記事の構成で変数が削除されていることにお気づきでしょうか。その理由は、このロック方式はもう使わないからです。少し複雑ですが、コントロール指標を1つのチャートに限定するためにより効果的な、別のアプローチを取ることにしよう。非常に特殊で定義されたリプレーサービスにします。

注:MetaTrader 5プラットフォームとMQL5言語の開発者が、特定のチャートに指標を追加する機能をサービスに提供したり、サービスがチャート上のスクリプトを呼び出して実行できるようにしたら興味深いでしょう。スクリプトを使えば、特定のチャートに指標を追加することができますが、今のところサービスでは不可能です。チャートを開くことはできますが、指標を追加することはできません。この行動を実行しようとすると、MQL5関数を使用しても常にエラーメッセージが表示されます。この記事を書いている時点で、MetaTrader 5のバージョンはビルド3280です。

重要な注意点:記事を書いているこの段階、つまりより進んだ段階で、これを達成することができました。しかし、この記事を書くにあたって、この問題に役立つ文献を見つけることができませんでした。そこで、このリプレイ/シミュレーションのシリーズで、私がどのように解決策を導き出したかを見てみましょう。

この文脈では、以下のスクリプトを実行すれば、指標を開いてチャートに追加することができます。

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart() 
{ 
  
        ENUM_TIMEFRAMES time = PERIOD_D1;
        string szSymbol = "EURUSD";
        long id = ChartOpen(szSymbol, time);
        ChartRedraw(id);

        ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));
}

しかし、同じスクリプトをサービスに変えると、同じ結果は得られません。

#property service
//+------------------------------------------------------------------+
void OnStart()
{
        ENUM_TIMEFRAMES time = PERIOD_D1;
        string szSymbol = "EURUSD";
        long id = ChartOpen(szSymbol, time);
        ChartRedraw(id);

        ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));
}

ここでの唯一の変更はコンパイルプロパティで、コンパイルされたコードがサービスになることを指定するようになったことにご注意ください。予約語を使ってスクリプトをサービスに変えるだけで、たとえ以前と同じタスクを実行するとしても、コードの動作方法は完全に変わってしまいます。したがって、指標をチャートに追加するには、テンプレートを使用する必要があります。サービスを通じて指標を追加することが可能であれば、内部サービスリソースとして指標をコンパイルすることができます。そのため、チャートを開くと、他の指標と混ぜる必要なく、サービスから直接指標を受け取ることができます。

上記のように、リプレイ銘柄に関連付けられていないチャートに指標を追加できないようにしても、ユーザーは市場リプレイを銘柄として持つチャートに指標を挿入することができます。これは許されることではありません。では、ヘッダーファイルInterprocess.mqhに変更を加えたら、サービスコードに集中しましょう。より正確には、ヘッダーファイルC_Replay.mqhに移動します。

要するに、こういうことです。サービスがアクティブかどうかを指標に表示します。アクティブな場合は、どのチャートがメインであるかを表示します。そのためには、以下のようにコードを修正する必要があります。

long ViewReplay(ENUM_TIMEFRAMES arg1)
{
        u_Interprocess info;
                        
        if ((m_IdReplay = ChartFirst()) > 0) do
        {
                if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
                {
                        ChartClose(m_IdReplay);
                        ChartRedraw();
                }
        }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
        info.u_Value.IdGraphic = m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
        ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
        ChartRedraw(m_IdReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
        GlobalVariableTemp(def_GlobalVariableIdGraphics);
        GlobalVariableSet(def_GlobalVariableIdGraphics, info.u_Value.df_Value);
        return m_IdReplay;
}


まず、グローバルターミナル変数をクリアします。次に、同じ変数をもう一度作成し、一時的な変数であることを確認します。そしてこの変数に、サービスによって開かれたチャートの識別子を書き込みます。したがって、サービスによって記録されたこの値を分析するだけで済むため、指標の作業はすでに大幅に簡素化されています。

ただし、サービスを終了する際には、以下のコードに示されているように、追加で作成したグローバル変数を削除する必要があることを忘れてはなりません。

void CloseReplay(void)
{
        ArrayFree(m_Ticks.Info);
        ChartClose(m_IdReplay);
        SymbolSelect(def_SymbolReplay, false);
        CustomSymbolDelete(def_SymbolReplay);
        GlobalVariableDel(def_GlobalVariableReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
}

これらの変更により、指標に新しいコントロールを追加することができます。

int OnInit()
{
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics)))
        {
                ChartIndicatorDelete(ChartID(), 0, def_ShortName);
                return INIT_FAILED;
        }
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
}


市場リプレイ銘柄にコントロール指標を追加しようとしても、リプレイサービスがグローバルターミナル変数を作成しない限り、それはできません。この変数が存在する場合のみ、リプレイ銘柄チャート上でも指標を実行することができます。しかし、それでも問題は解決しません。もう少しチェックが必要です。

次に、指標と対応するチャートとのリンクを開始するチェックを実装します。最初のステップを以下に示します。

int OnInit()
{
#define macro_INIT_FAILED { ChartIndicatorDelete(ChartID(), 0, def_ShortName); return INIT_FAILED; }
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
        if (Info.u_Value.IdGraphic != ChartID()) macro_INIT_FAILED;
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
        
#undef macro_INIT_FAILED
}


初期化関数では同じコードが頻繁に繰り返されるため、ヘルパーマクロを定義することにしました。これによって、コードを書くときに起こりうるエラーを避けることができます。では、ロックの第1段階に移りましょう。銘柄チャートの作成時に、このチャートのIDをグローバルターミナル変数で渡します。このようにしてこの値を取得し、コントロール指標がリプレイサービスによって生成されるはずの期待されるチャート上に本当にあることを確認することができます。リプレイサービスが作成したチャートと異なる指標をチャートに追加しようとした場合、この操作は拒否されます。

これは様々な場面で役立ちますが、まだ問題があります。同じチャートに追加の管理指標を追加する機能です。この問題を最終的に解決するために、少し型破りなアプローチを使います。これを書いている時点では、プラットフォームは下の画像にあるバージョンであることに留意してください。



読者がこれを読む頃には、プラットフォームが大幅なアップデートを受け、ここで紹介した仕組みが時代遅れになっている可能性もありますが、新しいコントロール指標を同じチャートに追加できないように設定し、特定のチャートに限定することができるメカニズムを見てみましょう。指標が削除されると、チャートは終了し、リプレイサービスも終了します。

実際のコードに移る前に、これから使う仕組みがブール論理に基づいていることに注意することが重要です。もしこれらの概念に馴染みがないのであれば、このトピックについて調べてみることをお勧めします。これらの概念は、あらゆるコードの作成と開発にとって基本的なものだからです。最も簡単な解決策は、DLLを使ってより直接的な方法で状況を解決することだと考える方もいらっしゃるかもしれません。部分的には同意しますが、外部プログラムで解決策を作成することにはデメリットがあります。

これではMQL5言語の限界を完全かつ正確に理解することはできず、改善のための提案を見つけることはできないでしょう。C/C++が最も強力な言語だと主張する方は多いです。そしてこの方たちは正しいです。ただ、それは単一の実体として現れたわけではありません。開発者たちがその限界を探るにつれ、進化し、新たな能力を獲得していったのです。これらの限界に達し、希望する機能が実現できなかったとき、新しい機能が生み出され、以前は実現不可能だったプロジェクトが実現可能になりました。そのため、C/C++はほとんどのプロジェクトに対応できる信頼性の高い言語であることが証明されています。

MQL5にはC/C++と同じ資質と可能性があると確信しています。MQL5言語をできるだけ勉強し、テストするだけでいいのです。そして、これらの限界に達したとき、開発者はMQL5に改善や新機能を提供することができます。時間が経てば、MetaTrader 5用の非常に強力なアプリケーション開発言語になる可能性があります。

実際に何をしようとしているのかを理解するためには、ある情報を抽象化することができない現在の言語の限界を理解する必要があります。これは不可能だと言ってはいないことにご注目ください。作業を容易にする抽象化を作成できないという意味です。これらは異なる概念です。データと情報の抽象化を開発することと、必要な方法でデータを操作できるようにすることは別のことです。これらを混同してはなりません。

C/C++では、ビット列の中の特定の1ビットを分離することができるデータ抽象化を作成することができます。これは非常に簡単なことです。

union u01
{
        
double  value;
        struct st
        {
                ulong info : 63;
                bool signal;
        }c;
}data;


奇妙で愚かなことのように思えるが、情報の符号を識別し、変更することさえ可能にするデータ抽象化の形式を作り出しているのです。今ここでこのコードはあまり役に立ちませんが、別の点を考えてみましょう。例えば、情報を送るためにビットを使い、何かをコントロールするとしましょう。こんな感じにです。

struct st
{
        bool PlayPause;
        bool Reservad : 6;
        bool RS_Info;
}ctrl;

このシナリオでは、抽象化レベルは、実際にアクセスしたいビットを分離し、コードを読みやすくするのに役立つでしょう。しかし、すでに述べたように、現在のMQL5では、提示された抽象化レベルを使用することはできません。純粋な抽象化は不可能なので、別のアプローチを取らなければなりませんが、言語の限界を理解すれば、データを操作することはできます。そのため、抽象化しなければ処理できないようなデータを処理するために、ブール論理に頼るのです。しかし、ブール論理を使うことで、プログラムの解釈は難しくなります。こうして、抽象化されたハイレベルのシステムから、抽象化がブール論理に還元されたローレベルのシステムへと移行します。

この議論については、また後で触れることにしよう。しかし、その理由は今示されているものよりも正当なものであることがわかるでしょう。これまで述べてきたことはどうでもいいことのように思えるかもしれませんが、最終的なコントロール指標のコードをご覧になれば、私が何を説明しようとしているのかご理解いただけるでしょう。多くの場合、MQL5は特定のことができないわけではありません。実際、多くのプログラマーは、データの抽象化が存在しないだけで、特定のものを作ることが不可能になるような、より深いレベルには進みたがらないものです。

以下は、現在の開発段階におけるコントロール指標の完全かつ包括的なコードです。このコードにより、チャート上の指標をロックし、1つのMetaTrader 5セッションで他のコントロール指標を追加することを禁止することができます。

#property copyright "Daniel Jose"
#property description "This indicator cannot be used\noutside of the market replay service."
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <Market Replay\C_Controls.mqh>
//+------------------------------------------------------------------+
C_Controls      Control;
//+------------------------------------------------------------------+
#define def_BitShift ((sizeof(ulong) * 8) - 1)
//+------------------------------------------------------------------+
int OnInit()
{
#define macro_INIT_FAILED { ChartIndicatorDelete(id, 0, def_ShortName); return INIT_FAILED; }
        u_Interprocess Info;
        long id = ChartID();
        ulong ul = 1;

        ul <<= def_BitShift;
        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
        if (Info.u_Value.IdGraphic != id) macro_INIT_FAILED;
        if ((Info.u_Value.IdGraphic >> def_BitShift) == 1) macro_INIT_FAILED;
        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
        Info.u_Value.IdGraphic |= ul;
        GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value); 
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
        
#undef macro_INIT_FAILED
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Control.DispatchMessage(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        u_Interprocess Info;
        ulong ul = 1;

        switch (reason)
        {
                case REASON_CHARTCHANGE:
                        ul <<= def_BitShift;
                        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
                        Info.u_Value.IdGraphic ^= ul;
                        GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
                        break;
                case REASON_REMOVE:
                case REASON_CHARTCLOSE:
                        if (_Symbol != def_SymbolReplay) break;
                        GlobalVariableDel(def_GlobalVariableReplay);
                        ChartClose(ChartID());
                        Control.Finish();
                        break;
        }
}
//+------------------------------------------------------------------+


何が起きているのか理解するのが難しかったり、コントロール指標の動作が理解できなかったりしたなら、まあ、そこがポイントです。このコードには、理解しやすくするための抽象化がありません。MQL5では、C/C++が提供するような抽象化レベルを実現できないからです。そのため、ブール論理に頼らざるを得ません。ブール論理は、より複雑ではありますが、データを操作し、抽象化を使った場合と同じ結果を得ることができます。

よく見ると、double型は値を格納するのに8バイト必要です。同様に、long(符号あり)型やulong(符号なし)型も同じ8バイトを使用します。ChartIDによって得られるチャートIDがlong型を返すことを考慮すると、1ビットが余っており、これは正確に符号を示すために使用されます。このビットを使って、指標をチャートに固定します。また、同じチャートに別のコントロール指標を追加しないように、指標名を操作します。次が方法です。説明に従ってください。

まず、ビットの数を決め、どのビットを使うかを決めます。つまり、64ビット、32ビット、128ビットのどのシステムを使用しているかに関係なく、この定義では適切な型と長さを使用します。64ビットであることは分かっていても、固定的ではなく柔軟な設定にしたいのです。そこで、この値から1を引き、longの符号ビットを分離します。

次に、この64ビットのうち最下位ビットをアクティブにします。そうすると、値1が得られ、これが出発点となります。次に、63ビットの左シフトをおこない、0x8000000000000000000000000という値を得ます。ここで、最上位ビットの値は1、つまりtrueです。この値を直接設定すればこのステップは避けられますが、間違って入力するリスクが高くなります。こうすることで、エラーの可能性を最小限に抑えるのです。

この値を得たら、2つの選択肢があります。まず1つ目は、システムをロックすることです。2つ目の選択肢は、MetaTrader 5が必要に応じてすぐにチャート上に指標を再適用できるように、システムのロックを解除することです。2つ目の選択肢はもっと簡単なので、まずそれを見てみましょう。

システムのロックを解除するには、チャートIDを含むグローバルターミナル変数の値を取り、この値に対してXOR演算を実行します。最上位ビットを除くすべてのビットを保持するようにします。XOR演算の代わりに、NOT演算の後にAND演算をおこなうのが理想的です。これにより、最上位ビットに含まれる情報はすべて取り除かれます。ただし、この演算は、与えられたビットがすでに何らかの情報を含んでいる場合にのみ発生するので、XOR演算を使うことに問題はないと思います。問題がある場合は、XOR演算を次の行に置き換えてください。

Info.u_Value.IdGraphic &= (~ul);

いずれにせよ、最上位ビットをリセットするという目的は達成されました。こうすることで、MetaTrader 5がコントロール指標をチャートに戻そうとする前に、グローバルターミナル変数に値を戻すことができます。これでロックシステムの最も単純な部分は完了しました。次に、より複雑な部分に移りましょう。

この段階でまずすべきことは、チャットの銘柄がリプレイの銘柄と一致しているかどうか、そしてリプレイサービスが機能しているかどうかを確認することです。これらの条件のいずれかが満たされない場合、指標はチャートから削除されます。そして、リプレイサービスが作成したチャートのIDを提供するグローバルターミナル変数に含まれる値をキャプチャします。次に、この値とチャートウィンドウのIDを比較し、異なる場合は指標も削除します。次に、得られた値を63ビット右にシフトし、このビットがアクティブかどうかを確認します。アクティブな場合、識別子は再び取り除かれます。

これで十分と思われるかもしれないが、解決しなければならない別の問題があります。この特定の問題により、すべてをMQL5内に維持しながら作業が追加されました。コードに特定の行を追加しなければならなかったのです。これがないと、システムがチャートに指標を追加しないようにしようとするたびに、指標が追加されてしまいます。問題が生じなくても、指標ウィンドウに表示されたままなので嫌でした。そのとき、指標の名前を変更することを思いついた。この指標は、サービスによってチャートが作成されるときに、チャートとともに生成されます。

テンプレートがチャートに適用されると、指標は自動的にアクティブになります。テンプレートといえば、もう1つポイントがあります。その前に、この説明を終えましょう。最後に、これらすべてのステップの後、OR演算を実行し、結果をグローバルターミナル変数に保存して、コントロール指標をロックします。このトピックの結論として、もう1つ修正が必要です。最終的な変更をおこなわなければ、ここでおこなったすべての作業は無駄になり、正しく機能しなかったでしょう。多くの人が不可能だと考えることをやり遂げたと自慢して、この情報を飛ばすこともできますが、そうしたならば、誰かが同じことをしようとしても、おそらく失敗するでしょう。私はここで自慢したり、自分がなくてはならない存在だと主張したいわけではありません。私はそのような評価を求めているわけではなく、逆に、多くの人が可能だと考えていることを超えることは可能であり、問題の解決策が思いがけないところに見つかることもあるということを示したいのです。

提供されたすべての指示に従い、すべてのプロセスを実行しようとすれば、1つを除いてほとんどすべての面で成功できることがわかるでしょう。どう頑張っても、リプレイサービスが作成したチャートに新しいコントロール指標が追加されるのを防ぐことはできません。「え?手順にすべて従って、このための特別な行も追加したのに、冗談でしょ?」とおっしゃるかもしれません。

冗談だと言いたいところですが、そうではありません。実際、この状況を避けることはできないでしょう。これはシステムの「欠陥」(鍵括弧に注意)によるものです。何が原因なのか、どうすればこのようなことが起こるのか、正確なことはわかりませんが、次のコードをご覧ください。

long ViewReplay(ENUM_TIMEFRAMES arg1)
{
        u_Interprocess info;
                                
        if ((m_IdReplay = ChartFirst()) > 0) do
        {
                if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
                {
                        ChartClose(m_IdReplay);
                        ChartRedraw();
                }
        }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
        info.u_Value.IdGraphic = m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
        ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
        ChartRedraw(m_IdReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
        GlobalVariableTemp(def_GlobalVariableIdGraphics);
        GlobalVariableSet(def_GlobalVariableIdGraphics, info.u_Value.df_Value);
        return m_IdReplay;
}


この行が実行されると奇妙なことが起こります。長い間頭を悩ませていましたが、テンプレートを開いてその仕組みを理解することにしました。そこで、テンプレートを使うのではなく、冒頭で説明したように、システムが自動的に指標を作成してチャートに適用するようにすべきであると気づきました。そこで、サービスを使ってもチャートに指標を追加できないこと、チャートはスクリプトを使わないと追加できないことを示しました。システムは機能しましたが、テンプレートを使うと機能しませんでした。

何が問題なのかを懸命に探しました。答えを知っている人も見つけられませんでしたが、長年のプログラミングで培ったテクニックとスキルを駆使して、問題を特定できました。過去の記事とテンプレートファイルのデータを見ると、次のようなデータがあります。

<indicator>
name=Custom Indicator
path=Indicators\Market Replay.ex5
apply=0
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=0.000000
scale_fix_max=0
scale_fix_max_val=0.000000
expertmode=0
fixed_height=-1



一見、すべてが正しいように見えるかもしれないし、実際正しいのですが、前のフラグメントで何かを変更することで、システムは期待通りに機能し始め、コントロール指標をロックし、他の指標を追加することを許しません。

修正版を以下に示します。

<indicator>
name=Custom Indicator
path=Indicators\Market Replay.ex5
apply=1
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=0.000000
scale_fix_max=0
scale_fix_max_val=0.000000
expertmode=0
fixed_height=-1

正確な変更点をお見せするつもりはありません。それを見て、理解してみてください。ただし、添付のコードバージョンは適切なシステム動作を提供するので心配無用です。


結論

先に述べたように、この情報を省略して、誰かが私になぜシステムがうまく作動しないのかと尋ねたとき、私の方が優れたプログラマーであるかのように振る舞うことができたでしょう。しかし、自慢するのは好きではありません。読者に学び、理解し、解決策を見つける意欲を感じてほしいのです。可能な限り、自分の知識を共有することです。これが私たちが進化に貢献する方法だからです。知識を隠すことは優越感の表れではなく、恐れや自信のなさの表れです。

私はこの段階を乗り越えました。それが、自分の仕事のやり方を説明している理由です。多くの人が同じようなことをする気になることを願っています。また次の記事でお会いしましょう。私たちの仕事は始まったばかりです。




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

添付されたファイル |
Market_Replay.zip (13058.5 KB)
ニューラルネットワークが簡単に(第40回):大量のデータでGo-Exploreを使用する ニューラルネットワークが簡単に(第40回):大量のデータでGo-Exploreを使用する
この記事では、長い訓練期間に対するGo-Exploreアルゴリズムの使用について説明します。訓練時間が長くなるにつれて、ランダムな行動選択戦略が有益なパスにつながらない可能性があるためです。
ニューラルネットワークが簡単に(第39回):Go-Explore、探検への異なるアプローチ ニューラルネットワークが簡単に(第39回):Go-Explore、探検への異なるアプローチ
強化学習モデルにおける環境の研究を続けます。この記事では、モデルの訓練段階で効果的に環境を探索することができる、もうひとつのアルゴリズム「Go-Explore」を見ていきます。
ニューラルネットワークが簡単に(第41回):階層モデル ニューラルネットワークが簡単に(第41回):階層モデル
この記事では、複雑な機械学習問題を解決するための効果的なアプローチを提供する階層的訓練モデルについて説明します。階層モデルはいくつかのレベルで構成され、それぞれがタスクの異なる側面を担当します。
ニューラルネットワークが簡単に(第38回):不一致による自己監視型探索 ニューラルネットワークが簡単に(第38回):不一致による自己監視型探索
強化学習における重要な問題のひとつは、環境探索です。前回までに、「内因性好奇心」に基づく研究方法について見てきました。今日は別のアルゴリズムを見てみましょう。不一致による探求です。