リプレイシステムの開発(第46回):Chart Tradeプロジェクト(V)
はじめに
前回の「リプレイシステムの開発(第45回):Chart Tradeプロジェクト(IV)」では、Chart Trade指標の機能を起動する方法を説明しました。その記事の添付ファイルには、転送するファイルがたくさんあったことにお気づきでしょうか。ファイルを追加し忘れたり、誤って重要なものを削除してしまったりといったミスを犯す危険性があるため、この方法はあまり魅力的ではありません。
多くの人がこのような配布・保管方法を採用していることは知っていますが、少なくとも、実行ファイルの配布や保管に関しては少なくとも、実行ファイルの配布と保存に関してはです。
ここで紹介する方法は、MQL5だけでなく、MetaTrader 5そのものを優れたアシスタントとして使うことができるので、非常に便利です。しかも、理解するのはそれほど難しくありません。この記事で説明する内容は、実行可能ファイルやその正しい運用に必要なすべてのものを適切に保存する際に、問題を回避するための助けとなるでしょう。
アイデアを理解する
その前に、この計画を実行するにはいくつかの問題や制約があることを理解しておく必要があります。これらの制約は、MQL5言語およびMetaTrader 5プラットフォームが特定の方法で使用されることを意図していないという事実から生じます。
言語もプラットフォームも、主に市場の可視化ツールとして使用されるように設計されています。それらを異なる形で使いたいと思ったとしても、それが必ずしも不可能というわけではありません。
これがまず最初に気を付けるべき制約です。次に注意すべき点は、実行可能ファイルにはほとんど何でも追加できるということです。「ほとんど」と言ったことに注意してください。理論的には、実行ファイルに何かを追加する方法さえ知っていれば、ほぼすべてを追加できます。もちろん、実行ファイルに情報を含めるとは、その情報を実行ファイル内のリソースとして組み込むことを意味します。
多くの人が画像やサウンドなどを追加しているのを見かけますが、テンプレートファイルを追加して実行ファイルの一部にすることは可能でしょうか。?ドキュメントを確認すれば、それが不可能であることがわかるでしょう。実際、そのような操作を試みると、コンパイラがエラーを返し、プログラムをコンパイルできません。
#define def_IDE_RAD "Files\\Chart Trade\\IDE_RAD.tpl" #resource "\\" + def_IDE_RAD;
上記のコードを含む実行ファイルをコンパイルしようとすると、定義がテンプレートを参照しているため、コンパイラがエラーを生成することがわかります。これは通常、MetaTrader 5プラットフォームで使用される実行可能ファイルには許可されていません。
この種の制限は非常に不便ですが、コストをかけずに簡単に回避できます。もちろん限界もあり、解決すべき問題もいくつか存在します。実際、MetaEditorでコンパイルした実行ファイルにテンプレートを追加することは可能です。ただし、主な問題は、テンプレートを実行ファイルに含める方法ではなく、そのテンプレートをどう活用するかです。
なぜその方法を説明する必要があるのでしょうか。いつものように、ただ使うだけの方が簡単ではないでしょうか。もちろん、慣れ親しんだ方法で使用するのは簡単です。しかし、冒頭で述べたように、すべてを実行ファイルに含める方がはるかに実用的です。MetaTrader 5で特定の機能を実行するために必要なファイルをすべてまとめ、取引に使用するマシンに転送する状況を考えてみてください。もしファイルを1つでも忘れたら、再びそのファイルを探さなければならないかもしれませんし、アプリケーションが正しく動作するようにディレクトリツリーの正しい場所にファイルを配置しなければなりません。
私見ですが、これはかなり煩わしいです。多くの人が同じデバイスでMetaTrader 5を開発・使用していますが、控えめに言っても、これは大きな間違いです。取引と開発に同じマシンやMetaTrader 5を使うことは絶対に避けるべきです。こうすることで、アプリケーションに不具合や脆弱性が生じ、自動EA(エキスパートアドバイザー)を使用した際に正しい動作が損なわれる可能性があります。
開発段階で予期しないことが起こるのを何度も目にしてきました。プログラムやアプリケーションがチャート上で動作する他のアプリケーションと予期せず相互作用し、結果として動作が非常に奇妙になることがあります。これにより、説明のつかないエラーや問題が発生するかもしれません。テストを頻繁におこなっているからではないかと言う人もいるでしょうが、それも事実です。しかし、すべてがテストによるものとは限りません。
このため、MetaTrader 5を使用する際には、開発用と取引用の2つのインストールを用意することが非常に重要です。もちろん、開発者であればの話です。もし開発者でなければ、その必要はありません。
どうやってこれを実現するかを説明すべきか考えました。多くの人は、MQL5の基本やMetaTrader 5の仕組みを完全に理解していないことが多いです。今から説明する内容を理解しようとする際に、難しく感じるかもしれません。わけがわからないと思う人もいるでしょう。しかし実際には、この記事と前回の記事に添付したファイルを、機能性と携帯性の両面で比較できます。機能は同じですが、携帯性という観点では、この方法の方がはるかに優れています。ディレクトリ構造を気にせずに、1つのファイルを送信するだけで済むからです。
重要: 「ディレクトリ構造を気にする必要はない」と言いましたが、それは実行ファイルを特定のディレクトリに保存する場合のみです。もしディレクトリを移動したり名前を変更したりすると、システムが正しく動作しなくなります。
では、これがどのように実装され、自分のコードでどう活用できるかを見てみましょう。
リソース、リソース、リソース
このモデルを使うには、コードを少し変更する必要があります。にもかかわらず、指標のソースコードは変更されなかったので、この記事では紹介しません。C_ChartFloatingRADクラスのコードに若干の変更が加えられましたが、クラス全体に影響を与えるものではないので、実際に変更があった部分だけに焦点を当てることにします。
以下にその部分と説明を掲載します。
068. inline void AdjustTemplate(const bool bFirst = false) 069. { 070. #define macro_AddAdjust(A) { \ 071. (*Template).Add(A, "size_x", NULL); \ 072. (*Template).Add(A, "size_y", NULL); \ 073. (*Template).Add(A, "pos_x", NULL); \ 074. (*Template).Add(A, "pos_y", NULL); \ 075. } 076. #define macro_GetAdjust(A) { \ 077. m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x")); \ 078. m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y")); \ 079. m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x")); \ 080. m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y")); \ 081. } 082. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade 083. 084. C_AdjustTemplate *Template; 085. 086. if (bFirst) 087. { 088. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true); 089. for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0)); 090. AdjustEditabled(Template, true); 091. }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate); 092. m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage); 093. m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage)); 094. m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage)); 095. (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol); 096. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage)); 097. (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2)); 098. (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2)); 099. (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0")); 100. (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0")); 101. (*Template).Execute(); 102. if (bFirst) 103. { 104. for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0); 105. m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x; 106. AdjustEditabled(Template, false); 107. }; 108. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6)); 109. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx)); 110. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny)); 111. 112. delete Template; 113. 114. ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate); 115. ChartRedraw(m_Info.WinHandle); 116. 117. #undef macro_PointsToFinance 118. #undef macro_GetAdjust 119. #undef macro_AddAdjust 120. }
C_ChartFloatingRADクラスのコード断片
ここでの違いには気づかないかもしれませんが、存在します。違いは88行目に見られますが、それほど明白ではありません。そこで、前回の記事からC_ChartFloatingRADクラスのコードを取り出して、この部分に示されているように88行目を正確に変更すれば、これから使用する新しいデータモデルを得ることができます。
元のコードと違って、ここでは文字列しか定義していないことにお気づきでしょう。なぜでしょうか。これまでのようにテンプレートを使用することがないからです。リソースを使います。言い換えれば、テンプレートは指標の実行ファイルに埋め込まれるため、多くのデータを伝える必要はなくなります。
しかし、この変化はすぐに別の問題を引き起こすでしょう。テンプレートを実行ファイルの一部としてコンパイルできない場合、つまりリソースとしてインクルードできない場合、どうやってテンプレートをリソースとして使うのでしょうか。実際、実行ファイルには何でも含めることができます。問題は、具体的にどうすべきかです。
これを理解するために、C_AdjustTemplateクラスのコードを見て、なぜC_ChartFloatingRADクラスの88行目に変更が加えられたのかを理解しましょう。C_AdjustTemplateクラスの完全なコードを以下に示します。それほど大きな変更ではありませんが、それ以上の変更が加えられているので、実際に何が起こったのかを理解するのは興味深いです。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "../Auxiliar/C_Terminal.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_PATH_BTN "Images\\Market Replay\\Chart Trade" 007. #define def_BTN_BUY def_PATH_BTN + "\\BUY.bmp" 008. #define def_BTN_SELL def_PATH_BTN + "\\SELL.bmp" 009. #define def_BTN_DT def_PATH_BTN + "\\DT.bmp" 010. #define def_BTN_SW def_PATH_BTN + "\\SW.bmp" 011. #define def_BTN_MAX def_PATH_BTN + "\\MAX.bmp" 012. #define def_BTN_MIN def_PATH_BTN + "\\MIN.bmp" 013. #define def_IDE_RAD "Files\\Chart Trade\\IDE_RAD.tpl" 014. //+------------------------------------------------------------------+ 015. #resource "\\" + def_BTN_BUY 016. #resource "\\" + def_BTN_SELL 017. #resource "\\" + def_BTN_DT 018. #resource "\\" + def_BTN_SW 019. #resource "\\" + def_BTN_MAX 020. #resource "\\" + def_BTN_MIN 021. #resource "\\" + def_IDE_RAD as string IdeRad; 022. //+------------------------------------------------------------------+ 023. class C_AdjustTemplate 024. { 025. private : 026. string m_szName[], 027. m_szFind[], 028. m_szReplace[], 029. m_szFileName; 030. int m_maxIndex, 031. m_FileIn, 032. m_FileOut; 033. bool m_bFirst; 034. //+------------------------------------------------------------------+ 035. public : 036. //+------------------------------------------------------------------+ 037. C_AdjustTemplate(const string szFile, const bool bFirst = false) 038. :m_maxIndex(0), 039. m_szFileName(szFile), 040. m_bFirst(bFirst), 041. m_FileIn(INVALID_HANDLE), 042. m_FileOut(INVALID_HANDLE) 043. { 044. ResetLastError(); 045. if (m_bFirst) 046. { 047. int handle = FileOpen(m_szFileName, FILE_TXT | FILE_WRITE); 048. FileWriteString(handle, IdeRad); 049. FileClose(handle); 050. } 051. if ((m_FileIn = FileOpen(m_szFileName, FILE_TXT | FILE_READ)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess); 052. if ((m_FileOut = FileOpen(m_szFileName + "_T", FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess); 053. } 054. //+------------------------------------------------------------------+ 055. ~C_AdjustTemplate() 056. { 057. FileClose(m_FileIn); 058. FileClose(m_FileOut); 059. FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE); 060. ArrayResize(m_szName, 0); 061. ArrayResize(m_szFind, 0); 062. ArrayResize(m_szReplace, 0); 063. } 064. //+------------------------------------------------------------------+ 065. void Add(const string szName, const string szFind, const string szReplace) 066. { 067. m_maxIndex++; 068. ArrayResize(m_szName, m_maxIndex); 069. ArrayResize(m_szFind, m_maxIndex); 070. ArrayResize(m_szReplace, m_maxIndex); 071. m_szName[m_maxIndex - 1] = szName; 072. m_szFind[m_maxIndex - 1] = szFind; 073. m_szReplace[m_maxIndex - 1] = szReplace; 074. } 075. //+------------------------------------------------------------------+ 076. string Get(const string szName, const string szFind) 077. { 078. for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0]; 079. 080. return NULL; 081. } 082. //+------------------------------------------------------------------+ 083. void Execute(void) 084. { 085. string sz0, tmp, res[]; 086. int count0 = 0, i0; 087. 088. if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS)) 089. { 090. sz0 = FileReadString(m_FileIn); 091. if (sz0 == "<object>") count0 = 1; 092. if (sz0 == "</object>") count0 = 0; 093. if (count0 > 0) if (StringSplit(sz0, '=', res) > 1) 094. { 095. if ((m_bFirst) && ((res[0] == "bmpfile_on") || (res[0] == "bmpfile_off"))) 096. sz0 = res[0] + "=\\Indicators\\Replay\\Chart Trade.ex5::" + def_PATH_BTN + res[1]; 097. i0 = (count0 == 1 ? 0 : i0); 098. for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0); 099. for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0])) 100. { 101. if (StringLen(m_szReplace[c0])) sz0 = m_szFind[c0] + "=" + m_szReplace[c0]; 102. else m_szReplace[c0] = res[1]; 103. } 104. } 105. FileWriteString(m_FileOut, sz0 + "\r\n"); 106. }; 107. } 108. //+------------------------------------------------------------------+ 109. }; 110. //+------------------------------------------------------------------+
C_AdjustTemplateクラスのソースコード
初心者には奇妙なコードに見えるかもしれません。このコードはかなり贅沢なものですが、前回の記事のようにファイルを移植する手間が省けます。ここでは、実行ファイルにすべてを含めます。しかし、もちろん無料ではありません。何事にも代償はつきものです。主な難点は、コンパイル後に実行ファイルのディレクトリや名前を変更できないことです。これを実行することは可能ですが、他の作業も必要になりますので、ここではプロセス全体については説明しません。
6行目から13行目の定義をよく見てください。ここにあるものはすべて、基本的にすでに慣れ親しんだものです。13行目をよく見てください。これはテンプレートです。以前使ったのと同じテンプレートです。これで別ファイルではなくなり、実行ファイルの一部となります。以下はその方法です。
15行目から20行目にかけては、いつものように定義をおこないます。これは、画像やサウンドを追加したいときにおこないます。しかし、21行目には違うものがあります。コードでは本当に珍しいものです。というのも、MQL5プログラミングでは通常、実行ファイルにリソースをインクルードする場合、エイリアスを使用しないからです。ドキュメントでRESOURCESをご覧になれば、何が起こるか見当がつくでしょう。しかし、これ以外にもまだいくつかの詳細を整理する必要があります。そうすることでしか、すべてを理解することはできません。
ある種の言語では、エイリアスは非常に一般的です。例えば、Excelでよく使われるVisual BasicやVBA (Visual Basic for Applications)では、これらのエイリアスは少し違った方法で物事にアクセスするために使われます。通常、リソースにアクセスするときは、スコープ解決演算子である「::」を使います。これを使用してリソースにアクセスする場合、リソース定義名を操作します。これは複雑に見えるかもしれませんが、実際はもっと単純です。これを理解するために、次のコードを見てください。
01. #define def_BTN_BUY "Images\\Market Replay\\Chart Trade\\BUY.bmp" 02. #define def_BTN_SELL "Images\\Market Replay\\Chart Trade\\SELL.bmp" 03. #resource "\\" + def_BTN_BUY 04. #resource "\\" + def_BTN_SELL 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. long id; 09. string sz; 10. 11. ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0); 12. ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "::" + def_BTN_BUY); 13. ResourceSave("::" + def_BTN_SELL, "BTN_SELL.bmp"); 14. ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp"); 15. 16. return INIT_SUCCEEDED; 17. } 18. //+------------------------------------------------------------------+
フラグメント01:使用例
このフラグメント01では、リソース使用の最初のレベルですべてがどのように機能するかを見て、よりよく理解することができます。この際、C_AdjustTemplateクラスのことは忘れましょう。まずは、すでにコンパイル済みのコードを移植しやすくするために、リソースの正しい使い方を学びましょう。
1行目と2行目では、2つの文字列を定義しています。これらの文字列が指す内容は、3行目と4行目によってコンパイラによってコンパイルされ、実行ファイルに組み込まれます。ここまでは、何も複雑なことはしていません。
11行目では、オブジェクトを作成することを示しています。この場合はビットマップです。繰り返しますが、特別なことは何もません。しかし、これからが第一段階です。実行ファイルに埋め込まれたリソースを使います。12行目を見てください。「::」を使ってリソースを使うことを示しています。この場合、実行ファイルに存在するリソースです。別のプログラムに目を向けることもできますが、物事をシンプルにするために、まずはこのシンプルなコンセプトを扱いましょう。12行目を読むと、コンパイラは次のように理解します。
12. ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "::Images\\Market Replay\\Chart Trade\\BUY.bmp");
しかし、オブジェクト内のテキスト文字列の中身を見ると、上に示したようなものではなく、別のものが見えます。とりあえず、物事の本質を理解するために必要なことなので、基本に立ち返りましょう。
さて、この12行目はわかりやすかったですが、次の行はどうでしょうか。違いはここから始まります。通常、このようなことはコードには書かれていませんが、Chart Tradeの指標を本当に理解するためには、このようなことを理解することが重要です。
13行目は、指定されたリソースを受け取り、指定された場所に指定された名前で保存します。コンパイラは13行目を次のように解釈します。
13. ResourceSave("::Images\\Market Replay\\Chart Trade\\SELL.bmp", "\\Files\\BTN_SELL.bmp");
繰り返しになりますが、ここでは最も基本的なコンセプトを使っています。今回は少し複雑です。ResourceSave呼び出しの以下の点に注意してください。 実行ファイルで利用可能なリソースは、通常のファイルとして保存されます。これは実質的にFileMove関数を使うのと同じことです。したがって、13行目は次のように理解できます。
FileMove("::Images\\Market Replay\\Chart Trade\\SELL.bmp", 0, "\\Files\\BTN_SELL.bmp", FILE_REWRITE);
これが実際に機能する方法です。
14行目では、実行ファイルの内部リソースに直接アクセスするのではなく、そのリソースを間接的に使用しています。これは、13行目でリソースをファイルに保存し、14行目でそのファイルを参照しているためです。次の点に注意してください。12行目とは異なり、14行目では物理的にディスク上に存在するファイルを使用しており、そのファイルは削除されるか変更されるまで同じ場所に留まります。
13行目と14行目で示したアプローチは、MQL5プログラムではあまり一般的ではありません。通常、私たちは実行ファイル内でリソースを管理し、直接そのリソースを使用します。普段はそのように作業をおこなっています。しかし、先に述べたように、このケースでは状況が少し複雑です。ただ、それが同時に興味深い点でもあります。実際のところ、各実行ファイルにリソースを含める必要はありません。そうすることで標準化が難しくなるからです。リソースとしてファイルを使用する方が良いでしょう。通常、WindowsではDLLにリソースが含まれています。これにより、ある程度の標準化が実現できます
同様のことをMQL5でも行うことができます。ただし、何らかの形でコードを標準化する必要があることを忘れないでください。さもなければ、膨大な量の無駄なデータが発生してしまいます。
では、MQL5ではどのようにすればいいのでしょうか。方法は簡単です。リソースを使用する瞬間に、そのファイル名を指定するだけです。以前に「物事はもっと複雑だ」と言ったことを覚えていらっしゃるでしょうか。それがまさにここで起こっているのです。実行ファイル名は無視されました。具体的には、例えば ICONS.LIB という実行ファイルがあり、その中にフラグメント01と同じ画像が含まれているとします。このICONS.LIBはIndicatorsフォルダのルートに保存されています。同じような方法で ICONS.LIB を使用する場合、次のコードが必要です。
01. //+------------------------------------------------------------------+ 02. #define def_BTN_BUY "Images\\Market Replay\\Chart Trade\\BUY.bmp" 03. #define def_BTN_SELL "Images\\Market Replay\\Chart Trade\\SELL.bmp" 04. #define def_LIB "\\Indicators\\Icons.Lib" 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. long id; 09. string sz; 10. 11. ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0); 12. ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, def_LIB + "::" + def_BTN_BUY); 13. ResourceSave(def_LIB + "::" + def_BTN_SELL, "BTN_SELL.bmp"); 14. ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp"); 15. 16. return INIT_SUCCEEDED; 17. } 18. //+------------------------------------------------------------------+
フラグメント02
ここで実行ファイルを定義していることに注目してください。したがって、上記の説明はすべて、このフラグメント02に当てはまります。もちろん、コンパイラがコードをどのように見ているかを理解する必要もあります。では、次のコードを見てみましょう。
01. //+------------------------------------------------------------------+ 02. int OnInit() 03. { 04. long id; 05. string sz; 06. 07. ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0); 08. ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\BUY.bmp"); 09. ResourceSave("\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\SELL.bmp", "\\Files\\BTN_SELL.bmp"); 10. ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp"); 11. 12. return INIT_SUCCEEDED; 13. } 14. //+------------------------------------------------------------------+
フラグメント02(拡張コード)
このような拡張コードを書く人はほとんどいないでしょう。なぜなら、そのようなコードを維持するには多くの労力がかかるからです。しかし、コンパイラにとっては、どの方法を使っても同じように機能します。
さて、簡単に説明が終わったところで、エイリアスの話に戻りましょう。MQL5でエイリアスを使用すると、コンパイラに対して通常とは少し異なる動作を「強制」することができます。たとえば、データ圧縮システムを利用してファイルサイズを小さくすることができ、同時にリソースへの直接アクセスを無視するように設定できます。つまり、そのリソースの定義を直接使うことはできないということです。この点を理解することが非常に重要です。実行ファイルには何でも追加できますが、追加したものを直接使用することはできません。
実際には、エイリアスを介して実行ファイルに追加されたものを使用するためには、リソースそのものではなく、エイリアスを使う必要があります。少し分かりにくいかもしれないので、これをもう少し詳しく解説しましょう。
01. #resource "\\Images\\euro.bmp" as bitmap euro[][] 02. #resource "\\Images\\dollar.bmp" 03. //+------------------------------------------------------------------+ 04. void Image(string name,string rc,int x,int y) 05. { 06. ObjectCreate(0, name, OBJ_BITMAP_LABEL, 0, 0, 0); 07. ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); 08. ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); 09. ObjectSetString(0, name, OBJPROP_BMPFILE, rc); 10. } 11. //+------------------------------------------------------------------+ 12. void OnStart() 13. { 14. for(int x = 0; x < ArrayRange(euro, 1); x++) 15. euro[ArrayRange(euro, 1) / 2][x] = 0xFFFF0000; 16. ResourceCreate("euro_icon", euro, ArrayRange(euro, 1), ArrayRange(euro, 0), 0, 0, ArrayRange(euro, 1), COLOR_FORMAT_ARGB_NORMALIZE); 17. Image("Euro" , "::euro_icon", 10, 40); 18. Image("USD", "::Images\\dollar.bmp", 15 + ArrayRange(euro,1), 40); 19. Image("E2", "::Images\\euro.bmp", 20 + ArrayRange(euro, 1) * 2, 40); 20. } 21. //+------------------------------------------------------------------+
ドキュメントで提供されるソースコード
上のソースコードは、エイリアスを使用した場合のリソースの使い方の一例を示しています。エイリアスは1行目で定義されており、それ以降はリソース名を使うべきではなく、エイリアスを使う必要があります。つまり、19行目を実行すると、実際にはエイリアスを使用すべきところでリソース名を使おうとしているため、エラーが発生します。
示したコードでは、エイリアスがデータをどの構造体でモデル化するかを指定しています。どの可能性もあります。このエイリアスを使用することで、さまざまなデータ型を別のデータ型に変換することも可能になります。これにより、多くの人が通常想像する以上のことが実現できます。私が使用しているコードと、ドキュメントからのコードの違いは、まさに情報の性質にあります。
ドキュメントにあるコードは、エイリアスの使用例を示していますが、この場合、既存のデータを実行ファイルのリソースとして保存する必要はありません。なぜなら、これは私たちが直接使えるデータだからです。しかし、もしそのリソースがテンプレートだった場合、どうなるでしょうか。ルールによれば、MetaTrader 5ではテンプレートをリソースとして使用することができません。したがって、実行ファイルからテンプレートを抽出し、それをリソースではなく通常のファイルとして扱う必要があります。
これは、フラグメント01のコードで示されている方法と同じやり方で実現できます。13行目では、実行ファイルにリソースとして含まれている画像をファイルに変換し、14行目のオブジェクトで使用できるようにしています。
理論的にはこれが可能ですが、実際にはもう少し複雑です。お気づきのように、コード01の13行目では、エイリアスではなくリソース名を使用しています。エイリアスの扱い方はリソースの扱い方とは若干異なりますが、それは今のところ主な問題ではありません。主な問題は、テンプレートを実行可能ファイルのリソースとして含めることができないという点です。そこで、C_AdjustTemplateクラスのコードに注目してみましょう。このクラスは、実行ファイルにテンプレートを含めることができないこと、さらには実行ファイルに保存されたテンプレートを使用できないことを示しています。
C_AdjustTemplateクラスの13行目で、使用するテンプレートを定義します。これは、Chart Tradeの過去の記事で登場したテンプレートと同じものです。ただし、21行目の定義をリソースに変換する際には、リソースを使用するように変換していないことに注意してください。ここではエイリアスを使用しています。このエイリアスは文字列データ型に基づいているため、テンプレートファイルに存在するすべてのコードが、まるで長い文字列のように実行ファイルに追加されます。しかし、この行は大きな不変のようなものです。この点をしっかり理解することが非常に重要です。言い換えれば、テンプレートファイルはシステムによって定数として認識されるものの、実際には定数ではないということです。
テンプレートファイルは、実行ファイル内のIdeRadエイリアスを持つテキスト文字列として考えられます。それをどう使うかを考え始める必要があります。
まず理解しなければならないのは、このテンプレートをOBJ_CHARTオブジェクトで直接IdeRadエイリアスを使用することはできないということです。これは不可能です。このデータをファイルに戻す必要があります。しかし、テンプレートはリソースではないため、ResourceSave関数はこのようなケースには対応できません。それでも、他にできることがあります。そのため、45行目から50行目にかけてコードを記述しています。
ただし、この方法を使用するのは必須だからではなく、既存の機能を持つクラスのコードを変更したくないからです。IdeRadのエイリアスを使用してテンプレートの内容を直接読み込み、必要な変更を加えることもできます。しかし、これはすでに作成されテストされているロジックを複雑にしてしまう可能性があります。
では、45行目から50行目で何が起こるかを考えてみましょう。テンプレートを操作するためにコンストラクタを呼び出す際には、呼び出しレベルとアクセスされるファイル名を指定します。これが最初の呼び出しの場合、45行目でテンプレートファイルが作成されます。ファイルは47行目で作成され、49行目で閉じられます。この2行が存在するだけで、テンプレートは空っぽになります。しかし、48行目はマジックが起こる場所です。
ここで、その行の内容全体をファイルの中に挿入します。この行は何でしょうか。それはIdeRad変数にある内容です。ちょっと待ってください。テンプレートをリソースとして実行ファイルに保存するということでしょうか。問題を回避するためにエイリアスを割り当て、その内容を復元したいときにはエイリアスの内容を取り出してファイルに書き込むのです。本当にですか。はい、本当にです。すると、なぜ今まで誰もこのようなことを考えなかったのか、あるいはその方法を示さなかったのかという疑問がわくかもしれません。それは分かりません。誰も実際にやろうとしなかったからか、誰もその方法を想像できなかったのかもしれません。
49行目でファイルが閉じられた後、残りの処理は先に説明したのと同じです。これは、MetaTrader 5がリソースではなく、ディスク上のファイルを扱わなければならなくなったからです。
最後に、無視できない小さなディテールがあります。多くの人は、Chart Trade指標を操作する際に何かを想像したり、データを操作して何が起こっているのかを理解しようとするかもしれません。質問はボタンについてです。チャート上で指標を実行すると、ボタンにどのようにアクセスするのか理解できないでしょう。これは、MQL5の経験が少ない場合にのみ起こることです。ボタンとして記載されていた画像はどこにも見当たりません。テンプレートの中身を見ると、こんな感じになります。
bmpfile_on=\Indicators\Replay\Chart Trade.ex5::Images\Market Replay\Chart Trade\BUY.bmp
bmpfile_off=\Indicators\Replay\Chart Trade.ex5::Images\Market Replay\Chart Trade\BUY.bmp
何の意味もありません。特にMQL5を学び始めたばかりの方にとってはそう感じるかもしれません。しかし、同じパターンをチャート上で再現してみると、正しく表示されていることに気づくでしょう。これはどのように可能なのでしょうか。参照されている画像はどこにあるのでしょうか。それが問題です。実は、画像は実行ファイルの中に含まれているのです。つまり、テンプレートは使用する画像を直接指定します。これは、先に紹介したコードやフラグメント02の拡張で見られたものと同じです。問題のコードは正確には08行目ですが、理解しやすくするために再度以下に示します。
08. ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\BUY.bmp");
コードとテンプレートの両方で行が似ていることに注目してください。これは、MetaTrader 5とMQL5言語が十分に研究されていないことを示しています。信じてください、MetaTrader 5とMQL5は、あなたの想像をはるかに超える可能性を秘めています。多くの人が口にするような制約があっても、思っているよりもずっと多くのことが実現可能です。他の言語に頼ることなく、です。
結論
この記事では、多くの人が不可能だと思っている実行ファイル内でテンプレートを使う方法を紹介しました。この知識はここでは簡単に理解できるものとして紹介されていますが、実際にはそれ以上のことが可能です。DLLを利用すれば、ここで紹介したような作業をさらに広範囲におこなうことができます。
すべてはプログラマーの創造性、能力、個性にかかっています。「何かができない」と言う人もいれば、「手段がない」と言う人もいます。しかし、挑戦し、成功を収める人たちもいるのです。困難に直面したときに諦めてしまうのではなく、問題を解決する人になりましょう。私のモットーはこうです。
真のプロのプログラマーは、他の人がただ見るだけの問題を解決します。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/11737
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索