取引き履歴に基づくトレーディングのプレーヤー
百聞は一見にしかず
トレード履歴の視覚的分析はトレーダーの分析的作業の大部分を占めます。もしそうでなければ、数字の世界を絵の世界に替えるテクニカル分析はないでしょう。ということでおわかりですね。人間の知覚の80%は視覚によるものなのですから。情報を一般化する統計では数多くの微妙な差異を伝えることはできません。そして、デジタル世界の直観的理解が可能とする可視化によってのみそれが可能なのです。百聞は一見にしかず、とはよく言われます。
本稿ではトレード履歴の視覚表示を自動化するための Expert Advisor の書き方については言及しません。オブジェクト間の情報伝達、大規模なアプリケーション計画、チャート管理、異なるシンボルの情報同期などについて考察していきます。
これまでどおり、まず再現アプリケーションと関連するスクリプトのメリットについてお話し、それからコード分析に入っていきます。
MetaTrader 5 ストラテジーテスタにおける取引実行
プレーヤーの操作は MetaTrader 5 HTML レポートに基づき行われます。よって自動売買チャンピオンシップ2010の履歴を取得するには、ATC-2010 の必要なアカウントにログインし HTML レポートとしてトレード履歴を保存します。
一方、自動売買チャンピオンシップ2008 のサーバーは停止されているので、同じ要領ではその履歴を取得することはできません。サイトには単一のzip形式のアーカイブとしてすべての出場者の一般報告書があります。Automated_Trading_Championship_2008_All_Trades.zipがそれです。
"Automated Trading Championship 2008 All Trades.zip" アーカイブは \Files フォルダ、または MetaTrader 5 インストールディレクトリに解凍する必要があります。
自動売買チャンピオンシップ 2008の履歴を分析するには、履歴を解析し、指定のログインを検索してバイナリファイルに保存するスクリプトレポートパーサー MT4 を実行する必要があります。このバイナリファイルはプレーヤーレポート Expert Advisorによって読み取ります。
プレーヤーレポート EA は指定された必要なログインをしてストラテジーテスタで実行する必要があります。検証が終わるとHTML 形式でレポートを保存します。指定のログインは検証結果に影響を与えませんが、入力パラメータ 「ログイン」としてレポートに表示されます。それでレポートをよりよく識別することができます。レポートは同一 Expert Advisorによって作成されるため、それらレポートには初期設定とは異なる名前を付けることが推奨されます。
スクリプトレポートパーサー MT4 にも履歴を閲覧したい出場者のログインを指定する入力パラメータ「ログイン」があります。出場者のログインは知らないがニックネームを知っている場合は、ログインゼロ値(初期値)でスクリプトを開始します。この場合、スクリプトはログインによる検索を行いません。それはすべてのログインがアルファベット順で挙げられているcsvファイルを作成します。ファイル名は "Automated Trading Championship 2008 All Trades_plus"です。このファイルで必要な出場者をみつけたらすぐに指定のログインでスクリプトを再び実行します。
スクリプトレポートパーサー MT4 と プレーヤーレポート EA の連携により MetaTrader 4 フォーマットでトレード履歴を基にMetaTrader 5 ストラテジーテスタ の標準html レポートを作成します。
プレーヤーレポート Expert Advisor は現実に実行されているのとまったく同じようにトレードを実行するわけではなく、近似的に実行するにすぎません。それはクオートが異なるためです。レポート上の時間および実行中の遅延が分に四捨五入されるのです。多くの場合、相違は数ポイントで、そこでそれはトレードの10%に起こります。ただ、たとえばストラテジーテスタ内の収益を17万~16万減らせば十分です。すべては遅延による取引ボリュームに依存します。
プレーヤーの操作
前述のように、プレーヤーを使用してトレード履歴を見る場合、 自動売買チャンピオンシップ2008 は別のアプリケーションを使って、また自動売買チャンピオンシップ2010 は直接見ることができます。
プレーヤーはすべての MetaTrader 5 レポートをサポートし、そのためストラテジーテスタで実行するあらゆる Expert Advisor のトレード履歴やテスターでフォーマットされていないマニュアルトレーディングをみることができますが、それは 「ツールボックス」 ウィンドウの「履歴」 タブからのレポートとして保存されているものに限ります。
Player History Trades exp v5 Expert Advisorのパラメータ
- ストラテジーテスタレポートのHTML ファイル
- 要求されるチャートのリスト
- EA削除時にチャートを削除
- 履歴の開始と終了
- ジェネレータ期間
- 取引きコメントのフォント
- 取引きコメントのフォントサイズ
- 売買処理の色
- スピードの数値
- 進捗ボタンの縦サイズ
MetaTrader 5 ストラテジーテスタのレポートは取引履歴プレーヤーへの入力ファイルとして使用されます。Player History Trades exp v5 EAの『ストラテジーテスタの html ファイル名』入力パラメータとして指定されるのはレポートファイル名です。プレーヤーをスタートするとき、ユーザーは入力変数「履歴開始」および 「履歴終了」に再現期間を指定することが可能です。
こういった変数が設定されていなければ、プレーヤーはトレード履歴から最初の取引き~最後の取引きまでの期間を取ります。トレーディングに使用されるシンボル数は重要ではありません。アカウントにおける開始取引時刻と終了取引時刻のみが対象とされます。
また、分析するチャートのシンボル名を設定することも可能です。名前は「要求されるチャートリスト」変数内で列挙として指定する必要があります。この変数の分析は大文字小文字およびセパレータタイプを区別しません。変数が設定されていなければ、アカウントで取引されたすべてのシンボルはオープンです。そして時としてそれは多く存在します。
たとえばManov はトレーディングで12種類の通貨ペアを使用しました。私は一度に4種以上のシンボル使用はお薦めしません。その理由は、まず配列するのに好都合です。チャートが多いと再現スピードが下がります。各シンボルは標準ループで処理されるため、シンボル数が増えるとティック生成が遅くなるのです。
またプレーヤーはトレーディングで使用されていないシンボルを指定しても動作します。この場合、チャートはどの取引きも表示せず、他のチャートとなんら変わりはありません。その上、アタッチされたバランスインディケータも持ちます。ただし、それは任意のバリアントの一般的残高履歴のみ表示します。
私は故意にパラメータ「EA削除時にチャートを削除します」 の記述はとばしました。それは Expert Advisorのふるまいに関わるもので、管理に関わるものではないからです。重要なことは Expert Advisor はそれが処理する多くの情報を分析するということです。EA が持つ情報の中にはファイル形式の分析に有用なものもあると判断しました。Expert Advisor は各シンボルに対するトレード処理を含む csv ファイル、および全シンボルの残高と同期するファイルを作成します。それは複数通貨バスケット内のシンボルを検出するのに役立ちます。
同様の変数は Expert Advisorにより自動で開かれたチャートを削除するのにも使われます。従来EA は処理の最後で作業場所を清掃します。ただ、 EA のコントロールなしにチャートを念入りに分析したければ、パラメータ「EA削除時にチャートを削除します」を'false'に設定してEAを起動する必要があります。
以下のパラメータはあまり重要なものではありません。
ジェネレータ期間はティックジェネレータの初期パラメータを設定します。ここで「ティック」期間は従来の意味合いで使われていません。ここではそれはレベルの変化を意味します。Expert Advisorでは、ティックはバーの4点に従い作成されます。パラメータ「ジェネレータ期間」 はジェネレータの初期状態を設定します。さらに、動作中にプレーヤーのこのパラメータを変更することも可能です。
なぜすべての期間を M1から始めて作成するのでしょうか?なぜジェネレータ期間を変更する必要があるのでしょうか?問題は、大きな時間枠のバーが多くの M1 バーを含むことで、それにより作成の処理スピードを上げる必要が出てきます。そのため、期間変更機能が実装されるのです。ジェネレータにはすべての時間枠ではなく、数個が実装されるだけです。コード内でそれを変更する方法はのちに述べます。
パラメータ「取引きコメントのフォント」 は有用です。たとえば取引に対するコメントが取引きそれ自体の閲覧を妨げる場合です。サイズを1に設定したら、題辞は細い線のようになり、閲覧を妨げることはありません。ツールチップからオブジェクト名を見つけ、「オブジェクトリスト」タブで取引きとポジションのボリュームを閲覧することができます。
トレード履歴は個別の取引で描画されますが、引かれる線はポジションタイプに依存します。
パラメータ「買いの色」 および 「売りの色」 を使用し、お好みの色を設定することができます。
上のスクリーンショットではポジションレベルは取り引きレベルとしばしば異なっているのがわかります。
収益はポジションレベルを基に計算されます。ですから、トレンドラインと共にポジションを表示し、縦線を使ってポジションレベルおよび取り引きレベルをつなげることにしました。ポジションレベルのそばのコメントは以下の情報を表示しています。
[deal volume|position volume]
取り引きタイプがポジションタイプ(たとえば部分的なクローズがあるなど)と一致しなければ、ボリュームは別の記号を使って表示されます。
[<deal volume>|position volume]
冒頭で取り引きレポートに表示されるボリュームを確認することができます。ポジションボリュームは前回のポジション状況を基に計算され、取り引きによって変更されます。
パラメータ 「スピード数」 は再現スピードを下げる段階の数値を決定します。プレーヤーは最高速度でスタートするようにできています。さらにスピードはパラメータ値「スピード数」内も変更が可能です。よってスピードボタンとジェネレータ期間がトレード履歴の再現スピードを管理するツールのすべてです。
最後のパラメータは 「進捗ボタンの縦サイズ」です。進捗バーのボタンは大きい方がよいという方のために作成しました。通常私はコントロールの背後にチャートを隠さないようにしています。これがパラメータ「進捗ボタンの縦サイズ」を8 に設定している理由です。
では、プレーヤーのコントロールに話を移します。
スピード は左右の矢印で調整します。コントロールモードは真ん中のボタン(正方形)の状態で決まります。押されていない状態ではスピードを変化させ、押されている状態ではジェネレータ期間を変化させます。
バランスインディケータ をコントロールするオブジェクトは楕円形で表示されますが、実際は視覚サイズの極限を超える大きな2つのボタンで作成されています。左側のボタンはチャートにバランスインディケータを追加や削除、右側のボタンはデータ内容をコントロールします。
「全」状態ではアカウントの残高合計が表示され、「合計」状態はインディケータが実行する対象のチャートシンボル残高のサンプルを表示するようになっています。インディケータのコントロールは非同期です。それはインディケータが一つのチャートで実行され、他のチャートでは実行されないことを意味しています。
残高のインディケータをコントロールするオブジェクトはチャートの唯一の例外で、その他オブジェクトはすべて同期します。言い換えれば、シンボルに加えられる変更は自動的に他のシンボルに対しても加えられる、ということです。
再現/停止 を押すとすぐにどの処理をおこなうのか表示します。再現中は、2本の小さいラインが表示されますが、それを押すと処理は一時停止します。そして停止中は三角が表示されます。これを押すとプレーヤーは処理を開始します。
進捗ライン はトリガーとして作成されるコントロールボタン100個で構成されます。ボタンが1個押されると、その他のボタンすべては押されない状態になります。ボタンが100個あるので、再現期間は100の部分に分けられます。バー数が100で割り切れない場合は最後の部分に余りが付け足されます。そのため設定に「履歴スタート」と「履歴終了」があるのです。これらパラメータを変更すると、履歴の必要な期間に移動することができます。
ボタンを押すと、ティックの内部ジェネレータの日付が変わり、ゼロバーに移動します。そのボタンを押さないが、アクティブなボタンの外部でジェネレータの内部時刻がすでに変化していると、プレーヤーは勝手に対応するスイッチを入れます。
よってオブジェクト「進捗ライン」は進捗のインディケータでありまた移動のアクティブなコントロールでもあるのです。プレーヤーのコントロールオブジェクトは自動で隠され、チャートの中央に拡張されます。このため進捗ラインの特定のボタンを押す必要があれば、チャートを全画面に拡張します。
ここからプレーヤーで管理されるチャートの動作についてお話します。プレーヤーはすべてのチャートを同期させますが、それはメインのチャートでスケール、チャートタイプ、スキーム色、ゼロバーの移動などが処理されると、別のチャートでもそういう処理が行われるということではありません。
変更には時間枠の変更も含まれます。プレーヤーによってメインのチャートとみなされるものはコントロールが表示されるチャートであり、処理のブルーの線を伴うチャートではないことに留意が必要です。通常それらは同一のチャートですが、必ずしも同一でない場合もあるのです。あるチャートをアクティブにするにはチャートフィールドでそれをクリックします。
プレーヤー使用についてのポイントがあります。2種類のオブジェクトが同じフィールドにあると、ボタンは動作を停止します。ビッドラインがプレーヤーフィールドをクロスするとき、ボタンを押すために別のチャートに切り替えたり、チャートの縦スケールを変更する必要があるのはこのためです。
ビデオで再現されているのはATC 2010の参加者の一人 Manovのトレーディングです。それを作成するのに、パラメータ login=630165 と password=MetaTrader を使ってクライアント端末のこの人のアカウントに連携しました。トレードレポートは、フォルダterminal_data_folder\MQL5\Filesに名前 ReportHistory-630165.html で保存されました。このファイルはアーカイブとしてダウンロードし、指定のフォルダに解凍することが可能です。
開始準備
- すべてが動作するように player_history_trades.zip をダウンロードし、フォルダ terminal_data_folder/MQL5/Indicatorsに解凍します。
- コピーしたフォルダ Player History Trades を開き、MetaEditorのルートディレクトリ内で4件のファイルをコンパイルします。ファイルをコンパイルする順番は問いません。
- トレードレポート内のすべてのシンボルについて要求される履歴期間は時間枠 М1 が使用可能だということを確認します。そのため時間枠 M1 で必要なチャートを手動で開き、縦線を引いてキー Ctrl+B を使ってコンテクストメニューから オブジェクトリスト を開きます。縦線の日付をトレーディング開始日に変更します。
- そして「表示」ボタンを押します。クオートがなければ、その理由は2つ考えられます。クオートがダウンロードされていないか、パラメータ「チャート内最大バー」が小さすぎるかです。それを確認するにはツール->オプション->チャートと進みます。
これですべてが動作します。
開発開始
アプリケーションを開発するには計画を立てる必要があります。それはそれはのちに調査するブロック図となり、またコードとなります。プロジェクトそのものはもっと以前にスタートします。プロジェクトは常に、ユーザーが求めるアプリケーションのプロパティを決めるところからスタートします。それではトレード履歴プレーヤーはどのようなプロパティを持つべきでしょうか?
- 複数通貨対応である
- 要求されるチャートを自動で開く
- 便利なナビゲーションインターフェースと履歴を双方向にスクロールする機能
- 全チャート上で同期表示
- 再現の開始/一時停止
- 表示されるチャート数とシンボルが選択可能(初期設定モードも)
- プレーヤーが動作する期間が選択可能(初期設定モードも)
- 取り引き履歴をチャート表示
- 残高と資本の履歴表示
- シンボル残高(資産)とアカウントのトータル残高(資産)を個別表示
最初の4個は一般的コンセプトを決定します。その他のプロパティはメソッド実装の方向を決めます。
プレーヤー操作の一般的計画
- HTML レポートをロードします。
- それを取り引きに対して解析しポジション履歴を復元します。
- 注文の開始/終了キューとして取り引きを準備します。
- ユーザーコマンドにてインディケータ形式(資産チャート、ドローダウン等)で必要な比率計算とともに取り引き履歴の変動状態を表示します。
- その他比率とともにチャート上の情報パネルを表示する手配をします。
また MetaTrader 4 レポートに基づきストラテジーテスタ内のトレーディングに特殊なExpert Advisorが要求されます。
- 解析された取り引きは Expert Advisor用のバイナリデータファイルとして書く必要があります。
- MetaTrader 5 ストラテジーテスタのレポートを作成します。
これが開発開始の一般的スキーム、要望の特定化です。それがあればコードを上から下に、コンセプトから機能性の実装へと書く計画ができます。
本稿が長くならないよう、これからコードの重要箇所のみ述べていきます。コードは詳細にコメントされているので、コードを読むうえで問題に出くわすことはないと思います。
注文と取り引き
今のところ、トレードコンセプトは2つあります。MetaTrader 4で使用されていた古いコンセプトと実際の売買および MetaTrader 5で使用されているいわゆる「ネッティング」コンセプトです。両者間の違いについての詳述は 『MetaTrader 5における注文、ポジション、取り引き』 稿にあります。
ここでは一つだけ重大な相違について述べていきます。MetaTrader 4は、注文は開始時刻、始値、トレードボリュームについての情報を格納する入れ物であると言うことができます。入れ物が開いていればそれはアクティブなトレード状態です。入れ物を閉じるとそこの情報はすべて履歴に移動します。
MetaTrader 5では、ポジションがそのような入れ物として使われます。そこで重大な相違はポジション履歴の不在です。そこには注文と取り引きの共通した履歴のみが存在します。履歴にはポジション履歴を復元するために必要な情報がすべてありますが、時間をかけて考えを整理する必要があります。
識別子ORDER_POSITION_ID および DEAL_POSITION_IDをそれぞれに用いて、選択された注文または取り引きがどのポジションに属するか確認することができます。 よって履歴をMetaTrader 5に適したフォーマットに変換するのに、私は MetaTrader 4 履歴の注文を開始取り引きと終了取り引きに二分しました。.
HTML 解析ツール
コンピュータの俗語をご存じない方のために解析ツールとは何かお話します。解析とはテキストやあらゆる連続した語彙(記号、語、バイト等)の構文(文法または単語)分析を意味し、なにについて今後の計算や変換を行うかに応じ、指定された文法に対し入力テキストが対応しているか確認し、解析木を構成することです。
解析ツールでは2大クラスCTable および CHTML が使用されます。CTable クラスの用途詳細は『MQL5の電子テーブル』 稿にあるので、ここでまたそれについて述べることはしません。
HTML解析のために CHTML クラスを作成しました。私の基本的考えについて述べるとそれだけで一つ記事が書けると思います。ただ記事にするにはこのクラスは単純なので、手短にお話します。
このクラスの一般的コンセプトは「タグ」という語で述べることができます。タグは囲いのある関数と表現できます。たとえば、タグ(ヘッダ、キャスケット)ですが、ここで『ヘッダ』はタグタイトル(ページの外観を調整するtag変数は通常そこで指定されます。)と『キャスケット』はタグの入れ物の内容です。そのようなタグがHTML 言語全体を構成するのです。
クラスの一般的ストラクチャはオブジェクトの三段階呼び出しで書くことができます。CHTML クラスのインスタンスは本文の可能なすべてのタグのオブジェクトを作成します。タグ関数はテンプレートによって作成され、関数同士の相違点は名前と2つのフラグ設定だけです。
ヘッダの有無を判断するフラグもあればキャスケットの有無を判断するものもあります。そういったフラグによってすべてのタグは共通のストラクチャであることが可能なのです。タグのインスタンスはそれぞれ本文の中で CTegsクラスのインスタンスを作成します。このクラスにはすべてのタグの共通メソッドが含まれ、ドキュメント本文内で必要なタグを検索する主要処理を行います。
三段階は以下のようなものです。
h.td.base.casket
この記述が意味するのは、オブジェクト 'h' がネスト化したオブジェクト'base' ( CTegsクラスのオブジェクト)を使い、さらにオブジェクト'td' (<td header >casket</td>タグのインスタンス)によって「キャスケット」変数の値を呼ぶということです。
このクラスはまたタグ検索のメソッドもインクルードし、それらはパブリックメソッド内でコンバインされます。
h.td.Search(text,start);
その結果はタグの終わりの検索地点を返し、タグの変数 'header' と 'casket'に書き込みます。
クラス内に準備されたその他関数は使用されません。よってそれらについては述べません。他にもっとおもしろいことがあるのですから。
+HTML ドキュメント作業の記述の末尾で、本稿で2種類のパラメータが使用されることを述べたいと思います。それらはファイルから取得した情報を保存するタイプが異なるだけです。最初のタイプは「文字列」タイプの単一変数にドキュメント全体を保存します。それはプレーヤーで使用されます。二番目のタイプはレポートを一行ずつ解析します。それはチャンピオンシップ 2008の履歴を準備するスクリプトで使用されます。
ではなぜ2とおりの方法を使うのでしょうか?問題は CTegs クラス関数の正しい処理に関してで、タグはすべて分析された文字列にセットされる必要があります。そしてそれは常に可能とは限らないのです。. たとえば、テーブル、html、本文(それらはマルチラインです)のようなタグの場合です。文字列タイプの変数は作表記号なしで (私の計算によると)32,750 個までシンボルを格納することができます。そして '\r' (各 -th シンボルのあと)を使うと(それぞれ32,748番目の後)シンボルを 2 000 000 個まで格納できました。この数字まできて、それ以上はやめました。おそらくもっと格納できると思われます。
それでなぜ2通りの方法を使うか?です。重要なことは、プレーヤーの汎用解析のために適切なテーブルを見つける必要があるということです。検証レポートと取り引き履歴レポートに要求されるテーブルは異なる場所にあります。安定性確保のために(両レポートを理解する解析ツールとして)私は "deals"を持つ 'td' タグにてテーブルを検索する方法を使います。
チャンピオンシップ2008のレポート構成は判っていて、必要なテーブルを検索することはありません。ただ、レポートドキュメントは巨大(35 MB)で、レポート全体を単一変数にセットするには時間がかかります。このため第二の解析方法が必要なのです。
プレーヤー
『開発開始』項でプレーヤーに求める10の条件について述べました。複数通貨対応が最初にきているので、Expert Advisor はチャートを管理する必要があります。プレーヤーが動作するために要求される機能性をすべて持つ個別のオブジェクトによって各チャートが処理されるなら合理的です。
ここでは履歴と連携しているので、いつでも欲しいときに履歴を取得したいと思うよりも、処理が中断されないようにする個別の履歴例が必要です。また、同一履歴を繰り返し取得するのはプレーヤーにそれを保持するのに比べると無駄が多いと思われます。最後に以下のスキームです。
オブジェクト指向プログラミング (OOP) によりブロックシステムを用いて巨大なアプリケーションを書くことが可能となります。作成済みのExpert Advisor のコード部分は前もってスクリプト内に記述、デバッグし、最小の適応でExpert Advisorに連携することが可能です。
そのよう開発スキームは便利です。というのも連携されたコードにはエラーが含まれず(スクリプトでエラーなく動作するので)、発見されたバグはどれも適応のエラーだからです。コードが下から上へ書かれるとき、手順としてすべてを一か所に記述する場合はそのようなメリットはありません。そして新しいバグはアプリケーションのあらゆる箇所に出現します。
ゆえに、上から下へのプログラミングには、簡素であることとアプリケーションの記載スピードの点でメリットがあります。「ここでいうシンプルとは?」という質問が出るかもしれません。寓話をもってこれに答えたいと思います。自転車に乗る練習は困難ですが、一度乗れるようになると練習の過程すら覚えていないものです。後は自転車のスピードを楽しむだけでしょう。OOP 構文を一度覚えてしまうと多大なメリットを手にします。
話を続けるためにはOOPの用語が3つ必要です。連携、集約、コンポジション集約です。
- 連携 オブジェクト間のつながりを意味します。集約およびコンポジション集約は連携の特殊ケースです。
- 集約 はオブジェクトが関係の「一部」 で連携していることを意味します。集約は重複的でありえます。すなわちあるオブジェクトが複数のクラスやオブジェクトに集約される可能性があります。
- コンポジション集約 は集約の厳格版です。「部分」要求に加え、この「部分」は異なる「所有者」に同時に所属することはできず、所有者が削除されるとそれも削除されます。
連携には集約およびコンポジション集約が含まれるため、詳細分析の間、集約またはコンポジション集約として述べられないケースは全部連携と呼ばれます。一般的に3語はすべて連携と呼ばれます。
class Base { public: Base(void){}; ~Base(void){}; int a; }; //+------------------------------------------------------------------+ class A_Association { public: A_Association(void){}; ~A_Association(void){}; void Association(Base *a_){}; // At association, data of the bound object // will be available through the object pointer only in the method, // where the pointer is passed. }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class A_Aggregation { Base *a; public: A_Aggregation(void){}; ~A_Aggregation(void){}; void Aggregation(Base *a_){a=a_;}; // At aggregation, data of the bound object // will be available through the object pointer in any method of the class. }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class A_Composition { Base *a; public: A_Composition(void){ a=new Base;}; ~A_Composition(void){delete a;}; // At composition, the object becomes the class member. };
MQL5 にはパラメータを用いてポインターを渡す関数があります。
GetPointer(pointer)
パラメータはオブジェクトポインターです。
例を示します。
void OnStart() { Base a; A_Association b; b.Association(GetPointer(a)); }
私のコードのOnInit() で呼ばれる関数は連携を使うことがよくあります。コンポジション集約が適用されるのはCHTML クラス内です。私は CPlayer クラス内でオブジェクトをバインディングするのに集約およびコンポジション集約を一緒に使います。たとえば集約を用いると、クラス CChartData と SBase のオブジェクトは、プレーヤー内でコンポジション集約によって作成されるすべてのオブジェクトに対する共通データフィールドを作成します。
それは以下のように見えます。
オブジェクトが CPlayer クラス内で複合的に作成されるクラスには機能性を拡張するテンプレートストラクチャがあります。テンプレートの用法は『C++ 言語テンプレートの代用としての仮テンプレートの使用』 稿に述べられていますので、ここでは省きます。
クラス用テンプレートは以下です。
//this_is_the_start_point //+******************************************************************+ class _XXX_ { private: long chart_id; string name; SBase *s; CChartData *d; public: bool state; _XXX_(){state=0;}; ~_XXX_(){}; void Create(long Chart_id, SBase *base, CChartData *data); void Update(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void _XXX_::Create(long Chart_id, SBase *base, CChartData *data) { chart_id=Chart_id; s=base; // binding data to the player structure d=data; // binding data to the chart structure name=" "+ChartSymbol(chart_id); if(ObjectFind(chart_id,name)<0)// if there is no object yet {//--- try to create the object if(ObjectCreate(chart_id,name,OBJ_TREND,0,0,0,0,0)) {//--- } else {//--- failed to create the object, tell about it Print("Failed to create the object"+name+". Error code ",GetLastError()); ResetLastError(); } } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void _XXX_::Update() { }; //+******************************************************************+ //this_is_the_end_point
テンプレートによって空のクラスを作成し、それらを連携し、要求をすべて正確に処理するか確認しました。その後初めて必要な機能性をコンポジット集約クラスに書き込みました。これがいわゆるトップダウンプログラミングです。誤動作がある場合はどこに原因があるか判ります。
プログラム構築の一般概念は明らかとなりました。ここから特定の部分に話を進めていきます。
まずPlayer History Trades exp v5 Expert Advisorで宣言された関数の処理を見ます。
OnInit() 関数はいつもどおり情報を準備します。それがストラテジーテスタのレポートを解析する CParser_Tester クラスのオブジェクトを作成、取り引きされた金融商品のリストを取得、取り引きを処理、ポジションボリュームおよびレベルを計算、チャートに履歴の線を引きます。最後の項目は日付が渡された直後にオブジェクトが削除されない理由を述べます。問題はチャートが開かれる前に情報が準備されることです。そしてグラフィカルオブジェクトは描画のためにチャートID を要求します。CParser_Tester クラスがのちに削除されるのはこのためです。
その後、トレーディングに使用されるシンボル名を得ているので、 Balance_Process() 関数が呼ばれ、それは M1 履歴のトータル残高と資産同様、それに渡される全シンボルの残高および資産を計算します。
この部分では、アプリケーションは特に情報欠如について敏感です。そのため、あるシンボルに対する情報がダウンロードされない場合 EA の実行中断を実装したのです。アプリケーションが実行を停止したとき、ダウンロードが必要なシンボル情報の警告を表示します。
Balance_Process() 関数の実行結果はM1における残高および資産履歴のバイナリファイルで、のちに残高インディケータによって必要な期間に区切られます。ここで少し先に進みます。残高インディケータの処理については後にお話しします。
Expert Advisor起動の次のステップはシンボル選択です。ここでは入力パラメータ「要求されるチャートリスト」を分析します。必要なシンボルが "Market Watch" リストにある場合は、それをシンボル配列に追加します。このようにして『馬鹿者』から自分自身を守るのです。ユーザーはシンボル名の代わりに訳の分からない言葉を使ったり印刷ミスをするからです。
ユーザーから開くように要求されるシンボルリストを検証したところで チャートを開くことができます。それは次の関数を使って行います。
ChartOpen(symbol,period)
この関数はパラメータ内でそれに渡されるシンボルと期間を持つチャートを開きます。理論的には、この関数は開かれたチャートの ID を返しますが常にそうなるとは限りません。
IDを失った結果、アプリケーションは誤作動します。それを避けるため私は関数を2つ作成しました。
ChartTotal(arrayID); // get the list of charts before opening additional charts CurrentChart(arrayID); // get the list of chart for operation
関数のひとつはチャートが開く前に実行され、もうひとつはチャートが開いた後に実行されます。ChartTotal() 関数は EA (EAが実行されている対象チャートを含め)起動前に開かれたチャートのリストを取得します。
CurrentChart() 関数はその情報を取得し、すでに開かれたチャートを考慮しつつ新規リストを作成し、そしてリストの差異に従いパラメトリック配列に対し EA によって作成されたチャートのIDを渡します。このスキームはチャートが開くという事実に従って動作するので信頼性があります。
必要なチャートの IDを取得したので、それらを制御することができます。そのためにループ内のチャートすべてを調べ、CParser_Tester (のちに必要になるとお話したのを覚えていますか)のオブジェクトを用いて取り引き履歴を描画しチャート管理のオブジェクトを作成します。
OnInit()で最後にすることはタイマーを作成し、それを呼んで動作させることです。その他処理はすべてOnTimer()で行われます。
プレーヤー作成の最初の問題は開発の初期段階に現れます。それはタイマー作成の問題です。EventSetTimer(timer) 関数により1秒以上の頻度のタイマーが作成可能となります。このバリアントによりティックは毎秒作成されることでしょう。人間の視力の限界をもっても1秒は長すぎます。最低でも100ミリ秒で、というのが必要です。
そのため、タイマー内部にループを実装しました。それは新規「タイマー」イベントの到来前に数ミリ秒抜けます。ただこの実装は不可能な技術的ソリューションを作り出します。たとえば、受信イベントが消える可能性です。なぜならそれらは常にタイマーがサイクルを抜けるのを待っているからです。そしてイベント受信ができないと、プレーヤオブジェクトをインディケータ内にセットしたり、同時に全チャートの並列計算をする機能が消えてしまいます。結果としてのチャート処理を伴うにもかかわらずExpert Advisor はひじょうに速く動作します。
チャートをアクティブ化するイベントはコンポジットクラスCClickと置き換えられます。そのオブジェクトは Click(n) 関数のサイクルで処理されているアクティブなチャートの変更シグナルを作成します。Click() 関数はチャートをアクティブ化するボタンの変更追跡を開始します。ボタンが押されたのを検出すると、それはその他オブジェクトをすべて受け身の状態に切り替えます。チャートをアクティブにするボタンは常にユーザーの近くにありますが、表示されていません。というのもチャートそのものとおなじサイズで背景と同色になっており、背景にあるからです。チャートがアクティブになるとき、ボタンはチャートで見える範囲の背後に移動します。それでプレーヤー制御のグラフィカルオブジェクトが見えるようになります。そのオブジェクトは受け身モードでチャートをアクティブにするボタンによって隠れています。
のちにClick() 関数を用いて主要チャートを検出したら、タイムモーションの計算に移り、アクティブなプレーヤーの Progress(Time) 関数を呼びます。この関数は以下の計算を行います。ユーザーがナビゲーションを行っているか確認する。そうでなければ、次のバーに行くタイミングか確認する。そのタイミングであれば進捗が次のセクションに移動すべきか確認する。
最後に Progress(Time) 関数を抜けると、サイクルはその後の計算に使用される現在時刻に関する情報を取得します。そして、アクティブなチャートの設定はスレーブチャートにコピーされます。それはループ内で CopyPlayer(n) 関数を用いて行われます。その後、Play(Time) 関数でチャートに加えられるべき変更をすべて実行し、ユーザーが、時間経過、クオートの到来、トレーディング実行について考えるようにします。
プレーヤーのコンポジット集約クラス
- CArrayRuler* :現在時間枠のバー間ですばやく移動する情報を格納し検索します。
- CArrayRuler* :ティック生成のためのM1履歴情報を格納し検索します。
- CSpeed :設定スピードとティック生成期間をコントロールします。
- CProgress :すべての進捗ボタンを単一オブジェクトに連携し、ひとつだけボタンが押されたのを見て、ボタンの色を変更します。
- CPlay : プレーヤーの開始と停止を行い、残高インディケータも制御します。
- CClick :チャートをアクティブにするシグナルの役目をします。
- CBackGround :ユーザーによってオブジェクトがゼロバーに隠されます。またチャートが右境界状態有効から移動するとき将来のバーを隠します。
- CBarDraw :チャートのスケールとタイプに応じてゼロバーを描画します。(バー、ろうそく足、線)
- CHistoryDraw:ユーザーに前回取り引きがリアルタイムで変化しているように見せます。
* - クラスはグラフィカルオブジェクトを含みません。
すでにのべたとおり、クラス CChartData および SBase のオブジェクトは集約によってプレーヤー内の全オブジェクトに対するデータの共通フィールドを作成します。CChartData クラスのオブジェクトはチャートについての情報を格納し更新します。またチャート管理も行います。チャート管理とはメインのチャート設定をコピーすることで設定を変更することです。これがチャートの同期方法です。ユーザーはアクティブなチャートの設定を変更することで初期シグナルを作成するだけです。そうするとプレーヤーの複数の関数がその他の同期処理を行います。
それは以下のように行います。
Expert Advisorに記述されるCopyPlayer(n) 関数はループの CPlayer::Copy(CPlayer *base) 関数を呼び、アクティブなチャートのプレーヤーにポインターを渡します。プレーヤーのポインターからのCPlayer::Copy(CPlayer *base) 内部では、アクティブなプレーヤーの CChartData オブジェクトのポインターが渡されます。アクティブなチャートの状態情報はコピーするためスレーブチャートのT CChartData クラスオブジェクトにセットされます。それから必要なチェックがすべて行われ、オブジェクトが必要な状態に切り替わる CPlayer::Update() 関数内で情報が更新されます。
以前ジェネレータの使用可能な期間リスト上に期間を追加する方法をお話すると約束しました。それにはインクルードファイル、"Player5_1.mqh"を開きます。静的配列TFarray[] がファイル冒頭で宣言されます。ここで配列に書かれている列挙に必要な期間を追加する必要があります。また、配列と変数 CountTF のサイズ変更を忘れないようにします。その後Player History Trades exp v5 Expert Advisorをコンパイルします。
残高およびドローダウンチャート
残高インディケータは CPlay クラスのオブジェクトで管理されます。そこには制御メソッドとボタンが含まれます。
インディケータ制御メソッドは以下です。
Ind_Balance_Create(); // add the indicator IndicatorDelete(int ind_total); // delete the indicator EventIndicators(bool &prev_state); // send an event to the indicator StateIndicators(); // state of the indicator, state checks
追加/削除メソッドは name_on_balance ボタンの状態に依存して動作します。それらはMQL5 関数 IndicatorCreate() および ChartIndicatorDelete()を使用します。
インディケータはイベントを受け取り、イベントのコードに応じてインディケータ関数 OnChartEvent() 内の計算を行います。イベントは3タイプに分けられます。
「インディケータの更新」、「トータル残高の計算」、「シンボル残高の計算」です。イベントを送信する際 name_all_balance ボタンの状態に応じてユーザーが計算タイプを制御します。ただし、インディケータコード自体はトレード履歴の解析、ポジション計算、収益の再計算は含みません。インディケータにはそれは必要ないのです。
残高インディケータは履歴データを表示するようにできています。よってインディケータタイプを変更、追加、消去するたびにすべてをやり直すことには意味がないのです。インディケータは M1 時間枠について計算されたデータのバイナリファイルで、チャートの現在時間枠に応じてデータを分割します。
このバイナリファイルはOnInit()で呼ばれるBalance_Process() 関数によって準備されます。トレーディングに使用しないシンボルを追加し、対応するバイナリファイルがない場合、インディケータは両バリアント内のトータル残高履歴を表示します。
それではインディケータに渡されるデータのフォーマットについてお話します。情報を正確に分割するためにはバーの4つのポイント(オープン、ハイ、ロー、クローズ)を知るだけでは十分とは言えません。
それ以外に第一の「ハイまたはロー」を理解する必要があります。情報を復元するためにBalance_Process() 関数はストラテジーテスタの "1 minute OHLC" モードと同じ原理を利用します。バーの終値が始値より低ければ、2番目のポイントが最大でそれ以外は最小である、というものです。
3番目のポイントに対しても同じスキームが使用されます。結果、すべてが合理的で明確なデータフォーマット(オープン、2番目のポイント、3番目のポイント、クローズ)を取得します。このフォーマットはクオートの M1 利益を分割するのに使用されます。結果は解析されたトレード履歴に応じて(同じフォーマットで)残高および資産履歴の計算に利用されます。
おわりに
結論では、この開発はテスターの可視化を進めるものではないということを述べたいと思います。そのように使用することが可能であっても、です。ただし、そこに導入される考えが現実の視覚化に便利なように見えるなら、本望です。プレーヤーはきたるチャンピオンシップへの準備をし、トレード戦略の分析に熱心に取り組んでいるトレーダーや EA プログラマーに役立つように開発されています。
また MQL5 言語はきわめて容量の大きなプロジェクトの実装が可能であるプログラムにとって強力なツールです。まだ本稿をお読みでしたら、「プレーヤー」プロジェクトのコードは8,000行におよぶことにお気づきでしょう。MQL4でそのようなコードを書くことは想像できませんし、問題はプロシージャでそれらすべてを記述することではありません。既製の開発があるなら、プロシージャ形式でそれを手直しすることができます。一からそのようなプロジェクトを開発することはたいへん困難です。
ご健闘をお祈りします!
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/242
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索