ティッカーテープパネルの作成:改良版
はじめに
以前の「ティッカーパネルの作成:基本バージョン(英語)」稿では、リアルタイムの銘柄価格のテープを表示するパネルの形でインジケーターを作成する方法を見てきました。しかし、前回の記事では、インジケーターを完全に実装しませんでした。それが不可能だったからではなく、目標が、インジケーターを作成するプロセスを示し、インジケーターが動くという印象を与えるために最小限のコードで動作させる方法を確認することだったからです。
パネルを左右に移動させるためには特別なコードや複雑な計算を作成する必要がないことがわかりました。ユーザーがしなければならなかった唯一のことは、値が増加しているか減少しているかを示すことでした。これだけで、インジケーターを左右に動かしたり静止させたりすることができます。
しかし、このデータは必ずしも十分ではなく、人々が本当にパネルで見たいと思っているものを常に反映しているわけではありません。もう少し詳細があればよいでしょう。ここで実装するのはこれです。また、パネルをより便利にするために、さらにいくつかのことを実装します。新しい形のパネルがあるという単なる事実は、それを作成する他のより適切な方法があるため、まだ完全に役に立ちません。
したがって、最初に、資産のロゴやその他の画像などの画像を追加してビューを変更し、表示された資産をユーザーがすばやく簡単に識別できるようにします。写真には千の言葉よりも価値があると言いますが、本当にそうなのか見てみましょう。
新しいクウォートパネルシステムの導入
この新しい実装で最初におこなうことは、新しいオブジェクトクラスを作成して、資産の画像を表すオブジェクトを抽象化することです。ここでは次のコードを使用します。
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ #include "C_Object_Base.mqh" //+------------------------------------------------------------------+ class C_Object_BtnBitMap : public C_Object_Base { public : //+------------------------------------------------------------------+ void Create(string szObjectName, string szResource1) { C_Object_Base::Create(szObjectName, OBJ_BITMAP_LABEL); ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_BMPFILE, 0, "\\Images\\Widget\\Bitmaps\\" + szResource1 + ".bmp"); ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_STATE, false); }; //+------------------------------------------------------------------+ };
ここで何が起こっているかを説明する前に、なぜこのようにしているのかを簡単に説明しましょう。画像を保存するBitmapオブジェクトを作成することから始めます。オブジェクトの作成を容易にするために一般的なクラスを使用します。次に、画像の場所と名前に基づいて、どの画像を使用するかをオブジェクトに通知します。最後に、表示する画像がインデックス0の画像であることをオブジェクトに通知します。このようにして、システムで非常に高いレベルの抽象化を実現します。
では、なぜこのようにしているのかを見てみましょう。
まず、オブジェクトに関連する特定の制限があることを覚えておく必要があります。何ができるかではなく、これをどのようにおこなうべきかについてです。クラスを使用してシステムの抽象化を作成すると、残りのコードで実際に何が起こっているかを隠すことができます。画面に画像を表示するためにC_Object_Bitmapクラスが何をすべきかはあまり気にしません。
ここで、次の点に注意してください。画像が存在しないか、別の形式の場合、残りのコードにもユーザーにも通知されません。エラーがあることを示す空の領域が表示されます。したがって、エラーがあるかどうかを調べるには、ObjectSetString関数の戻り値を確認する必要があります。falseが返された場合は、画像を読み込めなかったので、オブジェクトをオブジェクトのリストから削除することができますが、そうしていません。なぜでしょうか。
実際、パネルに配置されるすべての資産には、資産を表す画像があるため、私にとってはほとんど違いはありません。しかし、おそらくもっと重要な理由がもう1つあります。
注意深く見ると、このフォームのコードでは、ビットマップファイル以外の他の画像タイプを使用できないことがわかります。たとえば、透明な背景に画像を配置することはできません。透明な背景自体が便利な場合もありますが、既存のコードで実装しようとすると、必要な資産ロゴの代わりに非常に奇妙な情報を示す画像が表示される場合があります。そのため、上記のコードで示した方法に加えて、そのような画像を表示する別の方法を見つける必要があります。
そのような方法の1つは「チャートをより面白くする:背景の追加」稿に示されていました。その記事では、リソースを介して直接画像を作成しました。ビットマップファイルも使用しましたが(内部コード構成モデルが単純であるため)、アニメーションやその他のファイル形式を持つGIFファイルなど、他の形式を使用することを妨げるものは何もありません。重要な点は、この方法を使用することで、透明な背景を含む任意の画像を作成して使用できることです。これは、この記事で前述したコードでは不可能です。
したがって、この抽象化は非常に重要です。別の画像形式を使用したり、透明な背景画像を使用したりする場合でも、コードの残りの部分を変更する必要はありません。このクラスのコードを変更して、必要なものを実装するだけです。
このため、このコードは非常にコンパクトですが、クラスとして実装されています。クラスを使用することで、ここで起こっていることすべてをパネルの他の部分から隠すことができます。
C_Object_Editクラスにもう1つ変更があります。ここで何が変わったのか見てみましょう。
template < typename T > void Create(string szObjectName, color corTxt, color corBack, T InfoValue, string szFont = "Lucida Console", int iSize = 10) { C_Object_Base::Create(szObjectName, OBJ_EDIT); ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_FONT, szFont); ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_FONTSIZE, iSize); ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_ALIGN, ALIGN_LEFT); ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR, corBack); ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BORDER_COLOR, corBack); ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_READONLY, true); SetTextValue(szObjectName, InfoValue, corTxt); };
パネルに追加の情報を実装するため、チャートにテキストを配置するクラスを作成する必要があります。元々存在していたものよりも少し複雑です。次に、使用するフォントの種類とそのサイズを制御します。これにより、以前よりもはるかに多様なものをパネルに配置できるようになります。この多様性の増加は、このクラスの別の関数の変更も必要とします。
template < typename T > void SetTextValue(string szObjectName, T InfoValue, color cor = clrNONE, const string szSufix = "") { color clr = (cor != clrNONE ? cor : (color)ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR)); string sz0; if (typename(T) == "string") sz0 = (string)InfoValue; else if (typename(T) == "double") { clr = (cor != clrNONE ? cor : ((double)InfoValue < 0.0 ? def_ColorNegative : def_ColoPositive)); sz0 = Terminal.ViewDouble((double)InfoValue < 0.0 ? -((double)InfoValue) : (double)InfoValue) + szSufix; }else sz0 = "?#?"; ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT, sz0); ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, clr); };
抽象化がどのようにすべての違いをもたらすかを見ることができます。以前のバージョンで可能だったよりも多くのことを示すことができます。それでは、上記の関数で実際に何をしているのか見てみましょう。
色が指定されている場合は、この色が使用されます。色が指定されていない場合は、オブジェクトに既に存在する色が使用されます。オブジェクトがすでに持っている色やその他のものを変更せずにテキストを変更する場合があるため、これは非常に役立ちます。この段階で、指定された型が文字列かどうかを確認します。これは実行時におこなうため、物事が正しいことを確認する必要があります。
主に資産価格を指定するために使用されるdouble型を使用することもできます。これが実行時に検出されると、それに応じて値が設定され、適切な色が使用されます。ここで値についてもう少し話すことができる興味深い事実があります。呼び出し中に通知される接尾辞を使用します。不明な点がある場合は、これを示すフォーマットも用意しています。次に、テキストを表示し、その色を調整します。
これらの変更を実装するだけで、以下に示すような情報を作成できます。
左隅に画像があります。資産コードは左上隅に表示されます。右上隅には、毎日の資産の変化が表示されます。左下隅には、資産の変化がプラスかマイナスかを示す矢印が含まれています。右下隅には、資産の直近のクウォートが表示されます。
ただし、このすべての情報を表示するには、C_Widgetクラスにいくつかの変更を実装する必要があります。今何ができるか見てみましょう。
新しいC_Widgetクラス
ヘッダーファイルC_Widget.mqhを開いて、システムの最初の基本バージョンと比較して何が変更されたかを確認します。これらの変更を見てみましょう。
#include "Elements\C_Object_Edit.mqh" #include "Elements\C_Object_BackGround.mqh" #include "Elements\C_Object_BtnBitMap.mqh" //+------------------------------------------------------------------+ C_Terminal Terminal; //+------------------------------------------------------------------+ #define def_PrefixName "WidgetPrice" #define def_MaxWidth 160 //+------------------------------------------------------------------+ #define macro_MaxPosition (Terminal.GetWidth() >= (m_Infos.nSymbols * def_MaxWidth) ? Terminal.GetWidth() : m_Infos.nSymbols * def_MaxWidth) #define macro_ObjectName(A, B) (def_PrefixName + (string)Terminal.GetSubWin() + CharToString(A) + "#" + B)
上記のコードでは、C_Object_BtnBitMap.mqhクラスを接続しました。このヘッダーファイルは、ロゴとして使用する画像をサポートしています。また、セル幅が変更され、詳細が表示されます。
おそらく最も興味深いのは、オブジェクトで使用される名前を作成するマクロです。あと数回使用するので注意してください。
次に、オブジェクトクラス内のすべてのprivate要素の宣言がある部分に進みます。
class C_Widget { protected: enum EventCustom {Ev_RollingTo}; private : enum EnumTypeObject {en_Background = 35, en_Symbol, en_Price, en_Percentual, en_Icon, en_Arrow}; struct st00 { color CorBackGround, CorSymbol; int nSymbols, MaxPositionX; struct st01 { string szCode; }Symbols[]; }m_Infos; // ... Rest of the code
変化がないように見えるかもしれませんが、コードには実際にはいくつかの変更があります。それらを簡単に見てみましょう。これらは後で何度も出てきますが、使ってみるとよく理解できるようになります。最初に目を引くのは、オブジェクトタイプの列挙です。実際には、これは作成しているオブジェクトの種類を示すのではなく、そのオブジェクトの目的を示しています。これは、クラスコードを進めていくとより明確になります。
この列挙の代わりに定義を使用しない理由を不思議に思うかもしれません。これは、各オブジェクトが一意であることを保証する必要があるためです。列挙を使用してこれを保証します。定義を使用すると、同じ値を持つ2つの定義を持つリスクが発生し、オブジェクトが一意にならない可能性があります。プログラミングするときは、この詳細を覚えておいてください。
それでは、内部の手続きに関して、システムで起こった変更を見てみましょう。背景を作成する関数から始めます。
void CreateBackGround(void) { C_Object_BackGround backGround; string sz0 = macro_ObjectName(en_Background, ""); backGround.Create(sz0, m_Infos.CorBackGround); backGround.Size(sz0, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH), Terminal.GetHeight()); }
ここでは、最初の列挙を使用します。したがって、列挙が別の値ではなく値35で始まる理由を説明することには本当に価値があります。ただし、まずここで最後の重要なことを1つ終わらせましょう。チャートのサイズを変更すると、OnChartEvent呼び出しが生成され、新しいサイズを確認し、それに応じてパネルを更新できるようになります。
ただし、パネルの高さは常に固定されているため、ある意味で背景のサイズを変更する必要はありません。この値を定義する場所は後で確認しますが、幅を固定してパネル幅の変更を無視できるようにするために、あまり効果的ではないように見えるかもしれませんが、背景が十分に大きいことを保証する方法を使用します。
ここで、列挙を35から開始する理由を見てみましょう。より小さな値を使用することはできますが、使用できない値もあります。
オブジェクトの名前を生成するマクロを見ると、次のことがわかります。
#define macro_ObjectName(A, B) (def_PrefixName + (string)Terminal.GetSubWin() + CharToString(A) + "#" + B)
マクロの最初の引数はクラスで定義した列挙です。列挙は対応する文字列に変換されるため、値に基づいて、名前を一意にするために使用される文字が得られます。ASCIIテーブルを見ると、最初の有効な値はスペース文字を表す32であることがわかります。したがって、値を32で初期化することはできますが、それより小さい数値を初期化しても意味がありません。
よって、これは値を文字に変換しているためにのみ発生します。値が対応する文字列に変換されたならば列挙を0で初期化することができますが、ここでの場合、文字を扱う場合、適切な最小値は32です。
次に、使用するオブジェクトの追加を担当する関数を見てみましょう。
void AddSymbolInfo(const string szArg, const bool bRestore = false) { C_Object_Edit edit; C_Object_BtnBitMap bmp; string sz0; const int x = 9999; bmp.Create(sz0 = macro_ObjectName(en_Icon, szArg), szArg); bmp.PositionAxleX(sz0, x); bmp.PositionAxleY(sz0, 15); edit.Create(sz0 = macro_ObjectName(en_Symbol, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, szArg); edit.PositionAxleX(sz0, x); edit.PositionAxleY(sz0, 10); edit.Size(sz0, 56, 16); edit.Create(sz0 = macro_ObjectName(en_Percentual, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, 0.0, "Lucida Console", 8); edit.PositionAxleX(sz0, x); edit.PositionAxleY(sz0, 10); edit.Size(sz0, 50, 11); edit.Create(sz0 = macro_ObjectName(en_Arrow, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, "", "Wingdings 3", 10); edit.PositionAxleX(sz0, x); edit.PositionAxleY(sz0, 26); edit.Size(sz0, 20, 16); edit.Create(sz0 = macro_ObjectName(en_Price, szArg), 0, m_Infos.CorBackGround, 0.0); edit.PositionAxleX(sz0, x); edit.PositionAxleY(sz0, 26); edit.Size(sz0, 60, 16); if (!bRestore) { ArrayResize(m_Infos.Symbols, m_Infos.nSymbols + 1, 10); m_Infos.Symbols[m_Infos.nSymbols].szCode = szArg; m_Infos.nSymbols++; } }
ここで、多くの人が奇妙に感じる何かを実装できます。しかし、私にとっては単なる冗談です。私は遊んで物事の可能性を探求するのが好きです。それでもって、私は尋ねます。こうしたのはなぜでしょうか。
定数変数を宣言してから呼び出しに適用したのはなぜですか?その理由は、先ほど言ったように、言語が何であれ、その言語が私たちに何ができるかを知る可能性をからかうのが好きだからです.
実際、そのような定数を宣言しなくても実行できます。コンパイル定義(#define)を使用するか、単に定数値を各呼び出しに入れることができます。結果は同じです。これに関係なく、残りの関数コードを見てみましょう。要素が追加され、名前が一意になるようになっていることがわかります。つまり、同じ名前の要素が2つ存在することはありません。これらを、ビットマップ、資産コード、毎日の変化率、変化の方向を示す矢印、現在の資産価格の順序で作成します。
重要なことを1つ覚えておいてください。資産で使用されるビットマップは32x32ビットでなければなりません。別のサイズを使用する場合は、サイズを変更する必要がありますが、ここでは変更しません。後で、それがどこでおこなわれるかを確認します。ただし、他のすべてのオブジェクトの寸法もここで調整する必要があることに注意してください。したがって、パネルを添付ファイルにあるものよりも大きくまたは小さくしたい場合は、ここで必要な値を設定してください。 これらの各関数は、呼び出しの直前に宣言したオブジェクトに関連付けられていることに注意してください。
では、データが実際にどのように表示されるかを見てみましょう。
inline void UpdateSymbolInfo(int x, const string szArg) { C_Object_Edit edit; C_Object_BtnBitMap bmp; string sz0; double v0[], v1; ArraySetAsSeries(v0, true); if (CopyClose(szArg, PERIOD_D1, 0, 2, v0) < 2) return; v1 = ((v0[0] - v0[1]) / v0[(v0[0] > v0[1] ? 0 : 1)]) * 100.0; bmp.PositionAxleX(sz0 = macro_ObjectName(en_Icon, szArg), x); x += (int) ObjectGetInteger(Terminal.Get_ID(), sz0, OBJPROP_XSIZE); edit.PositionAxleX(macro_ObjectName(en_Symbol, szArg), x + 2); edit.PositionAxleX(sz0 = macro_ObjectName(en_Arrow, szArg), x + 2); edit.SetTextValue(sz0, CharToString(v1 >= 0 ? (uchar)230 : (uchar)232), (v1 >= 0 ? def_ColoPositive : def_ColorNegative)); edit.SetTextValue(sz0 = macro_ObjectName(en_Percentual, szArg), v1 , clrNONE, "%"); edit.PositionAxleX(sz0, x + 62); edit.SetTextValue(sz0 = macro_ObjectName(en_Price, szArg), v0[0] * (v1 >= 0 ? 1 : -1)); edit.PositionAxleX(sz0, x + 24); }
ここでは一連の値が常に最新のものから最も古いものになるようにします。 確信が持てたら、終値を捉えることができます。読んでいるのは日ごとです。パネルに毎日の変化を表示するためです。もう1つのポイントは、返されたデータの量が予想よりも少ない場合、関数をすぐに終了することです。続行すると、最終的にインジケーターが壊れるためです。関連情報がMetaTrader 5ツールボックスに表示されます。しかし、通常は一時的なエラーのためにインジケーターを壊したくありません。
では、次の説明に注目してください。これは、資産価格の変化率に関する情報の正確性に責任を負います。この計算は、価格の上昇率または下落率を決定するために使用される非常に一般的な計算であるため、理解するのに問題はないと思います。
次に、オブジェクトをキャンバスに配置します。まず、画像を配置します。オブジェクトを作成したときに、サイズが別の場所で使用されると述べたことを思い出してください。必要な情報は画像の幅だけですが、それはまさにここです。ただし、前に述べたように、32x32を使用することをお勧めします。別のサイズ、特に大きいサイズが必要な場合は、それに応じて他の値を調整する必要があるためです。
サイズが小さい場合、コードはより小さい画像サイズに調整されるため問題はありませんが、より大きなパネル、特に幅の広いパネルを作成すると、def_MaxWidthの値を調整しないと、情報が非常に奇妙な方法で回転することになります。ここで、コンパイル定義の値を増やす必要があります。ただし、注意してください。設定した値が高すぎると、適切な値を使用した場合よりも、情報が再表示されるまでに時間がかかります。
後続のすべての値は、画像の幅に含まれるこの値に依存するため、状況が決まります。意味をなさないかもしれない瞬間があります。文字に変換される値がありますが、文字自体は画面に表示されません。何が起こっているのでしょうか。ここでは、日々の変化に基づいて、価格が上がるか下がるかを示す矢印を作成します。これは少しわかりにくく思えるかもしれませんが、すべてうまく機能します。ただし、正確にWingdings3フォントを使用していることに注意してください。他のフォントが必要な場合は、これらの値を調整して正しく表示する必要があります。
もう1つ興味深い点があります。 stringとdoubleの値を同じ関数に送信していることです。2つの異なる関数が期待されますが、オブジェクトコードには1つしか含まれていません。ここでは、この関数はオーバーロードされています。ただし、オーバーロードはコンパイラとリンカーによって作成されます。関数内でテストをおこなう必要がありましたが、よく見ると、パーセント値(%)を表すために広く使用されている記号があることがわかります。次に、Editオブジェクト内に移動し、パーセント文字が追加された場所とその方法を確認します。
この関数を終了するには、パネルに色が正しく表示されるように価格を調整するポイントがあります。
これらの変更はすべて、関数内で直接実装されます。システムが実際に呼び出される方法に関して、変更はありませんでした。必要な変更はすべてC_Widgetクラス内にあります。ただし、インジケーターのコードにもいくつかの小さな変更があるので、それらを見てみましょう。
#property indicator_separate_window #property indicator_plots 0 #property indicator_height 32 //+------------------------------------------------------------------+ #include <Widget\Rolling Price\C_Widget.mqh> //+------------------------------------------------------------------+ input string user00 = "Config.cfg"; //Settings file input int user01 = -1; //Shift input int user02 = 10; //Pause in milliseconds input color user03 = clrWhiteSmoke; //Asset color input color user04 = clrBlack; //Price color //+------------------------------------------------------------------+ C_Widget Widget; //+------------------------------------------------------------------+ int OnInit() { if (!Widget.Initilize(user00, "Widget Price", user03, user04)) return INIT_FAILED; EventSetMillisecondTimer(user02); return INIT_SUCCEEDED; }
言及する価値のある唯一の瞬間は、インジケーターの高さの値を設定する場所です。それ以外は基本バージョンと同じです。
ここでインジケーターを終了できます。しかし、取引期間中に役立ついくつかの追加機能を実装するというアイデアはどうでしょうか。これについては次の記事で説明します。
追加機能
取引したい資産をパネルに追加してみてはどうでしょうか。それだけではありません。さらに一歩進みましょう。
これはどうでしょうか。パネルに表示された資産をクリックすると、関連する資産チャートが即座に開かれるので、状況を追跡し、トレンドが始まっていることがわかったときに取引を開始できます。そして、何も入力する必要なく、これらすべてを非常に簡単な方法でおこなうことができます。
いいアイデアですね。これは非常に難しい思っていらっしゃるに違いありません。核物理学かエイリアンテクノロジーの経験と知識を備えたプロのプログラマーでなければいけないと思われるかもしれません。しかしながら、これは、MetaTrader 5プラットフォームでMQL5を使用して簡単に実行できます。
これをおこなうには、メッセージ処理システムに小さなコードを追加する必要があります。これは以下でおこなわれます。
void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) { static int tx = 0; string szRet[]; switch (id) { // ... Internal code... case CHARTEVENT_OBJECT_CLICK: if (StringSplit(sparam, '#', szRet) == 2) { SymbolSelect(szRet[1], true); szRet[0] = ChartSymbol(Terminal.Get_ID()); if (ChartSetSymbolPeriod(Terminal.Get_ID(), szRet[1], PERIOD_CURRENT)) SymbolSelect(szRet[0], false); else SymbolSelect(szRet[1], false); } break; } }
オブジェクトを作成するとき、どうでもいい方法でオブジェクトを作成するのではなく、非常に特殊な形式を使用します。これにより、どのオブジェクトがクリックされ、どの資産に関連付けられているかを分析できます。ここで、オブジェクトが関連付けられている資産を見つけます。これをおこなうために、非常に興味深い関数を使用します。
StringSplitは、フォーマットされた方法に応じてデータを分割できます。クリックされたオブジェクトがどの資産に関連付けられているかを示す情報が適用されます。これに基づいて、MetaTrader 5プラットフォームに対応する資産チャートを開くように指示します。
ただし、これを機能させるには、資産が気配値ウィンドウに表示されている必要があります。この行を実行して、気配値表示に銘柄を表示します。 次に、ウィンドウから現在の資産を削除する前に、そのコードをキャプチャしてクリックした資産がウィンドウに表示されるようにします。成功した場合、チャートにあった資産を気配値表示から削除しようとします。しか、し資産を変更しようとして失敗した場合、開こうとしている資産は気配値表示から削除されます。
パネルを使用したこの資産の変更に関連するいくつかの点に注意してください。まず、必要な資産は、以前の資産と同じ時間枠で開かれます。時間枠は後で変更できますが、最初は同じ時間枠になります。同様に重要なもう1つのポイントは、パネルに適切な数の資産が表示されるように資産を選択する必要があることです。資産をパネルに再度表示するのに長時間かかるためです。時間枠または資産が変更されるたびに、パネルは常にリストの最初の資産から開始されます。
次のビデオは、システムが実際にどのように機能するかを示しています。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10963
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索