English Русский 中文 Español Deutsch Português
preview
リプレイシステムの開発(第27回):エキスパートアドバイザープロジェクト-C_Mouseクラス(I)

リプレイシステムの開発(第27回):エキスパートアドバイザープロジェクト-C_Mouseクラス(I)

MetaTrader 5テスター | 30 3月 2024, 15:11
145 0
Daniel Jose
Daniel Jose

はじめに

前回の「リプレイシステムの開発(第26回):エキスパートアドバイザープロジェクト(I)」稿では、最初のクラス構築の開始について詳細に検討しました。次に、これらのアイデアをさらに発展させ、より有用なものにしていきましょう。これでC_Mouseクラスができます。このクラスは、最高水準でプログラミングする能力を提供します。しかし、高水準や低水準のプログラミング言語について語ることは、コードに卑猥な言葉や専門用語を含めることではありません。逆です。高水準プログラミング、低水準プログラミングというのは、他のプログラマーが理解しやすいか、しにくいかという意味です。実際、高水準プログラミングと低水準プログラミングの違いは、他の開発者にとってコードがいかに単純か複雑かを示しています。したがって、コードが自然言語に似ている場合は高水準、自然言語に似ておらず、プロセッサが命令を解釈する方法に近い場合は低水準とみなされます。

私たちの目標は、クラスのコードをできるだけ高く保つ一方で、経験の浅い人たちには理解が難しくなるような特定のタイプのモデリングをできるだけ避けることです。これが目標ですが、完全に達成できる保証はありません。


C_Mouseクラス:ユーザーとの対話の開始

マウスとキーボードは、ユーザーとプラットフォーム間のインタラクションの最も一般的な手段です。したがって、このインタラクションがシンプルで効果的であることが非常に重要であり、ユーザーがアクションの実行方法を再学習する必要がないようにする必要があります。コードは次の行から始まります。

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Terminal.mqh"
//+------------------------------------------------------------------+
#define def_MousePrefixName "MOUSE_"
#define def_NameObjectLineH def_MousePrefixName + "H"
#define def_NameObjectLineV def_MousePrefixName + "TV"
#define def_NameObjectLineT def_MousePrefixName + "TT"
#define def_NameObjectBitMp def_MousePrefixName + "TB"
#define def_NameObjectText  def_MousePrefixName + "TI"
//+------------------------------------------------------------------+
#define def_Fillet      "Resource\\Fillet.bmp"
#resource def_Fillet
//+------------------------------------------------------------------+

C_Terminalクラスを含むヘッダーファイルを用意しました。前回の記事で述べたように、このC_MouseクラスファイルはC_Terminalクラスファイルと同じディレクトリにあるので、この構文を問題なく使用することができます。実行ファイルに含まれるリソース名を定義し、リソースを別途ダウンロードすることなく移植できるようにします。これは多くの場合、特にリソースが重要で、使用中の可用性が不可欠な場合に非常に便利です。通常、アクセスしやすいように特定のディレクトリにリソースを配置します。こうすれば、常にヘッダーファイルと一緒にコンパイルされます。C_Mouse.mqhファイルがあるフォルダにResourceというディレクトリを追加しました。Fillet.bmpファイルはこのResourceディレクトリの中にあります。同じモデリングを維持したままディレクトリ構造を変更すれば、コンパイラはFillet.bmpファイルがどこにあるかを正確に知ることができます。コードがコンパイルされれば、リソースが見つからないことを心配することなく、実行ファイルを読み込むことができます。

この手順では、まず名前を定義し、実際には後で定義する他の名前のプレフィックスを定義します。定義を使用することで、プロのコードで一般的なように、開発とメンテナンスが非常に簡単になります。プログラマーは、コード内で使用される様々な名前や要素を定義します。これは通常、Defines.mqhファイルや他の同様のファイルでおこなわれます。このファイルを使用すれば、定義の変更は簡単です。しかし、これらの定義はこのファイルにしか存在しないので、他の場所で宣言する必要はありません。

#undef def_MousePrefixName
#undef def_NameObjectLineV
#undef def_NameObjectBitMp
#undef def_NameObjectLineH
#undef def_NameObjectLineT
#undef def_NameObjectText
#undef def_Fillet

このコードは、C_Mouse.mqhファイルで定義され、表示されていたすべての銘柄と名前が、この瞬間から表示されなくなることをコンパイラに伝えます。通常、他のファイルの定義を削除したり変更したりすることは推奨されません。そのため、実際に登場し、使用されるようになった名前を発表しています。その後、これらの定義は削除されます。基準なしに定義を変更したり削除したりすることも、良いやり方ではありません。ある定義を複数のファイルで使用する必要がある場合は、その定義用に別のファイルを作成するのが最善です。

それでは、クラスコードの最初の数行に進んでみましょう。ここからが興味深いことになります。すべてはここから始まります。

class C_Mouse : public C_Terminal
{

   protected:
      enum eEventsMouse {ev_HideMouse, ev_ShowMouse};
      enum eBtnMouse {eKeyNull = 0x01, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
      struct st_Mouse
      {
         struct st00
         {
            int      X,
                     Y;
            double   Price;
            datetime dt;
         }Position;
         uint    ButtonStatus;
      };

このフラグメントでは、C_Terminalクラスが C_Mouseクラスによってpublicに継承されていることがわかります。つまり、C_Mouseクラスを使用して、_Terminalクラスのすべてのpublicメソッドにアクセスできます。このように、C_Mouseクラスは、C_Mouse.mqhファイルのコードだけに限定されるよりもはるかに多くの機能を持つことになります。継承はこのような利点だけでなく、クラスをより効率的にする他の利点もあります。これは今後の記事で説明します。このコード部分の作業を続けましょう。コードのprotected部には、2つの列挙宣言があり、これによって少し高水準でのプログラミングが可能になります。最初の列挙は非常にシンプルで、前回の記事で取り上げたのと同じ概念とルールに従っています。一方、2つ目のリストは少々難しく、複雑に見えるかもしれませんが、その複雑さの理由と、そもそもの存在意義を探っていきます。

この列挙は、そうでなければ維持するのがはるかに困難な機会を与えてくれます。つまり、多くの労力を節約できます。この特別な列挙は、#defineコンパイラ指令と同等の名前定義を作成します。しかし、定義の代わりに列挙を使用することにしました。これによって、少し違ったテクニックを使用することができるようになりますが、同時にコード上ではより理解しやすくなります。この列挙を使用することで、コードがより読みやすくなることがわかるでしょう。これは複雑なコードでは非常に重要になります。この列挙の宣言が一見して非常にわかりにくく複雑だと思うのであれば、おそらく列挙がどのように機能するかを十分に理解していないのでしょう。コンパイラから見れば、列挙は単なる定義の列であり、デフォルトでは最初の要素はインデックスゼロから始まります。しかし、列挙の希望する開始インデックスを設定することができ、コンパイラはそこから後続のインデックスの値をインクリメントし始めます。これは、あるインデックスの値がシーケンスの開始値となるような多くのシナリオで非常に有用です。多くのプログラムでは、エラー値が特定の基準に基づいて設定される長い列挙リストを使用します。名前を定義し、その名前に特定の値を割り当てると、コンパイラはそれ以降のすべての名前の値を自動的にインクリメントします。これによって、大きな定義リストを作成するのがより簡単になり、ある時点で値が重複するリスクもなくなります。

実際、多くのプログラマーがこのテクニックを使用していないのは驚きです。ある種のプロジェクトをプログラミングする際に、このテクニックを使用すればミスを避けることができるからです。このことを理解した今、実験してみて、列挙を使用することで、連続的かどうかにかかわらず、関連する要素の大きなリストを作成するプロセスが大幅に簡素化されることがわかるでしょう。私たちが探求しているアプローチは、コードを読みやすく理解しやすくすることでプログラミングを改善することを目的としています。

次の部分は、マウスが何をしているかをコードの残りの部分に知らせる役割を持つ構造体です。この時点で、多くの人は変数を宣言することを期待するかもしれませんが、private句の外でクラス内の変数を宣言することは、良いプログラミングの習慣とは考えられていません。また、これらの宣言をコードのpublic部分に置く方が適切だと考える方もいらっしゃるでしょう。しかし、私はより限定的なアクセスレベルから始めることを好み、publicアクセスは最後の手段としてのみ許可します。関数やメソッドは、クラスに直接関係するものを除いて、publicアクセスできるようにしなければなりません。そうでない場合は、要素に最小限の権限を与えることから始めることを常にお勧めします。

このアイデアを続けて、C_Mouseクラスにある変数を見てみましょう。


   private :
      enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
      struct st01
      {
         st_Mouse Data;
         color    corLineH,
                  corTrendP,
                  corTrendN;
         eStudy   Study;
      }m_Info;
      struct st_Mem
      {
         bool    CrossHair;
      }m_Mem;

特に興味深いのは、クラス外のコードの他の部分からは見えない列挙をすでに持っていることです。なぜなら、この列挙はそのクラス内でのみ有効であり、コードの他の部分がその存在を知る意味がないからです。この概念はカプセル化と呼ばれるもので、与えられたタスクを実行するコードが実際にどのように動作するかは、他のコード部分にはわからないという原則に基づいています。この種のアプローチは、ライブラリのコードが実際にどのように動作するかを明らかにすることなく、他のプログラマーが手続きにアクセスできるようにするライブラリ開発者に高く評価されています。

次に、構造体を見つけます。これは、クラス本体の外からアクセスできる別の構造体を使用するもので、アクセスプロシージャと関数の説明の際に詳しく説明します。この時点で、この変数がprivateなクラス構造を指していることを理解することが重要です。この場合、コンテンツが特別なものであり、コード内の非常に特定のポイントでのみアクセスされることが明確になるので、私はこの方法を好んで使います。しかし、前の構造体で同じデータを宣言することを妨げるものは何もありません。最初の構造体に含まれるデータは一般的なものであり、いつでも変更可能だからです。

変数に関する部分はすでに紹介したので、次は関数とメソッドの分析に移りましょう。次のコードをご覧ください。

inline void CreateObjectBase(const string szName, const ENUM_OBJECT obj, const color cor)
   {
      ObjectCreate(GetInfoTerminal().ID, szName, obj, 0, 0, 0);
      ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_TOOLTIP, "\n");
      ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BACK, false);
      ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, cor);
   }

C_Mouseクラスの開発全体を通して、何らかの標準化に従わなければならない様々な要素を作成する必要があるため、このコードはソフトウェアの再利用を容易にします。そこで、このプロセスを容易にするために、この作成を1つのメソッドに集中させます。宣言の中で、特にパフォーマンスが重要な要素である場合によく見られるのが、特定のキーワードの使用です。このような選択をしたのは、マクロと同じように、宣言された場所に直接コードをインクルードさせたいからです。実行ファイルのサイズの増加につながるかもしれませんが、その代わり、全体的なランタイムパフォーマンスはわずかではあるが向上するでしょう。実行ファイルのサイズの増加を正当化できないような様々な要因のために、パフォーマンス向上が最小限にとどまることもあります。

ここで、多くの人にとっては些細なことに思えるかもしれませんが、コード全体を通して繰り返し登場することになる状況があります。この関数は、C_Terminalクラスで宣言された構造体を参照します。しかし、コンパイラはこれを関数としてではなく、定数変数として解釈します。どうしてそんなことが可能なのでしょうか。関数のように見える宣言を、コンパイラはどのように定数変数として扱うことができるでしょうか。一見したところ、これにはあまり意味がありません。しかし、この呼び出しのコードとC_Terminalクラスでの実装を詳しく見てみましょう。

inline const st_Terminal GetInfoTerminal(void) const
   {
      return m_Infos;
   }

このコードは、C_Terminalクラスに属する構造体への参照を返します。リファレンスを返す変数はクラスにprivateなものであり、いかなる場合でも、C_Terminalクラスにすでに存在する以外のコードによってその値が変更されてはなりません。コードがこのprivate変数にアクセスする際に、その変数に変更を加えないようにするため、特別な宣言を入れることにしました。こうすることで、コンパイラは、定数への参照を受け取ったコードがその値を変更できないようにします。Ерыш対策は、偶発的な変更やプログラミングエラーを避けるために使用されます。したがって、C_Terminalクラスの内部で関数内の値を不適切な方法で変更しようとしても、コンパイラはこの処理をエラーとして認識します。コンパイラによれば、そこにある情報は変更できないためです。このようなことが起こるのは、この場所がそのような変更に適していないか、間違っているからです。

この種のプログラミングは、より手間がかかるものの、コードの堅牢性と信頼性を向上させます。しかし、この文脈には後述する欠陥があります。この決定を今説明することは、全体の解釈を複雑にしてしまうからです。C_Mouseクラスの次のメソッドを見てみましょう。

inline void CreateLineH(void)
   {
      CreateObjectBase(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
   }

価格線を表す水平線が作成されます。重要なのは、複雑な処理をすべて別のプロシージャに委ねることで、私たちが書く必要があるのは1行だけだということです。この種のアプローチは通常、マクロに置き換えられます。しかし、私はマクロを使わずにこれをやってみたいと思います。もう1つの方法は、同じ内容を、1行であることを前提に、呼び出しが発生する場所に貼り付けることです。個人的には、このやり方はお勧めしません。間違っているからではなく、すべての行を変更しなければならないのに、実際には1行しか変更しないからです。これは面倒な作業であり、ミスも起こりやすいです。そのため、参照される箇所に直接コードを配置する方が実用的に思えるかもしれませんが、マクロや宣言に「インライン」という言葉が含まれるコードを使用する方が安全です。

これから説明する方法は、今はあまり意味がないかもしれませんが、説明を始める前に知っておくことが重要です。最初の方法を以下に示します。

void CreateStudy(void)
{
   CreateObjectBase(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH);
   CreateObjectBase(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH);
   CreateObjectBase(def_NameObjectBitMp, OBJ_BITMAP, clrNONE);
   CreateObjectBase(def_NameObjectText, OBJ_TEXT, clrNONE);                                
   ObjectSetString(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_FONT, "Lucida Console");
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_FONTSIZE, 10);
   ObjectSetString(GetInfoTerminal().ID, def_NameObjectBitMp, OBJPROP_BMPFILE, "::" + def_Fillet);
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2);
   m_Info.Study = eStudyCreate;
}

チャートの調査に必要なオブジェクトを作成し、独自の分析スタイルを作り上げることができます。これは、最も関連性が高く、効果的な分析に必要と考えられる情報を強調するのに役立ちます。ここで紹介した調査モデルは非常にシンプルなものですが、より迅速で、チャートを視覚的に乱雑にしない、より多様な手法を開発することができます。最も単純な方法論の一例として、ある価格と別の価格との間のポイント数をテストする研究を作成し、その値がマイナスかプラスかを視覚的に示します。複雑なシステムではないが、より洗練された他のモデルを開発するための基礎となります。

多くの調査では、多種多様な対象やその組み合わせを使用し、時には計算や価格帯のポジションを見つけること(ハイロー調査)が必要となります。この作業をすべて手作業でおこなうのは、時間がかかるだけでなく、常にチャートからオブジェクトを追加したり削除したりしなければならないので、退屈でもあります。そうしないと、図表が混雑して混乱し、必要な情報を特定するのが難しくなります。よって、この方法を基礎として、より洗練された、ご自分のニーズに合ったものを作ってください。後で改善する機会があると思いますので、今焦る必要はありません。

唯一必要なことは、テキストを追加したいとき(その可能性は非常に高い)、それが作成シーケンスの最後のオブジェクトになるようにすることです。これは、情報を表示するテキストが最後に作成されたオブジェクトである、上のコードからわかります。これにより、他のオブジェクトによって非表示になるのを防ぐことができます。通常、最初にいくつかのオブジェクトが作成され、そのうちの1つに本当に重要なオブジェクトが隠されていることがあります。覚えておいていただきたいのは、最も興味がある最も重要な要素は、常に作成キューの最後のものであるべきだということです。

オブジェクトの色が最初はclrNONEに設定されていることに惑わされないでください。なぜなら、これらの色は分析の進行に伴って変化していくからです。前のメソッドが分析の作成を担当した場合、次のメソッドが実際にチャート上で分析を実行します。

void ExecuteStudy(const double memPrice)
{
   if (CheckClick(eClickLeft))
   {
      ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectBitMp, 0, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectText, 0, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
      ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > m_Info.Data.Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
      ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_COLOR, (memPrice > m_Info.Data.Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
      ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectBitMp, OBJPROP_ANCHOR, (memPrice > m_Info.Data.Position.Price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
      ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_ANCHOR, (memPrice > m_Info.Data.Position.Price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
      ObjectSetString(GetInfoTerminal().ID, def_NameObjectText, OBJPROP_TEXT, StringFormat("%." + (string)GetInfoTerminal().nDigits + "f ", m_Info.Data.Position.Price - memPrice));
   } else {
      m_Info.Study equal eStudyNull;
      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);
      ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T");
   }
   m_Info.Data.ButtonStatus equal eKeyNull;
}

チャートを分析することは、特にコードを単独で見れば、一見無意味に思えるかもしれません。しかし、インタラクションコードを勉強すれば、その目的はより明確で理解しやすくなります。ここで何が起こっているのかを理解しましょう。調査オブジェクトの作成に使用されるコードとは異なり、このセグメントには興味深い要素が含まれています。基本的に、コードには2つの主要な部分があります。最初のコードでは条件を確認します。高水準プログラミングでは、コードは自然言語に似ているため、プログラミングの知識がなくても正確に何が確認されているかを理解できるからです。2つ目では、分析を完了します。どちらの部分も非常にシンプルで、すぐに理解できます。この段階では、分析間隔を示すために画面上のオブジェクトを動かします。次の行では、これは通常明らかですが、動きが上向きか下向きかを示すために、オブジェクトの色を変えています。しかし、ある種の曲線を使用する場合など、単に観察するだけでは値がプラスかマイナスかを判断することが難しくなる状況が生じることがあります。分析は、さまざまな基準に基づいていくつかの方法で実施できることを覚えておくことが重要です。おそらく最も重要な部分は、何らかの計算や分析に基づいた値を提示する行でしょう。ここでは、それぞれのケースに応じて、さまざまな情報をさまざまな方法で提示する自由があります。

私たちが本当に必要としているのは、調査の発表を適切に完成させる方法であり、それが2つ目のコードの部分です。一見、この部分は特に注目に値しないように見えますが、特筆すべき点があります。ObjectsDeleteAll関数の使用です。なぜこの瞬間が重要で、注意を払う必要があるのでしょうか。答えはC_Terminalクラスにあります。C_Terminalクラスのコンストラクタには次の行があります。

ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);

この行は、チャートからオブジェクトが削除されるたびに、どのオブジェクトが削除されたかを通知するイベントを生成するよう、プラットフォームに指示します。ObjectsDeleteAll関数を使用して、分析に使用されたすべての要素またはオブジェクトを削除します。これにより、MetaTrader 5はチャートからオブジェクトが削除されるたびにイベントを生成します。プラットフォームがそうするのであって、それらのオブジェクトが再び作られるかどうかは、私たちのコード次第です。問題が発生するのは、オブジェクトが削除されなかったり(コードによって再度作成されるため)、コードの関連通知なしに削除された場合です。この場合、CHART_EVENT_OBJECT_DELETEプロパティはfalseに設定されます。当初はこのようなことは起こりませんが、コードが拡張されるにつれて、このプロパティが誤って変更され、再度有効にすることを忘れてしまうことがあります。その結果、プラットフォームは、オブジェクトがチャートから削除されたことをコードに通知するイベントを作成しません。これは、オブジェクトの管理における不正確さやエラーにつながる可能性があります。

C_Mouseクラスのコンストラクタを見てみましょう。

C_Mouse(color corH, color corP, color corN)
   :C_Terminal()
{
   m_Info.corLineH  = corH;
   m_Info.corTrendP = corP;
   m_Info.corTrendN = corN;
   m_Info.Study = eStudyNull;
   m_Mem.CrossHair  = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL);
   ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true);
   ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false);
   CreateLineH();
}

この時点で、C_Terminalクラスのコンストラクタを明示的に呼び出します。C_Terminalクラスの必要な値がすでに適切に初期化されているようにするために、このコンストラクタがC_Mouseクラスの他の実行の前に呼び出されていることを確認することがきわめて重要です。この初期化の後、特定のアスペクトを設定し、他のアスペクトの状態を保存します。この2行にご注目ください。この2行では、マウスイベントを受け取りたいという希望と、プラットフォームが提供する標準的な分析ツールを使用しないという意思をプラットフォームに伝えています。これらの定義が適用される場合、プラットフォームは、要求に応じてマウスイベントを報告することにより、当社の要件に準拠します。一方、マウスを使用して分析ツールを使おうとする場合、私たちのコードを使用してそのような分析をおこなう手段を提供しなければなりません。

次のコードはクラスのデストラクタです。デストラクタを実装することは非常に重要で、C_Mouseクラスを使い終わったときにプラットフォームの元の機能を復元できるようにします。

~C_Mouse()
{
   ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, 0, false);
   ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false);
   ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
   ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName);
}

この質問は、直感に反するように思えるかもしれませんが、重要です。この特定の行を含める理由は、C_Mouseクラスによって作成されたオブジェクト、特に価格行を削除しようとすると、プラットフォームはチャートからオブジェクトが削除されたことを通知するイベントを生成するということです。私たちのコードは、このオブジェクトが削除中であっても、それをチャートに返そうとします。プラットフォームがこのようなイベントを発生させないようにするには、このようなことが起こらないようにすることを明確に示さなければなりません。しかし、C_Terminalクラスは、チャートからオブジェクトが取り除かれたことに関連するイベントを受信しないようにすることで、この問題を解決してくれるのではないでしょうか。 しかし、C_Terminalクラスに存在するデータの一部がまだ必要なので、C_Mouseクラスのデストラクタの最後の行が実行された後にのみ、C_Terminalクラスのデストラクタを暗黙的に呼び出すようにコンパイラに許可しています。専用のコード行を追加しなくても、プラットフォームはイベントを生成し続けるので、最初に価格線が削除されても、コードが完全に完成する前に戻すことができます。デストラクタの残りの行は、チャートを元の状態に戻すだけなので、もっと単純です。

この記事で取り上げる最後の関数まで来ました。

inline bool CheckClick(const eBtnMouse value) { return (m_Info.Data.ButtonStatus & value) == value; }

この行では、マウスから受け取ったイベント用に定義した列挙を使用して、プラットフォームから提供された値が、コード内の特定のポイントが期待するものと一致するかどうかを確認します。一致が確認された場合、この関数はtrueを返し、そうでない場合はfalseを返します。現時点では些細なことに思えるかもしれませんが、このような確認は、システムとのやり取りをより集中的におこなうようになったときに非常に役に立つでしょう。この関数の宣言には、使いやすくするための特別なコツがあるのですが、今の時点では重要ではないので、詳しくは説明しません。

次の関数は、前の関数と同様に単純で、C_TerminalクラスのGetInfoTerminal関数と同じ原理に従っています。

inline const st_Mouse GetInfoMouse(void) const { return m_Info.Data; }

その目的は、マウスのデータ構造を格納する変数に含まれる情報を返すことであり、C_Mouseクラスの明示的な許可なしにこのデータを変更できないようにすることです。この方法についてはすでに話したので、繰り返し説明する必要はないでしょう。基本的には、どちらも似たような方法で動作します。

最後に、C_Mouseクラスのコードの集大成です。しかし、C_Mouseクラスに存在する最後の関数については、次回の議論のために残しておくことが重要だと思います。その理由を説明しましょう。


結論

まだ完成にはほど遠いものの、私たちは非常に有望なものを作っている最中です。次の記事に続きます。


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

リプレイシステムの開発(第28回):エキスパートアドバイザープロジェクト-C_Mouseクラス(II) リプレイシステムの開発(第28回):エキスパートアドバイザープロジェクト-C_Mouseクラス(II)
人々が初めてコンピューティングが可能なシステムを作り始めたとき、すべてには、プロジェクトを熟知しているエンジニアの参加が必要でした。コンピュータ技術の黎明期、プログラミング用の端末すらなかった時代の話です。それが発展し、より多くの人々が何かを創造できることに興味を持つようになると、新しいアイデアやプログラミングの方法が現れ、以前のようなコネクタの位置を変えるスタイルに取って変わりました。最初の端末が登場したのはこの時です。
リプレイシステムの開発(第26回):エキスパートアドバイザープロジェクト-C_Terminalクラス リプレイシステムの開発(第26回):エキスパートアドバイザープロジェクト-C_Terminalクラス
これで、リプレイ/シミュレーションシステムで使用するEAの作成を開始できます。ただし、行き当たりばったりの解決策ではなく、何か改善策が必要です。にもかかわらず、最初の複雑さに怯んではなりません。どこかで始めることが重要で、そうでなければ、その課題を克服しようともせずに、その難しさを反芻してしまうことになります。それこそがプログラミングの醍醐味であり、学習、テスト、徹底的な研究を通じて障害を克服することです。
リプレイシステムの開発(第29回):エキスパートアドバイザープロジェクト - C_Mouseクラス(III) リプレイシステムの開発(第29回):エキスパートアドバイザープロジェクト - C_Mouseクラス(III)
C_Mouseクラスを改良した後は、分析のためのまったく新しいフレームワークを作るためのクラスを作ることに集中しましょう。この新しいクラスを作るのに、継承やポリモーフィズムは使用しません。その代わりに、価格線に新しいオブジェクトを追加します。それがこの記事でやろうとしていることです。次回は、分析結果を変更する方法について見るつもりです。これらはすべて、C_Mouseクラスのコードを変更することなくおこなわれます。実際には、継承やポリモーフィズムを使用すれば、もっと簡単に実現できるでしょう。しかし、同じ結果を得る方法は他にもあります。
リプレイシステムの開発 - 市場シミュレーション(第25回):次の段階への準備 リプレイシステムの開発 - 市場シミュレーション(第25回):次の段階への準備
この記事では、リプレイ/シミュレーションシステム開発の第1段階を完了しました。この成果により、システムが高度なレベルに達したことを確認し、新機能の導入への道を開くことができました。目標は、システムをさらに充実させ、市場分析の調査開発のための強力なツールに変えることです。